Merge remote-tracking branch 'common/android-4.4' into android-4.4.y
Change-Id: Icf907f5067fb6da5935ab0d3271df54b8d5df405
This commit is contained in:
commit
232c28fe23
86 changed files with 4852 additions and 1339 deletions
|
@ -15,6 +15,7 @@ CONFIG_BLK_DEV_RAM=y
|
|||
CONFIG_BLK_DEV_RAM_SIZE=8192
|
||||
CONFIG_CC_STACKPROTECTOR_STRONG=y
|
||||
CONFIG_COMPACTION=y
|
||||
CONFIG_CPU_SW_DOMAIN_PAN=y
|
||||
CONFIG_DEBUG_RODATA=y
|
||||
CONFIG_DM_UEVENT=y
|
||||
CONFIG_DRAGONRISE_FF=y
|
||||
|
|
|
@ -369,7 +369,7 @@ static struct crypto_alg aes_algs[] = { {
|
|||
.cra_blkcipher = {
|
||||
.min_keysize = AES_MIN_KEY_SIZE,
|
||||
.max_keysize = AES_MAX_KEY_SIZE,
|
||||
.ivsize = AES_BLOCK_SIZE,
|
||||
.ivsize = 0,
|
||||
.setkey = ce_aes_setkey,
|
||||
.encrypt = ecb_encrypt,
|
||||
.decrypt = ecb_decrypt,
|
||||
|
@ -446,7 +446,7 @@ static struct crypto_alg aes_algs[] = { {
|
|||
.cra_ablkcipher = {
|
||||
.min_keysize = AES_MIN_KEY_SIZE,
|
||||
.max_keysize = AES_MAX_KEY_SIZE,
|
||||
.ivsize = AES_BLOCK_SIZE,
|
||||
.ivsize = 0,
|
||||
.setkey = ablk_set_key,
|
||||
.encrypt = ablk_encrypt,
|
||||
.decrypt = ablk_decrypt,
|
||||
|
|
|
@ -23,6 +23,11 @@ config CRYPTO_GHASH_ARM64_CE
|
|||
depends on ARM64 && KERNEL_MODE_NEON
|
||||
select CRYPTO_HASH
|
||||
|
||||
config CRYPTO_POLY_HASH_ARM64_CE
|
||||
tristate "poly_hash (for HEH encryption mode) using ARMv8 Crypto Extensions"
|
||||
depends on ARM64 && KERNEL_MODE_NEON
|
||||
select CRYPTO_HASH
|
||||
|
||||
config CRYPTO_AES_ARM64_CE
|
||||
tristate "AES core cipher using ARMv8 Crypto Extensions"
|
||||
depends on ARM64 && KERNEL_MODE_NEON
|
||||
|
|
|
@ -17,6 +17,9 @@ sha2-ce-y := sha2-ce-glue.o sha2-ce-core.o
|
|||
obj-$(CONFIG_CRYPTO_GHASH_ARM64_CE) += ghash-ce.o
|
||||
ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o
|
||||
|
||||
obj-$(CONFIG_CRYPTO_POLY_HASH_ARM64_CE) += poly-hash-ce.o
|
||||
poly-hash-ce-y := poly-hash-ce-glue.o poly-hash-ce-core.o
|
||||
|
||||
obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o
|
||||
CFLAGS_aes-ce-cipher.o += -march=armv8-a+crypto
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@ static struct crypto_alg aes_algs[] = { {
|
|||
.cra_blkcipher = {
|
||||
.min_keysize = AES_MIN_KEY_SIZE,
|
||||
.max_keysize = AES_MAX_KEY_SIZE,
|
||||
.ivsize = AES_BLOCK_SIZE,
|
||||
.ivsize = 0,
|
||||
.setkey = aes_setkey,
|
||||
.encrypt = ecb_encrypt,
|
||||
.decrypt = ecb_decrypt,
|
||||
|
@ -371,7 +371,7 @@ static struct crypto_alg aes_algs[] = { {
|
|||
.cra_ablkcipher = {
|
||||
.min_keysize = AES_MIN_KEY_SIZE,
|
||||
.max_keysize = AES_MAX_KEY_SIZE,
|
||||
.ivsize = AES_BLOCK_SIZE,
|
||||
.ivsize = 0,
|
||||
.setkey = ablk_set_key,
|
||||
.encrypt = ablk_encrypt,
|
||||
.decrypt = ablk_decrypt,
|
||||
|
|
163
arch/arm64/crypto/poly-hash-ce-core.S
Normal file
163
arch/arm64/crypto/poly-hash-ce-core.S
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Accelerated poly_hash implementation with ARMv8 PMULL instructions.
|
||||
*
|
||||
* Based on ghash-ce-core.S.
|
||||
*
|
||||
* Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
|
||||
* Copyright (C) 2017 Google, Inc. <ebiggers@google.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
|
||||
KEY .req v0
|
||||
KEY2 .req v1
|
||||
T1 .req v2
|
||||
T2 .req v3
|
||||
GSTAR .req v4
|
||||
XL .req v5
|
||||
XM .req v6
|
||||
XH .req v7
|
||||
|
||||
.text
|
||||
.arch armv8-a+crypto
|
||||
|
||||
/* 16-byte aligned (2**4 = 16); not required, but might as well */
|
||||
.align 4
|
||||
.Lgstar:
|
||||
.quad 0x87, 0x87
|
||||
|
||||
/*
|
||||
* void pmull_poly_hash_update(le128 *digest, const le128 *key,
|
||||
* const u8 *src, unsigned int blocks,
|
||||
* unsigned int partial);
|
||||
*/
|
||||
ENTRY(pmull_poly_hash_update)
|
||||
|
||||
/* Load digest into XL */
|
||||
ld1 {XL.16b}, [x0]
|
||||
|
||||
/* Load key into KEY */
|
||||
ld1 {KEY.16b}, [x1]
|
||||
|
||||
/* Load g*(x) = g(x) + x^128 = x^7 + x^2 + x + 1 into both halves of
|
||||
* GSTAR */
|
||||
adr x1, .Lgstar
|
||||
ld1 {GSTAR.2d}, [x1]
|
||||
|
||||
/* Set KEY2 to (KEY[1]+KEY[0]):(KEY[1]+KEY[0]). This is needed for
|
||||
* Karatsuba multiplication. */
|
||||
ext KEY2.16b, KEY.16b, KEY.16b, #8
|
||||
eor KEY2.16b, KEY2.16b, KEY.16b
|
||||
|
||||
/* If 'partial' is nonzero, then we're finishing a pending block and
|
||||
* should go right to the multiplication. */
|
||||
cbnz w4, 1f
|
||||
|
||||
0:
|
||||
/* Add the next block from 'src' to the digest */
|
||||
ld1 {T1.16b}, [x2], #16
|
||||
eor XL.16b, XL.16b, T1.16b
|
||||
sub w3, w3, #1
|
||||
|
||||
1:
|
||||
/*
|
||||
* Multiply the current 128-bit digest (a1:a0, in XL) by the 128-bit key
|
||||
* (b1:b0, in KEY) using Karatsuba multiplication.
|
||||
*/
|
||||
|
||||
/* T1 = (a1+a0):(a1+a0) */
|
||||
ext T1.16b, XL.16b, XL.16b, #8
|
||||
eor T1.16b, T1.16b, XL.16b
|
||||
|
||||
/* XH = a1 * b1 */
|
||||
pmull2 XH.1q, XL.2d, KEY.2d
|
||||
|
||||
/* XL = a0 * b0 */
|
||||
pmull XL.1q, XL.1d, KEY.1d
|
||||
|
||||
/* XM = (a1+a0) * (b1+b0) */
|
||||
pmull XM.1q, T1.1d, KEY2.1d
|
||||
|
||||
/* XM += (XH[0]:XL[1]) + XL + XH */
|
||||
ext T1.16b, XL.16b, XH.16b, #8
|
||||
eor T2.16b, XL.16b, XH.16b
|
||||
eor XM.16b, XM.16b, T1.16b
|
||||
eor XM.16b, XM.16b, T2.16b
|
||||
|
||||
/*
|
||||
* Now the 256-bit product is in XH[1]:XM:XL[0]. It represents a
|
||||
* polynomial over GF(2) with degree as large as 255. We need to
|
||||
* compute its remainder modulo g(x) = x^128+x^7+x^2+x+1. For this it
|
||||
* is sufficient to compute the remainder of the high half 'c(x)x^128'
|
||||
* add it to the low half. To reduce the high half we use the Barrett
|
||||
* reduction method. The basic idea is that we can express the
|
||||
* remainder p(x) as g(x)q(x) mod x^128, where q(x) = (c(x)x^128)/g(x).
|
||||
* As detailed in [1], to avoid having to divide by g(x) at runtime the
|
||||
* following equivalent expression can be derived:
|
||||
*
|
||||
* p(x) = [ g*(x)((c(x)q+(x))/x^128) ] mod x^128
|
||||
*
|
||||
* where g*(x) = x^128+g(x) = x^7+x^2+x+1, and q+(x) = x^256/g(x) = g(x)
|
||||
* in this case. This is also equivalent to:
|
||||
*
|
||||
* p(x) = [ g*(x)((c(x)(x^128 + g*(x)))/x^128) ] mod x^128
|
||||
* = [ g*(x)(c(x) + (c(x)g*(x))/x^128) ] mod x^128
|
||||
*
|
||||
* Since deg g*(x) < 64:
|
||||
*
|
||||
* p(x) = [ g*(x)(c(x) + ((c(x)/x^64)g*(x))/x^64) ] mod x^128
|
||||
* = [ g*(x)((c(x)/x^64)x^64 + (c(x) mod x^64) +
|
||||
* ((c(x)/x^64)g*(x))/x^64) ] mod x^128
|
||||
*
|
||||
* Letting t(x) = g*(x)(c(x)/x^64):
|
||||
*
|
||||
* p(x) = [ t(x)x^64 + g*(x)((c(x) mod x^64) + t(x)/x^64) ] mod x^128
|
||||
*
|
||||
* Therefore, to do the reduction we only need to issue two 64-bit =>
|
||||
* 128-bit carryless multiplications: g*(x) times c(x)/x^64, and g*(x)
|
||||
* times ((c(x) mod x^64) + t(x)/x^64). (Multiplication by x^64 doesn't
|
||||
* count since it is simply a shift or move.)
|
||||
*
|
||||
* An alternate reduction method, also based on Barrett reduction and
|
||||
* described in [1], uses only shifts and XORs --- no multiplications.
|
||||
* However, the method with multiplications requires fewer instructions
|
||||
* and is faster on processors with fast carryless multiplication.
|
||||
*
|
||||
* [1] "Intel Carry-Less Multiplication Instruction and its Usage for
|
||||
* Computing the GCM Mode",
|
||||
* https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf
|
||||
*/
|
||||
|
||||
/* 256-bit product is XH[1]:XM:XL[0], so c(x) is XH[1]:XM[1] */
|
||||
|
||||
/* T1 = t(x) = g*(x)(c(x)/x^64) */
|
||||
pmull2 T1.1q, GSTAR.2d, XH.2d
|
||||
|
||||
/* T2 = g*(x)((c(x) mod x^64) + t(x)/x^64) */
|
||||
eor T2.16b, XM.16b, T1.16b
|
||||
pmull2 T2.1q, GSTAR.2d, T2.2d
|
||||
|
||||
/* Make XL[0] be the low half of the 128-bit result by adding the low 64
|
||||
* bits of the T2 term to what was already there. The 't(x)x^64' term
|
||||
* makes no difference, so skip it. */
|
||||
eor XL.16b, XL.16b, T2.16b
|
||||
|
||||
/* Make XL[1] be the high half of the 128-bit result by adding the high
|
||||
* 64 bits of the 't(x)x^64' and T2 terms to what was already in XM[0],
|
||||
* then moving XM[0] to XL[1]. */
|
||||
eor XM.16b, XM.16b, T1.16b
|
||||
ext T2.16b, T2.16b, T2.16b, #8
|
||||
eor XM.16b, XM.16b, T2.16b
|
||||
mov XL.d[1], XM.d[0]
|
||||
|
||||
/* If more blocks remain, then loop back to process the next block;
|
||||
* else, store the digest and return. */
|
||||
cbnz w3, 0b
|
||||
st1 {XL.16b}, [x0]
|
||||
ret
|
||||
ENDPROC(pmull_poly_hash_update)
|
166
arch/arm64/crypto/poly-hash-ce-glue.c
Normal file
166
arch/arm64/crypto/poly-hash-ce-glue.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Accelerated poly_hash implementation with ARMv8 PMULL instructions.
|
||||
*
|
||||
* Based on ghash-ce-glue.c.
|
||||
*
|
||||
* poly_hash is part of the HEH (Hash-Encrypt-Hash) encryption mode, proposed in
|
||||
* Internet Draft https://tools.ietf.org/html/draft-cope-heh-01.
|
||||
*
|
||||
* poly_hash is very similar to GHASH: both algorithms are keyed hashes which
|
||||
* interpret their input data as coefficients of a polynomial over GF(2^128),
|
||||
* then calculate a hash value by evaluating that polynomial at the point given
|
||||
* by the key, e.g. using Horner's rule. The difference is that poly_hash uses
|
||||
* the more natural "ble" convention to represent GF(2^128) elements, whereas
|
||||
* GHASH uses the less natural "lle" convention (see include/crypto/gf128mul.h).
|
||||
* The ble convention makes it simpler to implement GF(2^128) multiplication.
|
||||
*
|
||||
* Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
|
||||
* Copyright (C) 2017 Google Inc. <ebiggers@google.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <asm/neon.h>
|
||||
#include <crypto/b128ops.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <linux/cpufeature.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/*
|
||||
* Note: in this algorithm we currently use 'le128' to represent GF(2^128)
|
||||
* elements, even though poly_hash-generic uses 'be128'. Both types are
|
||||
* actually "wrong" because the elements are actually in 'ble' format, and there
|
||||
* should be a ble type to represent this --- as well as lle, bbe, and lbe types
|
||||
* for the other conventions for representing GF(2^128) elements. But
|
||||
* practically it doesn't matter which type we choose here, so we just use le128
|
||||
* since it's arguably more accurate, while poly_hash-generic still has to use
|
||||
* be128 because the generic GF(2^128) multiplication functions all take be128.
|
||||
*/
|
||||
|
||||
struct poly_hash_desc_ctx {
|
||||
le128 digest;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
asmlinkage void pmull_poly_hash_update(le128 *digest, const le128 *key,
|
||||
const u8 *src, unsigned int blocks,
|
||||
unsigned int partial);
|
||||
|
||||
static int poly_hash_setkey(struct crypto_shash *tfm,
|
||||
const u8 *key, unsigned int keylen)
|
||||
{
|
||||
if (keylen != sizeof(le128)) {
|
||||
crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(crypto_shash_ctx(tfm), key, sizeof(le128));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int poly_hash_init(struct shash_desc *desc)
|
||||
{
|
||||
struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
|
||||
|
||||
ctx->digest = (le128) { 0 };
|
||||
ctx->count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int poly_hash_update(struct shash_desc *desc, const u8 *src,
|
||||
unsigned int len)
|
||||
{
|
||||
struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
|
||||
unsigned int partial = ctx->count % sizeof(le128);
|
||||
u8 *dst = (u8 *)&ctx->digest + partial;
|
||||
|
||||
ctx->count += len;
|
||||
|
||||
/* Finishing at least one block? */
|
||||
if (partial + len >= sizeof(le128)) {
|
||||
const le128 *key = crypto_shash_ctx(desc->tfm);
|
||||
|
||||
if (partial) {
|
||||
/* Finish the pending block. */
|
||||
unsigned int n = sizeof(le128) - partial;
|
||||
|
||||
len -= n;
|
||||
do {
|
||||
*dst++ ^= *src++;
|
||||
} while (--n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the real work. If 'partial' is nonzero, this starts by
|
||||
* multiplying 'digest' by 'key'. Then for each additional full
|
||||
* block it adds the block to 'digest' and multiplies by 'key'.
|
||||
*/
|
||||
kernel_neon_begin_partial(8);
|
||||
pmull_poly_hash_update(&ctx->digest, key, src,
|
||||
len / sizeof(le128), partial);
|
||||
kernel_neon_end();
|
||||
|
||||
src += len - (len % sizeof(le128));
|
||||
len %= sizeof(le128);
|
||||
dst = (u8 *)&ctx->digest;
|
||||
}
|
||||
|
||||
/* Continue adding the next block to 'digest'. */
|
||||
while (len--)
|
||||
*dst++ ^= *src++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int poly_hash_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc);
|
||||
unsigned int partial = ctx->count % sizeof(le128);
|
||||
|
||||
/* Finish the last block if needed. */
|
||||
if (partial) {
|
||||
const le128 *key = crypto_shash_ctx(desc->tfm);
|
||||
|
||||
kernel_neon_begin_partial(8);
|
||||
pmull_poly_hash_update(&ctx->digest, key, NULL, 0, partial);
|
||||
kernel_neon_end();
|
||||
}
|
||||
|
||||
memcpy(out, &ctx->digest, sizeof(le128));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct shash_alg poly_hash_alg = {
|
||||
.digestsize = sizeof(le128),
|
||||
.init = poly_hash_init,
|
||||
.update = poly_hash_update,
|
||||
.final = poly_hash_final,
|
||||
.setkey = poly_hash_setkey,
|
||||
.descsize = sizeof(struct poly_hash_desc_ctx),
|
||||
.base = {
|
||||
.cra_name = "poly_hash",
|
||||
.cra_driver_name = "poly_hash-ce",
|
||||
.cra_priority = 300,
|
||||
.cra_ctxsize = sizeof(le128),
|
||||
.cra_module = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init poly_hash_ce_mod_init(void)
|
||||
{
|
||||
return crypto_register_shash(&poly_hash_alg);
|
||||
}
|
||||
|
||||
static void __exit poly_hash_ce_mod_exit(void)
|
||||
{
|
||||
crypto_unregister_shash(&poly_hash_alg);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("Polynomial evaluation hash using ARMv8 Crypto Extensions");
|
||||
MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
module_cpu_feature_match(PMULL, poly_hash_ce_mod_init);
|
||||
module_exit(poly_hash_ce_mod_exit);
|
|
@ -68,7 +68,11 @@ static inline void decode_ctrl_reg(u32 reg,
|
|||
/* Lengths */
|
||||
#define ARM_BREAKPOINT_LEN_1 0x1
|
||||
#define ARM_BREAKPOINT_LEN_2 0x3
|
||||
#define ARM_BREAKPOINT_LEN_3 0x7
|
||||
#define ARM_BREAKPOINT_LEN_4 0xf
|
||||
#define ARM_BREAKPOINT_LEN_5 0x1f
|
||||
#define ARM_BREAKPOINT_LEN_6 0x3f
|
||||
#define ARM_BREAKPOINT_LEN_7 0x7f
|
||||
#define ARM_BREAKPOINT_LEN_8 0xff
|
||||
|
||||
/* Kernel stepping */
|
||||
|
@ -110,7 +114,7 @@ struct perf_event;
|
|||
struct pmu;
|
||||
|
||||
extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
|
||||
int *gen_len, int *gen_type);
|
||||
int *gen_len, int *gen_type, int *offset);
|
||||
extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
|
||||
extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
|
||||
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
|
||||
|
|
|
@ -313,9 +313,21 @@ static int get_hbp_len(u8 hbp_len)
|
|||
case ARM_BREAKPOINT_LEN_2:
|
||||
len_in_bytes = 2;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_3:
|
||||
len_in_bytes = 3;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_4:
|
||||
len_in_bytes = 4;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_5:
|
||||
len_in_bytes = 5;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_6:
|
||||
len_in_bytes = 6;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_7:
|
||||
len_in_bytes = 7;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_8:
|
||||
len_in_bytes = 8;
|
||||
break;
|
||||
|
@ -345,7 +357,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp)
|
|||
* to generic breakpoint descriptions.
|
||||
*/
|
||||
int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
|
||||
int *gen_len, int *gen_type)
|
||||
int *gen_len, int *gen_type, int *offset)
|
||||
{
|
||||
/* Type */
|
||||
switch (ctrl.type) {
|
||||
|
@ -365,17 +377,33 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ctrl.len)
|
||||
return -EINVAL;
|
||||
*offset = __ffs(ctrl.len);
|
||||
|
||||
/* Len */
|
||||
switch (ctrl.len) {
|
||||
switch (ctrl.len >> *offset) {
|
||||
case ARM_BREAKPOINT_LEN_1:
|
||||
*gen_len = HW_BREAKPOINT_LEN_1;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_2:
|
||||
*gen_len = HW_BREAKPOINT_LEN_2;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_3:
|
||||
*gen_len = HW_BREAKPOINT_LEN_3;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_4:
|
||||
*gen_len = HW_BREAKPOINT_LEN_4;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_5:
|
||||
*gen_len = HW_BREAKPOINT_LEN_5;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_6:
|
||||
*gen_len = HW_BREAKPOINT_LEN_6;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_7:
|
||||
*gen_len = HW_BREAKPOINT_LEN_7;
|
||||
break;
|
||||
case ARM_BREAKPOINT_LEN_8:
|
||||
*gen_len = HW_BREAKPOINT_LEN_8;
|
||||
break;
|
||||
|
@ -419,9 +447,21 @@ static int arch_build_bp_info(struct perf_event *bp)
|
|||
case HW_BREAKPOINT_LEN_2:
|
||||
info->ctrl.len = ARM_BREAKPOINT_LEN_2;
|
||||
break;
|
||||
case HW_BREAKPOINT_LEN_3:
|
||||
info->ctrl.len = ARM_BREAKPOINT_LEN_3;
|
||||
break;
|
||||
case HW_BREAKPOINT_LEN_4:
|
||||
info->ctrl.len = ARM_BREAKPOINT_LEN_4;
|
||||
break;
|
||||
case HW_BREAKPOINT_LEN_5:
|
||||
info->ctrl.len = ARM_BREAKPOINT_LEN_5;
|
||||
break;
|
||||
case HW_BREAKPOINT_LEN_6:
|
||||
info->ctrl.len = ARM_BREAKPOINT_LEN_6;
|
||||
break;
|
||||
case HW_BREAKPOINT_LEN_7:
|
||||
info->ctrl.len = ARM_BREAKPOINT_LEN_7;
|
||||
break;
|
||||
case HW_BREAKPOINT_LEN_8:
|
||||
info->ctrl.len = ARM_BREAKPOINT_LEN_8;
|
||||
break;
|
||||
|
@ -513,18 +553,17 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info->address &= ~alignment_mask;
|
||||
info->ctrl.len <<= offset;
|
||||
} else {
|
||||
if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE)
|
||||
alignment_mask = 0x3;
|
||||
else
|
||||
alignment_mask = 0x7;
|
||||
if (info->address & alignment_mask)
|
||||
return -EINVAL;
|
||||
offset = info->address & alignment_mask;
|
||||
}
|
||||
|
||||
info->address &= ~alignment_mask;
|
||||
info->ctrl.len <<= offset;
|
||||
|
||||
/*
|
||||
* Disallow per-task kernel breakpoints since these would
|
||||
* complicate the stepping code.
|
||||
|
@ -655,12 +694,47 @@ unlock:
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arm64 hardware does not always report a watchpoint hit address that matches
|
||||
* one of the watchpoints set. It can also report an address "near" the
|
||||
* watchpoint if a single instruction access both watched and unwatched
|
||||
* addresses. There is no straight-forward way, short of disassembling the
|
||||
* offending instruction, to map that address back to the watchpoint. This
|
||||
* function computes the distance of the memory access from the watchpoint as a
|
||||
* heuristic for the likelyhood that a given access triggered the watchpoint.
|
||||
*
|
||||
* See Section D2.10.5 "Determining the memory location that caused a Watchpoint
|
||||
* exception" of ARMv8 Architecture Reference Manual for details.
|
||||
*
|
||||
* The function returns the distance of the address from the bytes watched by
|
||||
* the watchpoint. In case of an exact match, it returns 0.
|
||||
*/
|
||||
static u64 get_distance_from_watchpoint(unsigned long addr, u64 val,
|
||||
struct arch_hw_breakpoint_ctrl *ctrl)
|
||||
{
|
||||
u64 wp_low, wp_high;
|
||||
u32 lens, lene;
|
||||
|
||||
lens = __ffs(ctrl->len);
|
||||
lene = __fls(ctrl->len);
|
||||
|
||||
wp_low = val + lens;
|
||||
wp_high = val + lene;
|
||||
if (addr < wp_low)
|
||||
return wp_low - addr;
|
||||
else if (addr > wp_high)
|
||||
return addr - wp_high;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int watchpoint_handler(unsigned long addr, unsigned int esr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int i, step = 0, *kernel_step, access;
|
||||
int i, step = 0, *kernel_step, access, closest_match = 0;
|
||||
u64 min_dist = -1, dist;
|
||||
u32 ctrl_reg;
|
||||
u64 val, alignment_mask;
|
||||
u64 val;
|
||||
struct perf_event *wp, **slots;
|
||||
struct debug_info *debug_info;
|
||||
struct arch_hw_breakpoint *info;
|
||||
|
@ -669,35 +743,15 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
|
|||
slots = this_cpu_ptr(wp_on_reg);
|
||||
debug_info = ¤t->thread.debug;
|
||||
|
||||
/*
|
||||
* Find all watchpoints that match the reported address. If no exact
|
||||
* match is found. Attribute the hit to the closest watchpoint.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < core_num_wrps; ++i) {
|
||||
rcu_read_lock();
|
||||
|
||||
wp = slots[i];
|
||||
|
||||
if (wp == NULL)
|
||||
goto unlock;
|
||||
|
||||
info = counter_arch_bp(wp);
|
||||
/* AArch32 watchpoints are either 4 or 8 bytes aligned. */
|
||||
if (is_compat_task()) {
|
||||
if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
|
||||
alignment_mask = 0x7;
|
||||
else
|
||||
alignment_mask = 0x3;
|
||||
} else {
|
||||
alignment_mask = 0x7;
|
||||
}
|
||||
|
||||
/* Check if the watchpoint value matches. */
|
||||
val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
|
||||
if (val != (addr & ~alignment_mask))
|
||||
goto unlock;
|
||||
|
||||
/* Possible match, check the byte address select to confirm. */
|
||||
ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
|
||||
decode_ctrl_reg(ctrl_reg, &ctrl);
|
||||
if (!((1 << (addr & alignment_mask)) & ctrl.len))
|
||||
goto unlock;
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Check that the access type matches.
|
||||
|
@ -706,18 +760,41 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
|
|||
access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W :
|
||||
HW_BREAKPOINT_R;
|
||||
if (!(access & hw_breakpoint_type(wp)))
|
||||
goto unlock;
|
||||
continue;
|
||||
|
||||
/* Check if the watchpoint value and byte select match. */
|
||||
val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
|
||||
ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
|
||||
decode_ctrl_reg(ctrl_reg, &ctrl);
|
||||
dist = get_distance_from_watchpoint(addr, val, &ctrl);
|
||||
if (dist < min_dist) {
|
||||
min_dist = dist;
|
||||
closest_match = i;
|
||||
}
|
||||
/* Is this an exact match? */
|
||||
if (dist != 0)
|
||||
continue;
|
||||
|
||||
info = counter_arch_bp(wp);
|
||||
info->trigger = addr;
|
||||
perf_bp_event(wp, regs);
|
||||
|
||||
/* Do we need to handle the stepping? */
|
||||
if (!wp->overflow_handler)
|
||||
step = 1;
|
||||
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
if (min_dist > 0 && min_dist != -1) {
|
||||
/* No exact match found. */
|
||||
wp = slots[closest_match];
|
||||
info = counter_arch_bp(wp);
|
||||
info->trigger = addr;
|
||||
perf_bp_event(wp, regs);
|
||||
|
||||
/* Do we need to handle the stepping? */
|
||||
if (!wp->overflow_handler)
|
||||
step = 1;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!step)
|
||||
return 0;
|
||||
|
|
|
@ -227,13 +227,13 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
|
|||
struct arch_hw_breakpoint_ctrl ctrl,
|
||||
struct perf_event_attr *attr)
|
||||
{
|
||||
int err, len, type, disabled = !ctrl.enabled;
|
||||
int err, len, type, offset, disabled = !ctrl.enabled;
|
||||
|
||||
attr->disabled = disabled;
|
||||
if (disabled)
|
||||
return 0;
|
||||
|
||||
err = arch_bp_generic_fields(ctrl, &len, &type);
|
||||
err = arch_bp_generic_fields(ctrl, &len, &type, &offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -252,6 +252,7 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
|
|||
|
||||
attr->bp_len = len;
|
||||
attr->bp_type = type;
|
||||
attr->bp_addr += offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -304,7 +305,7 @@ static int ptrace_hbp_get_addr(unsigned int note_type,
|
|||
if (IS_ERR(bp))
|
||||
return PTR_ERR(bp);
|
||||
|
||||
*addr = bp ? bp->attr.bp_addr : 0;
|
||||
*addr = bp ? counter_arch_bp(bp)->address : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -289,6 +289,24 @@ config CRYPTO_CBC
|
|||
CBC: Cipher Block Chaining mode
|
||||
This block cipher algorithm is required for IPSec.
|
||||
|
||||
config CRYPTO_HEH
|
||||
tristate "HEH support"
|
||||
select CRYPTO_CMAC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_GF128MUL
|
||||
select CRYPTO_MANAGER
|
||||
select CRYPTO_POLY_HASH_ARM64_CE if ARM64 && KERNEL_MODE_NEON
|
||||
help
|
||||
HEH: Hash-Encrypt-Hash mode
|
||||
HEH is a proposed block cipher mode of operation which extends the
|
||||
strong pseudo-random permutation (SPRP) property of block ciphers to
|
||||
arbitrary-length input strings. This provides a stronger notion of
|
||||
security than existing block cipher modes of operation (e.g. CBC, CTR,
|
||||
XTS), though it is usually less performant. Applications include disk
|
||||
encryption and encryption of file names and contents. Currently, this
|
||||
implementation only provides a symmetric cipher interface, so it can't
|
||||
yet be used as an AEAD.
|
||||
|
||||
config CRYPTO_CTR
|
||||
tristate "CTR support"
|
||||
select CRYPTO_BLKCIPHER
|
||||
|
|
|
@ -66,6 +66,7 @@ obj-$(CONFIG_CRYPTO_TGR192) += tgr192.o
|
|||
obj-$(CONFIG_CRYPTO_GF128MUL) += gf128mul.o
|
||||
obj-$(CONFIG_CRYPTO_ECB) += ecb.o
|
||||
obj-$(CONFIG_CRYPTO_CBC) += cbc.o
|
||||
obj-$(CONFIG_CRYPTO_HEH) += heh.o
|
||||
obj-$(CONFIG_CRYPTO_PCBC) += pcbc.o
|
||||
obj-$(CONFIG_CRYPTO_CTS) += cts.o
|
||||
obj-$(CONFIG_CRYPTO_LRW) += lrw.o
|
||||
|
|
|
@ -373,6 +373,27 @@ int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(blkcipher_aead_walk_virt_block);
|
||||
|
||||
/*
|
||||
* This function allows ablkcipher algorithms to use the blkcipher_walk API to
|
||||
* walk over their data. The specified crypto_ablkcipher tfm is used to
|
||||
* initialize the struct blkcipher_walk, and the crypto_blkcipher specified in
|
||||
* desc->tfm is never used so it can be left NULL. (Yes, this design is ugly,
|
||||
* but it parallels blkcipher_aead_walk_virt_block() above. In the 4.10 kernel
|
||||
* this is starting to be cleaned up...)
|
||||
*/
|
||||
int blkcipher_ablkcipher_walk_virt(struct blkcipher_desc *desc,
|
||||
struct blkcipher_walk *walk,
|
||||
struct crypto_ablkcipher *tfm)
|
||||
{
|
||||
walk->flags &= ~BLKCIPHER_WALK_PHYS;
|
||||
walk->walk_blocksize = crypto_ablkcipher_blocksize(tfm);
|
||||
walk->cipher_blocksize = walk->walk_blocksize;
|
||||
walk->ivsize = crypto_ablkcipher_ivsize(tfm);
|
||||
walk->alignmask = crypto_ablkcipher_alignmask(tfm);
|
||||
return blkcipher_walk_first(desc, walk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blkcipher_ablkcipher_walk_virt);
|
||||
|
||||
static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key,
|
||||
unsigned int keylen)
|
||||
{
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
---------------------------------------------------------------------------
|
||||
Issue 31/01/2006
|
||||
|
||||
This file provides fast multiplication in GF(128) as required by several
|
||||
This file provides fast multiplication in GF(2^128) as required by several
|
||||
cryptographic authentication modes
|
||||
*/
|
||||
|
||||
|
@ -88,37 +88,52 @@
|
|||
q(0xf8), q(0xf9), q(0xfa), q(0xfb), q(0xfc), q(0xfd), q(0xfe), q(0xff) \
|
||||
}
|
||||
|
||||
/* Given the value i in 0..255 as the byte overflow when a field element
|
||||
in GHASH is multiplied by x^8, this function will return the values that
|
||||
are generated in the lo 16-bit word of the field value by applying the
|
||||
modular polynomial. The values lo_byte and hi_byte are returned via the
|
||||
macro xp_fun(lo_byte, hi_byte) so that the values can be assembled into
|
||||
memory as required by a suitable definition of this macro operating on
|
||||
the table above
|
||||
*/
|
||||
/*
|
||||
* Given a value i in 0..255 as the byte overflow when a field element
|
||||
* in GF(2^128) is multiplied by x^8, the following macro returns the
|
||||
* 16-bit value that must be XOR-ed into the low-degree end of the
|
||||
* product to reduce it modulo the irreducible polynomial x^128 + x^7 +
|
||||
* x^2 + x + 1.
|
||||
*
|
||||
* There are two versions of the macro, and hence two tables: one for
|
||||
* the "be" convention where the highest-order bit is the coefficient of
|
||||
* the highest-degree polynomial term, and one for the "le" convention
|
||||
* where the highest-order bit is the coefficient of the lowest-degree
|
||||
* polynomial term. In both cases the values are stored in CPU byte
|
||||
* endianness such that the coefficients are ordered consistently across
|
||||
* bytes, i.e. in the "be" table bits 15..0 of the stored value
|
||||
* correspond to the coefficients of x^15..x^0, and in the "le" table
|
||||
* bits 15..0 correspond to the coefficients of x^0..x^15.
|
||||
*
|
||||
* Therefore, provided that the appropriate byte endianness conversions
|
||||
* are done by the multiplication functions (and these must be in place
|
||||
* anyway to support both little endian and big endian CPUs), the "be"
|
||||
* table can be used for multiplications of both "bbe" and "ble"
|
||||
* elements, and the "le" table can be used for multiplications of both
|
||||
* "lle" and "lbe" elements.
|
||||
*/
|
||||
|
||||
#define xx(p, q) 0x##p##q
|
||||
|
||||
#define xda_bbe(i) ( \
|
||||
(i & 0x80 ? xx(43, 80) : 0) ^ (i & 0x40 ? xx(21, c0) : 0) ^ \
|
||||
(i & 0x20 ? xx(10, e0) : 0) ^ (i & 0x10 ? xx(08, 70) : 0) ^ \
|
||||
(i & 0x08 ? xx(04, 38) : 0) ^ (i & 0x04 ? xx(02, 1c) : 0) ^ \
|
||||
(i & 0x02 ? xx(01, 0e) : 0) ^ (i & 0x01 ? xx(00, 87) : 0) \
|
||||
#define xda_be(i) ( \
|
||||
(i & 0x80 ? 0x4380 : 0) ^ (i & 0x40 ? 0x21c0 : 0) ^ \
|
||||
(i & 0x20 ? 0x10e0 : 0) ^ (i & 0x10 ? 0x0870 : 0) ^ \
|
||||
(i & 0x08 ? 0x0438 : 0) ^ (i & 0x04 ? 0x021c : 0) ^ \
|
||||
(i & 0x02 ? 0x010e : 0) ^ (i & 0x01 ? 0x0087 : 0) \
|
||||
)
|
||||
|
||||
#define xda_lle(i) ( \
|
||||
(i & 0x80 ? xx(e1, 00) : 0) ^ (i & 0x40 ? xx(70, 80) : 0) ^ \
|
||||
(i & 0x20 ? xx(38, 40) : 0) ^ (i & 0x10 ? xx(1c, 20) : 0) ^ \
|
||||
(i & 0x08 ? xx(0e, 10) : 0) ^ (i & 0x04 ? xx(07, 08) : 0) ^ \
|
||||
(i & 0x02 ? xx(03, 84) : 0) ^ (i & 0x01 ? xx(01, c2) : 0) \
|
||||
#define xda_le(i) ( \
|
||||
(i & 0x80 ? 0xe100 : 0) ^ (i & 0x40 ? 0x7080 : 0) ^ \
|
||||
(i & 0x20 ? 0x3840 : 0) ^ (i & 0x10 ? 0x1c20 : 0) ^ \
|
||||
(i & 0x08 ? 0x0e10 : 0) ^ (i & 0x04 ? 0x0708 : 0) ^ \
|
||||
(i & 0x02 ? 0x0384 : 0) ^ (i & 0x01 ? 0x01c2 : 0) \
|
||||
)
|
||||
|
||||
static const u16 gf128mul_table_lle[256] = gf128mul_dat(xda_lle);
|
||||
static const u16 gf128mul_table_bbe[256] = gf128mul_dat(xda_bbe);
|
||||
static const u16 gf128mul_table_le[256] = gf128mul_dat(xda_le);
|
||||
static const u16 gf128mul_table_be[256] = gf128mul_dat(xda_be);
|
||||
|
||||
/* These functions multiply a field element by x, by x^4 and by x^8
|
||||
* in the polynomial field representation. It uses 32-bit word operations
|
||||
* to gain speed but compensates for machine endianess and hence works
|
||||
/*
|
||||
* The following functions multiply a field element by x or by x^8 in
|
||||
* the polynomial field representation. They use 64-bit word operations
|
||||
* to gain speed but compensate for machine endianness and hence work
|
||||
* correctly on both styles of machine.
|
||||
*/
|
||||
|
||||
|
@ -126,7 +141,7 @@ static void gf128mul_x_lle(be128 *r, const be128 *x)
|
|||
{
|
||||
u64 a = be64_to_cpu(x->a);
|
||||
u64 b = be64_to_cpu(x->b);
|
||||
u64 _tt = gf128mul_table_lle[(b << 7) & 0xff];
|
||||
u64 _tt = gf128mul_table_le[(b << 7) & 0xff];
|
||||
|
||||
r->b = cpu_to_be64((b >> 1) | (a << 63));
|
||||
r->a = cpu_to_be64((a >> 1) ^ (_tt << 48));
|
||||
|
@ -136,7 +151,7 @@ static void gf128mul_x_bbe(be128 *r, const be128 *x)
|
|||
{
|
||||
u64 a = be64_to_cpu(x->a);
|
||||
u64 b = be64_to_cpu(x->b);
|
||||
u64 _tt = gf128mul_table_bbe[a >> 63];
|
||||
u64 _tt = gf128mul_table_be[a >> 63];
|
||||
|
||||
r->a = cpu_to_be64((a << 1) | (b >> 63));
|
||||
r->b = cpu_to_be64((b << 1) ^ _tt);
|
||||
|
@ -146,7 +161,7 @@ void gf128mul_x_ble(be128 *r, const be128 *x)
|
|||
{
|
||||
u64 a = le64_to_cpu(x->a);
|
||||
u64 b = le64_to_cpu(x->b);
|
||||
u64 _tt = gf128mul_table_bbe[b >> 63];
|
||||
u64 _tt = gf128mul_table_be[b >> 63];
|
||||
|
||||
r->a = cpu_to_le64((a << 1) ^ _tt);
|
||||
r->b = cpu_to_le64((b << 1) | (a >> 63));
|
||||
|
@ -157,7 +172,7 @@ static void gf128mul_x8_lle(be128 *x)
|
|||
{
|
||||
u64 a = be64_to_cpu(x->a);
|
||||
u64 b = be64_to_cpu(x->b);
|
||||
u64 _tt = gf128mul_table_lle[b & 0xff];
|
||||
u64 _tt = gf128mul_table_le[b & 0xff];
|
||||
|
||||
x->b = cpu_to_be64((b >> 8) | (a << 56));
|
||||
x->a = cpu_to_be64((a >> 8) ^ (_tt << 48));
|
||||
|
@ -167,12 +182,22 @@ static void gf128mul_x8_bbe(be128 *x)
|
|||
{
|
||||
u64 a = be64_to_cpu(x->a);
|
||||
u64 b = be64_to_cpu(x->b);
|
||||
u64 _tt = gf128mul_table_bbe[a >> 56];
|
||||
u64 _tt = gf128mul_table_be[a >> 56];
|
||||
|
||||
x->a = cpu_to_be64((a << 8) | (b >> 56));
|
||||
x->b = cpu_to_be64((b << 8) ^ _tt);
|
||||
}
|
||||
|
||||
static void gf128mul_x8_ble(be128 *x)
|
||||
{
|
||||
u64 a = le64_to_cpu(x->b);
|
||||
u64 b = le64_to_cpu(x->a);
|
||||
u64 _tt = gf128mul_table_be[a >> 56];
|
||||
|
||||
x->b = cpu_to_le64((a << 8) | (b >> 56));
|
||||
x->a = cpu_to_le64((b << 8) ^ _tt);
|
||||
}
|
||||
|
||||
void gf128mul_lle(be128 *r, const be128 *b)
|
||||
{
|
||||
be128 p[8];
|
||||
|
@ -249,9 +274,48 @@ void gf128mul_bbe(be128 *r, const be128 *b)
|
|||
}
|
||||
EXPORT_SYMBOL(gf128mul_bbe);
|
||||
|
||||
void gf128mul_ble(be128 *r, const be128 *b)
|
||||
{
|
||||
be128 p[8];
|
||||
int i;
|
||||
|
||||
p[0] = *r;
|
||||
for (i = 0; i < 7; ++i)
|
||||
gf128mul_x_ble((be128 *)&p[i + 1], (be128 *)&p[i]);
|
||||
|
||||
memset(r, 0, sizeof(*r));
|
||||
for (i = 0;;) {
|
||||
u8 ch = ((u8 *)b)[15 - i];
|
||||
|
||||
if (ch & 0x80)
|
||||
be128_xor(r, r, &p[7]);
|
||||
if (ch & 0x40)
|
||||
be128_xor(r, r, &p[6]);
|
||||
if (ch & 0x20)
|
||||
be128_xor(r, r, &p[5]);
|
||||
if (ch & 0x10)
|
||||
be128_xor(r, r, &p[4]);
|
||||
if (ch & 0x08)
|
||||
be128_xor(r, r, &p[3]);
|
||||
if (ch & 0x04)
|
||||
be128_xor(r, r, &p[2]);
|
||||
if (ch & 0x02)
|
||||
be128_xor(r, r, &p[1]);
|
||||
if (ch & 0x01)
|
||||
be128_xor(r, r, &p[0]);
|
||||
|
||||
if (++i >= 16)
|
||||
break;
|
||||
|
||||
gf128mul_x8_ble(r);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(gf128mul_ble);
|
||||
|
||||
|
||||
/* This version uses 64k bytes of table space.
|
||||
A 16 byte buffer has to be multiplied by a 16 byte key
|
||||
value in GF(128). If we consider a GF(128) value in
|
||||
value in GF(2^128). If we consider a GF(2^128) value in
|
||||
the buffer's lowest byte, we can construct a table of
|
||||
the 256 16 byte values that result from the 256 values
|
||||
of this byte. This requires 4096 bytes. But we also
|
||||
|
@ -352,8 +416,8 @@ void gf128mul_free_64k(struct gf128mul_64k *t)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
kfree(t->t[i]);
|
||||
kfree(t);
|
||||
kzfree(t->t[i]);
|
||||
kzfree(t);
|
||||
}
|
||||
EXPORT_SYMBOL(gf128mul_free_64k);
|
||||
|
||||
|
@ -385,7 +449,7 @@ EXPORT_SYMBOL(gf128mul_64k_bbe);
|
|||
|
||||
/* This version uses 4k bytes of table space.
|
||||
A 16 byte buffer has to be multiplied by a 16 byte key
|
||||
value in GF(128). If we consider a GF(128) value in a
|
||||
value in GF(2^128). If we consider a GF(2^128) value in a
|
||||
single byte, we can construct a table of the 256 16 byte
|
||||
values that result from the 256 values of this byte.
|
||||
This requires 4096 bytes. If we take the highest byte in
|
||||
|
@ -443,6 +507,28 @@ out:
|
|||
}
|
||||
EXPORT_SYMBOL(gf128mul_init_4k_bbe);
|
||||
|
||||
struct gf128mul_4k *gf128mul_init_4k_ble(const be128 *g)
|
||||
{
|
||||
struct gf128mul_4k *t;
|
||||
int j, k;
|
||||
|
||||
t = kzalloc(sizeof(*t), GFP_KERNEL);
|
||||
if (!t)
|
||||
goto out;
|
||||
|
||||
t->t[1] = *g;
|
||||
for (j = 1; j <= 64; j <<= 1)
|
||||
gf128mul_x_ble(&t->t[j + j], &t->t[j]);
|
||||
|
||||
for (j = 2; j < 256; j += j)
|
||||
for (k = 1; k < j; ++k)
|
||||
be128_xor(&t->t[j + k], &t->t[j], &t->t[k]);
|
||||
|
||||
out:
|
||||
return t;
|
||||
}
|
||||
EXPORT_SYMBOL(gf128mul_init_4k_ble);
|
||||
|
||||
void gf128mul_4k_lle(be128 *a, struct gf128mul_4k *t)
|
||||
{
|
||||
u8 *ap = (u8 *)a;
|
||||
|
@ -473,5 +559,20 @@ void gf128mul_4k_bbe(be128 *a, struct gf128mul_4k *t)
|
|||
}
|
||||
EXPORT_SYMBOL(gf128mul_4k_bbe);
|
||||
|
||||
void gf128mul_4k_ble(be128 *a, struct gf128mul_4k *t)
|
||||
{
|
||||
u8 *ap = (u8 *)a;
|
||||
be128 r[1];
|
||||
int i = 15;
|
||||
|
||||
*r = t->t[ap[15]];
|
||||
while (i--) {
|
||||
gf128mul_x8_ble(r);
|
||||
be128_xor(r, r, &t->t[ap[i]]);
|
||||
}
|
||||
*a = *r;
|
||||
}
|
||||
EXPORT_SYMBOL(gf128mul_4k_ble);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Functions for multiplying elements of GF(2^128)");
|
||||
|
|
1033
crypto/heh.c
Normal file
1033
crypto/heh.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -683,6 +683,14 @@ void shash_free_instance(struct crypto_instance *inst)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(shash_free_instance);
|
||||
|
||||
int crypto_grab_shash(struct crypto_shash_spawn *spawn,
|
||||
const char *name, u32 type, u32 mask)
|
||||
{
|
||||
spawn->base.frontend = &crypto_shash_type;
|
||||
return crypto_grab_spawn(&spawn->base, name, type, mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_grab_shash);
|
||||
|
||||
int crypto_init_shash_spawn(struct crypto_shash_spawn *spawn,
|
||||
struct shash_alg *alg,
|
||||
struct crypto_instance *inst)
|
||||
|
|
|
@ -3213,6 +3213,21 @@ static const struct alg_test_desc alg_test_descs[] = {
|
|||
.count = GHASH_TEST_VECTORS
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "heh(aes)",
|
||||
.test = alg_test_skcipher,
|
||||
.suite = {
|
||||
.cipher = {
|
||||
.enc = {
|
||||
.vecs = aes_heh_enc_tv_template,
|
||||
.count = AES_HEH_ENC_TEST_VECTORS
|
||||
},
|
||||
.dec = {
|
||||
.vecs = aes_heh_dec_tv_template,
|
||||
.count = AES_HEH_DEC_TEST_VECTORS
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "hmac(crc32)",
|
||||
.test = alg_test_hash,
|
||||
|
|
194
crypto/testmgr.h
194
crypto/testmgr.h
|
@ -14139,6 +14139,8 @@ static struct cipher_testvec cast6_xts_dec_tv_template[] = {
|
|||
#define AES_DEC_TEST_VECTORS 4
|
||||
#define AES_CBC_ENC_TEST_VECTORS 5
|
||||
#define AES_CBC_DEC_TEST_VECTORS 5
|
||||
#define AES_HEH_ENC_TEST_VECTORS 4
|
||||
#define AES_HEH_DEC_TEST_VECTORS 4
|
||||
#define HMAC_MD5_ECB_CIPHER_NULL_ENC_TEST_VECTORS 2
|
||||
#define HMAC_MD5_ECB_CIPHER_NULL_DEC_TEST_VECTORS 2
|
||||
#define HMAC_SHA1_ECB_CIPHER_NULL_ENC_TEST_VEC 2
|
||||
|
@ -14511,6 +14513,198 @@ static struct cipher_testvec aes_dec_tv_template[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct cipher_testvec aes_heh_enc_tv_template[] = {
|
||||
{
|
||||
.key = "\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
|
||||
.klen = 16,
|
||||
.iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
.input = "\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
|
||||
.ilen = 16,
|
||||
.result = "\xd8\xbd\x40\xbf\xca\xe5\xee\x81"
|
||||
"\x0f\x3d\x1f\x1f\xae\x89\x07\x55",
|
||||
.rlen = 16,
|
||||
.also_non_np = 1,
|
||||
.np = 2,
|
||||
.tap = { 8, 8 },
|
||||
}, {
|
||||
.key = "\xa8\xda\x24\x9b\x5e\xfa\x13\xc2"
|
||||
"\xc1\x94\xbf\x32\xba\x38\xa3\x77",
|
||||
.klen = 16,
|
||||
.iv = "\x4d\x47\x61\x37\x2b\x47\x86\xf0"
|
||||
"\xd6\x47\xb5\xc2\xe8\xcf\x85\x27",
|
||||
.input = "\xb8\xee\x29\xe4\xa5\xd1\xe7\x55"
|
||||
"\xd0\xfd\xe7\x22\x63\x76\x36\xe2"
|
||||
"\xf8\x0c\xf8\xfe\x65\x76\xe7\xca"
|
||||
"\xc1\x42\xf5\xca\x5a\xa8\xac\x2a",
|
||||
.ilen = 32,
|
||||
.result = "\x59\xf2\x78\x4e\x10\x94\xf9\x5c"
|
||||
"\x22\x23\x78\x2a\x30\x48\x11\x97"
|
||||
"\xb1\xfe\x70\xc4\xef\xdf\x04\xef"
|
||||
"\x16\x39\x04\xcf\xc0\x95\x9a\x98",
|
||||
.rlen = 32,
|
||||
.also_non_np = 1,
|
||||
.np = 3,
|
||||
.tap = { 16, 13, 3 },
|
||||
}, {
|
||||
.key = "\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
|
||||
.klen = 16,
|
||||
.iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
.input = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00",
|
||||
.ilen = 63,
|
||||
.result = "\xe0\x40\xeb\xe9\x52\xbe\x65\x60"
|
||||
"\xe4\x68\x68\xa3\x73\x75\xb8\x52"
|
||||
"\xef\x38\x6a\x87\x25\x25\xf6\x04"
|
||||
"\xe5\x8e\xbe\x14\x8b\x02\x14\x1f"
|
||||
"\xa9\x73\xb7\xad\x15\xbe\x9c\xa0"
|
||||
"\xd2\x8a\x2c\xdc\xd4\xe3\x05\x55"
|
||||
"\x0a\xf5\xf8\x51\xee\xe5\x62\xa5"
|
||||
"\x71\xa7\x7c\x15\x5d\x7a\x9e",
|
||||
.rlen = 63,
|
||||
.also_non_np = 1,
|
||||
.np = 8,
|
||||
.tap = { 20, 20, 10, 8, 2, 1, 1, 1 },
|
||||
}, {
|
||||
.key = "\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
|
||||
.klen = 16,
|
||||
.iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
.input = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x01"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00",
|
||||
.ilen = 63,
|
||||
.result = "\x4b\x1a\x15\xa0\xaf\x08\x6d\x70"
|
||||
"\xf0\xa7\x97\xb5\x31\x4b\x8c\xc3"
|
||||
"\x4d\xf2\x7a\x9d\xdd\xd4\x15\x99"
|
||||
"\x57\xad\xc6\xb1\x35\x69\xf5\x6a"
|
||||
"\x2d\x70\xe4\x97\x49\xb2\x9f\x71"
|
||||
"\xde\x22\xb5\x70\x8c\x69\x24\xd3"
|
||||
"\xad\x80\x58\x48\x90\xe4\xed\xba"
|
||||
"\x76\x3d\x71\x7c\x57\x25\x87",
|
||||
.rlen = 63,
|
||||
.also_non_np = 1,
|
||||
.np = 8,
|
||||
.tap = { 20, 20, 10, 8, 2, 1, 1, 1 },
|
||||
}
|
||||
};
|
||||
|
||||
static struct cipher_testvec aes_heh_dec_tv_template[] = {
|
||||
{
|
||||
.key = "\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
|
||||
.klen = 16,
|
||||
.iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
.input = "\xd8\xbd\x40\xbf\xca\xe5\xee\x81"
|
||||
"\x0f\x3d\x1f\x1f\xae\x89\x07\x55",
|
||||
.ilen = 16,
|
||||
.result = "\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
|
||||
.rlen = 16,
|
||||
.also_non_np = 1,
|
||||
.np = 2,
|
||||
.tap = { 8, 8 },
|
||||
}, {
|
||||
.key = "\xa8\xda\x24\x9b\x5e\xfa\x13\xc2"
|
||||
"\xc1\x94\xbf\x32\xba\x38\xa3\x77",
|
||||
.klen = 16,
|
||||
.iv = "\x4d\x47\x61\x37\x2b\x47\x86\xf0"
|
||||
"\xd6\x47\xb5\xc2\xe8\xcf\x85\x27",
|
||||
.input = "\x59\xf2\x78\x4e\x10\x94\xf9\x5c"
|
||||
"\x22\x23\x78\x2a\x30\x48\x11\x97"
|
||||
"\xb1\xfe\x70\xc4\xef\xdf\x04\xef"
|
||||
"\x16\x39\x04\xcf\xc0\x95\x9a\x98",
|
||||
.ilen = 32,
|
||||
.result = "\xb8\xee\x29\xe4\xa5\xd1\xe7\x55"
|
||||
"\xd0\xfd\xe7\x22\x63\x76\x36\xe2"
|
||||
"\xf8\x0c\xf8\xfe\x65\x76\xe7\xca"
|
||||
"\xc1\x42\xf5\xca\x5a\xa8\xac\x2a",
|
||||
.rlen = 32,
|
||||
.also_non_np = 1,
|
||||
.np = 3,
|
||||
.tap = { 16, 13, 3 },
|
||||
}, {
|
||||
.key = "\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
|
||||
.klen = 16,
|
||||
.iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
.input = "\xe0\x40\xeb\xe9\x52\xbe\x65\x60"
|
||||
"\xe4\x68\x68\xa3\x73\x75\xb8\x52"
|
||||
"\xef\x38\x6a\x87\x25\x25\xf6\x04"
|
||||
"\xe5\x8e\xbe\x14\x8b\x02\x14\x1f"
|
||||
"\xa9\x73\xb7\xad\x15\xbe\x9c\xa0"
|
||||
"\xd2\x8a\x2c\xdc\xd4\xe3\x05\x55"
|
||||
"\x0a\xf5\xf8\x51\xee\xe5\x62\xa5"
|
||||
"\x71\xa7\x7c\x15\x5d\x7a\x9e",
|
||||
.ilen = 63,
|
||||
.result = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00",
|
||||
.rlen = 63,
|
||||
.also_non_np = 1,
|
||||
.np = 8,
|
||||
.tap = { 20, 20, 10, 8, 2, 1, 1, 1 },
|
||||
}, {
|
||||
.key = "\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07"
|
||||
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
|
||||
.klen = 16,
|
||||
.iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
.input = "\x4b\x1a\x15\xa0\xaf\x08\x6d\x70"
|
||||
"\xf0\xa7\x97\xb5\x31\x4b\x8c\xc3"
|
||||
"\x4d\xf2\x7a\x9d\xdd\xd4\x15\x99"
|
||||
"\x57\xad\xc6\xb1\x35\x69\xf5\x6a"
|
||||
"\x2d\x70\xe4\x97\x49\xb2\x9f\x71"
|
||||
"\xde\x22\xb5\x70\x8c\x69\x24\xd3"
|
||||
"\xad\x80\x58\x48\x90\xe4\xed\xba"
|
||||
"\x76\x3d\x71\x7c\x57\x25\x87",
|
||||
.ilen = 63,
|
||||
.result = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x01"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00",
|
||||
.rlen = 63,
|
||||
.also_non_np = 1,
|
||||
.np = 8,
|
||||
.tap = { 20, 20, 10, 8, 2, 1, 1, 1 },
|
||||
}
|
||||
};
|
||||
|
||||
static struct cipher_testvec aes_cbc_enc_tv_template[] = {
|
||||
{ /* From RFC 3602 */
|
||||
.key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b"
|
||||
|
|
|
@ -2047,7 +2047,7 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
|
||||
binder_user_error("%d:%d got transaction with unaligned buffers size, %lld\n",
|
||||
proc->pid, thread->pid,
|
||||
extra_buffers_size);
|
||||
(u64)extra_buffers_size);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_bad_offset;
|
||||
}
|
||||
|
|
|
@ -283,6 +283,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
|
|||
case ACL_TYPE_ACCESS:
|
||||
if (acl) {
|
||||
struct iattr iattr;
|
||||
struct posix_acl *old_acl = acl;
|
||||
|
||||
retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
|
||||
if (retval)
|
||||
|
@ -293,6 +294,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
|
|||
* by the mode bits. So don't
|
||||
* update ACL.
|
||||
*/
|
||||
posix_acl_release(old_acl);
|
||||
value = NULL;
|
||||
size = 0;
|
||||
}
|
||||
|
|
12
fs/attr.c
12
fs/attr.c
|
@ -187,7 +187,7 @@ EXPORT_SYMBOL(setattr_copy);
|
|||
* the file open for write, as there can be no conflicting delegation in
|
||||
* that case.
|
||||
*/
|
||||
int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
|
||||
int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
umode_t mode = inode->i_mode;
|
||||
|
@ -277,7 +277,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
if (inode->i_op->setattr)
|
||||
if (mnt && inode->i_op->setattr2)
|
||||
error = inode->i_op->setattr2(mnt, dentry, attr);
|
||||
else if (inode->i_op->setattr)
|
||||
error = inode->i_op->setattr(dentry, attr);
|
||||
else
|
||||
error = simple_setattr(dentry, attr);
|
||||
|
@ -290,4 +292,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
|
|||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(notify_change2);
|
||||
|
||||
int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
|
||||
{
|
||||
return notify_change2(NULL, dentry, attr, delegated_inode);
|
||||
}
|
||||
EXPORT_SYMBOL(notify_change);
|
||||
|
|
|
@ -720,7 +720,7 @@ void do_coredump(const siginfo_t *siginfo)
|
|||
goto close_fail;
|
||||
if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
|
||||
goto close_fail;
|
||||
if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))
|
||||
if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file))
|
||||
goto close_fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -1132,8 +1132,10 @@ EXPORT_SYMBOL(flush_old_exec);
|
|||
void would_dump(struct linux_binprm *bprm, struct file *file)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
if (inode_permission(inode, MAY_READ) < 0) {
|
||||
|
||||
if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) {
|
||||
struct user_namespace *old, *user_ns;
|
||||
|
||||
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
|
||||
|
||||
/* Ensure mm->user_ns contains the executable */
|
||||
|
|
|
@ -106,6 +106,7 @@ config EXT4_ENCRYPTION
|
|||
select CRYPTO_ECB
|
||||
select CRYPTO_XTS
|
||||
select CRYPTO_CTS
|
||||
select CRYPTO_HEH
|
||||
select CRYPTO_CTR
|
||||
select CRYPTO_SHA256
|
||||
select KEYS
|
||||
|
|
|
@ -44,7 +44,8 @@ static void ext4_dir_crypt_complete(struct crypto_async_request *req, int res)
|
|||
|
||||
bool ext4_valid_filenames_enc_mode(uint32_t mode)
|
||||
{
|
||||
return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS);
|
||||
return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS ||
|
||||
mode == EXT4_ENCRYPTION_MODE_AES_256_HEH);
|
||||
}
|
||||
|
||||
static unsigned max_name_len(struct inode *inode)
|
||||
|
|
|
@ -29,16 +29,16 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc)
|
|||
}
|
||||
|
||||
/**
|
||||
* ext4_derive_key_aes() - Derive a key using AES-128-ECB
|
||||
* ext4_derive_key_v1() - Derive a key using AES-128-ECB
|
||||
* @deriving_key: Encryption key used for derivation.
|
||||
* @source_key: Source key to which to apply derivation.
|
||||
* @derived_key: Derived key.
|
||||
*
|
||||
* Return: Zero on success; non-zero otherwise.
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
static int ext4_derive_key_aes(char deriving_key[EXT4_AES_128_ECB_KEY_SIZE],
|
||||
char source_key[EXT4_AES_256_XTS_KEY_SIZE],
|
||||
char derived_key[EXT4_AES_256_XTS_KEY_SIZE])
|
||||
static int ext4_derive_key_v1(const char deriving_key[EXT4_AES_128_ECB_KEY_SIZE],
|
||||
const char source_key[EXT4_AES_256_XTS_KEY_SIZE],
|
||||
char derived_key[EXT4_AES_256_XTS_KEY_SIZE])
|
||||
{
|
||||
int res = 0;
|
||||
struct ablkcipher_request *req = NULL;
|
||||
|
@ -83,6 +83,91 @@ out:
|
|||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_derive_key_v2() - Derive a key non-reversibly
|
||||
* @nonce: the nonce associated with the file
|
||||
* @master_key: the master key referenced by the file
|
||||
* @derived_key: (output) the resulting derived key
|
||||
*
|
||||
* This function computes the following:
|
||||
* derived_key[0:127] = AES-256-ENCRYPT(master_key[0:255], nonce)
|
||||
* derived_key[128:255] = AES-256-ENCRYPT(master_key[0:255], nonce ^ 0x01)
|
||||
* derived_key[256:383] = AES-256-ENCRYPT(master_key[256:511], nonce)
|
||||
* derived_key[384:511] = AES-256-ENCRYPT(master_key[256:511], nonce ^ 0x01)
|
||||
*
|
||||
* 'nonce ^ 0x01' denotes flipping the low order bit of the last byte.
|
||||
*
|
||||
* Unlike the v1 algorithm, the v2 algorithm is "non-reversible", meaning that
|
||||
* compromising a derived key does not also compromise the master key.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
static int ext4_derive_key_v2(const char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE],
|
||||
const char master_key[EXT4_MAX_KEY_SIZE],
|
||||
char derived_key[EXT4_MAX_KEY_SIZE])
|
||||
{
|
||||
const int noncelen = EXT4_KEY_DERIVATION_NONCE_SIZE;
|
||||
struct crypto_cipher *tfm;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Since we only use each transform for a small number of encryptions,
|
||||
* requesting just "aes" turns out to be significantly faster than
|
||||
* "ecb(aes)", by about a factor of two.
|
||||
*/
|
||||
tfm = crypto_alloc_cipher("aes", 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
BUILD_BUG_ON(4 * EXT4_KEY_DERIVATION_NONCE_SIZE != EXT4_MAX_KEY_SIZE);
|
||||
BUILD_BUG_ON(2 * EXT4_AES_256_ECB_KEY_SIZE != EXT4_MAX_KEY_SIZE);
|
||||
for (i = 0; i < 2; i++) {
|
||||
memcpy(derived_key, nonce, noncelen);
|
||||
memcpy(derived_key + noncelen, nonce, noncelen);
|
||||
derived_key[2 * noncelen - 1] ^= 0x01;
|
||||
err = crypto_cipher_setkey(tfm, master_key,
|
||||
EXT4_AES_256_ECB_KEY_SIZE);
|
||||
if (err)
|
||||
break;
|
||||
crypto_cipher_encrypt_one(tfm, derived_key, derived_key);
|
||||
crypto_cipher_encrypt_one(tfm, derived_key + noncelen,
|
||||
derived_key + noncelen);
|
||||
master_key += EXT4_AES_256_ECB_KEY_SIZE;
|
||||
derived_key += 2 * noncelen;
|
||||
}
|
||||
crypto_free_cipher(tfm);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_derive_key() - Derive a per-file key from a nonce and master key
|
||||
* @ctx: the encryption context associated with the file
|
||||
* @master_key: the master key referenced by the file
|
||||
* @derived_key: (output) the resulting derived key
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
static int ext4_derive_key(const struct ext4_encryption_context *ctx,
|
||||
const char master_key[EXT4_MAX_KEY_SIZE],
|
||||
char derived_key[EXT4_MAX_KEY_SIZE])
|
||||
{
|
||||
BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE != EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
BUILD_BUG_ON(EXT4_AES_256_XTS_KEY_SIZE != EXT4_MAX_KEY_SIZE);
|
||||
|
||||
/*
|
||||
* Although the key derivation algorithm is logically independent of the
|
||||
* choice of encryption modes, in this kernel it is bundled with HEH
|
||||
* encryption of filenames, which is another crypto improvement that
|
||||
* requires an on-disk format change and requires userspace to specify
|
||||
* different encryption policies.
|
||||
*/
|
||||
if (ctx->filenames_encryption_mode == EXT4_ENCRYPTION_MODE_AES_256_HEH)
|
||||
return ext4_derive_key_v2(ctx->nonce, master_key, derived_key);
|
||||
else
|
||||
return ext4_derive_key_v1(ctx->nonce, master_key, derived_key);
|
||||
}
|
||||
|
||||
void ext4_free_crypt_info(struct ext4_crypt_info *ci)
|
||||
{
|
||||
if (!ci)
|
||||
|
@ -182,6 +267,9 @@ retry:
|
|||
case EXT4_ENCRYPTION_MODE_AES_256_CTS:
|
||||
cipher_str = "cts(cbc(aes))";
|
||||
break;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_HEH:
|
||||
cipher_str = "heh(aes)";
|
||||
break;
|
||||
default:
|
||||
printk_once(KERN_WARNING
|
||||
"ext4: unsupported key mode %d (ino %u)\n",
|
||||
|
@ -231,8 +319,7 @@ retry:
|
|||
up_read(&keyring_key->sem);
|
||||
goto out;
|
||||
}
|
||||
res = ext4_derive_key_aes(ctx.nonce, master_key->raw,
|
||||
raw_key);
|
||||
res = ext4_derive_key(&ctx, master_key->raw, raw_key);
|
||||
up_read(&keyring_key->sem);
|
||||
if (res)
|
||||
goto out;
|
||||
|
|
|
@ -589,6 +589,7 @@ enum {
|
|||
#define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_HEH 126
|
||||
|
||||
#include "ext4_crypto.h"
|
||||
|
||||
|
|
|
@ -58,8 +58,10 @@ struct ext4_encryption_context {
|
|||
#define EXT4_XTS_TWEAK_SIZE 16
|
||||
#define EXT4_AES_128_ECB_KEY_SIZE 16
|
||||
#define EXT4_AES_256_GCM_KEY_SIZE 32
|
||||
#define EXT4_AES_256_ECB_KEY_SIZE 32
|
||||
#define EXT4_AES_256_CBC_KEY_SIZE 32
|
||||
#define EXT4_AES_256_CTS_KEY_SIZE 32
|
||||
#define EXT4_AES_256_HEH_KEY_SIZE 32
|
||||
#define EXT4_AES_256_XTS_KEY_SIZE 64
|
||||
#define EXT4_MAX_KEY_SIZE 64
|
||||
|
||||
|
@ -121,6 +123,8 @@ static inline int ext4_encryption_key_size(int mode)
|
|||
return EXT4_AES_256_CBC_KEY_SIZE;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_CTS:
|
||||
return EXT4_AES_256_CTS_KEY_SIZE;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_HEH:
|
||||
return EXT4_AES_256_HEH_KEY_SIZE;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
|
|
@ -503,8 +503,16 @@ int ext4_readpage_inline(struct inode *inode, struct page *page)
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
trace_android_fs_dataread_start(inode, page_offset(page), PAGE_SIZE,
|
||||
current->pid, current->comm);
|
||||
if (trace_android_fs_dataread_start_enabled()) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
inode);
|
||||
trace_android_fs_dataread_start(inode, page_offset(page),
|
||||
PAGE_SIZE, current->pid,
|
||||
path, current->comm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Current inline data can only exist in the 1st page,
|
||||
|
|
|
@ -1017,8 +1017,16 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
|
|||
pgoff_t index;
|
||||
unsigned from, to;
|
||||
|
||||
trace_android_fs_datawrite_start(inode, pos, len,
|
||||
current->pid, current->comm);
|
||||
if (trace_android_fs_datawrite_start_enabled()) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
inode);
|
||||
trace_android_fs_datawrite_start(inode, pos, len,
|
||||
current->pid, path,
|
||||
current->comm);
|
||||
}
|
||||
trace_ext4_write_begin(inode, pos, len, flags);
|
||||
/*
|
||||
* Reserve one block more for addition to orphan list in case
|
||||
|
@ -2732,8 +2740,16 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
|
|||
len, flags, pagep, fsdata);
|
||||
}
|
||||
*fsdata = (void *)0;
|
||||
trace_android_fs_datawrite_start(inode, pos, len,
|
||||
current->pid, current->comm);
|
||||
if (trace_android_fs_datawrite_start_enabled()) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
inode);
|
||||
trace_android_fs_datawrite_start(inode, pos, len,
|
||||
current->pid,
|
||||
path, current->comm);
|
||||
}
|
||||
trace_ext4_da_write_begin(inode, pos, len, flags);
|
||||
|
||||
if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
|
||||
|
@ -3342,16 +3358,27 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
|||
return 0;
|
||||
|
||||
if (trace_android_fs_dataread_start_enabled() &&
|
||||
(iov_iter_rw(iter) == READ))
|
||||
trace_android_fs_dataread_start(inode, offset, count,
|
||||
current->pid,
|
||||
current->comm);
|
||||
if (trace_android_fs_datawrite_start_enabled() &&
|
||||
(iov_iter_rw(iter) == WRITE))
|
||||
trace_android_fs_datawrite_start(inode, offset, count,
|
||||
current->pid,
|
||||
current->comm);
|
||||
(iov_iter_rw(iter) == READ)) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
inode);
|
||||
trace_android_fs_dataread_start(inode, offset, count,
|
||||
current->pid, path,
|
||||
current->comm);
|
||||
}
|
||||
if (trace_android_fs_datawrite_start_enabled() &&
|
||||
(iov_iter_rw(iter) == WRITE)) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
inode);
|
||||
trace_android_fs_datawrite_start(inode, offset, count,
|
||||
current->pid, path,
|
||||
current->comm);
|
||||
}
|
||||
trace_ext4_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
|
||||
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
|
||||
ret = ext4_ext_direct_IO(iocb, iter, offset);
|
||||
|
@ -3587,6 +3614,11 @@ static int ext4_block_truncate_page(handle_t *handle,
|
|||
unsigned blocksize;
|
||||
struct inode *inode = mapping->host;
|
||||
|
||||
/* If we are processing an encrypted inode during orphan list
|
||||
* handling */
|
||||
if (ext4_encrypted_inode(inode) && !ext4_has_encryption_key(inode))
|
||||
return 0;
|
||||
|
||||
blocksize = inode->i_sb->s_blocksize;
|
||||
length = blocksize - (offset & (blocksize - 1));
|
||||
|
||||
|
|
|
@ -152,11 +152,17 @@ ext4_submit_bio_read(struct bio *bio)
|
|||
struct page *first_page = bio->bi_io_vec[0].bv_page;
|
||||
|
||||
if (first_page != NULL) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
first_page->mapping->host);
|
||||
trace_android_fs_dataread_start(
|
||||
first_page->mapping->host,
|
||||
page_offset(first_page),
|
||||
bio->bi_iter.bi_size,
|
||||
current->pid,
|
||||
path,
|
||||
current->comm);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1402,8 +1402,16 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
|
|||
struct dnode_of_data dn;
|
||||
int err = 0;
|
||||
|
||||
trace_android_fs_datawrite_start(inode, pos, len,
|
||||
current->pid, current->comm);
|
||||
if (trace_android_fs_datawrite_start_enabled()) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
inode);
|
||||
trace_android_fs_datawrite_start(inode, pos, len,
|
||||
current->pid, path,
|
||||
current->comm);
|
||||
}
|
||||
trace_f2fs_write_begin(inode, pos, len, flags);
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
@ -1587,15 +1595,27 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
|||
trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
|
||||
|
||||
if (trace_android_fs_dataread_start_enabled() &&
|
||||
(iov_iter_rw(iter) == READ))
|
||||
trace_android_fs_dataread_start(inode, offset,
|
||||
count, current->pid,
|
||||
current->comm);
|
||||
if (trace_android_fs_datawrite_start_enabled() &&
|
||||
(iov_iter_rw(iter) == WRITE))
|
||||
trace_android_fs_datawrite_start(inode, offset, count,
|
||||
current->pid, current->comm);
|
||||
(iov_iter_rw(iter) == READ)) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
inode);
|
||||
trace_android_fs_dataread_start(inode, offset,
|
||||
count, current->pid, path,
|
||||
current->comm);
|
||||
}
|
||||
if (trace_android_fs_datawrite_start_enabled() &&
|
||||
(iov_iter_rw(iter) == WRITE)) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
inode);
|
||||
trace_android_fs_datawrite_start(inode, offset, count,
|
||||
current->pid, path,
|
||||
current->comm);
|
||||
}
|
||||
if (iov_iter_rw(iter) == WRITE) {
|
||||
__allocate_data_blocks(inode, offset, count);
|
||||
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) {
|
||||
|
|
|
@ -85,9 +85,16 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
|
|||
{
|
||||
struct page *ipage;
|
||||
|
||||
trace_android_fs_dataread_start(inode, page_offset(page),
|
||||
PAGE_SIZE, current->pid,
|
||||
current->comm);
|
||||
if (trace_android_fs_dataread_start_enabled()) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
inode);
|
||||
trace_android_fs_dataread_start(inode, page_offset(page),
|
||||
PAGE_SIZE, current->pid,
|
||||
path, current->comm);
|
||||
}
|
||||
|
||||
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(ipage)) {
|
||||
|
|
|
@ -44,6 +44,7 @@ void set_fs_pwd(struct fs_struct *fs, const struct path *path)
|
|||
if (old_pwd.dentry)
|
||||
path_put(&old_pwd);
|
||||
}
|
||||
EXPORT_SYMBOL(set_fs_pwd);
|
||||
|
||||
static inline int replace_path(struct path *p, const struct path *old, const struct path *new)
|
||||
{
|
||||
|
@ -89,6 +90,7 @@ void free_fs_struct(struct fs_struct *fs)
|
|||
path_put(&fs->pwd);
|
||||
kmem_cache_free(fs_cachep, fs);
|
||||
}
|
||||
EXPORT_SYMBOL(free_fs_struct);
|
||||
|
||||
void exit_fs(struct task_struct *tsk)
|
||||
{
|
||||
|
@ -127,6 +129,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
|
|||
}
|
||||
return fs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(copy_fs_struct);
|
||||
|
||||
int unshare_fs_struct(void)
|
||||
{
|
||||
|
|
|
@ -1715,7 +1715,7 @@ int dentry_needs_remove_privs(struct dentry *dentry)
|
|||
}
|
||||
EXPORT_SYMBOL(dentry_needs_remove_privs);
|
||||
|
||||
static int __remove_privs(struct dentry *dentry, int kill)
|
||||
static int __remove_privs(struct vfsmount *mnt, struct dentry *dentry, int kill)
|
||||
{
|
||||
struct iattr newattrs;
|
||||
|
||||
|
@ -1724,7 +1724,7 @@ static int __remove_privs(struct dentry *dentry, int kill)
|
|||
* Note we call this on write, so notify_change will not
|
||||
* encounter any conflicting delegations:
|
||||
*/
|
||||
return notify_change(dentry, &newattrs, NULL);
|
||||
return notify_change2(mnt, dentry, &newattrs, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1746,7 +1746,7 @@ int file_remove_privs(struct file *file)
|
|||
if (kill < 0)
|
||||
return kill;
|
||||
if (kill)
|
||||
error = __remove_privs(dentry, kill);
|
||||
error = __remove_privs(file->f_path.mnt, dentry, kill);
|
||||
if (!error)
|
||||
inode_has_no_xattr(inode);
|
||||
|
||||
|
|
|
@ -84,9 +84,11 @@ extern struct file *get_empty_filp(void);
|
|||
* super.c
|
||||
*/
|
||||
extern int do_remount_sb(struct super_block *, int, void *, int);
|
||||
extern int do_remount_sb2(struct vfsmount *, struct super_block *, int,
|
||||
void *, int);
|
||||
extern bool trylock_super(struct super_block *sb);
|
||||
extern struct dentry *mount_fs(struct file_system_type *,
|
||||
int, const char *, void *);
|
||||
int, const char *, struct vfsmount *, void *);
|
||||
extern struct super_block *user_get_super(dev_t);
|
||||
|
||||
/*
|
||||
|
|
|
@ -79,11 +79,17 @@ static struct bio *mpage_bio_submit(int rw, struct bio *bio)
|
|||
struct page *first_page = bio->bi_io_vec[0].bv_page;
|
||||
|
||||
if (first_page != NULL) {
|
||||
char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
|
||||
|
||||
path = android_fstrace_get_pathname(pathbuf,
|
||||
MAX_TRACE_PATHBUF_LEN,
|
||||
first_page->mapping->host);
|
||||
trace_android_fs_dataread_start(
|
||||
first_page->mapping->host,
|
||||
page_offset(first_page),
|
||||
bio->bi_iter.bi_size,
|
||||
current->pid,
|
||||
path,
|
||||
current->comm);
|
||||
}
|
||||
}
|
||||
|
|
179
fs/namei.c
179
fs/namei.c
|
@ -373,9 +373,11 @@ EXPORT_SYMBOL(generic_permission);
|
|||
* flag in inode->i_opflags, that says "this has not special
|
||||
* permission function, use the fast case".
|
||||
*/
|
||||
static inline int do_inode_permission(struct inode *inode, int mask)
|
||||
static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||
{
|
||||
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
|
||||
if (likely(mnt && inode->i_op->permission2))
|
||||
return inode->i_op->permission2(mnt, inode, mask);
|
||||
if (likely(inode->i_op->permission))
|
||||
return inode->i_op->permission(inode, mask);
|
||||
|
||||
|
@ -399,7 +401,7 @@ static inline int do_inode_permission(struct inode *inode, int mask)
|
|||
* This does not check for a read-only file system. You probably want
|
||||
* inode_permission().
|
||||
*/
|
||||
int __inode_permission(struct inode *inode, int mask)
|
||||
int __inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||
{
|
||||
int retval;
|
||||
|
||||
|
@ -411,7 +413,7 @@ int __inode_permission(struct inode *inode, int mask)
|
|||
return -EACCES;
|
||||
}
|
||||
|
||||
retval = do_inode_permission(inode, mask);
|
||||
retval = do_inode_permission(mnt, inode, mask);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
|
@ -419,7 +421,14 @@ int __inode_permission(struct inode *inode, int mask)
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
return security_inode_permission(inode, mask);
|
||||
retval = security_inode_permission(inode, mask);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL(__inode_permission2);
|
||||
|
||||
int __inode_permission(struct inode *inode, int mask)
|
||||
{
|
||||
return __inode_permission2(NULL, inode, mask);
|
||||
}
|
||||
EXPORT_SYMBOL(__inode_permission);
|
||||
|
||||
|
@ -455,14 +464,20 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
|
|||
*
|
||||
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
|
||||
*/
|
||||
int inode_permission(struct inode *inode, int mask)
|
||||
int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = sb_permission(inode->i_sb, inode, mask);
|
||||
if (retval)
|
||||
return retval;
|
||||
return __inode_permission(inode, mask);
|
||||
return __inode_permission2(mnt, inode, mask);
|
||||
}
|
||||
EXPORT_SYMBOL(inode_permission2);
|
||||
|
||||
int inode_permission(struct inode *inode, int mask)
|
||||
{
|
||||
return inode_permission2(NULL, inode, mask);
|
||||
}
|
||||
EXPORT_SYMBOL(inode_permission);
|
||||
|
||||
|
@ -1645,13 +1660,13 @@ static int lookup_slow(struct nameidata *nd, struct path *path)
|
|||
static inline int may_lookup(struct nameidata *nd)
|
||||
{
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
|
||||
int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
|
||||
if (err != -ECHILD)
|
||||
return err;
|
||||
if (unlazy_walk(nd, NULL, 0))
|
||||
return -ECHILD;
|
||||
}
|
||||
return inode_permission(nd->inode, MAY_EXEC);
|
||||
return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC);
|
||||
}
|
||||
|
||||
static inline int handle_dots(struct nameidata *nd, int type)
|
||||
|
@ -2005,11 +2020,12 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
|
|||
nd->depth = 0;
|
||||
if (flags & LOOKUP_ROOT) {
|
||||
struct dentry *root = nd->root.dentry;
|
||||
struct vfsmount *mnt = nd->root.mnt;
|
||||
struct inode *inode = root->d_inode;
|
||||
if (*s) {
|
||||
if (!d_can_lookup(root))
|
||||
return ERR_PTR(-ENOTDIR);
|
||||
retval = inode_permission(inode, MAY_EXEC);
|
||||
retval = inode_permission2(mnt, inode, MAY_EXEC);
|
||||
if (retval)
|
||||
return ERR_PTR(retval);
|
||||
}
|
||||
|
@ -2280,13 +2296,14 @@ EXPORT_SYMBOL(vfs_path_lookup);
|
|||
/**
|
||||
* lookup_one_len - filesystem helper to lookup single pathname component
|
||||
* @name: pathname component to lookup
|
||||
* @mnt: mount we are looking up on
|
||||
* @base: base directory to lookup from
|
||||
* @len: maximum length @len should be interpreted to
|
||||
*
|
||||
* Note that this routine is purely a helper for filesystem usage and should
|
||||
* not be called by generic code.
|
||||
*/
|
||||
struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
||||
struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len)
|
||||
{
|
||||
struct qstr this;
|
||||
unsigned int c;
|
||||
|
@ -2320,12 +2337,18 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
err = inode_permission(base->d_inode, MAY_EXEC);
|
||||
err = inode_permission2(mnt, base->d_inode, MAY_EXEC);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return __lookup_hash(&this, base, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(lookup_one_len2);
|
||||
|
||||
struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
||||
{
|
||||
return lookup_one_len2(name, NULL, base, len);
|
||||
}
|
||||
EXPORT_SYMBOL(lookup_one_len);
|
||||
|
||||
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
|
||||
|
@ -2552,7 +2575,7 @@ EXPORT_SYMBOL(__check_sticky);
|
|||
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
|
||||
* nfs_async_unlink().
|
||||
*/
|
||||
static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
||||
static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *victim, bool isdir)
|
||||
{
|
||||
struct inode *inode = d_backing_inode(victim);
|
||||
int error;
|
||||
|
@ -2564,7 +2587,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
|||
BUG_ON(victim->d_parent->d_inode != dir);
|
||||
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
|
||||
|
||||
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
|
||||
if (error)
|
||||
return error;
|
||||
if (IS_APPEND(dir))
|
||||
|
@ -2595,14 +2618,14 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
|||
* 3. We should have write and exec permissions on dir
|
||||
* 4. We can't do it if dir is immutable (done in permission())
|
||||
*/
|
||||
static inline int may_create(struct inode *dir, struct dentry *child)
|
||||
static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child)
|
||||
{
|
||||
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
|
||||
if (child->d_inode)
|
||||
return -EEXIST;
|
||||
if (IS_DEADDIR(dir))
|
||||
return -ENOENT;
|
||||
return inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2649,10 +2672,10 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
|
|||
}
|
||||
EXPORT_SYMBOL(unlock_rename);
|
||||
|
||||
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
bool want_excl)
|
||||
int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, bool want_excl)
|
||||
{
|
||||
int error = may_create(dir, dentry);
|
||||
int error = may_create(mnt, dir, dentry);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -2668,11 +2691,19 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
fsnotify_create(dir, dentry);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_create2);
|
||||
|
||||
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
bool want_excl)
|
||||
{
|
||||
return vfs_create2(NULL, dir, dentry, mode, want_excl);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_create);
|
||||
|
||||
static int may_open(struct path *path, int acc_mode, int flag)
|
||||
{
|
||||
struct dentry *dentry = path->dentry;
|
||||
struct vfsmount *mnt = path->mnt;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int error;
|
||||
|
||||
|
@ -2701,7 +2732,7 @@ static int may_open(struct path *path, int acc_mode, int flag)
|
|||
break;
|
||||
}
|
||||
|
||||
error = inode_permission(inode, acc_mode);
|
||||
error = inode_permission2(mnt, inode, acc_mode);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -2736,7 +2767,7 @@ static int handle_truncate(struct file *filp)
|
|||
if (!error)
|
||||
error = security_path_truncate(path);
|
||||
if (!error) {
|
||||
error = do_truncate(path->dentry, 0,
|
||||
error = do_truncate2(path->mnt, path->dentry, 0,
|
||||
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
|
||||
filp);
|
||||
}
|
||||
|
@ -2757,7 +2788,7 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode)
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
|
||||
error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -2943,6 +2974,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
|
|||
bool got_write, int *opened)
|
||||
{
|
||||
struct dentry *dir = nd->path.dentry;
|
||||
struct vfsmount *mnt = nd->path.mnt;
|
||||
struct inode *dir_inode = dir->d_inode;
|
||||
struct dentry *dentry;
|
||||
int error;
|
||||
|
@ -2990,7 +3022,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
|
|||
error = security_path_mknod(&nd->path, dentry, mode, 0);
|
||||
if (error)
|
||||
goto out_dput;
|
||||
error = vfs_create(dir->d_inode, dentry, mode,
|
||||
error = vfs_create2(mnt, dir->d_inode, dentry, mode,
|
||||
nd->flags & LOOKUP_EXCL);
|
||||
if (error)
|
||||
goto out_dput;
|
||||
|
@ -3252,7 +3284,7 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
|
|||
goto out;
|
||||
dir = path.dentry->d_inode;
|
||||
/* we want directory to be writable */
|
||||
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
error = inode_permission2(path.mnt, dir, MAY_WRITE | MAY_EXEC);
|
||||
if (error)
|
||||
goto out2;
|
||||
if (!dir->i_op->tmpfile) {
|
||||
|
@ -3486,9 +3518,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
|
|||
}
|
||||
EXPORT_SYMBOL(user_path_create);
|
||||
|
||||
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||
int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||
{
|
||||
int error = may_create(dir, dentry);
|
||||
int error = may_create(mnt, dir, dentry);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -3512,6 +3544,12 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
|||
fsnotify_create(dir, dentry);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_mknod2);
|
||||
|
||||
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||
{
|
||||
return vfs_mknod2(NULL, dir, dentry, mode, dev);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_mknod);
|
||||
|
||||
static int may_mknod(umode_t mode)
|
||||
|
@ -3554,10 +3592,10 @@ retry:
|
|||
goto out;
|
||||
switch (mode & S_IFMT) {
|
||||
case 0: case S_IFREG:
|
||||
error = vfs_create(path.dentry->d_inode,dentry,mode,true);
|
||||
error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true);
|
||||
break;
|
||||
case S_IFCHR: case S_IFBLK:
|
||||
error = vfs_mknod(path.dentry->d_inode,dentry,mode,
|
||||
error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode,
|
||||
new_decode_dev(dev));
|
||||
break;
|
||||
case S_IFIFO: case S_IFSOCK:
|
||||
|
@ -3578,9 +3616,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
|
|||
return sys_mknodat(AT_FDCWD, filename, mode, dev);
|
||||
}
|
||||
|
||||
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
int error = may_create(dir, dentry);
|
||||
int error = may_create(mnt, dir, dentry);
|
||||
unsigned max_links = dir->i_sb->s_max_links;
|
||||
|
||||
if (error)
|
||||
|
@ -3602,6 +3640,12 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
fsnotify_mkdir(dir, dentry);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_mkdir2);
|
||||
|
||||
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
return vfs_mkdir2(NULL, dir, dentry, mode);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_mkdir);
|
||||
|
||||
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
|
||||
|
@ -3620,7 +3664,7 @@ retry:
|
|||
mode &= ~current_umask();
|
||||
error = security_path_mkdir(&path, dentry, mode);
|
||||
if (!error)
|
||||
error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
|
||||
error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode);
|
||||
done_path_create(&path, dentry);
|
||||
if (retry_estale(error, lookup_flags)) {
|
||||
lookup_flags |= LOOKUP_REVAL;
|
||||
|
@ -3659,9 +3703,9 @@ void dentry_unhash(struct dentry *dentry)
|
|||
}
|
||||
EXPORT_SYMBOL(dentry_unhash);
|
||||
|
||||
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int error = may_delete(dir, dentry, 1);
|
||||
int error = may_delete(mnt, dir, dentry, 1);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -3696,6 +3740,12 @@ out:
|
|||
d_delete(dentry);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_rmdir2);
|
||||
|
||||
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
return vfs_rmdir2(NULL, dir, dentry);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_rmdir);
|
||||
|
||||
static long do_rmdir(int dfd, const char __user *pathname)
|
||||
|
@ -3741,7 +3791,7 @@ retry:
|
|||
error = security_path_rmdir(&path, dentry);
|
||||
if (error)
|
||||
goto exit3;
|
||||
error = vfs_rmdir(path.dentry->d_inode, dentry);
|
||||
error = vfs_rmdir2(path.mnt, path.dentry->d_inode, dentry);
|
||||
exit3:
|
||||
dput(dentry);
|
||||
exit2:
|
||||
|
@ -3780,10 +3830,10 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
|
|||
* be appropriate for callers that expect the underlying filesystem not
|
||||
* to be NFS exported.
|
||||
*/
|
||||
int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
|
||||
int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
|
||||
{
|
||||
struct inode *target = dentry->d_inode;
|
||||
int error = may_delete(dir, dentry, 0);
|
||||
int error = may_delete(mnt, dir, dentry, 0);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -3818,6 +3868,12 @@ out:
|
|||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_unlink2);
|
||||
|
||||
int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
|
||||
{
|
||||
return vfs_unlink2(NULL, dir, dentry, delegated_inode);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_unlink);
|
||||
|
||||
/*
|
||||
|
@ -3865,7 +3921,7 @@ retry_deleg:
|
|||
error = security_path_unlink(&path, dentry);
|
||||
if (error)
|
||||
goto exit2;
|
||||
error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode);
|
||||
error = vfs_unlink2(path.mnt, path.dentry->d_inode, dentry, &delegated_inode);
|
||||
exit2:
|
||||
dput(dentry);
|
||||
}
|
||||
|
@ -3915,9 +3971,9 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
|
|||
return do_unlinkat(AT_FDCWD, pathname);
|
||||
}
|
||||
|
||||
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
||||
int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname)
|
||||
{
|
||||
int error = may_create(dir, dentry);
|
||||
int error = may_create(mnt, dir, dentry);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -3934,6 +3990,12 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
|||
fsnotify_create(dir, dentry);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_symlink2);
|
||||
|
||||
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
||||
{
|
||||
return vfs_symlink2(NULL, dir, dentry, oldname);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_symlink);
|
||||
|
||||
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
|
||||
|
@ -3956,7 +4018,7 @@ retry:
|
|||
|
||||
error = security_path_symlink(&path, dentry, from->name);
|
||||
if (!error)
|
||||
error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
|
||||
error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name);
|
||||
done_path_create(&path, dentry);
|
||||
if (retry_estale(error, lookup_flags)) {
|
||||
lookup_flags |= LOOKUP_REVAL;
|
||||
|
@ -3991,7 +4053,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
|
|||
* be appropriate for callers that expect the underlying filesystem not
|
||||
* to be NFS exported.
|
||||
*/
|
||||
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
|
||||
int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
|
||||
{
|
||||
struct inode *inode = old_dentry->d_inode;
|
||||
unsigned max_links = dir->i_sb->s_max_links;
|
||||
|
@ -4000,7 +4062,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
|||
if (!inode)
|
||||
return -ENOENT;
|
||||
|
||||
error = may_create(dir, new_dentry);
|
||||
error = may_create(mnt, dir, new_dentry);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -4043,6 +4105,12 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
|||
fsnotify_link(dir, inode, new_dentry);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_link2);
|
||||
|
||||
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
|
||||
{
|
||||
return vfs_link2(NULL, old_dentry, dir, new_dentry, delegated_inode);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_link);
|
||||
|
||||
/*
|
||||
|
@ -4098,7 +4166,7 @@ retry:
|
|||
error = security_path_link(old_path.dentry, &new_path, new_dentry);
|
||||
if (error)
|
||||
goto out_dput;
|
||||
error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
|
||||
error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
|
||||
out_dput:
|
||||
done_path_create(&new_path, new_dentry);
|
||||
if (delegated_inode) {
|
||||
|
@ -4173,7 +4241,8 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
|
|||
* ->i_mutex on parents, which works but leads to some truly excessive
|
||||
* locking].
|
||||
*/
|
||||
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
int vfs_rename2(struct vfsmount *mnt,
|
||||
struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
struct inode **delegated_inode, unsigned int flags)
|
||||
{
|
||||
|
@ -4192,19 +4261,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
if (vfs_select_inode(old_dentry, 0) == vfs_select_inode(new_dentry, 0))
|
||||
return 0;
|
||||
|
||||
error = may_delete(old_dir, old_dentry, is_dir);
|
||||
error = may_delete(mnt, old_dir, old_dentry, is_dir);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!target) {
|
||||
error = may_create(new_dir, new_dentry);
|
||||
error = may_create(mnt, new_dir, new_dentry);
|
||||
} else {
|
||||
new_is_dir = d_is_dir(new_dentry);
|
||||
|
||||
if (!(flags & RENAME_EXCHANGE))
|
||||
error = may_delete(new_dir, new_dentry, is_dir);
|
||||
error = may_delete(mnt, new_dir, new_dentry, is_dir);
|
||||
else
|
||||
error = may_delete(new_dir, new_dentry, new_is_dir);
|
||||
error = may_delete(mnt, new_dir, new_dentry, new_is_dir);
|
||||
}
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -4221,12 +4290,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
*/
|
||||
if (new_dir != old_dir) {
|
||||
if (is_dir) {
|
||||
error = inode_permission(source, MAY_WRITE);
|
||||
error = inode_permission2(mnt, source, MAY_WRITE);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
if ((flags & RENAME_EXCHANGE) && new_is_dir) {
|
||||
error = inode_permission(target, MAY_WRITE);
|
||||
error = inode_permission2(mnt, target, MAY_WRITE);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
@ -4309,6 +4378,14 @@ out:
|
|||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_rename2);
|
||||
|
||||
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
struct inode **delegated_inode, unsigned int flags)
|
||||
{
|
||||
return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry, delegated_inode, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_rename);
|
||||
|
||||
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
||||
|
@ -4422,7 +4499,7 @@ retry_deleg:
|
|||
&new_path, new_dentry, flags);
|
||||
if (error)
|
||||
goto exit5;
|
||||
error = vfs_rename(old_path.dentry->d_inode, old_dentry,
|
||||
error = vfs_rename2(old_path.mnt, old_path.dentry->d_inode, old_dentry,
|
||||
new_path.dentry->d_inode, new_dentry,
|
||||
&delegated_inode, flags);
|
||||
exit5:
|
||||
|
@ -4467,7 +4544,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
|
|||
|
||||
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int error = may_create(dir, dentry);
|
||||
int error = may_create(NULL, dir, dentry);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
|
|
@ -577,6 +577,7 @@ int sb_prepare_remount_readonly(struct super_block *sb)
|
|||
|
||||
static void free_vfsmnt(struct mount *mnt)
|
||||
{
|
||||
kfree(mnt->mnt.data);
|
||||
kfree_const(mnt->mnt_devname);
|
||||
#ifdef CONFIG_SMP
|
||||
free_percpu(mnt->mnt_pcp);
|
||||
|
@ -966,11 +967,21 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
|
|||
if (!mnt)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mnt->mnt.data = NULL;
|
||||
if (type->alloc_mnt_data) {
|
||||
mnt->mnt.data = type->alloc_mnt_data();
|
||||
if (!mnt->mnt.data) {
|
||||
mnt_free_id(mnt);
|
||||
free_vfsmnt(mnt);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
if (flags & MS_KERNMOUNT)
|
||||
mnt->mnt.mnt_flags = MNT_INTERNAL;
|
||||
|
||||
root = mount_fs(type, flags, name, data);
|
||||
root = mount_fs(type, flags, name, &mnt->mnt, data);
|
||||
if (IS_ERR(root)) {
|
||||
kfree(mnt->mnt.data);
|
||||
mnt_free_id(mnt);
|
||||
free_vfsmnt(mnt);
|
||||
return ERR_CAST(root);
|
||||
|
@ -998,6 +1009,14 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
|||
if (!mnt)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (sb->s_op->clone_mnt_data) {
|
||||
mnt->mnt.data = sb->s_op->clone_mnt_data(old->mnt.data);
|
||||
if (!mnt->mnt.data) {
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
|
||||
mnt->mnt_group_id = 0; /* not a peer of original */
|
||||
else
|
||||
|
@ -1066,6 +1085,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
|||
return mnt;
|
||||
|
||||
out_free:
|
||||
kfree(mnt->mnt.data);
|
||||
mnt_free_id(mnt);
|
||||
free_vfsmnt(mnt);
|
||||
return ERR_PTR(err);
|
||||
|
@ -2234,8 +2254,14 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
|
|||
err = change_mount_flags(path->mnt, flags);
|
||||
else if (!capable(CAP_SYS_ADMIN))
|
||||
err = -EPERM;
|
||||
else
|
||||
err = do_remount_sb(sb, flags, data, 0);
|
||||
else {
|
||||
err = do_remount_sb2(path->mnt, sb, flags, data, 0);
|
||||
namespace_lock();
|
||||
lock_mount_hash();
|
||||
propagate_remount(mnt);
|
||||
unlock_mount_hash();
|
||||
namespace_unlock();
|
||||
}
|
||||
if (!err) {
|
||||
lock_mount_hash();
|
||||
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
|
||||
|
|
|
@ -488,7 +488,7 @@ static int fanotify_find_path(int dfd, const char __user *filename,
|
|||
}
|
||||
|
||||
/* you can only watch an inode if you have read permissions on it */
|
||||
ret = inode_permission(path->dentry->d_inode, MAY_READ);
|
||||
ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
|
||||
if (ret)
|
||||
path_put(path);
|
||||
out:
|
||||
|
|
|
@ -337,7 +337,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
|
|||
if (error)
|
||||
return error;
|
||||
/* you can only watch an inode if you have read permissions on it */
|
||||
error = inode_permission(path->dentry->d_inode, MAY_READ);
|
||||
error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
|
||||
if (error)
|
||||
path_put(path);
|
||||
return error;
|
||||
|
|
37
fs/open.c
37
fs/open.c
|
@ -34,8 +34,8 @@
|
|||
|
||||
#include "internal.h"
|
||||
|
||||
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
||||
struct file *filp)
|
||||
int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length,
|
||||
unsigned int time_attrs, struct file *filp)
|
||||
{
|
||||
int ret;
|
||||
struct iattr newattrs;
|
||||
|
@ -60,17 +60,24 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
|||
|
||||
mutex_lock(&dentry->d_inode->i_mutex);
|
||||
/* Note any delegations or leases have already been broken: */
|
||||
ret = notify_change(dentry, &newattrs, NULL);
|
||||
ret = notify_change2(mnt, dentry, &newattrs, NULL);
|
||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
||||
return ret;
|
||||
}
|
||||
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
||||
struct file *filp)
|
||||
{
|
||||
return do_truncate2(NULL, dentry, length, time_attrs, filp);
|
||||
}
|
||||
|
||||
long vfs_truncate(struct path *path, loff_t length)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct vfsmount *mnt;
|
||||
long error;
|
||||
|
||||
inode = path->dentry->d_inode;
|
||||
mnt = path->mnt;
|
||||
|
||||
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
|
@ -82,7 +89,7 @@ long vfs_truncate(struct path *path, loff_t length)
|
|||
if (error)
|
||||
goto out;
|
||||
|
||||
error = inode_permission(inode, MAY_WRITE);
|
||||
error = inode_permission2(mnt, inode, MAY_WRITE);
|
||||
if (error)
|
||||
goto mnt_drop_write_and_out;
|
||||
|
||||
|
@ -106,7 +113,7 @@ long vfs_truncate(struct path *path, loff_t length)
|
|||
if (!error)
|
||||
error = security_path_truncate(path);
|
||||
if (!error)
|
||||
error = do_truncate(path->dentry, length, 0, NULL);
|
||||
error = do_truncate2(mnt, path->dentry, length, 0, NULL);
|
||||
|
||||
put_write_and_out:
|
||||
put_write_access(inode);
|
||||
|
@ -155,6 +162,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
|
|||
{
|
||||
struct inode *inode;
|
||||
struct dentry *dentry;
|
||||
struct vfsmount *mnt;
|
||||
struct fd f;
|
||||
int error;
|
||||
|
||||
|
@ -171,6 +179,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
|
|||
small = 0;
|
||||
|
||||
dentry = f.file->f_path.dentry;
|
||||
mnt = f.file->f_path.mnt;
|
||||
inode = dentry->d_inode;
|
||||
error = -EINVAL;
|
||||
if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
|
||||
|
@ -190,7 +199,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
|
|||
if (!error)
|
||||
error = security_path_truncate(&f.file->f_path);
|
||||
if (!error)
|
||||
error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
|
||||
error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
|
||||
sb_end_write(inode->i_sb);
|
||||
out_putf:
|
||||
fdput(f);
|
||||
|
@ -340,6 +349,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
|||
struct cred *override_cred;
|
||||
struct path path;
|
||||
struct inode *inode;
|
||||
struct vfsmount *mnt;
|
||||
int res;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
|
||||
|
@ -370,6 +380,7 @@ retry:
|
|||
goto out;
|
||||
|
||||
inode = d_backing_inode(path.dentry);
|
||||
mnt = path.mnt;
|
||||
|
||||
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
|
||||
/*
|
||||
|
@ -381,7 +392,7 @@ retry:
|
|||
goto out_path_release;
|
||||
}
|
||||
|
||||
res = inode_permission(inode, mode | MAY_ACCESS);
|
||||
res = inode_permission2(mnt, inode, mode | MAY_ACCESS);
|
||||
/* SuS v2 requires we report a read only fs too */
|
||||
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
|
||||
goto out_path_release;
|
||||
|
@ -425,7 +436,7 @@ retry:
|
|||
if (error)
|
||||
goto out;
|
||||
|
||||
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||
error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||
if (error)
|
||||
goto dput_and_out;
|
||||
|
||||
|
@ -445,6 +456,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
|
|||
{
|
||||
struct fd f = fdget_raw(fd);
|
||||
struct inode *inode;
|
||||
struct vfsmount *mnt;
|
||||
int error = -EBADF;
|
||||
|
||||
error = -EBADF;
|
||||
|
@ -452,12 +464,13 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
|
|||
goto out;
|
||||
|
||||
inode = file_inode(f.file);
|
||||
mnt = f.file->f_path.mnt;
|
||||
|
||||
error = -ENOTDIR;
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
goto out_putf;
|
||||
|
||||
error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
|
||||
error = inode_permission2(mnt, inode, MAY_EXEC | MAY_CHDIR);
|
||||
if (!error)
|
||||
set_fs_pwd(current->fs, &f.file->f_path);
|
||||
out_putf:
|
||||
|
@ -476,7 +489,7 @@ retry:
|
|||
if (error)
|
||||
goto out;
|
||||
|
||||
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||
error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||
if (error)
|
||||
goto dput_and_out;
|
||||
|
||||
|
@ -516,7 +529,7 @@ retry_deleg:
|
|||
goto out_unlock;
|
||||
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
|
||||
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||
error = notify_change(path->dentry, &newattrs, &delegated_inode);
|
||||
error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
|
||||
out_unlock:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
if (delegated_inode) {
|
||||
|
@ -596,7 +609,7 @@ retry_deleg:
|
|||
mutex_lock(&inode->i_mutex);
|
||||
error = security_path_chown(path, uid, gid);
|
||||
if (!error)
|
||||
error = notify_change(path->dentry, &newattrs, &delegated_inode);
|
||||
error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
if (delegated_inode) {
|
||||
error = break_deleg_wait(&delegated_inode);
|
||||
|
|
29
fs/pnode.c
29
fs/pnode.c
|
@ -458,3 +458,32 @@ int propagate_umount(struct list_head *list)
|
|||
__propagate_umount(mnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterates over all slaves, and slaves of slaves.
|
||||
*/
|
||||
static struct mount *next_descendent(struct mount *root, struct mount *cur)
|
||||
{
|
||||
if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list))
|
||||
return first_slave(cur);
|
||||
do {
|
||||
if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list)
|
||||
return next_slave(cur);
|
||||
cur = cur->mnt_master;
|
||||
} while (cur != root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void propagate_remount(struct mount *mnt)
|
||||
{
|
||||
struct mount *m = mnt;
|
||||
struct super_block *sb = mnt->mnt.mnt_sb;
|
||||
|
||||
if (sb->s_op->copy_mnt_data) {
|
||||
m = next_descendent(mnt, m);
|
||||
while (m) {
|
||||
sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data);
|
||||
m = next_descendent(mnt, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ int propagate_mnt(struct mount *, struct mountpoint *, struct mount *,
|
|||
int propagate_umount(struct list_head *);
|
||||
int propagate_mount_busy(struct mount *, int);
|
||||
void propagate_mount_unlock(struct mount *);
|
||||
void propagate_remount(struct mount *);
|
||||
void mnt_release_group_id(struct mount *);
|
||||
int get_dominating_id(struct mount *mnt, const struct path *root);
|
||||
unsigned int mnt_get_count(struct mount *mnt);
|
||||
|
|
|
@ -118,7 +118,9 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
|
|||
if (err)
|
||||
goto out;
|
||||
show_mnt_opts(m, mnt);
|
||||
if (sb->s_op->show_options)
|
||||
if (sb->s_op->show_options2)
|
||||
err = sb->s_op->show_options2(mnt, m, mnt_path.dentry);
|
||||
else if (sb->s_op->show_options)
|
||||
err = sb->s_op->show_options(m, mnt_path.dentry);
|
||||
seq_puts(m, " 0 0\n");
|
||||
out:
|
||||
|
@ -178,7 +180,9 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
|
|||
err = show_sb_opts(m, sb);
|
||||
if (err)
|
||||
goto out;
|
||||
if (sb->s_op->show_options)
|
||||
if (sb->s_op->show_options2) {
|
||||
err = sb->s_op->show_options2(mnt, m, mnt->mnt_root);
|
||||
} else if (sb->s_op->show_options)
|
||||
err = sb->s_op->show_options(m, mnt->mnt_root);
|
||||
seq_putc(m, '\n');
|
||||
out:
|
||||
|
|
|
@ -30,11 +30,14 @@ static void inherit_derived_state(struct inode *parent, struct inode *child)
|
|||
ci->userid = pi->userid;
|
||||
ci->d_uid = pi->d_uid;
|
||||
ci->under_android = pi->under_android;
|
||||
ci->under_cache = pi->under_cache;
|
||||
ci->under_obb = pi->under_obb;
|
||||
set_top(ci, pi->top);
|
||||
}
|
||||
|
||||
/* helper function for derived state */
|
||||
void setup_derived_state(struct inode *inode, perm_t perm,
|
||||
userid_t userid, uid_t uid, bool under_android)
|
||||
void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
|
||||
uid_t uid, bool under_android, struct inode *top)
|
||||
{
|
||||
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
|
||||
|
||||
|
@ -42,84 +45,293 @@ void setup_derived_state(struct inode *inode, perm_t perm,
|
|||
info->userid = userid;
|
||||
info->d_uid = uid;
|
||||
info->under_android = under_android;
|
||||
info->under_cache = false;
|
||||
info->under_obb = false;
|
||||
set_top(info, top);
|
||||
}
|
||||
|
||||
/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
|
||||
void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry)
|
||||
void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name)
|
||||
{
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode);
|
||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
|
||||
struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
|
||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
|
||||
appid_t appid;
|
||||
struct qstr q_Android = QSTR_LITERAL("Android");
|
||||
struct qstr q_data = QSTR_LITERAL("data");
|
||||
struct qstr q_obb = QSTR_LITERAL("obb");
|
||||
struct qstr q_media = QSTR_LITERAL("media");
|
||||
struct qstr q_cache = QSTR_LITERAL("cache");
|
||||
|
||||
/* By default, each inode inherits from its parent.
|
||||
* the properties are maintained on its private fields
|
||||
* because the inode attributes will be modified with that of
|
||||
* its lower inode.
|
||||
* The derived state will be updated on the last
|
||||
* stage of each system call by fix_derived_permission(inode).
|
||||
* These values are used by our custom permission call instead
|
||||
* of using the inode permissions.
|
||||
*/
|
||||
|
||||
inherit_derived_state(parent->d_inode, dentry->d_inode);
|
||||
inherit_derived_state(d_inode(parent), d_inode(dentry));
|
||||
|
||||
/* Files don't get special labels */
|
||||
if (!S_ISDIR(d_inode(dentry)->i_mode))
|
||||
return;
|
||||
/* Derive custom permissions based on parent and current node */
|
||||
switch (parent_info->perm) {
|
||||
case PERM_INHERIT:
|
||||
case PERM_ANDROID_PACKAGE_CACHE:
|
||||
/* Already inherited above */
|
||||
break;
|
||||
case PERM_PRE_ROOT:
|
||||
/* Legacy internal layout places users at top level */
|
||||
info->perm = PERM_ROOT;
|
||||
info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
|
||||
info->userid = simple_strtoul(name->name, NULL, 10);
|
||||
set_top(info, &info->vfs_inode);
|
||||
break;
|
||||
case PERM_ROOT:
|
||||
/* Assume masked off by default. */
|
||||
if (!strcasecmp(newdentry->d_name.name, "Android")) {
|
||||
if (qstr_case_eq(name, &q_Android)) {
|
||||
/* App-specific directories inside; let anyone traverse */
|
||||
info->perm = PERM_ANDROID;
|
||||
info->under_android = true;
|
||||
set_top(info, &info->vfs_inode);
|
||||
}
|
||||
break;
|
||||
case PERM_ANDROID:
|
||||
if (!strcasecmp(newdentry->d_name.name, "data")) {
|
||||
if (qstr_case_eq(name, &q_data)) {
|
||||
/* App-specific directories inside; let anyone traverse */
|
||||
info->perm = PERM_ANDROID_DATA;
|
||||
} else if (!strcasecmp(newdentry->d_name.name, "obb")) {
|
||||
set_top(info, &info->vfs_inode);
|
||||
} else if (qstr_case_eq(name, &q_obb)) {
|
||||
/* App-specific directories inside; let anyone traverse */
|
||||
info->perm = PERM_ANDROID_OBB;
|
||||
info->under_obb = true;
|
||||
set_top(info, &info->vfs_inode);
|
||||
/* Single OBB directory is always shared */
|
||||
} else if (!strcasecmp(newdentry->d_name.name, "media")) {
|
||||
} else if (qstr_case_eq(name, &q_media)) {
|
||||
/* App-specific directories inside; let anyone traverse */
|
||||
info->perm = PERM_ANDROID_MEDIA;
|
||||
set_top(info, &info->vfs_inode);
|
||||
}
|
||||
break;
|
||||
case PERM_ANDROID_DATA:
|
||||
case PERM_ANDROID_OBB:
|
||||
case PERM_ANDROID_DATA:
|
||||
case PERM_ANDROID_MEDIA:
|
||||
appid = get_appid(sbi->pkgl_id, newdentry->d_name.name);
|
||||
if (appid != 0) {
|
||||
info->perm = PERM_ANDROID_PACKAGE;
|
||||
appid = get_appid(name->name);
|
||||
if (appid != 0 && !is_excluded(name->name, parent_info->userid)) {
|
||||
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
|
||||
}
|
||||
set_top(info, &info->vfs_inode);
|
||||
break;
|
||||
case PERM_ANDROID_PACKAGE:
|
||||
if (qstr_case_eq(name, &q_cache)) {
|
||||
info->perm = PERM_ANDROID_PACKAGE_CACHE;
|
||||
info->under_cache = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
|
||||
{
|
||||
get_derived_permission_new(parent, dentry, dentry);
|
||||
get_derived_permission_new(parent, dentry, &dentry->d_name);
|
||||
}
|
||||
|
||||
void get_derive_permissions_recursive(struct dentry *parent) {
|
||||
static appid_t get_type(const char *name) {
|
||||
const char *ext = strrchr(name, '.');
|
||||
appid_t id;
|
||||
|
||||
if (ext && ext[0]) {
|
||||
ext = &ext[1];
|
||||
id = get_ext_gid(ext);
|
||||
return id?:AID_MEDIA_RW;
|
||||
}
|
||||
return AID_MEDIA_RW;
|
||||
}
|
||||
|
||||
void fixup_lower_ownership(struct dentry* dentry, const char *name) {
|
||||
struct path path;
|
||||
struct inode *inode;
|
||||
struct inode *delegated_inode = NULL;
|
||||
int error;
|
||||
struct sdcardfs_inode_info *info;
|
||||
struct sdcardfs_inode_info *info_top;
|
||||
perm_t perm;
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
uid_t uid = sbi->options.fs_low_uid;
|
||||
gid_t gid = sbi->options.fs_low_gid;
|
||||
struct iattr newattrs;
|
||||
|
||||
info = SDCARDFS_I(d_inode(dentry));
|
||||
perm = info->perm;
|
||||
if (info->under_obb) {
|
||||
perm = PERM_ANDROID_OBB;
|
||||
} else if (info->under_cache) {
|
||||
perm = PERM_ANDROID_PACKAGE_CACHE;
|
||||
} else if (perm == PERM_INHERIT) {
|
||||
info_top = SDCARDFS_I(grab_top(info));
|
||||
perm = info_top->perm;
|
||||
release_top(info);
|
||||
}
|
||||
|
||||
switch (perm) {
|
||||
case PERM_ROOT:
|
||||
case PERM_ANDROID:
|
||||
case PERM_ANDROID_DATA:
|
||||
case PERM_ANDROID_MEDIA:
|
||||
case PERM_ANDROID_PACKAGE:
|
||||
case PERM_ANDROID_PACKAGE_CACHE:
|
||||
uid = multiuser_get_uid(info->userid, uid);
|
||||
break;
|
||||
case PERM_ANDROID_OBB:
|
||||
uid = AID_MEDIA_OBB;
|
||||
break;
|
||||
case PERM_PRE_ROOT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (perm) {
|
||||
case PERM_ROOT:
|
||||
case PERM_ANDROID:
|
||||
case PERM_ANDROID_DATA:
|
||||
case PERM_ANDROID_MEDIA:
|
||||
if (S_ISDIR(d_inode(dentry)->i_mode))
|
||||
gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
|
||||
else
|
||||
gid = multiuser_get_uid(info->userid, get_type(name));
|
||||
break;
|
||||
case PERM_ANDROID_OBB:
|
||||
gid = AID_MEDIA_OBB;
|
||||
break;
|
||||
case PERM_ANDROID_PACKAGE:
|
||||
if (info->d_uid != 0)
|
||||
gid = multiuser_get_ext_gid(info->userid, info->d_uid);
|
||||
else
|
||||
gid = multiuser_get_uid(info->userid, uid);
|
||||
break;
|
||||
case PERM_ANDROID_PACKAGE_CACHE:
|
||||
if (info->d_uid != 0)
|
||||
gid = multiuser_get_cache_gid(info->userid, info->d_uid);
|
||||
else
|
||||
gid = multiuser_get_uid(info->userid, uid);
|
||||
break;
|
||||
case PERM_PRE_ROOT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sdcardfs_get_lower_path(dentry, &path);
|
||||
inode = d_inode(path.dentry);
|
||||
if (d_inode(path.dentry)->i_gid.val != gid || d_inode(path.dentry)->i_uid.val != uid) {
|
||||
retry_deleg:
|
||||
newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE;
|
||||
newattrs.ia_uid = make_kuid(current_user_ns(), uid);
|
||||
newattrs.ia_gid = make_kgid(current_user_ns(), gid);
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
newattrs.ia_valid |=
|
||||
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
|
||||
mutex_lock(&inode->i_mutex);
|
||||
error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid);
|
||||
if (!error)
|
||||
error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
if (delegated_inode) {
|
||||
error = break_deleg_wait(&delegated_inode);
|
||||
if (!error)
|
||||
goto retry_deleg;
|
||||
}
|
||||
if (error)
|
||||
pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) {
|
||||
if (info->perm == PERM_ROOT)
|
||||
return (limit->flags & BY_USERID)?info->userid == limit->userid:1;
|
||||
if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int needs_fixup(perm_t perm) {
|
||||
if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB
|
||||
|| perm == PERM_ANDROID_MEDIA)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) {
|
||||
struct dentry *child;
|
||||
struct sdcardfs_inode_info *info;
|
||||
if (!dget(dentry))
|
||||
return;
|
||||
if (!d_inode(dentry)) {
|
||||
dput(dentry);
|
||||
return;
|
||||
}
|
||||
info = SDCARDFS_I(d_inode(dentry));
|
||||
|
||||
if (needs_fixup(info->perm)) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||
dget(child);
|
||||
if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) {
|
||||
if (d_inode(child)) {
|
||||
get_derived_permission(dentry, child);
|
||||
fixup_tmp_permissions(d_inode(child));
|
||||
dput(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dput(child);
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
} else if (descendant_may_need_fixup(info, limit)) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||
fixup_perms_recursive(child, limit);
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
dput(dentry);
|
||||
}
|
||||
|
||||
void drop_recursive(struct dentry *parent) {
|
||||
struct dentry *dentry;
|
||||
struct sdcardfs_inode_info *info;
|
||||
if (!d_inode(parent))
|
||||
return;
|
||||
info = SDCARDFS_I(d_inode(parent));
|
||||
spin_lock(&parent->d_lock);
|
||||
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
|
||||
if (dentry->d_inode) {
|
||||
mutex_lock(&dentry->d_inode->i_mutex);
|
||||
get_derived_permission(parent, dentry);
|
||||
fix_derived_permission(dentry->d_inode);
|
||||
get_derive_permissions_recursive(dentry);
|
||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
||||
if (d_inode(dentry)) {
|
||||
if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
|
||||
drop_recursive(dentry);
|
||||
d_drop(dentry);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&parent->d_lock);
|
||||
}
|
||||
|
||||
void fixup_top_recursive(struct dentry *parent) {
|
||||
struct dentry *dentry;
|
||||
struct sdcardfs_inode_info *info;
|
||||
|
||||
if (!d_inode(parent))
|
||||
return;
|
||||
info = SDCARDFS_I(d_inode(parent));
|
||||
spin_lock(&parent->d_lock);
|
||||
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
|
||||
if (d_inode(dentry)) {
|
||||
if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
|
||||
get_derived_permission(parent, dentry);
|
||||
fixup_tmp_permissions(d_inode(dentry));
|
||||
fixup_top_recursive(dentry);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&parent->d_lock);
|
||||
}
|
||||
|
||||
/* main function for updating derived permission */
|
||||
|
@ -127,7 +339,7 @@ inline void update_derived_permission_lock(struct dentry *dentry)
|
|||
{
|
||||
struct dentry *parent;
|
||||
|
||||
if(!dentry || !dentry->d_inode) {
|
||||
if(!dentry || !d_inode(dentry)) {
|
||||
printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
@ -135,9 +347,8 @@ inline void update_derived_permission_lock(struct dentry *dentry)
|
|||
* 1. need to check whether the dentry is updated or not
|
||||
* 2. remove the root dentry update
|
||||
*/
|
||||
mutex_lock(&dentry->d_inode->i_mutex);
|
||||
if(IS_ROOT(dentry)) {
|
||||
//setup_default_pre_root_state(dentry->d_inode);
|
||||
//setup_default_pre_root_state(d_inode(dentry));
|
||||
} else {
|
||||
parent = dget_parent(dentry);
|
||||
if(parent) {
|
||||
|
@ -145,19 +356,19 @@ inline void update_derived_permission_lock(struct dentry *dentry)
|
|||
dput(parent);
|
||||
}
|
||||
}
|
||||
fix_derived_permission(dentry->d_inode);
|
||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
||||
fixup_tmp_permissions(d_inode(dentry));
|
||||
}
|
||||
|
||||
int need_graft_path(struct dentry *dentry)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
|
||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
struct qstr obb = QSTR_LITERAL("obb");
|
||||
|
||||
if(parent_info->perm == PERM_ANDROID &&
|
||||
!strcasecmp(dentry->d_name.name, "obb")) {
|
||||
qstr_case_eq(&dentry->d_name, &obb)) {
|
||||
|
||||
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
|
||||
if(!(sbi->options.multiuser == false
|
||||
|
@ -194,7 +405,7 @@ int is_obbpath_invalid(struct dentry *dent)
|
|||
} else {
|
||||
obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX);
|
||||
if (d_unhashed(di->lower_path.dentry) ||
|
||||
strcasecmp(sbi->obbpath_s, obbpath_s)) {
|
||||
!str_case_eq(sbi->obbpath_s, obbpath_s)) {
|
||||
ret = 1;
|
||||
}
|
||||
kfree(path_buf);
|
||||
|
@ -212,17 +423,18 @@ int is_base_obbpath(struct dentry *dentry)
|
|||
{
|
||||
int ret = 0;
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
|
||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
struct qstr q_obb = QSTR_LITERAL("obb");
|
||||
|
||||
spin_lock(&SDCARDFS_D(dentry)->lock);
|
||||
if (sbi->options.multiuser) {
|
||||
if(parent_info->perm == PERM_PRE_ROOT &&
|
||||
!strcasecmp(dentry->d_name.name, "obb")) {
|
||||
qstr_case_eq(&dentry->d_name, &q_obb)) {
|
||||
ret = 1;
|
||||
}
|
||||
} else if (parent_info->perm == PERM_ANDROID &&
|
||||
!strcasecmp(dentry->d_name.name, "obb")) {
|
||||
qstr_case_eq(&dentry->d_name, &q_obb)) {
|
||||
ret = 1;
|
||||
}
|
||||
spin_unlock(&SDCARDFS_D(dentry)->lock);
|
||||
|
|
|
@ -216,7 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
|
|||
goto out_err;
|
||||
}
|
||||
|
||||
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
|
||||
if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" dentry: %s, task:%s\n",
|
||||
__func__, dentry->d_name.name, current->comm);
|
||||
|
@ -225,7 +225,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
|
|||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(sbi, saved_cred);
|
||||
OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode));
|
||||
|
||||
file->private_data =
|
||||
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
|
||||
|
|
|
@ -19,18 +19,24 @@
|
|||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
#include <linux/fs_struct.h>
|
||||
|
||||
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
|
||||
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
|
||||
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info)
|
||||
{
|
||||
struct cred * cred;
|
||||
const struct cred * old_cred;
|
||||
uid_t uid;
|
||||
|
||||
cred = prepare_creds();
|
||||
if (!cred)
|
||||
return NULL;
|
||||
|
||||
cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid);
|
||||
if (info->under_obb)
|
||||
uid = AID_MEDIA_OBB;
|
||||
else
|
||||
uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid);
|
||||
cred->fsuid = make_kuid(&init_user_ns, uid);
|
||||
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
|
||||
|
||||
old_cred = override_creds(cred);
|
||||
|
@ -53,11 +59,14 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
|
|||
{
|
||||
int err;
|
||||
struct dentry *lower_dentry;
|
||||
struct vfsmount *lower_dentry_mnt;
|
||||
struct dentry *lower_parent_dentry = NULL;
|
||||
struct path lower_path;
|
||||
const struct cred *saved_cred = NULL;
|
||||
struct fs_struct *saved_fs;
|
||||
struct fs_struct *copied_fs;
|
||||
|
||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
||||
if(!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" dentry: %s, task:%s\n",
|
||||
__func__, dentry->d_name.name, current->comm);
|
||||
|
@ -66,15 +75,26 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
|
|||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
lower_dentry = lower_path.dentry;
|
||||
lower_dentry_mnt = lower_path.mnt;
|
||||
lower_parent_dentry = lock_parent(lower_dentry);
|
||||
|
||||
/* set last 16bytes of mode field to 0664 */
|
||||
mode = (mode & S_IFMT) | 00664;
|
||||
err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
|
||||
|
||||
/* temporarily change umask for lower fs write */
|
||||
saved_fs = current->fs;
|
||||
copied_fs = copy_fs_struct(current->fs);
|
||||
if (!copied_fs) {
|
||||
err = -ENOMEM;
|
||||
goto out_unlock;
|
||||
}
|
||||
current->fs = copied_fs;
|
||||
current->fs->umask = 0;
|
||||
err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
@ -83,8 +103,12 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
|
|||
goto out;
|
||||
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
|
||||
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
|
||||
fixup_lower_ownership(dentry, dentry->d_name.name);
|
||||
|
||||
out:
|
||||
current->fs = saved_fs;
|
||||
free_fs_struct(copied_fs);
|
||||
out_unlock:
|
||||
unlock_dir(lower_parent_dentry);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
REVERT_CRED(saved_cred);
|
||||
|
@ -138,12 +162,13 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
|
|||
{
|
||||
int err;
|
||||
struct dentry *lower_dentry;
|
||||
struct vfsmount *lower_mnt;
|
||||
struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
|
||||
struct dentry *lower_dir_dentry;
|
||||
struct path lower_path;
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
||||
if(!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" dentry: %s, task:%s\n",
|
||||
__func__, dentry->d_name.name, current->comm);
|
||||
|
@ -152,14 +177,15 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
|
|||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
lower_dentry = lower_path.dentry;
|
||||
lower_mnt = lower_path.mnt;
|
||||
dget(lower_dentry);
|
||||
lower_dir_dentry = lock_parent(lower_dentry);
|
||||
|
||||
err = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
|
||||
err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL);
|
||||
|
||||
/*
|
||||
* Note: unlinking on top of NFS can cause silly-renamed files.
|
||||
|
@ -240,18 +266,19 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
|
|||
int err;
|
||||
int make_nomedia_in_obb = 0;
|
||||
struct dentry *lower_dentry;
|
||||
struct vfsmount *lower_mnt;
|
||||
struct dentry *lower_parent_dentry = NULL;
|
||||
struct path lower_path;
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
const struct cred *saved_cred = NULL;
|
||||
struct sdcardfs_inode_info *pi = SDCARDFS_I(dir);
|
||||
char *page_buf;
|
||||
char *nomedia_dir_name;
|
||||
char *nomedia_fullpath;
|
||||
int fullpath_namelen;
|
||||
int touch_err = 0;
|
||||
struct fs_struct *saved_fs;
|
||||
struct fs_struct *copied_fs;
|
||||
struct qstr q_obb = QSTR_LITERAL("obb");
|
||||
struct qstr q_data = QSTR_LITERAL("data");
|
||||
|
||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
||||
if(!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" dentry: %s, task:%s\n",
|
||||
__func__, dentry->d_name.name, current->comm);
|
||||
|
@ -260,7 +287,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
|
|||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||
|
||||
/* check disk space */
|
||||
if (!check_min_free_space(dentry, 0, 1)) {
|
||||
|
@ -272,14 +299,28 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
|
|||
/* the lower_dentry is negative here */
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
lower_dentry = lower_path.dentry;
|
||||
lower_mnt = lower_path.mnt;
|
||||
lower_parent_dentry = lock_parent(lower_dentry);
|
||||
|
||||
/* set last 16bytes of mode field to 0775 */
|
||||
mode = (mode & S_IFMT) | 00775;
|
||||
err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode);
|
||||
|
||||
if (err)
|
||||
/* temporarily change umask for lower fs write */
|
||||
saved_fs = current->fs;
|
||||
copied_fs = copy_fs_struct(current->fs);
|
||||
if (!copied_fs) {
|
||||
err = -ENOMEM;
|
||||
unlock_dir(lower_parent_dentry);
|
||||
goto out_unlock;
|
||||
}
|
||||
current->fs = copied_fs;
|
||||
current->fs->umask = 0;
|
||||
err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode);
|
||||
|
||||
if (err) {
|
||||
unlock_dir(lower_parent_dentry);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if it is a local obb dentry, setup it with the base obbpath */
|
||||
if(need_graft_path(dentry)) {
|
||||
|
@ -301,58 +342,38 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
|
|||
}
|
||||
|
||||
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
|
||||
if (err)
|
||||
if (err) {
|
||||
unlock_dir(lower_parent_dentry);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
|
||||
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
|
||||
/* update number of links on parent directory */
|
||||
set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
|
||||
|
||||
if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb"))
|
||||
fixup_lower_ownership(dentry, dentry->d_name.name);
|
||||
unlock_dir(lower_parent_dentry);
|
||||
if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb))
|
||||
&& (pi->perm == PERM_ANDROID) && (pi->userid == 0))
|
||||
make_nomedia_in_obb = 1;
|
||||
|
||||
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
|
||||
if (make_nomedia_in_obb ||
|
||||
((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) {
|
||||
|
||||
page_buf = (char *)__get_free_page(GFP_KERNEL);
|
||||
if (!page_buf) {
|
||||
printk(KERN_ERR "sdcardfs: failed to allocate page buf\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE);
|
||||
if (IS_ERR(nomedia_dir_name)) {
|
||||
free_page((unsigned long)page_buf);
|
||||
printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1;
|
||||
fullpath_namelen += strlen("/.nomedia");
|
||||
nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL);
|
||||
if (!nomedia_fullpath) {
|
||||
free_page((unsigned long)page_buf);
|
||||
printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
strcpy(nomedia_fullpath, nomedia_dir_name);
|
||||
free_page((unsigned long)page_buf);
|
||||
strcat(nomedia_fullpath, "/.nomedia");
|
||||
touch_err = touch(nomedia_fullpath, 0664);
|
||||
((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) {
|
||||
REVERT_CRED(saved_cred);
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry)));
|
||||
set_fs_pwd(current->fs, &lower_path);
|
||||
touch_err = touch(".nomedia", 0664);
|
||||
if (touch_err) {
|
||||
printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n",
|
||||
nomedia_fullpath, touch_err);
|
||||
kfree(nomedia_fullpath);
|
||||
printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n",
|
||||
lower_path.dentry->d_name.name, touch_err);
|
||||
goto out;
|
||||
}
|
||||
kfree(nomedia_fullpath);
|
||||
}
|
||||
out:
|
||||
unlock_dir(lower_parent_dentry);
|
||||
current->fs = saved_fs;
|
||||
free_fs_struct(copied_fs);
|
||||
out_unlock:
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
out_revert:
|
||||
REVERT_CRED(saved_cred);
|
||||
|
@ -364,11 +385,12 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
{
|
||||
struct dentry *lower_dentry;
|
||||
struct dentry *lower_dir_dentry;
|
||||
struct vfsmount *lower_mnt;
|
||||
int err;
|
||||
struct path lower_path;
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
||||
if(!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" dentry: %s, task:%s\n",
|
||||
__func__, dentry->d_name.name, current->comm);
|
||||
|
@ -377,16 +399,17 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||
|
||||
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
|
||||
* the dentry on the original path should be deleted. */
|
||||
sdcardfs_get_real_lower(dentry, &lower_path);
|
||||
|
||||
lower_dentry = lower_path.dentry;
|
||||
lower_mnt = lower_path.mnt;
|
||||
lower_dir_dentry = lock_parent(lower_dentry);
|
||||
|
||||
err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
|
||||
err = vfs_rmdir2(lower_mnt, d_inode(lower_dir_dentry), lower_dentry);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
@ -450,13 +473,13 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
struct dentry *lower_new_dentry = NULL;
|
||||
struct dentry *lower_old_dir_dentry = NULL;
|
||||
struct dentry *lower_new_dir_dentry = NULL;
|
||||
struct vfsmount *lower_mnt = NULL;
|
||||
struct dentry *trap = NULL;
|
||||
struct dentry *new_parent = NULL;
|
||||
struct path lower_old_path, lower_new_path;
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) ||
|
||||
!check_caller_access_to_name(new_dir, new_dentry->d_name.name)) {
|
||||
if(!check_caller_access_to_name(old_dir, &old_dentry->d_name) ||
|
||||
!check_caller_access_to_name(new_dir, &new_dentry->d_name)) {
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" new_dentry: %s, task:%s\n",
|
||||
__func__, new_dentry->d_name.name, current->comm);
|
||||
|
@ -465,12 +488,13 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred);
|
||||
OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir));
|
||||
|
||||
sdcardfs_get_real_lower(old_dentry, &lower_old_path);
|
||||
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
|
||||
lower_old_dentry = lower_old_path.dentry;
|
||||
lower_new_dentry = lower_new_path.dentry;
|
||||
lower_mnt = lower_old_path.mnt;
|
||||
lower_old_dir_dentry = dget_parent(lower_old_dentry);
|
||||
lower_new_dir_dentry = dget_parent(lower_new_dentry);
|
||||
|
||||
|
@ -486,7 +510,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry,
|
||||
err = vfs_rename2(lower_mnt,
|
||||
d_inode(lower_old_dir_dentry), lower_old_dentry,
|
||||
d_inode(lower_new_dir_dentry), lower_new_dentry,
|
||||
NULL, 0);
|
||||
if (err)
|
||||
|
@ -499,25 +524,11 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
if (new_dir != old_dir) {
|
||||
sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry));
|
||||
fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry));
|
||||
|
||||
/* update the derived permission of the old_dentry
|
||||
* with its new parent
|
||||
*/
|
||||
new_parent = dget_parent(new_dentry);
|
||||
if(new_parent) {
|
||||
if(d_inode(old_dentry)) {
|
||||
update_derived_permission_lock(old_dentry);
|
||||
}
|
||||
dput(new_parent);
|
||||
}
|
||||
}
|
||||
/* At this point, not all dentry information has been moved, so
|
||||
* we pass along new_dentry for the name.*/
|
||||
mutex_lock(&d_inode(old_dentry)->i_mutex);
|
||||
get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
|
||||
fix_derived_permission(d_inode(old_dentry));
|
||||
get_derive_permissions_recursive(old_dentry);
|
||||
mutex_unlock(&d_inode(old_dentry)->i_mutex);
|
||||
get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name);
|
||||
fixup_tmp_permissions(d_inode(old_dentry));
|
||||
fixup_lower_ownership(old_dentry, new_dentry->d_name.name);
|
||||
drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */
|
||||
out:
|
||||
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
||||
dput(lower_old_dir_dentry);
|
||||
|
@ -586,16 +597,63 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int sdcardfs_permission(struct inode *inode, int mask)
|
||||
static int sdcardfs_permission_wrn(struct inode *inode, int mask)
|
||||
{
|
||||
WARN(1, "sdcardfs does not support permission. Use permission2.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void copy_attrs(struct inode *dest, const struct inode *src)
|
||||
{
|
||||
dest->i_mode = src->i_mode;
|
||||
dest->i_uid = src->i_uid;
|
||||
dest->i_gid = src->i_gid;
|
||||
dest->i_rdev = src->i_rdev;
|
||||
dest->i_atime = src->i_atime;
|
||||
dest->i_mtime = src->i_mtime;
|
||||
dest->i_ctime = src->i_ctime;
|
||||
dest->i_blkbits = src->i_blkbits;
|
||||
dest->i_flags = src->i_flags;
|
||||
#ifdef CONFIG_FS_POSIX_ACL
|
||||
dest->i_acl = src->i_acl;
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY
|
||||
dest->i_security = src->i_security;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||
{
|
||||
int err;
|
||||
struct inode tmp;
|
||||
struct inode *top = grab_top(SDCARDFS_I(inode));
|
||||
|
||||
if (!top) {
|
||||
release_top(SDCARDFS_I(inode));
|
||||
WARN(1, "Top value was null!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Permission check on sdcardfs inode.
|
||||
* Calling process should have AID_SDCARD_RW permission
|
||||
* Since generic_permission only needs i_mode, i_uid,
|
||||
* i_gid, and i_sb, we can create a fake inode to pass
|
||||
* this information down in.
|
||||
*
|
||||
* The underlying code may attempt to take locks in some
|
||||
* cases for features we're not using, but if that changes,
|
||||
* locks must be dealt with to avoid undefined behavior.
|
||||
*/
|
||||
err = generic_permission(inode, mask);
|
||||
|
||||
copy_attrs(&tmp, inode);
|
||||
tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
|
||||
tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
|
||||
tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
|
||||
release_top(SDCARDFS_I(inode));
|
||||
tmp.i_sb = inode->i_sb;
|
||||
if (IS_POSIXACL(inode))
|
||||
printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__);
|
||||
err = generic_permission(&tmp, mask);
|
||||
/* XXX
|
||||
* Original sdcardfs code calls inode_permission(lower_inode,.. )
|
||||
* for checking inode permission. But doing such things here seems
|
||||
|
@ -624,30 +682,70 @@ static int sdcardfs_permission(struct inode *inode, int mask)
|
|||
|
||||
}
|
||||
|
||||
static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
||||
static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia)
|
||||
{
|
||||
WARN(1, "sdcardfs does not support setattr. User setattr2.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia)
|
||||
{
|
||||
int err;
|
||||
struct dentry *lower_dentry;
|
||||
struct vfsmount *lower_mnt;
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
struct path lower_path;
|
||||
struct iattr lower_ia;
|
||||
struct dentry *parent;
|
||||
struct inode tmp;
|
||||
struct inode *top;
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
inode = d_inode(dentry);
|
||||
top = grab_top(SDCARDFS_I(inode));
|
||||
|
||||
if (!top) {
|
||||
release_top(SDCARDFS_I(inode));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Permission check on sdcardfs inode.
|
||||
* Calling process should have AID_SDCARD_RW permission
|
||||
* Since generic_permission only needs i_mode, i_uid,
|
||||
* i_gid, and i_sb, we can create a fake inode to pass
|
||||
* this information down in.
|
||||
*
|
||||
* The underlying code may attempt to take locks in some
|
||||
* cases for features we're not using, but if that changes,
|
||||
* locks must be dealt with to avoid undefined behavior.
|
||||
*
|
||||
*/
|
||||
copy_attrs(&tmp, inode);
|
||||
tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
|
||||
tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
|
||||
tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
|
||||
tmp.i_size = i_size_read(inode);
|
||||
release_top(SDCARDFS_I(inode));
|
||||
tmp.i_sb = inode->i_sb;
|
||||
|
||||
/*
|
||||
* Check if user has permission to change inode. We don't check if
|
||||
* this user can change the lower inode: that should happen when
|
||||
* calling notify_change on the lower inode.
|
||||
*/
|
||||
err = inode_change_ok(inode, ia);
|
||||
/* prepare our own lower struct iattr (with the lower file) */
|
||||
memcpy(&lower_ia, ia, sizeof(lower_ia));
|
||||
/* Allow touch updating timestamps. A previous permission check ensures
|
||||
* we have write access. Changes to mode, owner, and group are ignored*/
|
||||
ia->ia_valid |= ATTR_FORCE;
|
||||
err = inode_change_ok(&tmp, ia);
|
||||
|
||||
/* no vfs_XXX operations required, cred overriding will be skipped. wj*/
|
||||
if (!err) {
|
||||
/* check the Android group ID */
|
||||
parent = dget_parent(dentry);
|
||||
if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
|
||||
if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" dentry: %s, task:%s\n",
|
||||
__func__, dentry->d_name.name, current->comm);
|
||||
|
@ -659,12 +757,14 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
|||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode));
|
||||
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
lower_dentry = lower_path.dentry;
|
||||
lower_mnt = lower_path.mnt;
|
||||
lower_inode = sdcardfs_lower_inode(inode);
|
||||
|
||||
/* prepare our own lower struct iattr (with the lower file) */
|
||||
memcpy(&lower_ia, ia, sizeof(lower_ia));
|
||||
if (ia->ia_valid & ATTR_FILE)
|
||||
lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file);
|
||||
|
||||
|
@ -681,7 +781,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
|||
if (current->mm)
|
||||
down_write(¤t->mm->mmap_sem);
|
||||
if (ia->ia_valid & ATTR_SIZE) {
|
||||
err = inode_newsize_ok(inode, ia->ia_size);
|
||||
err = inode_newsize_ok(&tmp, ia->ia_size);
|
||||
if (err) {
|
||||
if (current->mm)
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
|
@ -704,7 +804,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
|||
* tries to open(), unlink(), then ftruncate() a file.
|
||||
*/
|
||||
mutex_lock(&d_inode(lower_dentry)->i_mutex);
|
||||
err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */
|
||||
err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */
|
||||
NULL);
|
||||
mutex_unlock(&d_inode(lower_dentry)->i_mutex);
|
||||
if (current->mm)
|
||||
|
@ -723,10 +823,35 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
|||
|
||||
out:
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
REVERT_CRED(saved_cred);
|
||||
out_err:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat)
|
||||
{
|
||||
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
|
||||
struct inode *top = grab_top(info);
|
||||
if (!top)
|
||||
return -EINVAL;
|
||||
|
||||
stat->dev = inode->i_sb->s_dev;
|
||||
stat->ino = inode->i_ino;
|
||||
stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
|
||||
stat->nlink = inode->i_nlink;
|
||||
stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
|
||||
stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
|
||||
stat->rdev = inode->i_rdev;
|
||||
stat->size = i_size_read(inode);
|
||||
stat->atime = inode->i_atime;
|
||||
stat->mtime = inode->i_mtime;
|
||||
stat->ctime = inode->i_ctime;
|
||||
stat->blksize = (1 << inode->i_blkbits);
|
||||
stat->blocks = inode->i_blocks;
|
||||
release_top(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
struct kstat *stat)
|
||||
{
|
||||
|
@ -735,9 +860,10 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
|||
struct inode *lower_inode;
|
||||
struct path lower_path;
|
||||
struct dentry *parent;
|
||||
int err;
|
||||
|
||||
parent = dget_parent(dentry);
|
||||
if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
|
||||
if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" dentry: %s, task:%s\n",
|
||||
__func__, dentry->d_name.name, current->comm);
|
||||
|
@ -752,19 +878,17 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
|||
lower_dentry = lower_path.dentry;
|
||||
lower_inode = sdcardfs_lower_inode(inode);
|
||||
|
||||
|
||||
sdcardfs_copy_and_fix_attrs(inode, lower_inode);
|
||||
fsstack_copy_inode_size(inode, lower_inode);
|
||||
|
||||
|
||||
generic_fillattr(inode, stat);
|
||||
err = sdcardfs_fillattr(mnt, inode, stat);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
const struct inode_operations sdcardfs_symlink_iops = {
|
||||
.permission = sdcardfs_permission,
|
||||
.setattr = sdcardfs_setattr,
|
||||
.permission2 = sdcardfs_permission,
|
||||
.setattr2 = sdcardfs_setattr,
|
||||
/* XXX Following operations are implemented,
|
||||
* but FUSE(sdcard) or FAT does not support them
|
||||
* These methods are *NOT* perfectly tested.
|
||||
|
@ -777,14 +901,14 @@ const struct inode_operations sdcardfs_symlink_iops = {
|
|||
const struct inode_operations sdcardfs_dir_iops = {
|
||||
.create = sdcardfs_create,
|
||||
.lookup = sdcardfs_lookup,
|
||||
#if 0
|
||||
.permission = sdcardfs_permission,
|
||||
#endif
|
||||
.permission = sdcardfs_permission_wrn,
|
||||
.permission2 = sdcardfs_permission,
|
||||
.unlink = sdcardfs_unlink,
|
||||
.mkdir = sdcardfs_mkdir,
|
||||
.rmdir = sdcardfs_rmdir,
|
||||
.rename = sdcardfs_rename,
|
||||
.setattr = sdcardfs_setattr,
|
||||
.setattr = sdcardfs_setattr_wrn,
|
||||
.setattr2 = sdcardfs_setattr,
|
||||
.getattr = sdcardfs_getattr,
|
||||
/* XXX Following operations are implemented,
|
||||
* but FUSE(sdcard) or FAT does not support them
|
||||
|
@ -796,7 +920,9 @@ const struct inode_operations sdcardfs_dir_iops = {
|
|||
};
|
||||
|
||||
const struct inode_operations sdcardfs_main_iops = {
|
||||
.permission = sdcardfs_permission,
|
||||
.setattr = sdcardfs_setattr,
|
||||
.permission = sdcardfs_permission_wrn,
|
||||
.permission2 = sdcardfs_permission,
|
||||
.setattr = sdcardfs_setattr_wrn,
|
||||
.setattr2 = sdcardfs_setattr,
|
||||
.getattr = sdcardfs_getattr,
|
||||
};
|
||||
|
|
|
@ -179,7 +179,7 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
|
|||
struct inode *lower_inode;
|
||||
struct super_block *lower_sb;
|
||||
|
||||
lower_inode = lower_path->dentry->d_inode;
|
||||
lower_inode = d_inode(lower_path->dentry);
|
||||
lower_sb = sdcardfs_lower_super(sb);
|
||||
|
||||
/* check that the lower file system didn't cross a mount point */
|
||||
|
@ -219,9 +219,9 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
|
|||
struct vfsmount *lower_dir_mnt;
|
||||
struct dentry *lower_dir_dentry = NULL;
|
||||
struct dentry *lower_dentry;
|
||||
const char *name;
|
||||
const struct qstr *name;
|
||||
struct path lower_path;
|
||||
struct qstr this;
|
||||
struct qstr dname;
|
||||
struct sdcardfs_sb_info *sbi;
|
||||
|
||||
sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
|
@ -231,15 +231,39 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
|
|||
if (IS_ROOT(dentry))
|
||||
goto out;
|
||||
|
||||
name = dentry->d_name.name;
|
||||
name = &dentry->d_name;
|
||||
|
||||
/* now start the actual lookup procedure */
|
||||
lower_dir_dentry = lower_parent_path->dentry;
|
||||
lower_dir_mnt = lower_parent_path->mnt;
|
||||
|
||||
/* Use vfs_path_lookup to check if the dentry exists or not */
|
||||
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
|
||||
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0,
|
||||
&lower_path);
|
||||
/* check for other cases */
|
||||
if (err == -ENOENT) {
|
||||
struct dentry *child;
|
||||
struct dentry *match = NULL;
|
||||
mutex_lock(&d_inode(lower_dir_dentry)->i_mutex);
|
||||
spin_lock(&lower_dir_dentry->d_lock);
|
||||
list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) {
|
||||
if (child && d_inode(child)) {
|
||||
if (qstr_case_eq(&child->d_name, name)) {
|
||||
match = dget(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&lower_dir_dentry->d_lock);
|
||||
mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex);
|
||||
if (match) {
|
||||
err = vfs_path_lookup(lower_dir_dentry,
|
||||
lower_dir_mnt,
|
||||
match->d_name.name, 0,
|
||||
&lower_path);
|
||||
dput(match);
|
||||
}
|
||||
}
|
||||
|
||||
/* no error: handle positive dentries */
|
||||
if (!err) {
|
||||
|
@ -283,14 +307,14 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
|
|||
goto out;
|
||||
|
||||
/* instatiate a new negative dentry */
|
||||
this.name = name;
|
||||
this.len = strlen(name);
|
||||
this.hash = full_name_hash(this.name, this.len);
|
||||
lower_dentry = d_lookup(lower_dir_dentry, &this);
|
||||
dname.name = name->name;
|
||||
dname.len = name->len;
|
||||
dname.hash = full_name_hash(dname.name, dname.len);
|
||||
lower_dentry = d_lookup(lower_dir_dentry, &dname);
|
||||
if (lower_dentry)
|
||||
goto setup_lower;
|
||||
|
||||
lower_dentry = d_alloc(lower_dir_dentry, &this);
|
||||
lower_dentry = d_alloc(lower_dir_dentry, &dname);
|
||||
if (!lower_dentry) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
|
@ -335,7 +359,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
parent = dget_parent(dentry);
|
||||
|
||||
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
|
||||
if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||
ret = ERR_PTR(-EACCES);
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" dentry: %s, task:%s\n",
|
||||
|
@ -344,7 +368,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
|||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred);
|
||||
OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||
|
||||
sdcardfs_get_lower_path(parent, &lower_parent_path);
|
||||
|
||||
|
@ -362,18 +386,17 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
|||
}
|
||||
if (ret)
|
||||
dentry = ret;
|
||||
if (dentry->d_inode) {
|
||||
fsstack_copy_attr_times(dentry->d_inode,
|
||||
sdcardfs_lower_inode(dentry->d_inode));
|
||||
/* get drived permission */
|
||||
mutex_lock(&dentry->d_inode->i_mutex);
|
||||
if (d_inode(dentry)) {
|
||||
fsstack_copy_attr_times(d_inode(dentry),
|
||||
sdcardfs_lower_inode(d_inode(dentry)));
|
||||
/* get derived permission */
|
||||
get_derived_permission(parent, dentry);
|
||||
fix_derived_permission(dentry->d_inode);
|
||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
||||
fixup_tmp_permissions(d_inode(dentry));
|
||||
fixup_lower_ownership(dentry, dentry->d_name.name);
|
||||
}
|
||||
/* update parent directory's atime */
|
||||
fsstack_copy_attr_atime(parent->d_inode,
|
||||
sdcardfs_lower_inode(parent->d_inode));
|
||||
fsstack_copy_attr_atime(d_inode(parent),
|
||||
sdcardfs_lower_inode(d_inode(parent)));
|
||||
|
||||
out:
|
||||
sdcardfs_put_lower_path(parent, &lower_parent_path);
|
||||
|
|
|
@ -28,7 +28,6 @@ enum {
|
|||
Opt_fsgid,
|
||||
Opt_gid,
|
||||
Opt_debug,
|
||||
Opt_lower_fs,
|
||||
Opt_mask,
|
||||
Opt_multiuser, // May need?
|
||||
Opt_userid,
|
||||
|
@ -49,7 +48,8 @@ static const match_table_t sdcardfs_tokens = {
|
|||
};
|
||||
|
||||
static int parse_options(struct super_block *sb, char *options, int silent,
|
||||
int *debug, struct sdcardfs_mount_options *opts)
|
||||
int *debug, struct sdcardfs_vfsmount_options *vfsopts,
|
||||
struct sdcardfs_mount_options *opts)
|
||||
{
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
@ -58,10 +58,10 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
|||
/* by default, we use AID_MEDIA_RW as uid, gid */
|
||||
opts->fs_low_uid = AID_MEDIA_RW;
|
||||
opts->fs_low_gid = AID_MEDIA_RW;
|
||||
opts->mask = 0;
|
||||
vfsopts->mask = 0;
|
||||
opts->multiuser = false;
|
||||
opts->fs_user_id = 0;
|
||||
opts->gid = 0;
|
||||
vfsopts->gid = 0;
|
||||
/* by default, 0MB is reserved */
|
||||
opts->reserved_mb = 0;
|
||||
|
||||
|
@ -94,7 +94,7 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
|||
case Opt_gid:
|
||||
if (match_int(&args[0], &option))
|
||||
return 0;
|
||||
opts->gid = option;
|
||||
vfsopts->gid = option;
|
||||
break;
|
||||
case Opt_userid:
|
||||
if (match_int(&args[0], &option))
|
||||
|
@ -104,7 +104,7 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
|||
case Opt_mask:
|
||||
if (match_int(&args[0], &option))
|
||||
return 0;
|
||||
opts->mask = option;
|
||||
vfsopts->mask = option;
|
||||
break;
|
||||
case Opt_multiuser:
|
||||
opts->multiuser = true;
|
||||
|
@ -135,6 +135,65 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int parse_options_remount(struct super_block *sb, char *options, int silent,
|
||||
struct sdcardfs_vfsmount_options *vfsopts)
|
||||
{
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int option;
|
||||
int debug;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token;
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, sdcardfs_tokens, args);
|
||||
|
||||
switch (token) {
|
||||
case Opt_debug:
|
||||
debug = 1;
|
||||
break;
|
||||
case Opt_gid:
|
||||
if (match_int(&args[0], &option))
|
||||
return 0;
|
||||
vfsopts->gid = option;
|
||||
|
||||
break;
|
||||
case Opt_mask:
|
||||
if (match_int(&args[0], &option))
|
||||
return 0;
|
||||
vfsopts->mask = option;
|
||||
break;
|
||||
case Opt_multiuser:
|
||||
case Opt_userid:
|
||||
case Opt_fsuid:
|
||||
case Opt_fsgid:
|
||||
case Opt_reserved_mb:
|
||||
printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p);
|
||||
break;
|
||||
/* unknown option */
|
||||
default:
|
||||
if (!silent) {
|
||||
printk( KERN_ERR "Unrecognized mount option \"%s\" "
|
||||
"or missing value", p);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug);
|
||||
printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid);
|
||||
printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* our custom d_alloc_root work-alike
|
||||
|
@ -172,14 +231,15 @@ EXPORT_SYMBOL_GPL(sdcardfs_super_list);
|
|||
* There is no need to lock the sdcardfs_super_info's rwsem as there is no
|
||||
* way anyone can have a reference to the superblock at this point in time.
|
||||
*/
|
||||
static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
||||
void *raw_data, int silent)
|
||||
static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
|
||||
const char *dev_name, void *raw_data, int silent)
|
||||
{
|
||||
int err = 0;
|
||||
int debug;
|
||||
struct super_block *lower_sb;
|
||||
struct path lower_path;
|
||||
struct sdcardfs_sb_info *sb_info;
|
||||
struct sdcardfs_vfsmount_options *mnt_opt = mnt->data;
|
||||
struct inode *inode;
|
||||
|
||||
printk(KERN_INFO "sdcardfs version 2.0\n");
|
||||
|
@ -193,6 +253,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
|||
|
||||
printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
|
||||
printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
|
||||
printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt);
|
||||
|
||||
/* parse lower path */
|
||||
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
|
||||
|
@ -212,7 +273,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
|||
|
||||
sb_info = sb->s_fs_info;
|
||||
/* parse options */
|
||||
err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
|
||||
err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options);
|
||||
if (err) {
|
||||
printk(KERN_ERR "sdcardfs: invalid options\n");
|
||||
goto out_freesbi;
|
||||
|
@ -236,7 +297,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
|||
sb->s_op = &sdcardfs_sops;
|
||||
|
||||
/* get a new inode and allocate our root dentry */
|
||||
inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0);
|
||||
inode = sdcardfs_iget(sb, d_inode(lower_path.dentry), 0);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto out_sput;
|
||||
|
@ -268,16 +329,16 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
|||
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
|
||||
mutex_lock(&sdcardfs_super_list_lock);
|
||||
if(sb_info->options.multiuser) {
|
||||
setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false);
|
||||
setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
|
||||
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
|
||||
/*err = prepare_dir(sb_info->obbpath_s,
|
||||
sb_info->options.fs_low_uid,
|
||||
sb_info->options.fs_low_gid, 00755);*/
|
||||
} else {
|
||||
setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false);
|
||||
setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
|
||||
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
|
||||
}
|
||||
fix_derived_permission(sb->s_root->d_inode);
|
||||
fixup_tmp_permissions(d_inode(sb->s_root));
|
||||
sb_info->sb = sb;
|
||||
list_add(&sb_info->list, &sdcardfs_super_list);
|
||||
mutex_unlock(&sdcardfs_super_list_lock);
|
||||
|
@ -306,9 +367,9 @@ out:
|
|||
}
|
||||
|
||||
/* A feature which supports mount_nodev() with options */
|
||||
static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data,
|
||||
int (*fill_super)(struct super_block *, const char *, void *, int))
|
||||
static struct dentry *mount_nodev_with_options(struct vfsmount *mnt,
|
||||
struct file_system_type *fs_type, int flags, const char *dev_name, void *data,
|
||||
int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int))
|
||||
|
||||
{
|
||||
int error;
|
||||
|
@ -319,7 +380,7 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
|
|||
|
||||
s->s_flags = flags;
|
||||
|
||||
error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0);
|
||||
error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0);
|
||||
if (error) {
|
||||
deactivate_locked_super(s);
|
||||
return ERR_PTR(error);
|
||||
|
@ -328,15 +389,27 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
|
|||
return dget(s->s_root);
|
||||
}
|
||||
|
||||
struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags,
|
||||
static struct dentry *sdcardfs_mount(struct vfsmount *mnt,
|
||||
struct file_system_type *fs_type, int flags,
|
||||
const char *dev_name, void *raw_data)
|
||||
{
|
||||
/*
|
||||
* dev_name is a lower_path_name,
|
||||
* raw_data is a option string.
|
||||
*/
|
||||
return mount_nodev_with_options(fs_type, flags, dev_name,
|
||||
raw_data, sdcardfs_read_super);
|
||||
return mount_nodev_with_options(mnt, fs_type, flags, dev_name,
|
||||
raw_data, sdcardfs_read_super);
|
||||
}
|
||||
|
||||
static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags,
|
||||
const char *dev_name, void *raw_data)
|
||||
{
|
||||
WARN(1, "sdcardfs does not support mount. Use mount2.\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
void *sdcardfs_alloc_mnt_data(void) {
|
||||
return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
|
||||
}
|
||||
|
||||
void sdcardfs_kill_sb(struct super_block *sb) {
|
||||
|
@ -353,7 +426,9 @@ void sdcardfs_kill_sb(struct super_block *sb) {
|
|||
static struct file_system_type sdcardfs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = SDCARDFS_NAME,
|
||||
.mount = sdcardfs_mount,
|
||||
.mount = sdcardfs_mount_wrn,
|
||||
.mount2 = sdcardfs_mount,
|
||||
.alloc_mnt_data = sdcardfs_alloc_mnt_data,
|
||||
.kill_sb = sdcardfs_kill_sb,
|
||||
.fs_flags = 0,
|
||||
};
|
||||
|
|
|
@ -18,20 +18,32 @@
|
|||
* General Public License.
|
||||
*/
|
||||
|
||||
#define MULTIUSER_APP_PER_USER_RANGE 100000
|
||||
#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
|
||||
#define AID_APP_START 10000 /* first app user */
|
||||
#define AID_APP_END 19999 /* last app user */
|
||||
#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
|
||||
#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
|
||||
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
|
||||
|
||||
typedef uid_t userid_t;
|
||||
typedef uid_t appid_t;
|
||||
|
||||
static inline userid_t multiuser_get_user_id(uid_t uid) {
|
||||
return uid / MULTIUSER_APP_PER_USER_RANGE;
|
||||
static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
|
||||
return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
|
||||
}
|
||||
|
||||
static inline appid_t multiuser_get_app_id(uid_t uid) {
|
||||
return uid % MULTIUSER_APP_PER_USER_RANGE;
|
||||
static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
|
||||
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
|
||||
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
|
||||
return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
|
||||
static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
|
||||
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
|
||||
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -65,17 +65,26 @@
|
|||
#define AID_SDCARD_PICS 1033 /* external storage photos access */
|
||||
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
|
||||
#define AID_SDCARD_ALL 1035 /* access all users external storage */
|
||||
#define AID_MEDIA_OBB 1059 /* obb files */
|
||||
|
||||
#define AID_SDCARD_IMAGE 1057
|
||||
|
||||
#define AID_PACKAGE_INFO 1027
|
||||
|
||||
#define fix_derived_permission(x) \
|
||||
|
||||
/*
|
||||
* Permissions are handled by our permission function.
|
||||
* We don't want anyone who happens to look at our inode value to prematurely
|
||||
* block access, so store more permissive values. These are probably never
|
||||
* used.
|
||||
*/
|
||||
#define fixup_tmp_permissions(x) \
|
||||
do { \
|
||||
(x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \
|
||||
(x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \
|
||||
(x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\
|
||||
(x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \
|
||||
(x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
|
||||
} while (0)
|
||||
|
||||
|
||||
/* OVERRIDE_CRED() and REVERT_CRED()
|
||||
* OVERRID_CRED()
|
||||
* backup original task->cred
|
||||
|
@ -85,12 +94,12 @@
|
|||
* These two macro should be used in pair, and OVERRIDE_CRED() should be
|
||||
* placed at the beginning of a function, right after variable declaration.
|
||||
*/
|
||||
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \
|
||||
saved_cred = override_fsids(sdcardfs_sbi); \
|
||||
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \
|
||||
saved_cred = override_fsids(sdcardfs_sbi, info); \
|
||||
if (!saved_cred) { return -ENOMEM; }
|
||||
|
||||
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \
|
||||
saved_cred = override_fsids(sdcardfs_sbi); \
|
||||
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \
|
||||
saved_cred = override_fsids(sdcardfs_sbi, info); \
|
||||
if (!saved_cred) { return ERR_PTR(-ENOMEM); }
|
||||
|
||||
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
|
||||
|
@ -121,13 +130,18 @@ typedef enum {
|
|||
PERM_ANDROID_OBB,
|
||||
/* This node is "/Android/media" */
|
||||
PERM_ANDROID_MEDIA,
|
||||
/* This node is "/Android/[data|media|obb]/[package]" */
|
||||
PERM_ANDROID_PACKAGE,
|
||||
/* This node is "/Android/[data|media|obb]/[package]/cache" */
|
||||
PERM_ANDROID_PACKAGE_CACHE,
|
||||
} perm_t;
|
||||
|
||||
struct sdcardfs_sb_info;
|
||||
struct sdcardfs_mount_options;
|
||||
struct sdcardfs_inode_info;
|
||||
|
||||
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
|
||||
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi);
|
||||
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info);
|
||||
/* Do not directly use this function, use REVERT_CRED() instead. */
|
||||
void revert_fsids(const struct cred * old_cred);
|
||||
|
||||
|
@ -169,6 +183,10 @@ struct sdcardfs_inode_info {
|
|||
userid_t userid;
|
||||
uid_t d_uid;
|
||||
bool under_android;
|
||||
bool under_cache;
|
||||
bool under_obb;
|
||||
/* top folder for ownership */
|
||||
struct inode *top;
|
||||
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
@ -185,12 +203,18 @@ struct sdcardfs_mount_options {
|
|||
uid_t fs_low_uid;
|
||||
gid_t fs_low_gid;
|
||||
userid_t fs_user_id;
|
||||
gid_t gid;
|
||||
mode_t mask;
|
||||
bool multiuser;
|
||||
unsigned int reserved_mb;
|
||||
};
|
||||
|
||||
struct sdcardfs_vfsmount_options {
|
||||
gid_t gid;
|
||||
mode_t mask;
|
||||
};
|
||||
|
||||
extern int parse_options_remount(struct super_block *sb, char *options, int silent,
|
||||
struct sdcardfs_vfsmount_options *vfsopts);
|
||||
|
||||
/* sdcardfs super-block data in memory */
|
||||
struct sdcardfs_sb_info {
|
||||
struct super_block *sb;
|
||||
|
@ -321,9 +345,44 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \
|
|||
SDCARDFS_DENT_FUNC(lower_path)
|
||||
SDCARDFS_DENT_FUNC(orig_path)
|
||||
|
||||
static inline int get_gid(struct sdcardfs_inode_info *info) {
|
||||
struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
|
||||
if (sb_info->options.gid == AID_SDCARD_RW) {
|
||||
static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo)
|
||||
{
|
||||
return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
|
||||
}
|
||||
|
||||
/* grab a refererence if we aren't linking to ourself */
|
||||
static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top)
|
||||
{
|
||||
struct inode *old_top = NULL;
|
||||
BUG_ON(IS_ERR_OR_NULL(top));
|
||||
if (info->top && info->top != &info->vfs_inode) {
|
||||
old_top = info->top;
|
||||
}
|
||||
if (top != &info->vfs_inode)
|
||||
igrab(top);
|
||||
info->top = top;
|
||||
iput(old_top);
|
||||
}
|
||||
|
||||
static inline struct inode *grab_top(struct sdcardfs_inode_info *info)
|
||||
{
|
||||
struct inode *top = info->top;
|
||||
if (top) {
|
||||
return igrab(top);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void release_top(struct sdcardfs_inode_info *info)
|
||||
{
|
||||
iput(info->top);
|
||||
}
|
||||
|
||||
static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
|
||||
struct sdcardfs_vfsmount_options *opts = mnt->data;
|
||||
|
||||
if (opts->gid == AID_SDCARD_RW) {
|
||||
/* As an optimization, certain trusted system components only run
|
||||
* as owner but operate across all users. Since we're now handing
|
||||
* out the sdcard_rw GID only to trusted apps, we're okay relaxing
|
||||
|
@ -331,14 +390,15 @@ static inline int get_gid(struct sdcardfs_inode_info *info) {
|
|||
* assigned to app directories are still multiuser aware. */
|
||||
return AID_SDCARD_RW;
|
||||
} else {
|
||||
return multiuser_get_uid(info->userid, sb_info->options.gid);
|
||||
return multiuser_get_uid(info->userid, opts->gid);
|
||||
}
|
||||
}
|
||||
static inline int get_mode(struct sdcardfs_inode_info *info) {
|
||||
static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
|
||||
int owner_mode;
|
||||
int filtered_mode;
|
||||
struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
|
||||
int visible_mode = 0775 & ~sb_info->options.mask;
|
||||
struct sdcardfs_vfsmount_options *opts = mnt->data;
|
||||
int visible_mode = 0775 & ~opts->mask;
|
||||
|
||||
|
||||
if (info->perm == PERM_PRE_ROOT) {
|
||||
/* Top of multi-user view should always be visible to ensure
|
||||
|
@ -348,7 +408,7 @@ static inline int get_mode(struct sdcardfs_inode_info *info) {
|
|||
/* Block "other" access to Android directories, since only apps
|
||||
* belonging to a specific user should be in there; we still
|
||||
* leave +x open for the default view. */
|
||||
if (sb_info->options.gid == AID_SDCARD_RW) {
|
||||
if (opts->gid == AID_SDCARD_RW) {
|
||||
visible_mode = visible_mode & ~0006;
|
||||
} else {
|
||||
visible_mode = visible_mode & ~0007;
|
||||
|
@ -396,20 +456,34 @@ extern struct mutex sdcardfs_super_list_lock;
|
|||
extern struct list_head sdcardfs_super_list;
|
||||
|
||||
/* for packagelist.c */
|
||||
extern appid_t get_appid(void *pkgl_id, const char *app_name);
|
||||
extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
|
||||
extern appid_t get_appid(const char *app_name);
|
||||
extern appid_t get_ext_gid(const char *app_name);
|
||||
extern appid_t is_excluded(const char *app_name, userid_t userid);
|
||||
extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr* name);
|
||||
extern int open_flags_to_access_mode(int open_flags);
|
||||
extern int packagelist_init(void);
|
||||
extern void packagelist_exit(void);
|
||||
|
||||
/* for derived_perm.c */
|
||||
extern void setup_derived_state(struct inode *inode, perm_t perm,
|
||||
userid_t userid, uid_t uid, bool under_android);
|
||||
#define BY_NAME (1 << 0)
|
||||
#define BY_USERID (1 << 1)
|
||||
struct limit_search {
|
||||
unsigned int flags;
|
||||
const char *name;
|
||||
size_t length;
|
||||
userid_t userid;
|
||||
};
|
||||
|
||||
extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
|
||||
uid_t uid, bool under_android, struct inode *top);
|
||||
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
|
||||
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry);
|
||||
extern void get_derive_permissions_recursive(struct dentry *parent);
|
||||
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name);
|
||||
extern void drop_recursive(struct dentry *parent);
|
||||
extern void fixup_top_recursive(struct dentry *parent);
|
||||
extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
|
||||
|
||||
extern void update_derived_permission_lock(struct dentry *dentry);
|
||||
void fixup_lower_ownership(struct dentry* dentry, const char *name);
|
||||
extern int need_graft_path(struct dentry *dentry);
|
||||
extern int is_base_obbpath(struct dentry *dentry);
|
||||
extern int is_obbpath_invalid(struct dentry *dentry);
|
||||
|
@ -444,7 +518,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = vfs_mkdir(d_inode(parent.dentry), dent, mode);
|
||||
err = vfs_mkdir2(parent.mnt, d_inode(parent.dentry), dent, mode);
|
||||
if (err) {
|
||||
if (err == -EEXIST)
|
||||
err = 0;
|
||||
|
@ -455,7 +529,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m
|
|||
attrs.ia_gid = make_kgid(&init_user_ns, gid);
|
||||
attrs.ia_valid = ATTR_UID | ATTR_GID;
|
||||
mutex_lock(&d_inode(dent)->i_mutex);
|
||||
notify_change(dent, &attrs, NULL);
|
||||
notify_change2(parent.mnt, dent, &attrs, NULL);
|
||||
mutex_unlock(&d_inode(dent)->i_mutex);
|
||||
|
||||
out_dput:
|
||||
|
@ -513,12 +587,16 @@ static inline int check_min_free_space(struct dentry *dentry, size_t size, int d
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Copies attrs and maintains sdcardfs managed attrs */
|
||||
/*
|
||||
* Copies attrs and maintains sdcardfs managed attrs
|
||||
* Since our permission check handles all special permissions, set those to be open
|
||||
*/
|
||||
static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
|
||||
{
|
||||
dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest));
|
||||
dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG |
|
||||
S_IROTH | S_IXOTH; /* 0775 */
|
||||
dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
|
||||
dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest)));
|
||||
dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW);
|
||||
dest->i_rdev = src->i_rdev;
|
||||
dest->i_atime = src->i_atime;
|
||||
dest->i_mtime = src->i_mtime;
|
||||
|
@ -527,4 +605,17 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct
|
|||
dest->i_flags = src->i_flags;
|
||||
set_nlink(dest, src->i_nlink);
|
||||
}
|
||||
|
||||
static inline bool str_case_eq(const char *s1, const char *s2)
|
||||
{
|
||||
return !strcasecmp(s1, s2);
|
||||
}
|
||||
|
||||
static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2)
|
||||
{
|
||||
return q1->len == q2->len && str_case_eq(q1->name, q2->name);
|
||||
}
|
||||
|
||||
#define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1)
|
||||
|
||||
#endif /* not _SDCARDFS_H_ */
|
||||
|
|
|
@ -108,6 +108,50 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options
|
|||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* @mnt: mount point we are remounting
|
||||
* @sb: superblock we are remounting
|
||||
* @flags: numeric mount options
|
||||
* @options: mount options string
|
||||
*/
|
||||
static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb,
|
||||
int *flags, char *options)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* The VFS will take care of "ro" and "rw" flags among others. We
|
||||
* can safely accept a few flags (RDONLY, MANDLOCK), and honor
|
||||
* SILENT, but anything else left over is an error.
|
||||
*/
|
||||
if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) {
|
||||
printk(KERN_ERR
|
||||
"sdcardfs: remount flags 0x%x unsupported\n", *flags);
|
||||
err = -EINVAL;
|
||||
}
|
||||
printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt);
|
||||
err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data);
|
||||
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void* sdcardfs_clone_mnt_data(void *data) {
|
||||
struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
|
||||
struct sdcardfs_vfsmount_options* old = data;
|
||||
if(!opt) return NULL;
|
||||
opt->gid = old->gid;
|
||||
opt->mask = old->mask;
|
||||
return opt;
|
||||
}
|
||||
|
||||
static void sdcardfs_copy_mnt_data(void *data, void *newdata) {
|
||||
struct sdcardfs_vfsmount_options* old = data;
|
||||
struct sdcardfs_vfsmount_options* new = newdata;
|
||||
old->gid = new->gid;
|
||||
old->mask = new->mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by iput() when the inode reference count reached zero
|
||||
* and the inode is not hashed anywhere. Used to clear anything
|
||||
|
@ -126,6 +170,7 @@ static void sdcardfs_evict_inode(struct inode *inode)
|
|||
*/
|
||||
lower_inode = sdcardfs_lower_inode(inode);
|
||||
sdcardfs_set_lower_inode(inode, NULL);
|
||||
set_top(SDCARDFS_I(inode), inode);
|
||||
iput(lower_inode);
|
||||
}
|
||||
|
||||
|
@ -190,19 +235,24 @@ static void sdcardfs_umount_begin(struct super_block *sb)
|
|||
lower_sb->s_op->umount_begin(lower_sb);
|
||||
}
|
||||
|
||||
static int sdcardfs_show_options(struct seq_file *m, struct dentry *root)
|
||||
static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root)
|
||||
{
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
|
||||
struct sdcardfs_mount_options *opts = &sbi->options;
|
||||
struct sdcardfs_vfsmount_options *vfsopts = mnt->data;
|
||||
|
||||
if (opts->fs_low_uid != 0)
|
||||
seq_printf(m, ",uid=%u", opts->fs_low_uid);
|
||||
seq_printf(m, ",fsuid=%u", opts->fs_low_uid);
|
||||
if (opts->fs_low_gid != 0)
|
||||
seq_printf(m, ",gid=%u", opts->fs_low_gid);
|
||||
|
||||
seq_printf(m, ",fsgid=%u", opts->fs_low_gid);
|
||||
if (vfsopts->gid != 0)
|
||||
seq_printf(m, ",gid=%u", vfsopts->gid);
|
||||
if (opts->multiuser)
|
||||
seq_printf(m, ",multiuser");
|
||||
|
||||
if (vfsopts->mask)
|
||||
seq_printf(m, ",mask=%u", vfsopts->mask);
|
||||
if (opts->fs_user_id)
|
||||
seq_printf(m, ",userid=%u", opts->fs_user_id);
|
||||
if (opts->reserved_mb != 0)
|
||||
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
|
||||
|
||||
|
@ -213,9 +263,12 @@ const struct super_operations sdcardfs_sops = {
|
|||
.put_super = sdcardfs_put_super,
|
||||
.statfs = sdcardfs_statfs,
|
||||
.remount_fs = sdcardfs_remount_fs,
|
||||
.remount_fs2 = sdcardfs_remount_fs2,
|
||||
.clone_mnt_data = sdcardfs_clone_mnt_data,
|
||||
.copy_mnt_data = sdcardfs_copy_mnt_data,
|
||||
.evict_inode = sdcardfs_evict_inode,
|
||||
.umount_begin = sdcardfs_umount_begin,
|
||||
.show_options = sdcardfs_show_options,
|
||||
.show_options2 = sdcardfs_show_options,
|
||||
.alloc_inode = sdcardfs_alloc_inode,
|
||||
.destroy_inode = sdcardfs_destroy_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
|
|
|
@ -25,34 +25,6 @@ config SQUASHFS
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
choice
|
||||
prompt "File decompression options"
|
||||
depends on SQUASHFS
|
||||
help
|
||||
Squashfs now supports two options for decompressing file
|
||||
data. Traditionally Squashfs has decompressed into an
|
||||
intermediate buffer and then memcopied it into the page cache.
|
||||
Squashfs now supports the ability to decompress directly into
|
||||
the page cache.
|
||||
|
||||
If unsure, select "Decompress file data into an intermediate buffer"
|
||||
|
||||
config SQUASHFS_FILE_CACHE
|
||||
bool "Decompress file data into an intermediate buffer"
|
||||
help
|
||||
Decompress file data into an intermediate buffer and then
|
||||
memcopy it into the page cache.
|
||||
|
||||
config SQUASHFS_FILE_DIRECT
|
||||
bool "Decompress files directly into the page cache"
|
||||
help
|
||||
Directly decompress file data into the page cache.
|
||||
Doing so can significantly improve performance because
|
||||
it eliminates a memcpy and it also removes the lock contention
|
||||
on the single buffer.
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Decompressor parallelisation options"
|
||||
depends on SQUASHFS
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
obj-$(CONFIG_SQUASHFS) += squashfs.o
|
||||
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
|
||||
squashfs-y += namei.o super.o symlink.o decompressor.o
|
||||
squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o
|
||||
squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o
|
||||
squashfs-y += file_direct.o page_actor.o
|
||||
squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
|
||||
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
|
||||
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
|
||||
|
|
|
@ -28,9 +28,12 @@
|
|||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "squashfs_fs.h"
|
||||
#include "squashfs_fs_sb.h"
|
||||
|
@ -38,45 +41,380 @@
|
|||
#include "decompressor.h"
|
||||
#include "page_actor.h"
|
||||
|
||||
/*
|
||||
* Read the metadata block length, this is stored in the first two
|
||||
* bytes of the metadata block.
|
||||
*/
|
||||
static struct buffer_head *get_block_length(struct super_block *sb,
|
||||
u64 *cur_index, int *offset, int *length)
|
||||
static struct workqueue_struct *squashfs_read_wq;
|
||||
|
||||
struct squashfs_read_request {
|
||||
struct super_block *sb;
|
||||
u64 index;
|
||||
int length;
|
||||
int compressed;
|
||||
int offset;
|
||||
u64 read_end;
|
||||
struct squashfs_page_actor *output;
|
||||
enum {
|
||||
SQUASHFS_COPY,
|
||||
SQUASHFS_DECOMPRESS,
|
||||
SQUASHFS_METADATA,
|
||||
} data_processing;
|
||||
bool synchronous;
|
||||
|
||||
/*
|
||||
* If the read is synchronous, it is possible to retrieve information
|
||||
* about the request by setting these pointers.
|
||||
*/
|
||||
int *res;
|
||||
int *bytes_read;
|
||||
int *bytes_uncompressed;
|
||||
|
||||
int nr_buffers;
|
||||
struct buffer_head **bh;
|
||||
struct work_struct offload;
|
||||
};
|
||||
|
||||
struct squashfs_bio_request {
|
||||
struct buffer_head **bh;
|
||||
int nr_buffers;
|
||||
};
|
||||
|
||||
static int squashfs_bio_submit(struct squashfs_read_request *req);
|
||||
|
||||
int squashfs_init_read_wq(void)
|
||||
{
|
||||
struct squashfs_sb_info *msblk = sb->s_fs_info;
|
||||
struct buffer_head *bh;
|
||||
squashfs_read_wq = create_workqueue("SquashFS read wq");
|
||||
return !!squashfs_read_wq;
|
||||
}
|
||||
|
||||
bh = sb_bread(sb, *cur_index);
|
||||
if (bh == NULL)
|
||||
return NULL;
|
||||
void squashfs_destroy_read_wq(void)
|
||||
{
|
||||
flush_workqueue(squashfs_read_wq);
|
||||
destroy_workqueue(squashfs_read_wq);
|
||||
}
|
||||
|
||||
if (msblk->devblksize - *offset == 1) {
|
||||
*length = (unsigned char) bh->b_data[*offset];
|
||||
put_bh(bh);
|
||||
bh = sb_bread(sb, ++(*cur_index));
|
||||
if (bh == NULL)
|
||||
return NULL;
|
||||
*length |= (unsigned char) bh->b_data[0] << 8;
|
||||
*offset = 1;
|
||||
} else {
|
||||
*length = (unsigned char) bh->b_data[*offset] |
|
||||
(unsigned char) bh->b_data[*offset + 1] << 8;
|
||||
*offset += 2;
|
||||
static void free_read_request(struct squashfs_read_request *req, int error)
|
||||
{
|
||||
if (!req->synchronous)
|
||||
squashfs_page_actor_free(req->output, error);
|
||||
if (req->res)
|
||||
*(req->res) = error;
|
||||
kfree(req->bh);
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
if (*offset == msblk->devblksize) {
|
||||
put_bh(bh);
|
||||
bh = sb_bread(sb, ++(*cur_index));
|
||||
if (bh == NULL)
|
||||
return NULL;
|
||||
*offset = 0;
|
||||
static void squashfs_process_blocks(struct squashfs_read_request *req)
|
||||
{
|
||||
int error = 0;
|
||||
int bytes, i, length;
|
||||
struct squashfs_sb_info *msblk = req->sb->s_fs_info;
|
||||
struct squashfs_page_actor *actor = req->output;
|
||||
struct buffer_head **bh = req->bh;
|
||||
int nr_buffers = req->nr_buffers;
|
||||
|
||||
for (i = 0; i < nr_buffers; ++i) {
|
||||
if (!bh[i])
|
||||
continue;
|
||||
wait_on_buffer(bh[i]);
|
||||
if (!buffer_uptodate(bh[i]))
|
||||
error = -EIO;
|
||||
}
|
||||
if (error)
|
||||
goto cleanup;
|
||||
|
||||
if (req->data_processing == SQUASHFS_METADATA) {
|
||||
/* Extract the length of the metadata block */
|
||||
if (req->offset != msblk->devblksize - 1)
|
||||
length = *((u16 *)(bh[0]->b_data + req->offset));
|
||||
else {
|
||||
length = bh[0]->b_data[req->offset];
|
||||
length |= bh[1]->b_data[0] << 8;
|
||||
}
|
||||
req->compressed = SQUASHFS_COMPRESSED(length);
|
||||
req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS
|
||||
: SQUASHFS_COPY;
|
||||
length = SQUASHFS_COMPRESSED_SIZE(length);
|
||||
if (req->index + length + 2 > req->read_end) {
|
||||
for (i = 0; i < nr_buffers; ++i)
|
||||
put_bh(bh[i]);
|
||||
kfree(bh);
|
||||
req->length = length;
|
||||
req->index += 2;
|
||||
squashfs_bio_submit(req);
|
||||
return;
|
||||
}
|
||||
req->length = length;
|
||||
req->offset = (req->offset + 2) % PAGE_SIZE;
|
||||
if (req->offset < 2) {
|
||||
put_bh(bh[0]);
|
||||
++bh;
|
||||
--nr_buffers;
|
||||
}
|
||||
}
|
||||
if (req->bytes_read)
|
||||
*(req->bytes_read) = req->length;
|
||||
|
||||
if (req->data_processing == SQUASHFS_COPY) {
|
||||
squashfs_bh_to_actor(bh, nr_buffers, req->output, req->offset,
|
||||
req->length, msblk->devblksize);
|
||||
} else if (req->data_processing == SQUASHFS_DECOMPRESS) {
|
||||
req->length = squashfs_decompress(msblk, bh, nr_buffers,
|
||||
req->offset, req->length, actor);
|
||||
if (req->length < 0) {
|
||||
error = -EIO;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
return bh;
|
||||
/* Last page may have trailing bytes not filled */
|
||||
bytes = req->length % PAGE_SIZE;
|
||||
if (bytes && actor->page[actor->pages - 1])
|
||||
zero_user_segment(actor->page[actor->pages - 1], bytes,
|
||||
PAGE_SIZE);
|
||||
|
||||
cleanup:
|
||||
if (req->bytes_uncompressed)
|
||||
*(req->bytes_uncompressed) = req->length;
|
||||
if (error) {
|
||||
for (i = 0; i < nr_buffers; ++i)
|
||||
if (bh[i])
|
||||
put_bh(bh[i]);
|
||||
}
|
||||
free_read_request(req, error);
|
||||
}
|
||||
|
||||
static void read_wq_handler(struct work_struct *work)
|
||||
{
|
||||
squashfs_process_blocks(container_of(work,
|
||||
struct squashfs_read_request, offload));
|
||||
}
|
||||
|
||||
static void squashfs_bio_end_io(struct bio *bio)
|
||||
{
|
||||
int i;
|
||||
int error = bio->bi_error;
|
||||
struct squashfs_bio_request *bio_req = bio->bi_private;
|
||||
|
||||
bio_put(bio);
|
||||
|
||||
for (i = 0; i < bio_req->nr_buffers; ++i) {
|
||||
if (!bio_req->bh[i])
|
||||
continue;
|
||||
if (!error)
|
||||
set_buffer_uptodate(bio_req->bh[i]);
|
||||
else
|
||||
clear_buffer_uptodate(bio_req->bh[i]);
|
||||
unlock_buffer(bio_req->bh[i]);
|
||||
}
|
||||
kfree(bio_req);
|
||||
}
|
||||
|
||||
static int bh_is_optional(struct squashfs_read_request *req, int idx)
|
||||
{
|
||||
int start_idx, end_idx;
|
||||
struct squashfs_sb_info *msblk = req->sb->s_fs_info;
|
||||
|
||||
start_idx = (idx * msblk->devblksize - req->offset) / PAGE_CACHE_SIZE;
|
||||
end_idx = ((idx + 1) * msblk->devblksize - req->offset + 1) / PAGE_CACHE_SIZE;
|
||||
if (start_idx >= req->output->pages)
|
||||
return 1;
|
||||
if (start_idx < 0)
|
||||
start_idx = end_idx;
|
||||
if (end_idx >= req->output->pages)
|
||||
end_idx = start_idx;
|
||||
return !req->output->page[start_idx] && !req->output->page[end_idx];
|
||||
}
|
||||
|
||||
static int actor_getblks(struct squashfs_read_request *req, u64 block)
|
||||
{
|
||||
int i;
|
||||
|
||||
req->bh = kmalloc_array(req->nr_buffers, sizeof(*(req->bh)), GFP_NOIO);
|
||||
if (!req->bh)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < req->nr_buffers; ++i) {
|
||||
/*
|
||||
* When dealing with an uncompressed block, the actor may
|
||||
* contains NULL pages. There's no need to read the buffers
|
||||
* associated with these pages.
|
||||
*/
|
||||
if (!req->compressed && bh_is_optional(req, i)) {
|
||||
req->bh[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
req->bh[i] = sb_getblk(req->sb, block + i);
|
||||
if (!req->bh[i]) {
|
||||
while (--i) {
|
||||
if (req->bh[i])
|
||||
put_bh(req->bh[i]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int squashfs_bio_submit(struct squashfs_read_request *req)
|
||||
{
|
||||
struct bio *bio = NULL;
|
||||
struct buffer_head *bh;
|
||||
struct squashfs_bio_request *bio_req = NULL;
|
||||
int b = 0, prev_block = 0;
|
||||
struct squashfs_sb_info *msblk = req->sb->s_fs_info;
|
||||
|
||||
u64 read_start = round_down(req->index, msblk->devblksize);
|
||||
u64 read_end = round_up(req->index + req->length, msblk->devblksize);
|
||||
sector_t block = read_start >> msblk->devblksize_log2;
|
||||
sector_t block_end = read_end >> msblk->devblksize_log2;
|
||||
int offset = read_start - round_down(req->index, PAGE_SIZE);
|
||||
int nr_buffers = block_end - block;
|
||||
int blksz = msblk->devblksize;
|
||||
int bio_max_pages = nr_buffers > BIO_MAX_PAGES ? BIO_MAX_PAGES
|
||||
: nr_buffers;
|
||||
|
||||
/* Setup the request */
|
||||
req->read_end = read_end;
|
||||
req->offset = req->index - read_start;
|
||||
req->nr_buffers = nr_buffers;
|
||||
if (actor_getblks(req, block) < 0)
|
||||
goto getblk_failed;
|
||||
|
||||
/* Create and submit the BIOs */
|
||||
for (b = 0; b < nr_buffers; ++b, offset += blksz) {
|
||||
bh = req->bh[b];
|
||||
if (!bh || !trylock_buffer(bh))
|
||||
continue;
|
||||
if (buffer_uptodate(bh)) {
|
||||
unlock_buffer(bh);
|
||||
continue;
|
||||
}
|
||||
offset %= PAGE_SIZE;
|
||||
|
||||
/* Append the buffer to the current BIO if it is contiguous */
|
||||
if (bio && bio_req && prev_block + 1 == b) {
|
||||
if (bio_add_page(bio, bh->b_page, blksz, offset)) {
|
||||
bio_req->nr_buffers += 1;
|
||||
prev_block = b;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, submit the current BIO and create a new one */
|
||||
if (bio)
|
||||
submit_bio(READ, bio);
|
||||
bio_req = kcalloc(1, sizeof(struct squashfs_bio_request),
|
||||
GFP_NOIO);
|
||||
if (!bio_req)
|
||||
goto req_alloc_failed;
|
||||
bio_req->bh = &req->bh[b];
|
||||
bio = bio_alloc(GFP_NOIO, bio_max_pages);
|
||||
if (!bio)
|
||||
goto bio_alloc_failed;
|
||||
bio->bi_bdev = req->sb->s_bdev;
|
||||
bio->bi_iter.bi_sector = (block + b)
|
||||
<< (msblk->devblksize_log2 - 9);
|
||||
bio->bi_private = bio_req;
|
||||
bio->bi_end_io = squashfs_bio_end_io;
|
||||
|
||||
bio_add_page(bio, bh->b_page, blksz, offset);
|
||||
bio_req->nr_buffers += 1;
|
||||
prev_block = b;
|
||||
}
|
||||
if (bio)
|
||||
submit_bio(READ, bio);
|
||||
|
||||
if (req->synchronous)
|
||||
squashfs_process_blocks(req);
|
||||
else {
|
||||
INIT_WORK(&req->offload, read_wq_handler);
|
||||
schedule_work(&req->offload);
|
||||
}
|
||||
return 0;
|
||||
|
||||
bio_alloc_failed:
|
||||
kfree(bio_req);
|
||||
req_alloc_failed:
|
||||
unlock_buffer(bh);
|
||||
while (--nr_buffers >= b)
|
||||
if (req->bh[nr_buffers])
|
||||
put_bh(req->bh[nr_buffers]);
|
||||
while (--b >= 0)
|
||||
if (req->bh[b])
|
||||
wait_on_buffer(req->bh[b]);
|
||||
getblk_failed:
|
||||
free_read_request(req, -ENOMEM);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int read_metadata_block(struct squashfs_read_request *req,
|
||||
u64 *next_index)
|
||||
{
|
||||
int ret, error, bytes_read = 0, bytes_uncompressed = 0;
|
||||
struct squashfs_sb_info *msblk = req->sb->s_fs_info;
|
||||
|
||||
if (req->index + 2 > msblk->bytes_used) {
|
||||
free_read_request(req, -EINVAL);
|
||||
return -EINVAL;
|
||||
}
|
||||
req->length = 2;
|
||||
|
||||
/* Do not read beyond the end of the device */
|
||||
if (req->index + req->length > msblk->bytes_used)
|
||||
req->length = msblk->bytes_used - req->index;
|
||||
req->data_processing = SQUASHFS_METADATA;
|
||||
|
||||
/*
|
||||
* Reading metadata is always synchronous because we don't know the
|
||||
* length in advance and the function is expected to update
|
||||
* 'next_index' and return the length.
|
||||
*/
|
||||
req->synchronous = true;
|
||||
req->res = &error;
|
||||
req->bytes_read = &bytes_read;
|
||||
req->bytes_uncompressed = &bytes_uncompressed;
|
||||
|
||||
TRACE("Metadata block @ 0x%llx, %scompressed size %d, src size %d\n",
|
||||
req->index, req->compressed ? "" : "un", bytes_read,
|
||||
req->output->length);
|
||||
|
||||
ret = squashfs_bio_submit(req);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (error)
|
||||
return error;
|
||||
if (next_index)
|
||||
*next_index += 2 + bytes_read;
|
||||
return bytes_uncompressed;
|
||||
}
|
||||
|
||||
static int read_data_block(struct squashfs_read_request *req, int length,
|
||||
u64 *next_index, bool synchronous)
|
||||
{
|
||||
int ret, error = 0, bytes_uncompressed = 0, bytes_read = 0;
|
||||
|
||||
req->compressed = SQUASHFS_COMPRESSED_BLOCK(length);
|
||||
req->length = length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
|
||||
req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS
|
||||
: SQUASHFS_COPY;
|
||||
|
||||
req->synchronous = synchronous;
|
||||
if (synchronous) {
|
||||
req->res = &error;
|
||||
req->bytes_read = &bytes_read;
|
||||
req->bytes_uncompressed = &bytes_uncompressed;
|
||||
}
|
||||
|
||||
TRACE("Data block @ 0x%llx, %scompressed size %d, src size %d\n",
|
||||
req->index, req->compressed ? "" : "un", req->length,
|
||||
req->output->length);
|
||||
|
||||
ret = squashfs_bio_submit(req);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (synchronous)
|
||||
ret = error ? error : bytes_uncompressed;
|
||||
if (next_index)
|
||||
*next_index += length;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and decompress a metadata block or datablock. Length is non-zero
|
||||
|
@ -87,128 +425,50 @@ static struct buffer_head *get_block_length(struct super_block *sb,
|
|||
* generated a larger block - this does occasionally happen with compression
|
||||
* algorithms).
|
||||
*/
|
||||
int squashfs_read_data(struct super_block *sb, u64 index, int length,
|
||||
u64 *next_index, struct squashfs_page_actor *output)
|
||||
static int __squashfs_read_data(struct super_block *sb, u64 index, int length,
|
||||
u64 *next_index, struct squashfs_page_actor *output, bool sync)
|
||||
{
|
||||
struct squashfs_sb_info *msblk = sb->s_fs_info;
|
||||
struct buffer_head **bh;
|
||||
int offset = index & ((1 << msblk->devblksize_log2) - 1);
|
||||
u64 cur_index = index >> msblk->devblksize_log2;
|
||||
int bytes, compressed, b = 0, k = 0, avail, i;
|
||||
struct squashfs_read_request *req;
|
||||
|
||||
bh = kcalloc(((output->length + msblk->devblksize - 1)
|
||||
>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
|
||||
if (bh == NULL)
|
||||
req = kcalloc(1, sizeof(struct squashfs_read_request), GFP_KERNEL);
|
||||
if (!req) {
|
||||
if (!sync)
|
||||
squashfs_page_actor_free(output, -ENOMEM);
|
||||
return -ENOMEM;
|
||||
|
||||
if (length) {
|
||||
/*
|
||||
* Datablock.
|
||||
*/
|
||||
bytes = -offset;
|
||||
compressed = SQUASHFS_COMPRESSED_BLOCK(length);
|
||||
length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
|
||||
if (next_index)
|
||||
*next_index = index + length;
|
||||
|
||||
TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
|
||||
index, compressed ? "" : "un", length, output->length);
|
||||
|
||||
if (length < 0 || length > output->length ||
|
||||
(index + length) > msblk->bytes_used)
|
||||
goto read_failure;
|
||||
|
||||
for (b = 0; bytes < length; b++, cur_index++) {
|
||||
bh[b] = sb_getblk(sb, cur_index);
|
||||
if (bh[b] == NULL)
|
||||
goto block_release;
|
||||
bytes += msblk->devblksize;
|
||||
}
|
||||
ll_rw_block(READ, b, bh);
|
||||
} else {
|
||||
/*
|
||||
* Metadata block.
|
||||
*/
|
||||
if ((index + 2) > msblk->bytes_used)
|
||||
goto read_failure;
|
||||
|
||||
bh[0] = get_block_length(sb, &cur_index, &offset, &length);
|
||||
if (bh[0] == NULL)
|
||||
goto read_failure;
|
||||
b = 1;
|
||||
|
||||
bytes = msblk->devblksize - offset;
|
||||
compressed = SQUASHFS_COMPRESSED(length);
|
||||
length = SQUASHFS_COMPRESSED_SIZE(length);
|
||||
if (next_index)
|
||||
*next_index = index + length + 2;
|
||||
|
||||
TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
|
||||
compressed ? "" : "un", length);
|
||||
|
||||
if (length < 0 || length > output->length ||
|
||||
(index + length) > msblk->bytes_used)
|
||||
goto block_release;
|
||||
|
||||
for (; bytes < length; b++) {
|
||||
bh[b] = sb_getblk(sb, ++cur_index);
|
||||
if (bh[b] == NULL)
|
||||
goto block_release;
|
||||
bytes += msblk->devblksize;
|
||||
}
|
||||
ll_rw_block(READ, b - 1, bh + 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < b; i++) {
|
||||
wait_on_buffer(bh[i]);
|
||||
if (!buffer_uptodate(bh[i]))
|
||||
goto block_release;
|
||||
req->sb = sb;
|
||||
req->index = index;
|
||||
req->output = output;
|
||||
|
||||
if (next_index)
|
||||
*next_index = index;
|
||||
|
||||
if (length)
|
||||
length = read_data_block(req, length, next_index, sync);
|
||||
else
|
||||
length = read_metadata_block(req, next_index);
|
||||
|
||||
if (length < 0) {
|
||||
ERROR("squashfs_read_data failed to read block 0x%llx\n",
|
||||
(unsigned long long)index);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (compressed) {
|
||||
length = squashfs_decompress(msblk, bh, b, offset, length,
|
||||
output);
|
||||
if (length < 0)
|
||||
goto read_failure;
|
||||
} else {
|
||||
/*
|
||||
* Block is uncompressed.
|
||||
*/
|
||||
int in, pg_offset = 0;
|
||||
void *data = squashfs_first_page(output);
|
||||
|
||||
for (bytes = length; k < b; k++) {
|
||||
in = min(bytes, msblk->devblksize - offset);
|
||||
bytes -= in;
|
||||
while (in) {
|
||||
if (pg_offset == PAGE_CACHE_SIZE) {
|
||||
data = squashfs_next_page(output);
|
||||
pg_offset = 0;
|
||||
}
|
||||
avail = min_t(int, in, PAGE_CACHE_SIZE -
|
||||
pg_offset);
|
||||
memcpy(data + pg_offset, bh[k]->b_data + offset,
|
||||
avail);
|
||||
in -= avail;
|
||||
pg_offset += avail;
|
||||
offset += avail;
|
||||
}
|
||||
offset = 0;
|
||||
put_bh(bh[k]);
|
||||
}
|
||||
squashfs_finish_page(output);
|
||||
}
|
||||
|
||||
kfree(bh);
|
||||
return length;
|
||||
|
||||
block_release:
|
||||
for (; k < b; k++)
|
||||
put_bh(bh[k]);
|
||||
|
||||
read_failure:
|
||||
ERROR("squashfs_read_data failed to read block 0x%llx\n",
|
||||
(unsigned long long) index);
|
||||
kfree(bh);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int squashfs_read_data(struct super_block *sb, u64 index, int length,
|
||||
u64 *next_index, struct squashfs_page_actor *output)
|
||||
{
|
||||
return __squashfs_read_data(sb, index, length, next_index, output,
|
||||
true);
|
||||
}
|
||||
|
||||
int squashfs_read_data_async(struct super_block *sb, u64 index, int length,
|
||||
u64 *next_index, struct squashfs_page_actor *output)
|
||||
{
|
||||
|
||||
return __squashfs_read_data(sb, index, length, next_index, output,
|
||||
false);
|
||||
}
|
||||
|
|
|
@ -209,17 +209,14 @@ void squashfs_cache_put(struct squashfs_cache_entry *entry)
|
|||
*/
|
||||
void squashfs_cache_delete(struct squashfs_cache *cache)
|
||||
{
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
if (cache == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < cache->entries; i++) {
|
||||
if (cache->entry[i].data) {
|
||||
for (j = 0; j < cache->pages; j++)
|
||||
kfree(cache->entry[i].data[j]);
|
||||
kfree(cache->entry[i].data);
|
||||
}
|
||||
if (cache->entry[i].page)
|
||||
free_page_array(cache->entry[i].page, cache->pages);
|
||||
kfree(cache->entry[i].actor);
|
||||
}
|
||||
|
||||
|
@ -236,7 +233,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)
|
|||
struct squashfs_cache *squashfs_cache_init(char *name, int entries,
|
||||
int block_size)
|
||||
{
|
||||
int i, j;
|
||||
int i;
|
||||
struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);
|
||||
|
||||
if (cache == NULL) {
|
||||
|
@ -268,22 +265,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,
|
|||
init_waitqueue_head(&cache->entry[i].wait_queue);
|
||||
entry->cache = cache;
|
||||
entry->block = SQUASHFS_INVALID_BLK;
|
||||
entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL);
|
||||
if (entry->data == NULL) {
|
||||
entry->page = alloc_page_array(cache->pages, GFP_KERNEL);
|
||||
if (!entry->page) {
|
||||
ERROR("Failed to allocate %s cache entry\n", name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (j = 0; j < cache->pages; j++) {
|
||||
entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
|
||||
if (entry->data[j] == NULL) {
|
||||
ERROR("Failed to allocate %s buffer\n", name);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
entry->actor = squashfs_page_actor_init(entry->data,
|
||||
cache->pages, 0);
|
||||
entry->actor = squashfs_page_actor_init(entry->page,
|
||||
cache->pages, 0, NULL);
|
||||
if (entry->actor == NULL) {
|
||||
ERROR("Failed to allocate %s cache entry\n", name);
|
||||
goto cleanup;
|
||||
|
@ -314,18 +302,20 @@ int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry,
|
|||
return min(length, entry->length - offset);
|
||||
|
||||
while (offset < entry->length) {
|
||||
void *buff = entry->data[offset / PAGE_CACHE_SIZE]
|
||||
+ (offset % PAGE_CACHE_SIZE);
|
||||
void *buff = kmap_atomic(entry->page[offset / PAGE_CACHE_SIZE])
|
||||
+ (offset % PAGE_CACHE_SIZE);
|
||||
int bytes = min_t(int, entry->length - offset,
|
||||
PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE));
|
||||
|
||||
if (bytes >= remaining) {
|
||||
memcpy(buffer, buff, remaining);
|
||||
kunmap_atomic(buff);
|
||||
remaining = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(buffer, buff, bytes);
|
||||
kunmap_atomic(buff);
|
||||
buffer += bytes;
|
||||
remaining -= bytes;
|
||||
offset += bytes;
|
||||
|
@ -416,43 +406,38 @@ struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb,
|
|||
void *squashfs_read_table(struct super_block *sb, u64 block, int length)
|
||||
{
|
||||
int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
||||
int i, res;
|
||||
void *table, *buffer, **data;
|
||||
struct page **page;
|
||||
void *buff;
|
||||
int res;
|
||||
struct squashfs_page_actor *actor;
|
||||
|
||||
table = buffer = kmalloc(length, GFP_KERNEL);
|
||||
if (table == NULL)
|
||||
page = alloc_page_array(pages, GFP_KERNEL);
|
||||
if (!page)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
actor = squashfs_page_actor_init(page, pages, length, NULL);
|
||||
if (actor == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
actor = squashfs_page_actor_init(data, pages, length);
|
||||
if (actor == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto failed2;
|
||||
}
|
||||
|
||||
for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
|
||||
data[i] = buffer;
|
||||
|
||||
res = squashfs_read_data(sb, block, length |
|
||||
SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
|
||||
|
||||
kfree(data);
|
||||
kfree(actor);
|
||||
|
||||
if (res < 0)
|
||||
goto failed;
|
||||
goto failed2;
|
||||
|
||||
return table;
|
||||
buff = kmalloc(length, GFP_KERNEL);
|
||||
if (!buff)
|
||||
goto failed2;
|
||||
squashfs_actor_to_buf(actor, buff, length);
|
||||
squashfs_page_actor_free(actor, 0);
|
||||
free_page_array(page, pages);
|
||||
return buff;
|
||||
|
||||
failed2:
|
||||
kfree(data);
|
||||
squashfs_page_actor_free(actor, 0);
|
||||
failed:
|
||||
kfree(table);
|
||||
free_page_array(page, pages);
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "squashfs_fs.h"
|
||||
#include "squashfs_fs_sb.h"
|
||||
|
@ -94,40 +95,44 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
|
|||
static void *get_comp_opts(struct super_block *sb, unsigned short flags)
|
||||
{
|
||||
struct squashfs_sb_info *msblk = sb->s_fs_info;
|
||||
void *buffer = NULL, *comp_opts;
|
||||
void *comp_opts, *buffer = NULL;
|
||||
struct page *page;
|
||||
struct squashfs_page_actor *actor = NULL;
|
||||
int length = 0;
|
||||
|
||||
if (!SQUASHFS_COMP_OPTS(flags))
|
||||
return squashfs_comp_opts(msblk, buffer, length);
|
||||
|
||||
/*
|
||||
* Read decompressor specific options from file system if present
|
||||
*/
|
||||
if (SQUASHFS_COMP_OPTS(flags)) {
|
||||
buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
|
||||
if (buffer == NULL) {
|
||||
comp_opts = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
actor = squashfs_page_actor_init(&buffer, 1, 0);
|
||||
if (actor == NULL) {
|
||||
comp_opts = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
if (!page)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
length = squashfs_read_data(sb,
|
||||
sizeof(struct squashfs_super_block), 0, NULL, actor);
|
||||
|
||||
if (length < 0) {
|
||||
comp_opts = ERR_PTR(length);
|
||||
goto out;
|
||||
}
|
||||
actor = squashfs_page_actor_init(&page, 1, 0, NULL);
|
||||
if (actor == NULL) {
|
||||
comp_opts = ERR_PTR(-ENOMEM);
|
||||
goto actor_error;
|
||||
}
|
||||
|
||||
comp_opts = squashfs_comp_opts(msblk, buffer, length);
|
||||
length = squashfs_read_data(sb,
|
||||
sizeof(struct squashfs_super_block), 0, NULL, actor);
|
||||
|
||||
out:
|
||||
kfree(actor);
|
||||
kfree(buffer);
|
||||
if (length < 0) {
|
||||
comp_opts = ERR_PTR(length);
|
||||
goto read_error;
|
||||
}
|
||||
|
||||
buffer = kmap_atomic(page);
|
||||
comp_opts = squashfs_comp_opts(msblk, buffer, length);
|
||||
kunmap_atomic(buffer);
|
||||
|
||||
read_error:
|
||||
squashfs_page_actor_free(actor, 0);
|
||||
actor_error:
|
||||
__free_page(page);
|
||||
return comp_opts;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,12 +47,16 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mm_inline.h>
|
||||
|
||||
#include "squashfs_fs.h"
|
||||
#include "squashfs_fs_sb.h"
|
||||
#include "squashfs_fs_i.h"
|
||||
#include "squashfs.h"
|
||||
|
||||
// Backported from 4.5
|
||||
#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))
|
||||
|
||||
/*
|
||||
* Locate cache slot in range [offset, index] for specified inode. If
|
||||
* there's more than one return the slot closest to index.
|
||||
|
@ -438,6 +442,21 @@ static int squashfs_readpage_fragment(struct page *page)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int squashfs_readpages_fragment(struct page *page,
|
||||
struct list_head *readahead_pages, struct address_space *mapping)
|
||||
{
|
||||
if (!page) {
|
||||
page = lru_to_page(readahead_pages);
|
||||
list_del(&page->lru);
|
||||
if (add_to_page_cache_lru(page, mapping, page->index,
|
||||
mapping_gfp_constraint(mapping, GFP_KERNEL))) {
|
||||
put_page(page);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return squashfs_readpage_fragment(page);
|
||||
}
|
||||
|
||||
static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
|
@ -450,54 +469,105 @@ static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int squashfs_readpage(struct file *file, struct page *page)
|
||||
static int squashfs_readpages_sparse(struct page *page,
|
||||
struct list_head *readahead_pages, int index, int file_end,
|
||||
struct address_space *mapping)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
if (!page) {
|
||||
page = lru_to_page(readahead_pages);
|
||||
list_del(&page->lru);
|
||||
if (add_to_page_cache_lru(page, mapping, page->index,
|
||||
mapping_gfp_constraint(mapping, GFP_KERNEL))) {
|
||||
put_page(page);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return squashfs_readpage_sparse(page, index, file_end);
|
||||
}
|
||||
|
||||
static int __squashfs_readpages(struct file *file, struct page *page,
|
||||
struct list_head *readahead_pages, unsigned int nr_pages,
|
||||
struct address_space *mapping)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
|
||||
int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
|
||||
int file_end = i_size_read(inode) >> msblk->block_log;
|
||||
int res;
|
||||
void *pageaddr;
|
||||
|
||||
do {
|
||||
struct page *cur_page = page ? page
|
||||
: lru_to_page(readahead_pages);
|
||||
int page_index = cur_page->index;
|
||||
int index = page_index >> (msblk->block_log - PAGE_CACHE_SHIFT);
|
||||
|
||||
if (page_index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
|
||||
PAGE_CACHE_SHIFT))
|
||||
return 1;
|
||||
|
||||
if (index < file_end || squashfs_i(inode)->fragment_block ==
|
||||
SQUASHFS_INVALID_BLK) {
|
||||
u64 block = 0;
|
||||
int bsize = read_blocklist(inode, index, &block);
|
||||
|
||||
if (bsize < 0)
|
||||
return -1;
|
||||
|
||||
if (bsize == 0) {
|
||||
res = squashfs_readpages_sparse(page,
|
||||
readahead_pages, index, file_end,
|
||||
mapping);
|
||||
} else {
|
||||
res = squashfs_readpages_block(page,
|
||||
readahead_pages, &nr_pages, mapping,
|
||||
page_index, block, bsize);
|
||||
}
|
||||
} else {
|
||||
res = squashfs_readpages_fragment(page,
|
||||
readahead_pages, mapping);
|
||||
}
|
||||
if (res)
|
||||
return 0;
|
||||
page = NULL;
|
||||
} while (readahead_pages && !list_empty(readahead_pages));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int squashfs_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
int ret;
|
||||
|
||||
TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
|
||||
page->index, squashfs_i(inode)->start);
|
||||
page->index, squashfs_i(page->mapping->host)->start);
|
||||
|
||||
if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
|
||||
PAGE_CACHE_SHIFT))
|
||||
goto out;
|
||||
get_page(page);
|
||||
|
||||
if (index < file_end || squashfs_i(inode)->fragment_block ==
|
||||
SQUASHFS_INVALID_BLK) {
|
||||
u64 block = 0;
|
||||
int bsize = read_blocklist(inode, index, &block);
|
||||
if (bsize < 0)
|
||||
goto error_out;
|
||||
|
||||
if (bsize == 0)
|
||||
res = squashfs_readpage_sparse(page, index, file_end);
|
||||
ret = __squashfs_readpages(file, page, NULL, 1, page->mapping);
|
||||
if (ret) {
|
||||
flush_dcache_page(page);
|
||||
if (ret < 0)
|
||||
SetPageError(page);
|
||||
else
|
||||
res = squashfs_readpage_block(page, block, bsize);
|
||||
} else
|
||||
res = squashfs_readpage_fragment(page);
|
||||
SetPageUptodate(page);
|
||||
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
if (!res)
|
||||
return 0;
|
||||
|
||||
error_out:
|
||||
SetPageError(page);
|
||||
out:
|
||||
pageaddr = kmap_atomic(page);
|
||||
memset(pageaddr, 0, PAGE_CACHE_SIZE);
|
||||
kunmap_atomic(pageaddr);
|
||||
flush_dcache_page(page);
|
||||
if (!PageError(page))
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int squashfs_readpages(struct file *file, struct address_space *mapping,
|
||||
struct list_head *pages, unsigned int nr_pages)
|
||||
{
|
||||
TRACE("Entered squashfs_readpages, %u pages, first page index %lx\n",
|
||||
nr_pages, lru_to_page(pages)->index);
|
||||
__squashfs_readpages(file, NULL, pages, nr_pages, mapping);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const struct address_space_operations squashfs_aops = {
|
||||
.readpage = squashfs_readpage
|
||||
.readpage = squashfs_readpage,
|
||||
.readpages = squashfs_readpages,
|
||||
};
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2013
|
||||
* Phillip Lougher <phillip@squashfs.org.uk>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "squashfs_fs.h"
|
||||
#include "squashfs_fs_sb.h"
|
||||
#include "squashfs_fs_i.h"
|
||||
#include "squashfs.h"
|
||||
|
||||
/* Read separately compressed datablock and memcopy into page cache */
|
||||
int squashfs_readpage_block(struct page *page, u64 block, int bsize)
|
||||
{
|
||||
struct inode *i = page->mapping->host;
|
||||
struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
|
||||
block, bsize);
|
||||
int res = buffer->error;
|
||||
|
||||
if (res)
|
||||
ERROR("Unable to read page, block %llx, size %x\n", block,
|
||||
bsize);
|
||||
else
|
||||
squashfs_copy_cache(page, buffer, buffer->length, 0);
|
||||
|
||||
squashfs_cache_put(buffer);
|
||||
return res;
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mm_inline.h>
|
||||
|
||||
#include "squashfs_fs.h"
|
||||
#include "squashfs_fs_sb.h"
|
||||
|
@ -20,157 +21,139 @@
|
|||
#include "squashfs.h"
|
||||
#include "page_actor.h"
|
||||
|
||||
static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
|
||||
int pages, struct page **page);
|
||||
|
||||
/* Read separately compressed datablock directly into page cache */
|
||||
int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
|
||||
// Backported from 4.5
|
||||
#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))
|
||||
|
||||
static void release_actor_pages(struct page **page, int pages, int error)
|
||||
{
|
||||
struct inode *inode = target_page->mapping->host;
|
||||
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
|
||||
int i;
|
||||
|
||||
int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT;
|
||||
int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
|
||||
int start_index = target_page->index & ~mask;
|
||||
int end_index = start_index | mask;
|
||||
int i, n, pages, missing_pages, bytes, res = -ENOMEM;
|
||||
for (i = 0; i < pages; i++) {
|
||||
if (!page[i])
|
||||
continue;
|
||||
flush_dcache_page(page[i]);
|
||||
if (!error)
|
||||
SetPageUptodate(page[i]);
|
||||
else {
|
||||
SetPageError(page[i]);
|
||||
zero_user_segment(page[i], 0, PAGE_CACHE_SIZE);
|
||||
}
|
||||
unlock_page(page[i]);
|
||||
put_page(page[i]);
|
||||
}
|
||||
kfree(page);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a "page actor" which will kmap and kunmap the
|
||||
* page cache pages appropriately within the decompressor
|
||||
*/
|
||||
static struct squashfs_page_actor *actor_from_page_cache(
|
||||
unsigned int actor_pages, struct page *target_page,
|
||||
struct list_head *rpages, unsigned int *nr_pages, int start_index,
|
||||
struct address_space *mapping)
|
||||
{
|
||||
struct page **page;
|
||||
struct squashfs_page_actor *actor;
|
||||
void *pageaddr;
|
||||
int i, n;
|
||||
gfp_t gfp = mapping_gfp_constraint(mapping, GFP_KERNEL);
|
||||
|
||||
if (end_index > file_end)
|
||||
end_index = file_end;
|
||||
page = kmalloc_array(actor_pages, sizeof(void *), GFP_KERNEL);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
pages = end_index - start_index + 1;
|
||||
for (i = 0, n = start_index; i < actor_pages; i++, n++) {
|
||||
if (target_page == NULL && rpages && !list_empty(rpages)) {
|
||||
struct page *cur_page = lru_to_page(rpages);
|
||||
|
||||
page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL);
|
||||
if (page == NULL)
|
||||
return res;
|
||||
if (cur_page->index < start_index + actor_pages) {
|
||||
list_del(&cur_page->lru);
|
||||
--(*nr_pages);
|
||||
if (add_to_page_cache_lru(cur_page, mapping,
|
||||
cur_page->index, gfp))
|
||||
put_page(cur_page);
|
||||
else
|
||||
target_page = cur_page;
|
||||
} else
|
||||
rpages = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a "page actor" which will kmap and kunmap the
|
||||
* page cache pages appropriately within the decompressor
|
||||
*/
|
||||
actor = squashfs_page_actor_init_special(page, pages, 0);
|
||||
if (actor == NULL)
|
||||
goto out;
|
||||
|
||||
/* Try to grab all the pages covered by the Squashfs block */
|
||||
for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
|
||||
page[i] = (n == target_page->index) ? target_page :
|
||||
grab_cache_page_nowait(target_page->mapping, n);
|
||||
|
||||
if (page[i] == NULL) {
|
||||
missing_pages++;
|
||||
continue;
|
||||
if (target_page && target_page->index == n) {
|
||||
page[i] = target_page;
|
||||
target_page = NULL;
|
||||
} else {
|
||||
page[i] = grab_cache_page_nowait(mapping, n);
|
||||
if (page[i] == NULL)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PageUptodate(page[i])) {
|
||||
unlock_page(page[i]);
|
||||
page_cache_release(page[i]);
|
||||
put_page(page[i]);
|
||||
page[i] = NULL;
|
||||
missing_pages++;
|
||||
}
|
||||
}
|
||||
|
||||
if (missing_pages) {
|
||||
/*
|
||||
* Couldn't get one or more pages, this page has either
|
||||
* been VM reclaimed, but others are still in the page cache
|
||||
* and uptodate, or we're racing with another thread in
|
||||
* squashfs_readpage also trying to grab them. Fall back to
|
||||
* using an intermediate buffer.
|
||||
*/
|
||||
res = squashfs_read_cache(target_page, block, bsize, pages,
|
||||
page);
|
||||
if (res < 0)
|
||||
goto mark_errored;
|
||||
|
||||
goto out;
|
||||
actor = squashfs_page_actor_init(page, actor_pages, 0,
|
||||
release_actor_pages);
|
||||
if (!actor) {
|
||||
release_actor_pages(page, actor_pages, -ENOMEM);
|
||||
kfree(page);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Decompress directly into the page cache buffers */
|
||||
res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
|
||||
if (res < 0)
|
||||
goto mark_errored;
|
||||
|
||||
/* Last page may have trailing bytes not filled */
|
||||
bytes = res % PAGE_CACHE_SIZE;
|
||||
if (bytes) {
|
||||
pageaddr = kmap_atomic(page[pages - 1]);
|
||||
memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
|
||||
kunmap_atomic(pageaddr);
|
||||
}
|
||||
|
||||
/* Mark pages as uptodate, unlock and release */
|
||||
for (i = 0; i < pages; i++) {
|
||||
flush_dcache_page(page[i]);
|
||||
SetPageUptodate(page[i]);
|
||||
unlock_page(page[i]);
|
||||
if (page[i] != target_page)
|
||||
page_cache_release(page[i]);
|
||||
}
|
||||
|
||||
kfree(actor);
|
||||
kfree(page);
|
||||
|
||||
return 0;
|
||||
|
||||
mark_errored:
|
||||
/* Decompression failed, mark pages as errored. Target_page is
|
||||
* dealt with by the caller
|
||||
*/
|
||||
for (i = 0; i < pages; i++) {
|
||||
if (page[i] == NULL || page[i] == target_page)
|
||||
continue;
|
||||
flush_dcache_page(page[i]);
|
||||
SetPageError(page[i]);
|
||||
unlock_page(page[i]);
|
||||
page_cache_release(page[i]);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(actor);
|
||||
kfree(page);
|
||||
return res;
|
||||
return actor;
|
||||
}
|
||||
|
||||
int squashfs_readpages_block(struct page *target_page,
|
||||
struct list_head *readahead_pages,
|
||||
unsigned int *nr_pages,
|
||||
struct address_space *mapping,
|
||||
int page_index, u64 block, int bsize)
|
||||
|
||||
static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
|
||||
int pages, struct page **page)
|
||||
{
|
||||
struct inode *i = target_page->mapping->host;
|
||||
struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
|
||||
block, bsize);
|
||||
int bytes = buffer->length, res = buffer->error, n, offset = 0;
|
||||
void *pageaddr;
|
||||
struct squashfs_page_actor *actor;
|
||||
struct inode *inode = mapping->host;
|
||||
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
|
||||
int start_index, end_index, file_end, actor_pages, res;
|
||||
int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
|
||||
|
||||
if (res) {
|
||||
ERROR("Unable to read page, block %llx, size %x\n", block,
|
||||
bsize);
|
||||
goto out;
|
||||
/*
|
||||
* If readpage() is called on an uncompressed datablock, we can just
|
||||
* read the pages instead of fetching the whole block.
|
||||
* This greatly improves the performance when a process keep doing
|
||||
* random reads because we only fetch the necessary data.
|
||||
* The readahead algorithm will take care of doing speculative reads
|
||||
* if necessary.
|
||||
* We can't read more than 1 block even if readahead provides use more
|
||||
* pages because we don't know yet if the next block is compressed or
|
||||
* not.
|
||||
*/
|
||||
if (bsize && !SQUASHFS_COMPRESSED_BLOCK(bsize)) {
|
||||
u64 block_end = block + msblk->block_size;
|
||||
|
||||
block += (page_index & mask) * PAGE_CACHE_SIZE;
|
||||
actor_pages = (block_end - block) / PAGE_CACHE_SIZE;
|
||||
if (*nr_pages < actor_pages)
|
||||
actor_pages = *nr_pages;
|
||||
start_index = page_index;
|
||||
bsize = min_t(int, bsize, (PAGE_CACHE_SIZE * actor_pages)
|
||||
| SQUASHFS_COMPRESSED_BIT_BLOCK);
|
||||
} else {
|
||||
file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT;
|
||||
start_index = page_index & ~mask;
|
||||
end_index = start_index | mask;
|
||||
if (end_index > file_end)
|
||||
end_index = file_end;
|
||||
actor_pages = end_index - start_index + 1;
|
||||
}
|
||||
|
||||
for (n = 0; n < pages && bytes > 0; n++,
|
||||
bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
|
||||
int avail = min_t(int, bytes, PAGE_CACHE_SIZE);
|
||||
actor = actor_from_page_cache(actor_pages, target_page,
|
||||
readahead_pages, nr_pages, start_index,
|
||||
mapping);
|
||||
if (!actor)
|
||||
return -ENOMEM;
|
||||
|
||||
if (page[n] == NULL)
|
||||
continue;
|
||||
|
||||
pageaddr = kmap_atomic(page[n]);
|
||||
squashfs_copy_data(pageaddr, buffer, offset, avail);
|
||||
memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
|
||||
kunmap_atomic(pageaddr);
|
||||
flush_dcache_page(page[n]);
|
||||
SetPageUptodate(page[n]);
|
||||
unlock_page(page[n]);
|
||||
if (page[n] != target_page)
|
||||
page_cache_release(page[n]);
|
||||
}
|
||||
|
||||
out:
|
||||
squashfs_cache_put(buffer);
|
||||
return res;
|
||||
res = squashfs_read_data_async(inode->i_sb, block, bsize, NULL,
|
||||
actor);
|
||||
return res < 0 ? res : 0;
|
||||
}
|
||||
|
|
|
@ -94,39 +94,17 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
|||
struct buffer_head **bh, int b, int offset, int length,
|
||||
struct squashfs_page_actor *output)
|
||||
{
|
||||
struct squashfs_lz4 *stream = strm;
|
||||
void *buff = stream->input, *data;
|
||||
int avail, i, bytes = length, res;
|
||||
int res;
|
||||
size_t dest_len = output->length;
|
||||
struct squashfs_lz4 *stream = strm;
|
||||
|
||||
for (i = 0; i < b; i++) {
|
||||
avail = min(bytes, msblk->devblksize - offset);
|
||||
memcpy(buff, bh[i]->b_data + offset, avail);
|
||||
buff += avail;
|
||||
bytes -= avail;
|
||||
offset = 0;
|
||||
put_bh(bh[i]);
|
||||
}
|
||||
|
||||
squashfs_bh_to_buf(bh, b, stream->input, offset, length,
|
||||
msblk->devblksize);
|
||||
res = lz4_decompress_unknownoutputsize(stream->input, length,
|
||||
stream->output, &dest_len);
|
||||
if (res)
|
||||
return -EIO;
|
||||
|
||||
bytes = dest_len;
|
||||
data = squashfs_first_page(output);
|
||||
buff = stream->output;
|
||||
while (data) {
|
||||
if (bytes <= PAGE_CACHE_SIZE) {
|
||||
memcpy(data, buff, bytes);
|
||||
break;
|
||||
}
|
||||
memcpy(data, buff, PAGE_CACHE_SIZE);
|
||||
buff += PAGE_CACHE_SIZE;
|
||||
bytes -= PAGE_CACHE_SIZE;
|
||||
data = squashfs_next_page(output);
|
||||
}
|
||||
squashfs_finish_page(output);
|
||||
squashfs_buf_to_actor(stream->output, output, dest_len);
|
||||
|
||||
return dest_len;
|
||||
}
|
||||
|
|
|
@ -79,45 +79,19 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
|||
struct buffer_head **bh, int b, int offset, int length,
|
||||
struct squashfs_page_actor *output)
|
||||
{
|
||||
struct squashfs_lzo *stream = strm;
|
||||
void *buff = stream->input, *data;
|
||||
int avail, i, bytes = length, res;
|
||||
int res;
|
||||
size_t out_len = output->length;
|
||||
struct squashfs_lzo *stream = strm;
|
||||
|
||||
for (i = 0; i < b; i++) {
|
||||
avail = min(bytes, msblk->devblksize - offset);
|
||||
memcpy(buff, bh[i]->b_data + offset, avail);
|
||||
buff += avail;
|
||||
bytes -= avail;
|
||||
offset = 0;
|
||||
put_bh(bh[i]);
|
||||
}
|
||||
|
||||
squashfs_bh_to_buf(bh, b, stream->input, offset, length,
|
||||
msblk->devblksize);
|
||||
res = lzo1x_decompress_safe(stream->input, (size_t)length,
|
||||
stream->output, &out_len);
|
||||
if (res != LZO_E_OK)
|
||||
goto failed;
|
||||
return -EIO;
|
||||
squashfs_buf_to_actor(stream->output, output, out_len);
|
||||
|
||||
res = bytes = (int)out_len;
|
||||
data = squashfs_first_page(output);
|
||||
buff = stream->output;
|
||||
while (data) {
|
||||
if (bytes <= PAGE_CACHE_SIZE) {
|
||||
memcpy(data, buff, bytes);
|
||||
break;
|
||||
} else {
|
||||
memcpy(data, buff, PAGE_CACHE_SIZE);
|
||||
buff += PAGE_CACHE_SIZE;
|
||||
bytes -= PAGE_CACHE_SIZE;
|
||||
data = squashfs_next_page(output);
|
||||
}
|
||||
}
|
||||
squashfs_finish_page(output);
|
||||
|
||||
return res;
|
||||
|
||||
failed:
|
||||
return -EIO;
|
||||
return out_len;
|
||||
}
|
||||
|
||||
const struct squashfs_decompressor squashfs_lzo_comp_ops = {
|
||||
|
|
|
@ -9,79 +9,11 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include "page_actor.h"
|
||||
|
||||
/*
|
||||
* This file contains implementations of page_actor for decompressing into
|
||||
* an intermediate buffer, and for decompressing directly into the
|
||||
* page cache.
|
||||
*
|
||||
* Calling code should avoid sleeping between calls to squashfs_first_page()
|
||||
* and squashfs_finish_page().
|
||||
*/
|
||||
|
||||
/* Implementation of page_actor for decompressing into intermediate buffer */
|
||||
static void *cache_first_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
actor->next_page = 1;
|
||||
return actor->buffer[0];
|
||||
}
|
||||
|
||||
static void *cache_next_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
if (actor->next_page == actor->pages)
|
||||
return NULL;
|
||||
|
||||
return actor->buffer[actor->next_page++];
|
||||
}
|
||||
|
||||
static void cache_finish_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
|
||||
int pages, int length)
|
||||
{
|
||||
struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
|
||||
|
||||
if (actor == NULL)
|
||||
return NULL;
|
||||
|
||||
actor->length = length ? : pages * PAGE_CACHE_SIZE;
|
||||
actor->buffer = buffer;
|
||||
actor->pages = pages;
|
||||
actor->next_page = 0;
|
||||
actor->squashfs_first_page = cache_first_page;
|
||||
actor->squashfs_next_page = cache_next_page;
|
||||
actor->squashfs_finish_page = cache_finish_page;
|
||||
return actor;
|
||||
}
|
||||
|
||||
/* Implementation of page_actor for decompressing directly into page cache. */
|
||||
static void *direct_first_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
actor->next_page = 1;
|
||||
return actor->pageaddr = kmap_atomic(actor->page[0]);
|
||||
}
|
||||
|
||||
static void *direct_next_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
if (actor->pageaddr)
|
||||
kunmap_atomic(actor->pageaddr);
|
||||
|
||||
return actor->pageaddr = actor->next_page == actor->pages ? NULL :
|
||||
kmap_atomic(actor->page[actor->next_page++]);
|
||||
}
|
||||
|
||||
static void direct_finish_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
if (actor->pageaddr)
|
||||
kunmap_atomic(actor->pageaddr);
|
||||
}
|
||||
|
||||
struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
|
||||
int pages, int length)
|
||||
struct squashfs_page_actor *squashfs_page_actor_init(struct page **page,
|
||||
int pages, int length, void (*release_pages)(struct page **, int, int))
|
||||
{
|
||||
struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
|
||||
|
||||
|
@ -93,8 +25,129 @@ struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
|
|||
actor->pages = pages;
|
||||
actor->next_page = 0;
|
||||
actor->pageaddr = NULL;
|
||||
actor->squashfs_first_page = direct_first_page;
|
||||
actor->squashfs_next_page = direct_next_page;
|
||||
actor->squashfs_finish_page = direct_finish_page;
|
||||
actor->release_pages = release_pages;
|
||||
return actor;
|
||||
}
|
||||
|
||||
void squashfs_page_actor_free(struct squashfs_page_actor *actor, int error)
|
||||
{
|
||||
if (!actor)
|
||||
return;
|
||||
|
||||
if (actor->release_pages)
|
||||
actor->release_pages(actor->page, actor->pages, error);
|
||||
kfree(actor);
|
||||
}
|
||||
|
||||
void squashfs_actor_to_buf(struct squashfs_page_actor *actor, void *buf,
|
||||
int length)
|
||||
{
|
||||
void *pageaddr;
|
||||
int pos = 0, avail, i;
|
||||
|
||||
for (i = 0; i < actor->pages && pos < length; ++i) {
|
||||
avail = min_t(int, length - pos, PAGE_CACHE_SIZE);
|
||||
if (actor->page[i]) {
|
||||
pageaddr = kmap_atomic(actor->page[i]);
|
||||
memcpy(buf + pos, pageaddr, avail);
|
||||
kunmap_atomic(pageaddr);
|
||||
}
|
||||
pos += avail;
|
||||
}
|
||||
}
|
||||
|
||||
void squashfs_buf_to_actor(void *buf, struct squashfs_page_actor *actor,
|
||||
int length)
|
||||
{
|
||||
void *pageaddr;
|
||||
int pos = 0, avail, i;
|
||||
|
||||
for (i = 0; i < actor->pages && pos < length; ++i) {
|
||||
avail = min_t(int, length - pos, PAGE_CACHE_SIZE);
|
||||
if (actor->page[i]) {
|
||||
pageaddr = kmap_atomic(actor->page[i]);
|
||||
memcpy(pageaddr, buf + pos, avail);
|
||||
kunmap_atomic(pageaddr);
|
||||
}
|
||||
pos += avail;
|
||||
}
|
||||
}
|
||||
|
||||
void squashfs_bh_to_actor(struct buffer_head **bh, int nr_buffers,
|
||||
struct squashfs_page_actor *actor, int offset, int length, int blksz)
|
||||
{
|
||||
void *kaddr = NULL;
|
||||
int bytes = 0, pgoff = 0, b = 0, p = 0, avail, i;
|
||||
|
||||
while (bytes < length) {
|
||||
if (actor->page[p]) {
|
||||
kaddr = kmap_atomic(actor->page[p]);
|
||||
while (pgoff < PAGE_CACHE_SIZE && bytes < length) {
|
||||
avail = min_t(int, blksz - offset,
|
||||
PAGE_CACHE_SIZE - pgoff);
|
||||
memcpy(kaddr + pgoff, bh[b]->b_data + offset,
|
||||
avail);
|
||||
pgoff += avail;
|
||||
bytes += avail;
|
||||
offset = (offset + avail) % blksz;
|
||||
if (!offset) {
|
||||
put_bh(bh[b]);
|
||||
++b;
|
||||
}
|
||||
}
|
||||
kunmap_atomic(kaddr);
|
||||
pgoff = 0;
|
||||
} else {
|
||||
for (i = 0; i < PAGE_CACHE_SIZE / blksz; ++i) {
|
||||
if (bh[b])
|
||||
put_bh(bh[b]);
|
||||
++b;
|
||||
}
|
||||
bytes += PAGE_CACHE_SIZE;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
void squashfs_bh_to_buf(struct buffer_head **bh, int nr_buffers, void *buf,
|
||||
int offset, int length, int blksz)
|
||||
{
|
||||
int i, avail, bytes = 0;
|
||||
|
||||
for (i = 0; i < nr_buffers && bytes < length; ++i) {
|
||||
avail = min_t(int, length - bytes, blksz - offset);
|
||||
if (bh[i]) {
|
||||
memcpy(buf + bytes, bh[i]->b_data + offset, avail);
|
||||
put_bh(bh[i]);
|
||||
}
|
||||
bytes += avail;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void free_page_array(struct page **page, int nr_pages)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_pages; ++i)
|
||||
__free_page(page[i]);
|
||||
kfree(page);
|
||||
}
|
||||
|
||||
struct page **alloc_page_array(int nr_pages, int gfp_mask)
|
||||
{
|
||||
int i;
|
||||
struct page **page;
|
||||
|
||||
page = kcalloc(nr_pages, sizeof(struct page *), gfp_mask);
|
||||
if (!page)
|
||||
return NULL;
|
||||
for (i = 0; i < nr_pages; ++i) {
|
||||
page[i] = alloc_page(gfp_mask);
|
||||
if (!page[i]) {
|
||||
free_page_array(page, i);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
|
|
@ -5,77 +5,61 @@
|
|||
* Phillip Lougher <phillip@squashfs.org.uk>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
* the COPYING file in the top-level squashfsory.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_SQUASHFS_FILE_DIRECT
|
||||
struct squashfs_page_actor {
|
||||
void **page;
|
||||
struct page **page;
|
||||
void *pageaddr;
|
||||
int pages;
|
||||
int length;
|
||||
int next_page;
|
||||
void (*release_pages)(struct page **, int, int);
|
||||
};
|
||||
|
||||
static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
|
||||
int pages, int length)
|
||||
{
|
||||
struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
|
||||
extern struct squashfs_page_actor *squashfs_page_actor_init(struct page **,
|
||||
int, int, void (*)(struct page **, int, int));
|
||||
extern void squashfs_page_actor_free(struct squashfs_page_actor *, int);
|
||||
|
||||
if (actor == NULL)
|
||||
return NULL;
|
||||
|
||||
actor->length = length ? : pages * PAGE_CACHE_SIZE;
|
||||
actor->page = page;
|
||||
actor->pages = pages;
|
||||
actor->next_page = 0;
|
||||
return actor;
|
||||
}
|
||||
extern void squashfs_actor_to_buf(struct squashfs_page_actor *, void *, int);
|
||||
extern void squashfs_buf_to_actor(void *, struct squashfs_page_actor *, int);
|
||||
extern void squashfs_bh_to_actor(struct buffer_head **, int,
|
||||
struct squashfs_page_actor *, int, int, int);
|
||||
extern void squashfs_bh_to_buf(struct buffer_head **, int, void *, int, int,
|
||||
int);
|
||||
|
||||
/*
|
||||
* Calling code should avoid sleeping between calls to squashfs_first_page()
|
||||
* and squashfs_finish_page().
|
||||
*/
|
||||
static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
actor->next_page = 1;
|
||||
return actor->page[0];
|
||||
return actor->pageaddr = actor->page[0] ? kmap_atomic(actor->page[0])
|
||||
: NULL;
|
||||
}
|
||||
|
||||
static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
return actor->next_page == actor->pages ? NULL :
|
||||
actor->page[actor->next_page++];
|
||||
if (!IS_ERR_OR_NULL(actor->pageaddr))
|
||||
kunmap_atomic(actor->pageaddr);
|
||||
|
||||
if (actor->next_page == actor->pages)
|
||||
return actor->pageaddr = ERR_PTR(-ENODATA);
|
||||
|
||||
actor->pageaddr = actor->page[actor->next_page] ?
|
||||
kmap_atomic(actor->page[actor->next_page]) : NULL;
|
||||
++actor->next_page;
|
||||
return actor->pageaddr;
|
||||
}
|
||||
|
||||
static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
/* empty */
|
||||
if (!IS_ERR_OR_NULL(actor->pageaddr))
|
||||
kunmap_atomic(actor->pageaddr);
|
||||
}
|
||||
#else
|
||||
struct squashfs_page_actor {
|
||||
union {
|
||||
void **buffer;
|
||||
struct page **page;
|
||||
};
|
||||
void *pageaddr;
|
||||
void *(*squashfs_first_page)(struct squashfs_page_actor *);
|
||||
void *(*squashfs_next_page)(struct squashfs_page_actor *);
|
||||
void (*squashfs_finish_page)(struct squashfs_page_actor *);
|
||||
int pages;
|
||||
int length;
|
||||
int next_page;
|
||||
};
|
||||
|
||||
extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
|
||||
extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
|
||||
**, int, int);
|
||||
static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
return actor->squashfs_first_page(actor);
|
||||
}
|
||||
static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
return actor->squashfs_next_page(actor);
|
||||
}
|
||||
static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
actor->squashfs_finish_page(actor);
|
||||
}
|
||||
#endif
|
||||
extern struct page **alloc_page_array(int, int);
|
||||
extern void free_page_array(struct page **, int);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,8 +28,14 @@
|
|||
#define WARNING(s, args...) pr_warn("SQUASHFS: "s, ## args)
|
||||
|
||||
/* block.c */
|
||||
extern int squashfs_init_read_wq(void);
|
||||
extern void squashfs_destroy_read_wq(void);
|
||||
extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
|
||||
struct squashfs_page_actor *);
|
||||
extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
|
||||
struct squashfs_page_actor *);
|
||||
extern int squashfs_read_data_async(struct super_block *, u64, int, u64 *,
|
||||
struct squashfs_page_actor *);
|
||||
|
||||
/* cache.c */
|
||||
extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
|
||||
|
@ -70,8 +76,9 @@ extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
|
|||
void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
|
||||
int);
|
||||
|
||||
/* file_xxx.c */
|
||||
extern int squashfs_readpage_block(struct page *, u64, int);
|
||||
/* file_direct.c */
|
||||
extern int squashfs_readpages_block(struct page *, struct list_head *,
|
||||
unsigned int *, struct address_space *, int, u64, int);
|
||||
|
||||
/* id.c */
|
||||
extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
|
||||
|
|
|
@ -49,7 +49,7 @@ struct squashfs_cache_entry {
|
|||
int num_waiters;
|
||||
wait_queue_head_t wait_queue;
|
||||
struct squashfs_cache *cache;
|
||||
void **data;
|
||||
struct page **page;
|
||||
struct squashfs_page_actor *actor;
|
||||
};
|
||||
|
||||
|
|
|
@ -444,9 +444,15 @@ static int __init init_squashfs_fs(void)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (!squashfs_init_read_wq()) {
|
||||
destroy_inodecache();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = register_filesystem(&squashfs_fs_type);
|
||||
if (err) {
|
||||
destroy_inodecache();
|
||||
squashfs_destroy_read_wq();
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -460,6 +466,7 @@ static void __exit exit_squashfs_fs(void)
|
|||
{
|
||||
unregister_filesystem(&squashfs_fs_type);
|
||||
destroy_inodecache();
|
||||
squashfs_destroy_read_wq();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk,
|
|||
struct comp_opts *opts;
|
||||
int err = 0, n;
|
||||
|
||||
opts = kmalloc(sizeof(*opts), GFP_KERNEL);
|
||||
opts = kmalloc(sizeof(*opts), GFP_ATOMIC);
|
||||
if (opts == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto out2;
|
||||
|
@ -136,6 +136,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
|||
enum xz_ret xz_err;
|
||||
int avail, total = 0, k = 0;
|
||||
struct squashfs_xz *stream = strm;
|
||||
void *buf = NULL;
|
||||
|
||||
xz_dec_reset(stream->state);
|
||||
stream->buf.in_pos = 0;
|
||||
|
@ -156,12 +157,20 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
|||
|
||||
if (stream->buf.out_pos == stream->buf.out_size) {
|
||||
stream->buf.out = squashfs_next_page(output);
|
||||
if (stream->buf.out != NULL) {
|
||||
if (!IS_ERR(stream->buf.out)) {
|
||||
stream->buf.out_pos = 0;
|
||||
total += PAGE_CACHE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stream->buf.out) {
|
||||
if (!buf) {
|
||||
buf = kmalloc(PAGE_CACHE_SIZE, GFP_ATOMIC);
|
||||
if (!buf)
|
||||
goto out;
|
||||
}
|
||||
stream->buf.out = buf;
|
||||
}
|
||||
xz_err = xz_dec_run(stream->state, &stream->buf);
|
||||
|
||||
if (stream->buf.in_pos == stream->buf.in_size && k < b)
|
||||
|
@ -173,11 +182,13 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
|||
if (xz_err != XZ_STREAM_END || k < b)
|
||||
goto out;
|
||||
|
||||
kfree(buf);
|
||||
return total + stream->buf.out_pos;
|
||||
|
||||
out:
|
||||
for (; k < b; k++)
|
||||
put_bh(bh[k]);
|
||||
kfree(buf);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
|||
struct buffer_head **bh, int b, int offset, int length,
|
||||
struct squashfs_page_actor *output)
|
||||
{
|
||||
void *buf = NULL;
|
||||
int zlib_err, zlib_init = 0, k = 0;
|
||||
z_stream *stream = strm;
|
||||
|
||||
|
@ -84,10 +85,19 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
|||
|
||||
if (stream->avail_out == 0) {
|
||||
stream->next_out = squashfs_next_page(output);
|
||||
if (stream->next_out != NULL)
|
||||
if (!IS_ERR(stream->next_out))
|
||||
stream->avail_out = PAGE_CACHE_SIZE;
|
||||
}
|
||||
|
||||
if (!stream->next_out) {
|
||||
if (!buf) {
|
||||
buf = kmalloc(PAGE_CACHE_SIZE, GFP_ATOMIC);
|
||||
if (!buf)
|
||||
goto out;
|
||||
}
|
||||
stream->next_out = buf;
|
||||
}
|
||||
|
||||
if (!zlib_init) {
|
||||
zlib_err = zlib_inflateInit(stream);
|
||||
if (zlib_err != Z_OK) {
|
||||
|
@ -115,11 +125,13 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
|||
if (k < b)
|
||||
goto out;
|
||||
|
||||
kfree(buf);
|
||||
return stream->total_out;
|
||||
|
||||
out:
|
||||
for (; k < b; k++)
|
||||
put_bh(bh[k]);
|
||||
kfree(buf);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
|
28
fs/super.c
28
fs/super.c
|
@ -703,7 +703,8 @@ rescan:
|
|||
}
|
||||
|
||||
/**
|
||||
* do_remount_sb - asks filesystem to change mount options.
|
||||
* do_remount_sb2 - asks filesystem to change mount options.
|
||||
* @mnt: mount we are looking at
|
||||
* @sb: superblock in question
|
||||
* @flags: numeric part of options
|
||||
* @data: the rest of options
|
||||
|
@ -711,7 +712,7 @@ rescan:
|
|||
*
|
||||
* Alters the mount options of a mounted file system.
|
||||
*/
|
||||
int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
|
||||
int do_remount_sb2(struct vfsmount *mnt, struct super_block *sb, int flags, void *data, int force)
|
||||
{
|
||||
int retval;
|
||||
int remount_ro;
|
||||
|
@ -753,7 +754,16 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
|
|||
}
|
||||
}
|
||||
|
||||
if (sb->s_op->remount_fs) {
|
||||
if (mnt && sb->s_op->remount_fs2) {
|
||||
retval = sb->s_op->remount_fs2(mnt, sb, &flags, data);
|
||||
if (retval) {
|
||||
if (!force)
|
||||
goto cancel_readonly;
|
||||
/* If forced remount, go ahead despite any errors */
|
||||
WARN(1, "forced remount of a %s fs returned %i\n",
|
||||
sb->s_type->name, retval);
|
||||
}
|
||||
} else if (sb->s_op->remount_fs) {
|
||||
retval = sb->s_op->remount_fs(sb, &flags, data);
|
||||
if (retval) {
|
||||
if (!force)
|
||||
|
@ -785,6 +795,11 @@ cancel_readonly:
|
|||
return retval;
|
||||
}
|
||||
|
||||
int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
|
||||
{
|
||||
return do_remount_sb2(NULL, sb, flags, data, force);
|
||||
}
|
||||
|
||||
static void do_emergency_remount(struct work_struct *work)
|
||||
{
|
||||
struct super_block *sb, *p = NULL;
|
||||
|
@ -1104,7 +1119,7 @@ struct dentry *mount_single(struct file_system_type *fs_type,
|
|||
EXPORT_SYMBOL(mount_single);
|
||||
|
||||
struct dentry *
|
||||
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
|
||||
mount_fs(struct file_system_type *type, int flags, const char *name, struct vfsmount *mnt, void *data)
|
||||
{
|
||||
struct dentry *root;
|
||||
struct super_block *sb;
|
||||
|
@ -1121,7 +1136,10 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
|
|||
goto out_free_secdata;
|
||||
}
|
||||
|
||||
root = type->mount(type, flags, name, data);
|
||||
if (type->mount2)
|
||||
root = type->mount2(mnt, type, flags, name, data);
|
||||
else
|
||||
root = type->mount(type, flags, name, data);
|
||||
if (IS_ERR(root)) {
|
||||
error = PTR_ERR(root);
|
||||
goto out_free_secdata;
|
||||
|
|
|
@ -91,7 +91,7 @@ static int utimes_common(struct path *path, struct timespec *times)
|
|||
}
|
||||
retry_deleg:
|
||||
mutex_lock(&inode->i_mutex);
|
||||
error = notify_change(path->dentry, &newattrs, &delegated_inode);
|
||||
error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
if (delegated_inode) {
|
||||
error = break_deleg_wait(&delegated_inode);
|
||||
|
|
|
@ -202,6 +202,9 @@ int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc,
|
|||
struct blkcipher_walk *walk,
|
||||
struct crypto_aead *tfm,
|
||||
unsigned int blocksize);
|
||||
int blkcipher_ablkcipher_walk_virt(struct blkcipher_desc *desc,
|
||||
struct blkcipher_walk *walk,
|
||||
struct crypto_ablkcipher *tfm);
|
||||
|
||||
int ablkcipher_walk_done(struct ablkcipher_request *req,
|
||||
struct ablkcipher_walk *walk, int err);
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
---------------------------------------------------------------------------
|
||||
Issue Date: 31/01/2006
|
||||
|
||||
An implementation of field multiplication in Galois Field GF(128)
|
||||
An implementation of field multiplication in Galois Field GF(2^128)
|
||||
*/
|
||||
|
||||
#ifndef _CRYPTO_GF128MUL_H
|
||||
|
@ -65,7 +65,7 @@
|
|||
* are left and the lsb's are right. char b[16] is an array and b[0] is
|
||||
* the first octet.
|
||||
*
|
||||
* 80000000 00000000 00000000 00000000 .... 00000000 00000000 00000000
|
||||
* 10000000 00000000 00000000 00000000 .... 00000000 00000000 00000000
|
||||
* b[0] b[1] b[2] b[3] b[13] b[14] b[15]
|
||||
*
|
||||
* Every bit is a coefficient of some power of X. We can store the bits
|
||||
|
@ -99,21 +99,21 @@
|
|||
*
|
||||
* bbe on a little endian machine u32 x[4]:
|
||||
*
|
||||
* MS x[0] LS MS x[1] LS
|
||||
* MS x[0] LS MS x[1] LS
|
||||
* ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
|
||||
* 103..96 111.104 119.112 127.120 71...64 79...72 87...80 95...88
|
||||
*
|
||||
* MS x[2] LS MS x[3] LS
|
||||
* MS x[2] LS MS x[3] LS
|
||||
* ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
|
||||
* 39...32 47...40 55...48 63...56 07...00 15...08 23...16 31...24
|
||||
*
|
||||
* ble on a little endian machine
|
||||
*
|
||||
* MS x[0] LS MS x[1] LS
|
||||
* MS x[0] LS MS x[1] LS
|
||||
* ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
|
||||
* 31...24 23...16 15...08 07...00 63...56 55...48 47...40 39...32
|
||||
*
|
||||
* MS x[2] LS MS x[3] LS
|
||||
* MS x[2] LS MS x[3] LS
|
||||
* ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
|
||||
* 95...88 87...80 79...72 71...64 127.120 199.112 111.104 103..96
|
||||
*
|
||||
|
@ -127,7 +127,7 @@
|
|||
* machines this will automatically aligned to wordsize and on a 64-bit
|
||||
* machine also.
|
||||
*/
|
||||
/* Multiply a GF128 field element by x. Field elements are held in arrays
|
||||
/* Multiply a GF128 field element by x. Field elements are held in arrays
|
||||
of bytes in which field bits 8n..8n + 7 are held in byte[n], with lower
|
||||
indexed bits placed in the more numerically significant bit positions
|
||||
within bytes.
|
||||
|
@ -135,62 +135,65 @@
|
|||
On little endian machines the bit indexes translate into the bit
|
||||
positions within four 32-bit words in the following way
|
||||
|
||||
MS x[0] LS MS x[1] LS
|
||||
MS x[0] LS MS x[1] LS
|
||||
ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
|
||||
24...31 16...23 08...15 00...07 56...63 48...55 40...47 32...39
|
||||
|
||||
MS x[2] LS MS x[3] LS
|
||||
MS x[2] LS MS x[3] LS
|
||||
ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
|
||||
88...95 80...87 72...79 64...71 120.127 112.119 104.111 96..103
|
||||
|
||||
On big endian machines the bit indexes translate into the bit
|
||||
positions within four 32-bit words in the following way
|
||||
|
||||
MS x[0] LS MS x[1] LS
|
||||
MS x[0] LS MS x[1] LS
|
||||
ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
|
||||
00...07 08...15 16...23 24...31 32...39 40...47 48...55 56...63
|
||||
|
||||
MS x[2] LS MS x[3] LS
|
||||
MS x[2] LS MS x[3] LS
|
||||
ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls
|
||||
64...71 72...79 80...87 88...95 96..103 104.111 112.119 120.127
|
||||
*/
|
||||
|
||||
/* A slow generic version of gf_mul, implemented for lle and bbe
|
||||
* It multiplies a and b and puts the result in a */
|
||||
/* A slow generic version of gf_mul, implemented for lle, bbe, and ble.
|
||||
* It multiplies a and b and puts the result in a
|
||||
*/
|
||||
void gf128mul_lle(be128 *a, const be128 *b);
|
||||
|
||||
void gf128mul_bbe(be128 *a, const be128 *b);
|
||||
void gf128mul_ble(be128 *a, const be128 *b);
|
||||
|
||||
/* multiply by x in ble format, needed by XTS */
|
||||
/* multiply by x in ble format, needed by XTS and HEH */
|
||||
void gf128mul_x_ble(be128 *a, const be128 *b);
|
||||
|
||||
/* 4k table optimization */
|
||||
|
||||
struct gf128mul_4k {
|
||||
be128 t[256];
|
||||
};
|
||||
|
||||
struct gf128mul_4k *gf128mul_init_4k_lle(const be128 *g);
|
||||
struct gf128mul_4k *gf128mul_init_4k_bbe(const be128 *g);
|
||||
struct gf128mul_4k *gf128mul_init_4k_ble(const be128 *g);
|
||||
void gf128mul_4k_lle(be128 *a, struct gf128mul_4k *t);
|
||||
void gf128mul_4k_bbe(be128 *a, struct gf128mul_4k *t);
|
||||
void gf128mul_4k_ble(be128 *a, struct gf128mul_4k *t);
|
||||
|
||||
static inline void gf128mul_free_4k(struct gf128mul_4k *t)
|
||||
{
|
||||
kfree(t);
|
||||
kzfree(t);
|
||||
}
|
||||
|
||||
|
||||
/* 64k table optimization, implemented for lle and bbe */
|
||||
/* 64k table optimization, implemented for lle, ble, and bbe */
|
||||
|
||||
struct gf128mul_64k {
|
||||
struct gf128mul_4k *t[16];
|
||||
};
|
||||
|
||||
/* first initialize with the constant factor with which you
|
||||
* want to multiply and then call gf128_64k_lle with the other
|
||||
* factor in the first argument, the table in the second and a
|
||||
* scratch register in the third. Afterwards *a = *r. */
|
||||
/* First initialize with the constant factor with which you
|
||||
* want to multiply and then call gf128mul_64k_bbe with the other
|
||||
* factor in the first argument, and the table in the second.
|
||||
* Afterwards, the result is stored in *a.
|
||||
*/
|
||||
struct gf128mul_64k *gf128mul_init_64k_lle(const be128 *g);
|
||||
struct gf128mul_64k *gf128mul_init_64k_bbe(const be128 *g);
|
||||
void gf128mul_free_64k(struct gf128mul_64k *t);
|
||||
|
|
|
@ -102,6 +102,8 @@ int shash_register_instance(struct crypto_template *tmpl,
|
|||
struct shash_instance *inst);
|
||||
void shash_free_instance(struct crypto_instance *inst);
|
||||
|
||||
int crypto_grab_shash(struct crypto_shash_spawn *spawn,
|
||||
const char *name, u32 type, u32 mask);
|
||||
int crypto_init_shash_spawn(struct crypto_shash_spawn *spawn,
|
||||
struct shash_alg *alg,
|
||||
struct crypto_instance *inst);
|
||||
|
@ -111,6 +113,12 @@ static inline void crypto_drop_shash(struct crypto_shash_spawn *spawn)
|
|||
crypto_drop_spawn(&spawn->base);
|
||||
}
|
||||
|
||||
static inline struct shash_alg *crypto_spawn_shash_alg(
|
||||
struct crypto_shash_spawn *spawn)
|
||||
{
|
||||
return container_of(spawn->base.alg, struct shash_alg, base);
|
||||
}
|
||||
|
||||
struct shash_alg *shash_attr_alg(struct rtattr *rta, u32 type, u32 mask);
|
||||
|
||||
int shash_ahash_update(struct ahash_request *req, struct shash_desc *desc);
|
||||
|
|
|
@ -1539,13 +1539,21 @@ extern bool inode_owner_or_capable(const struct inode *inode);
|
|||
* VFS helper functions..
|
||||
*/
|
||||
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
|
||||
extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool);
|
||||
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
|
||||
extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t);
|
||||
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
|
||||
extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t);
|
||||
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
|
||||
extern int vfs_symlink2(struct vfsmount *, struct inode *, struct dentry *, const char *);
|
||||
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
|
||||
extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *, struct inode **);
|
||||
extern int vfs_rmdir(struct inode *, struct dentry *);
|
||||
extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *);
|
||||
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
|
||||
extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *, struct inode **);
|
||||
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
|
||||
extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
|
||||
extern int vfs_whiteout(struct inode *, struct dentry *);
|
||||
|
||||
/*
|
||||
|
@ -1671,6 +1679,7 @@ struct inode_operations {
|
|||
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
|
||||
const char * (*follow_link) (struct dentry *, void **);
|
||||
int (*permission) (struct inode *, int);
|
||||
int (*permission2) (struct vfsmount *, struct inode *, int);
|
||||
struct posix_acl * (*get_acl)(struct inode *, int);
|
||||
|
||||
int (*readlink) (struct dentry *, char __user *,int);
|
||||
|
@ -1688,6 +1697,7 @@ struct inode_operations {
|
|||
int (*rename2) (struct inode *, struct dentry *,
|
||||
struct inode *, struct dentry *, unsigned int);
|
||||
int (*setattr) (struct dentry *, struct iattr *);
|
||||
int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *);
|
||||
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
|
||||
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
|
||||
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
|
||||
|
@ -1733,9 +1743,13 @@ struct super_operations {
|
|||
int (*unfreeze_fs) (struct super_block *);
|
||||
int (*statfs) (struct dentry *, struct kstatfs *);
|
||||
int (*remount_fs) (struct super_block *, int *, char *);
|
||||
int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *);
|
||||
void *(*clone_mnt_data) (void *);
|
||||
void (*copy_mnt_data) (void *, void *);
|
||||
void (*umount_begin) (struct super_block *);
|
||||
|
||||
int (*show_options)(struct seq_file *, struct dentry *);
|
||||
int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *);
|
||||
int (*show_devname)(struct seq_file *, struct dentry *);
|
||||
int (*show_path)(struct seq_file *, struct dentry *);
|
||||
int (*show_stats)(struct seq_file *, struct dentry *);
|
||||
|
@ -1967,6 +1981,9 @@ struct file_system_type {
|
|||
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
|
||||
struct dentry *(*mount) (struct file_system_type *, int,
|
||||
const char *, void *);
|
||||
struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int,
|
||||
const char *, void *);
|
||||
void *(*alloc_mnt_data) (void);
|
||||
void (*kill_sb) (struct super_block *);
|
||||
struct module *owner;
|
||||
struct file_system_type * next;
|
||||
|
@ -2246,6 +2263,8 @@ struct filename {
|
|||
extern long vfs_truncate(struct path *, loff_t);
|
||||
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
|
||||
struct file *filp);
|
||||
extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start,
|
||||
unsigned int time_attrs, struct file *filp);
|
||||
extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
|
||||
loff_t len);
|
||||
extern long do_sys_open(int dfd, const char __user *filename, int flags,
|
||||
|
@ -2470,8 +2489,11 @@ extern void emergency_remount(void);
|
|||
extern sector_t bmap(struct inode *, sector_t);
|
||||
#endif
|
||||
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
|
||||
extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **);
|
||||
extern int inode_permission(struct inode *, int);
|
||||
extern int inode_permission2(struct vfsmount *, struct inode *, int);
|
||||
extern int __inode_permission(struct inode *, int);
|
||||
extern int __inode_permission2(struct vfsmount *, struct inode *, int);
|
||||
extern int generic_permission(struct inode *, int);
|
||||
extern int __check_sticky(struct inode *dir, struct inode *inode);
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ struct vfsmount {
|
|||
struct dentry *mnt_root; /* root of the mounted tree */
|
||||
struct super_block *mnt_sb; /* pointer to superblock */
|
||||
int mnt_flags;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct file; /* forward dec */
|
||||
|
|
|
@ -79,6 +79,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
|
|||
const char *, unsigned int, struct path *);
|
||||
|
||||
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
|
||||
extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int);
|
||||
|
||||
extern int follow_down_one(struct path *);
|
||||
extern int follow_down(struct path *);
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
DEFINE_EVENT(android_fs_data_start_template, android_fs_dataread_start,
|
||||
TP_PROTO(struct inode *inode, loff_t offset, int bytes,
|
||||
pid_t pid, char *command),
|
||||
TP_ARGS(inode, offset, bytes, pid, command));
|
||||
pid_t pid, char *pathname, char *command),
|
||||
TP_ARGS(inode, offset, bytes, pid, pathname, command));
|
||||
|
||||
DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end,
|
||||
TP_PROTO(struct inode *inode, loff_t offset, int bytes),
|
||||
|
@ -18,14 +18,48 @@ DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end,
|
|||
|
||||
DEFINE_EVENT(android_fs_data_start_template, android_fs_datawrite_start,
|
||||
TP_PROTO(struct inode *inode, loff_t offset, int bytes,
|
||||
pid_t pid, char *command),
|
||||
TP_ARGS(inode, offset, bytes, pid, command));
|
||||
pid_t pid, char *pathname, char *command),
|
||||
TP_ARGS(inode, offset, bytes, pid, pathname, command));
|
||||
|
||||
DEFINE_EVENT(android_fs_data_end_template, android_fs_datawrite_end,
|
||||
TP_PROTO(struct inode *inode, loff_t offset, int bytes),
|
||||
TP_ARGS(inode, offset, bytes));
|
||||
TP_ARGS(inode, offset, bytes));
|
||||
|
||||
#endif /* _TRACE_ANDROID_FS_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
||||
|
||||
#ifndef ANDROID_FSTRACE_GET_PATHNAME
|
||||
#define ANDROID_FSTRACE_GET_PATHNAME
|
||||
|
||||
/* Sizes an on-stack array, so careful if sizing this up ! */
|
||||
#define MAX_TRACE_PATHBUF_LEN 256
|
||||
|
||||
static inline char *
|
||||
android_fstrace_get_pathname(char *buf, int buflen, struct inode *inode)
|
||||
{
|
||||
char *path;
|
||||
struct dentry *d;
|
||||
|
||||
/*
|
||||
* d_obtain_alias() will either iput() if it locates an existing
|
||||
* dentry or transfer the reference to the new dentry created.
|
||||
* So get an extra reference here.
|
||||
*/
|
||||
ihold(inode);
|
||||
d = d_obtain_alias(inode);
|
||||
if (likely(!IS_ERR(d))) {
|
||||
path = dentry_path_raw(d, buf, buflen);
|
||||
if (unlikely(IS_ERR(path))) {
|
||||
strcpy(buf, "ERROR");
|
||||
path = buf;
|
||||
}
|
||||
dput(d);
|
||||
} else {
|
||||
strcpy(buf, "ERROR");
|
||||
path = buf;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
|
||||
DECLARE_EVENT_CLASS(android_fs_data_start_template,
|
||||
TP_PROTO(struct inode *inode, loff_t offset, int bytes,
|
||||
pid_t pid, char *command),
|
||||
TP_ARGS(inode, offset, bytes, pid, command),
|
||||
pid_t pid, char *pathname, char *command),
|
||||
TP_ARGS(inode, offset, bytes, pid, pathname, command),
|
||||
TP_STRUCT__entry(
|
||||
__array(char, path, MAX_FILTER_STR_VAL);
|
||||
__field(char *, pathname);
|
||||
__string(pathbuf, pathname);
|
||||
__field(loff_t, offset);
|
||||
__field(int, bytes);
|
||||
__field(loff_t, i_size);
|
||||
|
@ -19,27 +18,7 @@ DECLARE_EVENT_CLASS(android_fs_data_start_template,
|
|||
),
|
||||
TP_fast_assign(
|
||||
{
|
||||
struct dentry *d;
|
||||
|
||||
/*
|
||||
* Grab a reference to the inode here because
|
||||
* d_obtain_alias() will either drop the inode
|
||||
* reference if it locates an existing dentry
|
||||
* or transfer the reference to the new dentry
|
||||
* created. In our case, the file is still open,
|
||||
* so the dentry is guaranteed to exist (connected),
|
||||
* so d_obtain_alias() drops the reference we
|
||||
* grabbed here.
|
||||
*/
|
||||
ihold(inode);
|
||||
d = d_obtain_alias(inode);
|
||||
if (!IS_ERR(d)) {
|
||||
__entry->pathname = dentry_path(d,
|
||||
__entry->path,
|
||||
MAX_FILTER_STR_VAL);
|
||||
dput(d);
|
||||
} else
|
||||
__entry->pathname = ERR_PTR(-EINVAL);
|
||||
__assign_str(pathbuf, pathname);
|
||||
__entry->offset = offset;
|
||||
__entry->bytes = bytes;
|
||||
__entry->i_size = i_size_read(inode);
|
||||
|
@ -50,9 +29,8 @@ DECLARE_EVENT_CLASS(android_fs_data_start_template,
|
|||
),
|
||||
TP_printk("entry_name %s, offset %llu, bytes %d, cmdline %s,"
|
||||
" pid %d, i_size %llu, ino %lu",
|
||||
(IS_ERR(__entry->pathname) ? "ERROR" : __entry->pathname),
|
||||
__entry->offset, __entry->bytes, __get_str(cmdline),
|
||||
__entry->pid, __entry->i_size,
|
||||
__get_str(pathbuf), __entry->offset, __entry->bytes,
|
||||
__get_str(cmdline), __entry->pid, __entry->i_size,
|
||||
(unsigned long) __entry->ino)
|
||||
);
|
||||
|
||||
|
|
|
@ -4,7 +4,11 @@
|
|||
enum {
|
||||
HW_BREAKPOINT_LEN_1 = 1,
|
||||
HW_BREAKPOINT_LEN_2 = 2,
|
||||
HW_BREAKPOINT_LEN_3 = 3,
|
||||
HW_BREAKPOINT_LEN_4 = 4,
|
||||
HW_BREAKPOINT_LEN_5 = 5,
|
||||
HW_BREAKPOINT_LEN_6 = 6,
|
||||
HW_BREAKPOINT_LEN_7 = 7,
|
||||
HW_BREAKPOINT_LEN_8 = 8,
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
|
||||
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
|
||||
|
||||
#define SDCARDFS_SUPER_MAGIC 0xb550ca10
|
||||
#define SDCARDFS_SUPER_MAGIC 0x5dca2df5
|
||||
|
||||
#define SMB_SUPER_MAGIC 0x517B
|
||||
#define CGROUP_SUPER_MAGIC 0x27e0eb
|
||||
|
|
10
ipc/mqueue.c
10
ipc/mqueue.c
|
@ -747,7 +747,7 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir,
|
|||
}
|
||||
|
||||
mode &= ~current_umask();
|
||||
ret = vfs_create(dir, path->dentry, mode, true);
|
||||
ret = vfs_create2(path->mnt, dir, path->dentry, mode, true);
|
||||
path->dentry->d_fsdata = NULL;
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
@ -763,7 +763,7 @@ static struct file *do_open(struct path *path, int oflag)
|
|||
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
|
||||
return ERR_PTR(-EINVAL);
|
||||
acc = oflag2acc[oflag & O_ACCMODE];
|
||||
if (inode_permission(d_inode(path->dentry), acc))
|
||||
if (inode_permission2(path->mnt, d_inode(path->dentry), acc))
|
||||
return ERR_PTR(-EACCES);
|
||||
return dentry_open(path, oflag, current_cred());
|
||||
}
|
||||
|
@ -796,7 +796,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
|
|||
ro = mnt_want_write(mnt); /* we'll drop it in any case */
|
||||
error = 0;
|
||||
mutex_lock(&d_inode(root)->i_mutex);
|
||||
path.dentry = lookup_one_len(name->name, root, strlen(name->name));
|
||||
path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name));
|
||||
if (IS_ERR(path.dentry)) {
|
||||
error = PTR_ERR(path.dentry);
|
||||
goto out_putfd;
|
||||
|
@ -867,7 +867,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
|
|||
if (err)
|
||||
goto out_name;
|
||||
mutex_lock_nested(&d_inode(mnt->mnt_root)->i_mutex, I_MUTEX_PARENT);
|
||||
dentry = lookup_one_len(name->name, mnt->mnt_root,
|
||||
dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root,
|
||||
strlen(name->name));
|
||||
if (IS_ERR(dentry)) {
|
||||
err = PTR_ERR(dentry);
|
||||
|
@ -879,7 +879,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
|
|||
err = -ENOENT;
|
||||
} else {
|
||||
ihold(inode);
|
||||
err = vfs_unlink(d_inode(dentry->d_parent), dentry, NULL);
|
||||
err = vfs_unlink2(mnt, d_inode(dentry->d_parent), dentry, NULL);
|
||||
}
|
||||
dput(dentry);
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode,
|
|||
dir = d_inode(parent);
|
||||
|
||||
mutex_lock(&dir->i_mutex);
|
||||
dentry = lookup_one_len(name, parent, strlen(name));
|
||||
dentry = lookup_one_len2(name, mount, parent, strlen(name));
|
||||
if (IS_ERR(dentry))
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -498,6 +498,7 @@ int security_path_chown(struct path *path, kuid_t uid, kgid_t gid)
|
|||
return 0;
|
||||
return call_int_hook(path_chown, 0, path, uid, gid);
|
||||
}
|
||||
EXPORT_SYMBOL(security_path_chown);
|
||||
|
||||
int security_path_chroot(struct path *path)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue