Merge remote-tracking branch 'common/android-4.4' into android-4.4.y

Change-Id: Icf907f5067fb6da5935ab0d3271df54b8d5df405
This commit is contained in:
Dmitry Shmidt 2017-02-15 18:02:55 -08:00
commit 232c28fe23
86 changed files with 4852 additions and 1339 deletions

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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,

View 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)

View 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);

View file

@ -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,

View file

@ -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 = &current->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;

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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)
{

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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)

View file

@ -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,

View file

@ -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"

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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 */

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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"

View file

@ -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();
}

View file

@ -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,

View file

@ -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));

View file

@ -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);
}
}

View file

@ -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)))) {

View file

@ -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)) {

View file

@ -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)
{

View file

@ -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);

View file

@ -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);
/*

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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:

View file

@ -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;

View file

@ -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);

View file

@ -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);
}
}
}

View file

@ -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);

View file

@ -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:

View file

@ -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);

View file

@ -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);

View file

@ -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(&current->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(&current->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,
};

View file

@ -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);

View file

@ -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,
};

View file

@ -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

View file

@ -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_ */

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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,
};

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 = {

View file

@ -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;
}

View file

@ -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

View file

@ -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 *);

View file

@ -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;
};

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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 */

View file

@ -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 *);

View file

@ -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

View file

@ -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)
);

View file

@ -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,
};

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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)
{