Merge branch 'tmp-bab1564' into msm-4.4

* tmp-bab1564:
  ANDROID: mmc: Add CONFIG_MMC_SIMULATE_MAX_SPEED
  android: base-cfg: Add CONFIG_INET_DIAG_DESTROY
  cpufreq: interactive: only apply interactive boost when enabled
  cpufreq: interactive: fix policy locking
  ANDROID: dm verity fec: add sysfs attribute fec/corrected
  ANDROID: android: base-cfg: enable CONFIG_DM_VERITY_FEC
  UPSTREAM: dm verity: add ignore_zero_blocks feature
  UPSTREAM: dm verity: add support for forward error correction
  UPSTREAM: dm verity: factor out verity_for_bv_block()
  UPSTREAM: dm verity: factor out structures and functions useful to separate object
  UPSTREAM: dm verity: move dm-verity.c to dm-verity-target.c
  UPSTREAM: dm verity: separate function for parsing opt args
  UPSTREAM: dm verity: clean up duplicate hashing code
  UPSTREAM: dm: don't save and restore bi_private
  mm: Export do_munmap
  sdcardfs: remove unneeded __init and __exit
  sdcardfs: Remove unused code
  fs: Export d_absolute_path
  sdcardfs: remove effectless config option
  inotify: Fix erroneous update of bit count
  fs: sdcardfs: Declare LOOKUP_CASE_INSENSITIVE unconditionally
  trace: cpufreq: fix typo in min/max cpufreq
  sdcardfs: Add support for d_canonical_path
  vfs: add d_canonical_path for stacked filesystem support
  sdcardfs: Bring up to date with Android M permissions:
  Changed type-casting in packagelist management
  Port of sdcardfs to 4.4
  Included sdcardfs source code for kernel 3.0
  ANDROID: usb: gadget: Add support for MTP OS desc
  CHROMIUM: usb: gadget: f_accessory: add .raw_request callback
  CHROMIUM: usb: gadget: audio_source: add .free_func callback
  CHROMIUM: usb: gadget: f_mtp: fix usb_ss_ep_comp_descriptor
  CHROMIUM: usb: gadget: f_mtp: Add SuperSpeed support
  FROMLIST: mmc: block: fix ABI regression of mmc_blk_ioctl
  FROMLIST: mm: ASLR: use get_random_long()
  FROMLIST: drivers: char: random: add get_random_long()
  FROMLIST: pstore-ram: fix NULL reference when used with pdata
  usb: u_ether: Add missing rx_work init
  ANDROID: dm-crypt: run in a WQ_HIGHPRI workqueue
  misc: uid_stat: Include linux/atomic.h instead of asm/atomic.h
  hid-sensor-hub.c: fix wrong do_div() usage
  power: Provide dummy log_suspend_abort_reason() if SUSPEND is disabled
  PM / suspend: Add dependency on RTC_LIB
  drivers: power: use 'current' instead of 'get_current()'
  video: adf: Set ADF_MEMBLOCK to boolean
  video: adf: Fix modular build
  net: ppp: Fix modular build for PPPOLAC and PPPOPNS
  net: pppolac/pppopns: Replace msg.msg_iov with iov_iter_kvec()
  ANDROID: mmc: sdio: Disable retuning in sdio_reset_comm()
  ANDROID: mmc: Move tracepoint creation and export symbols
  ANDROID: kernel/watchdog: fix unused variable warning
  ANDROID: usb: gadget: f_mtp: don't use le16 for u8 field
  ANDROID: lowmemorykiller: fix declaration order warnings
  ANDROID: net: fix 'const' warnings
  net: diag: support v4mapped sockets in inet_diag_find_one_icsk()
  net: tcp: deal with listen sockets properly in tcp_abort.
  tcp: diag: add support for request sockets to tcp_abort()
  net: diag: Support destroying TCP sockets.
  net: diag: Support SOCK_DESTROY for inet sockets.
  net: diag: Add the ability to destroy a socket.
  net: diag: split inet_diag_dump_one_icsk into two
  Revert "mmc: Extend wakelock if bus is dead"
  Revert "mmc: core: Hold a wake lock accross delayed work + mmc rescan"
  ANDROID: mmc: move to a SCHED_FIFO thread

Conflicts:
	drivers/cpufreq/cpufreq_interactive.c
	drivers/misc/uid_stat.c
	drivers/mmc/card/block.c
	drivers/mmc/card/queue.c
	drivers/mmc/card/queue.h
	drivers/mmc/core/core.c
	drivers/mmc/core/sdio.c
	drivers/staging/android/lowmemorykiller.c
	drivers/usb/gadget/function/f_mtp.c
	kernel/watchdog.c

Signed-off-by: Runmin Wang <runminw@codeaurora.org>
Change-Id: Ibb4db11c57395f67dee86211a110c462e6181552
This commit is contained in:
Runmin Wang 2016-09-12 18:23:50 -07:00
commit c568eb7aca
66 changed files with 5978 additions and 337 deletions

View file

@ -26,3 +26,9 @@ switching-sched.txt
- Switching I/O schedulers at runtime
writeback_cache_control.txt
- Control of volatile write back caches
mmc-max-speed.txt
- eMMC layer speed simulation, related to /sys/block/mmcblk*/
attributes:
max_read_speed
max_write_speed
cache_size

View file

@ -0,0 +1,38 @@
eMMC Block layer simulation speed controls in /sys/block/mmcblk*/
===============================================
Turned on with CONFIG_MMC_SIMULATE_MAX_SPEED which enables MMC device speed
limiting. Used to test and simulate the behavior of the system when
confronted with a slow MMC.
Enables max_read_speed, max_write_speed and cache_size attributes and module
default parameters to control the write or read maximum KB/second speed
behaviors.
NB: There is room for improving the algorithm for aspects tied directly to
eMMC specific behavior. For instance, wear leveling and stalls from an
exhausted erase pool. We would expect that if there was a need to provide
similar speed simulation controls to other types of block devices, aspects of
their behavior are modelled separately (e.g. head seek times, heat assist,
shingling and rotational latency).
/sys/block/mmcblk0/max_read_speed:
Number of KB/second reads allowed to the block device. Used to test and
simulate the behavior of the system when confronted with a slow reading MMC.
Set to 0 or "off" to place no speed limit.
/sys/block/mmcblk0/max_write_speed:
Number of KB/second writes allowed to the block device. Used to test and
simulate the behavior of the system when confronted with a slow writing MMC.
Set to 0 or "off" to place no speed limit.
/sys/block/mmcblk0/cache_size:
Number of MB of high speed memory or high speed SLC cache expected on the
eMMC device being simulated. Used to help simulate the write-back behavior
more accurately. The assumption is the cache has no delay, but draws down
in the background to the MLC/TLC primary store at the max_write_speed rate.
Any write speed delays will show up when the cache is full, or when an I/O
request to flush is issued.

View file

@ -18,11 +18,11 @@ Construction Parameters
0 is the original format used in the Chromium OS.
The salt is appended when hashing, digests are stored continuously and
the rest of the block is padded with zeros.
the rest of the block is padded with zeroes.
1 is the current format that should be used for new devices.
The salt is prepended when hashing and each digest is
padded with zeros to the power of two.
padded with zeroes to the power of two.
<dev>
This is the device containing data, the integrity of which needs to be
@ -79,6 +79,37 @@ restart_on_corruption
not compatible with ignore_corruption and requires user space support to
avoid restart loops.
ignore_zero_blocks
Do not verify blocks that are expected to contain zeroes and always return
zeroes instead. This may be useful if the partition contains unused blocks
that are not guaranteed to contain zeroes.
use_fec_from_device <fec_dev>
Use forward error correction (FEC) to recover from corruption if hash
verification fails. Use encoding data from the specified device. This
may be the same device where data and hash blocks reside, in which case
fec_start must be outside data and hash areas.
If the encoding data covers additional metadata, it must be accessible
on the hash device after the hash blocks.
Note: block sizes for data and hash devices must match. Also, if the
verity <dev> is encrypted the <fec_dev> should be too.
fec_roots <num>
Number of generator roots. This equals to the number of parity bytes in
the encoding data. For example, in RS(M, N) encoding, the number of roots
is M-N.
fec_blocks <num>
The number of encoding data blocks on the FEC device. The block size for
the FEC device is <data_block_size>.
fec_start <offset>
This is the offset, in <data_block_size> blocks, from the start of the
FEC device to the beginning of the encoding data.
Theory of operation
===================
@ -98,6 +129,11 @@ per-block basis. This allows for a lightweight hash computation on first read
into the page cache. Block hashes are stored linearly, aligned to the nearest
block size.
If forward error correction (FEC) support is enabled any recovery of
corrupted data will be verified using the cryptographic hash of the
corresponding data. This is why combining error correction with
integrity checking is essential.
Hash Tree
---------

View file

@ -21,6 +21,7 @@ CONFIG_CGROUP_SCHED=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_DM_CRYPT=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
CONFIG_EMBEDDED=y
CONFIG_FB=y
CONFIG_HIGH_RES_TIMERS=y
@ -28,6 +29,7 @@ CONFIG_INET6_AH=y
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_INET=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_INET_ESP=y
CONFIG_INET_XFRM_MODE_TUNNEL=y
CONFIG_IP6_NF_FILTER=y

View file

@ -173,7 +173,7 @@ unsigned long arch_mmap_rnd(void)
{
unsigned long rnd;
rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);
return rnd << PAGE_SHIFT;
}

View file

@ -53,10 +53,10 @@ unsigned long arch_mmap_rnd(void)
#ifdef CONFIG_COMPAT
if (test_thread_flag(TIF_32BIT))
rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_compat_bits) - 1);
rnd = get_random_long() & ((1UL << mmap_rnd_compat_bits) - 1);
else
#endif
rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);
return rnd << PAGE_SHIFT;
}

View file

@ -146,7 +146,7 @@ unsigned long arch_mmap_rnd(void)
{
unsigned long rnd;
rnd = (unsigned long)get_random_int();
rnd = get_random_long();
rnd <<= PAGE_SHIFT;
if (TASK_IS_32BIT_ADDR)
rnd &= 0xfffffful;
@ -174,7 +174,7 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
static inline unsigned long brk_rnd(void)
{
unsigned long rnd = get_random_int();
unsigned long rnd = get_random_long();
rnd = rnd << PAGE_SHIFT;
/* 8MB for 32bit, 256MB for 64bit */

View file

@ -1659,9 +1659,9 @@ static inline unsigned long brk_rnd(void)
/* 8MB for 32bit, 1GB for 64bit */
if (is_32bit_task())
rnd = (long)(get_random_int() % (1<<(23-PAGE_SHIFT)));
rnd = (get_random_long() % (1UL<<(23-PAGE_SHIFT)));
else
rnd = (long)(get_random_int() % (1<<(30-PAGE_SHIFT)));
rnd = (get_random_long() % (1UL<<(30-PAGE_SHIFT)));
return rnd << PAGE_SHIFT;
}

View file

@ -59,9 +59,9 @@ unsigned long arch_mmap_rnd(void)
/* 8MB for 32bit, 1GB for 64bit */
if (is_32bit_task())
rnd = (unsigned long)get_random_int() % (1<<(23-PAGE_SHIFT));
rnd = get_random_long() % (1<<(23-PAGE_SHIFT));
else
rnd = (unsigned long)get_random_int() % (1<<(30-PAGE_SHIFT));
rnd = get_random_long() % (1UL<<(30-PAGE_SHIFT));
return rnd << PAGE_SHIFT;
}

View file

@ -264,7 +264,7 @@ static unsigned long mmap_rnd(void)
unsigned long rnd = 0UL;
if (current->flags & PF_RANDOMIZE) {
unsigned long val = get_random_int();
unsigned long val = get_random_long();
if (test_thread_flag(TIF_32BIT))
rnd = (val % (1UL << (23UL-PAGE_SHIFT)));
else

View file

@ -71,12 +71,12 @@ unsigned long arch_mmap_rnd(void)
if (mmap_is_ia32())
#ifdef CONFIG_COMPAT
rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_compat_bits) - 1);
rnd = get_random_long() & ((1UL << mmap_rnd_compat_bits) - 1);
#else
rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);
#endif
else
rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);
return rnd << PAGE_SHIFT;
}

View file

@ -35,6 +35,8 @@
#include <linux/timer.h>
#include <linux/wakeup_reason.h>
#include <asm/current.h>
#include "../base.h"
#include "power.h"
@ -1412,7 +1414,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
goto Complete;
data.dev = dev;
data.tsk = get_current();
data.tsk = current;
init_timer_on_stack(&timer);
timer.expires = jiffies + HZ * 12;
timer.function = dpm_drv_timeout;

View file

@ -1818,6 +1818,28 @@ unsigned int get_random_int(void)
}
EXPORT_SYMBOL(get_random_int);
/*
* Same as get_random_int(), but returns unsigned long.
*/
unsigned long get_random_long(void)
{
__u32 *hash;
unsigned long ret;
if (arch_get_random_long(&ret))
return ret;
hash = get_cpu_var(get_random_int_hash);
hash[0] += current->pid + jiffies + random_get_entropy();
md5_transform(hash, random_int_secret);
ret = *(unsigned long *)hash;
put_cpu_var(get_random_int_hash);
return ret;
}
EXPORT_SYMBOL(get_random_long);
/*
* randomize_range() returns a start address such that
*

View file

@ -218,7 +218,8 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
goto done_proc;
}
remaining_bytes = do_div(buffer_size, sizeof(__s32));
remaining_bytes = buffer_size % sizeof(__s32);
buffer_size = buffer_size / sizeof(__s32);
if (buffer_size) {
for (i = 0; i < buffer_size; ++i) {
hid_set_field(report->field[field_index], i,

View file

@ -475,6 +475,18 @@ config DM_VERITY
If unsure, say N.
config DM_VERITY_FEC
bool "Verity forward error correction support"
depends on DM_VERITY
select REED_SOLOMON
select REED_SOLOMON_DEC8
---help---
Add forward error correction support to dm-verity. This option
makes it possible to use pre-generated error correction data to
recover from corrupted blocks.
If unsure, say N.
config DM_SWITCH
tristate "Switch target support (EXPERIMENTAL)"
depends on BLK_DEV_DM

View file

@ -16,6 +16,7 @@ dm-cache-mq-y += dm-cache-policy-mq.o
dm-cache-smq-y += dm-cache-policy-smq.o
dm-cache-cleaner-y += dm-cache-policy-cleaner.o
dm-era-y += dm-era-target.o
dm-verity-y += dm-verity-target.o
md-mod-y += md.o bitmap.o
raid456-y += raid5.o raid5-cache.o
@ -64,3 +65,7 @@ obj-$(CONFIG_DM_REQ_CRYPT) += dm-req-crypt.o
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
endif
ifeq ($(CONFIG_DM_VERITY_FEC),y)
dm-verity-objs += dm-verity-fec.o
endif

View file

@ -118,14 +118,12 @@ static void iot_io_end(struct io_tracker *iot, sector_t len)
*/
struct dm_hook_info {
bio_end_io_t *bi_end_io;
void *bi_private;
};
static void dm_hook_bio(struct dm_hook_info *h, struct bio *bio,
bio_end_io_t *bi_end_io, void *bi_private)
{
h->bi_end_io = bio->bi_end_io;
h->bi_private = bio->bi_private;
bio->bi_end_io = bi_end_io;
bio->bi_private = bi_private;
@ -134,7 +132,6 @@ static void dm_hook_bio(struct dm_hook_info *h, struct bio *bio,
static void dm_unhook_bio(struct dm_hook_info *h, struct bio *bio)
{
bio->bi_end_io = h->bi_end_io;
bio->bi_private = h->bi_private;
}
/*----------------------------------------------------------------*/

View file

@ -1864,16 +1864,24 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
ret = -ENOMEM;
cc->io_queue = alloc_workqueue("kcryptd_io", WQ_MEM_RECLAIM, 1);
cc->io_queue = alloc_workqueue("kcryptd_io",
WQ_HIGHPRI |
WQ_MEM_RECLAIM,
1);
if (!cc->io_queue) {
ti->error = "Couldn't create kcryptd io queue";
goto bad;
}
if (test_bit(DM_CRYPT_SAME_CPU, &cc->flags))
cc->crypt_queue = alloc_workqueue("kcryptd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM, 1);
cc->crypt_queue = alloc_workqueue("kcryptd",
WQ_HIGHPRI |
WQ_MEM_RECLAIM, 1);
else
cc->crypt_queue = alloc_workqueue("kcryptd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND,
cc->crypt_queue = alloc_workqueue("kcryptd",
WQ_HIGHPRI |
WQ_MEM_RECLAIM |
WQ_UNBOUND,
num_online_cpus());
if (!cc->crypt_queue) {
ti->error = "Couldn't create kcryptd queue";

View file

@ -207,7 +207,6 @@ struct dm_snap_pending_exception {
*/
struct bio *full_bio;
bio_end_io_t *full_bio_end_io;
void *full_bio_private;
};
/*
@ -1495,10 +1494,8 @@ out:
snapshot_bios = bio_list_get(&pe->snapshot_bios);
origin_bios = bio_list_get(&pe->origin_bios);
full_bio = pe->full_bio;
if (full_bio) {
if (full_bio)
full_bio->bi_end_io = pe->full_bio_end_io;
full_bio->bi_private = pe->full_bio_private;
}
increment_pending_exceptions_done_count();
up_write(&s->lock);
@ -1604,7 +1601,6 @@ static void start_full_bio(struct dm_snap_pending_exception *pe,
pe->full_bio = bio;
pe->full_bio_end_io = bio->bi_end_io;
pe->full_bio_private = bio->bi_private;
callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client,
copy_callback, pe);

861
drivers/md/dm-verity-fec.c Normal file
View file

@ -0,0 +1,861 @@
/*
* Copyright (C) 2015 Google, Inc.
*
* Author: Sami Tolvanen <samitolvanen@google.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include "dm-verity-fec.h"
#include <linux/math64.h>
#include <linux/sysfs.h>
#define DM_MSG_PREFIX "verity-fec"
/*
* If error correction has been configured, returns true.
*/
bool verity_fec_is_enabled(struct dm_verity *v)
{
return v->fec && v->fec->dev;
}
/*
* Return a pointer to dm_verity_fec_io after dm_verity_io and its variable
* length fields.
*/
static inline struct dm_verity_fec_io *fec_io(struct dm_verity_io *io)
{
return (struct dm_verity_fec_io *) verity_io_digest_end(io->v, io);
}
/*
* Return an interleaved offset for a byte in RS block.
*/
static inline u64 fec_interleave(struct dm_verity *v, u64 offset)
{
u32 mod;
mod = do_div(offset, v->fec->rsn);
return offset + mod * (v->fec->rounds << v->data_dev_block_bits);
}
/*
* Decode an RS block using Reed-Solomon.
*/
static int fec_decode_rs8(struct dm_verity *v, struct dm_verity_fec_io *fio,
u8 *data, u8 *fec, int neras)
{
int i;
uint16_t par[DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN];
for (i = 0; i < v->fec->roots; i++)
par[i] = fec[i];
return decode_rs8(fio->rs, data, par, v->fec->rsn, NULL, neras,
fio->erasures, 0, NULL);
}
/*
* Read error-correcting codes for the requested RS block. Returns a pointer
* to the data block. Caller is responsible for releasing buf.
*/
static u8 *fec_read_parity(struct dm_verity *v, u64 rsb, int index,
unsigned *offset, struct dm_buffer **buf)
{
u64 position, block;
u8 *res;
position = (index + rsb) * v->fec->roots;
block = position >> v->data_dev_block_bits;
*offset = (unsigned)(position - (block << v->data_dev_block_bits));
res = dm_bufio_read(v->fec->bufio, v->fec->start + block, buf);
if (unlikely(IS_ERR(res))) {
DMERR("%s: FEC %llu: parity read failed (block %llu): %ld",
v->data_dev->name, (unsigned long long)rsb,
(unsigned long long)(v->fec->start + block),
PTR_ERR(res));
*buf = NULL;
}
return res;
}
/* Loop over each preallocated buffer slot. */
#define fec_for_each_prealloc_buffer(__i) \
for (__i = 0; __i < DM_VERITY_FEC_BUF_PREALLOC; __i++)
/* Loop over each extra buffer slot. */
#define fec_for_each_extra_buffer(io, __i) \
for (__i = DM_VERITY_FEC_BUF_PREALLOC; __i < DM_VERITY_FEC_BUF_MAX; __i++)
/* Loop over each allocated buffer. */
#define fec_for_each_buffer(io, __i) \
for (__i = 0; __i < (io)->nbufs; __i++)
/* Loop over each RS block in each allocated buffer. */
#define fec_for_each_buffer_rs_block(io, __i, __j) \
fec_for_each_buffer(io, __i) \
for (__j = 0; __j < 1 << DM_VERITY_FEC_BUF_RS_BITS; __j++)
/*
* Return a pointer to the current RS block when called inside
* fec_for_each_buffer_rs_block.
*/
static inline u8 *fec_buffer_rs_block(struct dm_verity *v,
struct dm_verity_fec_io *fio,
unsigned i, unsigned j)
{
return &fio->bufs[i][j * v->fec->rsn];
}
/*
* Return an index to the current RS block when called inside
* fec_for_each_buffer_rs_block.
*/
static inline unsigned fec_buffer_rs_index(unsigned i, unsigned j)
{
return (i << DM_VERITY_FEC_BUF_RS_BITS) + j;
}
/*
* Decode all RS blocks from buffers and copy corrected bytes into fio->output
* starting from block_offset.
*/
static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio,
u64 rsb, int byte_index, unsigned block_offset,
int neras)
{
int r, corrected = 0, res;
struct dm_buffer *buf;
unsigned n, i, offset;
u8 *par, *block;
par = fec_read_parity(v, rsb, block_offset, &offset, &buf);
if (IS_ERR(par))
return PTR_ERR(par);
/*
* Decode the RS blocks we have in bufs. Each RS block results in
* one corrected target byte and consumes fec->roots parity bytes.
*/
fec_for_each_buffer_rs_block(fio, n, i) {
block = fec_buffer_rs_block(v, fio, n, i);
res = fec_decode_rs8(v, fio, block, &par[offset], neras);
if (res < 0) {
dm_bufio_release(buf);
r = res;
goto error;
}
corrected += res;
fio->output[block_offset] = block[byte_index];
block_offset++;
if (block_offset >= 1 << v->data_dev_block_bits)
goto done;
/* read the next block when we run out of parity bytes */
offset += v->fec->roots;
if (offset >= 1 << v->data_dev_block_bits) {
dm_bufio_release(buf);
par = fec_read_parity(v, rsb, block_offset, &offset, &buf);
if (unlikely(IS_ERR(par)))
return PTR_ERR(par);
}
}
done:
r = corrected;
error:
if (r < 0 && neras)
DMERR_LIMIT("%s: FEC %llu: failed to correct: %d",
v->data_dev->name, (unsigned long long)rsb, r);
else if (r > 0) {
DMWARN_LIMIT("%s: FEC %llu: corrected %d errors",
v->data_dev->name, (unsigned long long)rsb, r);
atomic_add_unless(&v->fec->corrected, 1, INT_MAX);
}
return r;
}
/*
* Locate data block erasures using verity hashes.
*/
static int fec_is_erasure(struct dm_verity *v, struct dm_verity_io *io,
u8 *want_digest, u8 *data)
{
if (unlikely(verity_hash(v, verity_io_hash_desc(v, io),
data, 1 << v->data_dev_block_bits,
verity_io_real_digest(v, io))))
return 0;
return memcmp(verity_io_real_digest(v, io), want_digest,
v->digest_size) != 0;
}
/*
* Read data blocks that are part of the RS block and deinterleave as much as
* fits into buffers. Check for erasure locations if @neras is non-NULL.
*/
static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io,
u64 rsb, u64 target, unsigned block_offset,
int *neras)
{
bool is_zero;
int i, j, target_index = -1;
struct dm_buffer *buf;
struct dm_bufio_client *bufio;
struct dm_verity_fec_io *fio = fec_io(io);
u64 block, ileaved;
u8 *bbuf, *rs_block;
u8 want_digest[v->digest_size];
unsigned n, k;
if (neras)
*neras = 0;
/*
* read each of the rsn data blocks that are part of the RS block, and
* interleave contents to available bufs
*/
for (i = 0; i < v->fec->rsn; i++) {
ileaved = fec_interleave(v, rsb * v->fec->rsn + i);
/*
* target is the data block we want to correct, target_index is
* the index of this block within the rsn RS blocks
*/
if (ileaved == target)
target_index = i;
block = ileaved >> v->data_dev_block_bits;
bufio = v->fec->data_bufio;
if (block >= v->data_blocks) {
block -= v->data_blocks;
/*
* blocks outside the area were assumed to contain
* zeros when encoding data was generated
*/
if (unlikely(block >= v->fec->hash_blocks))
continue;
block += v->hash_start;
bufio = v->bufio;
}
bbuf = dm_bufio_read(bufio, block, &buf);
if (unlikely(IS_ERR(bbuf))) {
DMWARN_LIMIT("%s: FEC %llu: read failed (%llu): %ld",
v->data_dev->name,
(unsigned long long)rsb,
(unsigned long long)block, PTR_ERR(bbuf));
/* assume the block is corrupted */
if (neras && *neras <= v->fec->roots)
fio->erasures[(*neras)++] = i;
continue;
}
/* locate erasures if the block is on the data device */
if (bufio == v->fec->data_bufio &&
verity_hash_for_block(v, io, block, want_digest,
&is_zero) == 0) {
/* skip known zero blocks entirely */
if (is_zero)
continue;
/*
* skip if we have already found the theoretical
* maximum number (i.e. fec->roots) of erasures
*/
if (neras && *neras <= v->fec->roots &&
fec_is_erasure(v, io, want_digest, bbuf))
fio->erasures[(*neras)++] = i;
}
/*
* deinterleave and copy the bytes that fit into bufs,
* starting from block_offset
*/
fec_for_each_buffer_rs_block(fio, n, j) {
k = fec_buffer_rs_index(n, j) + block_offset;
if (k >= 1 << v->data_dev_block_bits)
goto done;
rs_block = fec_buffer_rs_block(v, fio, n, j);
rs_block[i] = bbuf[k];
}
done:
dm_bufio_release(buf);
}
return target_index;
}
/*
* Allocate RS control structure and FEC buffers from preallocated mempools,
* and attempt to allocate as many extra buffers as available.
*/
static int fec_alloc_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio)
{
unsigned n;
if (!fio->rs) {
fio->rs = mempool_alloc(v->fec->rs_pool, 0);
if (unlikely(!fio->rs)) {
DMERR("failed to allocate RS");
return -ENOMEM;
}
}
fec_for_each_prealloc_buffer(n) {
if (fio->bufs[n])
continue;
fio->bufs[n] = mempool_alloc(v->fec->prealloc_pool, GFP_NOIO);
if (unlikely(!fio->bufs[n])) {
DMERR("failed to allocate FEC buffer");
return -ENOMEM;
}
}
/* try to allocate the maximum number of buffers */
fec_for_each_extra_buffer(fio, n) {
if (fio->bufs[n])
continue;
fio->bufs[n] = mempool_alloc(v->fec->extra_pool, GFP_NOIO);
/* we can manage with even one buffer if necessary */
if (unlikely(!fio->bufs[n]))
break;
}
fio->nbufs = n;
if (!fio->output) {
fio->output = mempool_alloc(v->fec->output_pool, GFP_NOIO);
if (!fio->output) {
DMERR("failed to allocate FEC page");
return -ENOMEM;
}
}
return 0;
}
/*
* Initialize buffers and clear erasures. fec_read_bufs() assumes buffers are
* zeroed before deinterleaving.
*/
static void fec_init_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio)
{
unsigned n;
fec_for_each_buffer(fio, n)
memset(fio->bufs[n], 0, v->fec->rsn << DM_VERITY_FEC_BUF_RS_BITS);
memset(fio->erasures, 0, sizeof(fio->erasures));
}
/*
* Decode all RS blocks in a single data block and return the target block
* (indicated by @offset) in fio->output. If @use_erasures is non-zero, uses
* hashes to locate erasures.
*/
static int fec_decode_rsb(struct dm_verity *v, struct dm_verity_io *io,
struct dm_verity_fec_io *fio, u64 rsb, u64 offset,
bool use_erasures)
{
int r, neras = 0;
unsigned pos;
r = fec_alloc_bufs(v, fio);
if (unlikely(r < 0))
return r;
for (pos = 0; pos < 1 << v->data_dev_block_bits; ) {
fec_init_bufs(v, fio);
r = fec_read_bufs(v, io, rsb, offset, pos,
use_erasures ? &neras : NULL);
if (unlikely(r < 0))
return r;
r = fec_decode_bufs(v, fio, rsb, r, pos, neras);
if (r < 0)
return r;
pos += fio->nbufs << DM_VERITY_FEC_BUF_RS_BITS;
}
/* Always re-validate the corrected block against the expected hash */
r = verity_hash(v, verity_io_hash_desc(v, io), fio->output,
1 << v->data_dev_block_bits,
verity_io_real_digest(v, io));
if (unlikely(r < 0))
return r;
if (memcmp(verity_io_real_digest(v, io), verity_io_want_digest(v, io),
v->digest_size)) {
DMERR_LIMIT("%s: FEC %llu: failed to correct (%d erasures)",
v->data_dev->name, (unsigned long long)rsb, neras);
return -EILSEQ;
}
return 0;
}
static int fec_bv_copy(struct dm_verity *v, struct dm_verity_io *io, u8 *data,
size_t len)
{
struct dm_verity_fec_io *fio = fec_io(io);
memcpy(data, &fio->output[fio->output_pos], len);
fio->output_pos += len;
return 0;
}
/*
* Correct errors in a block. Copies corrected block to dest if non-NULL,
* otherwise to a bio_vec starting from iter.
*/
int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
enum verity_block_type type, sector_t block, u8 *dest,
struct bvec_iter *iter)
{
int r;
struct dm_verity_fec_io *fio = fec_io(io);
u64 offset, res, rsb;
if (!verity_fec_is_enabled(v))
return -EOPNOTSUPP;
if (type == DM_VERITY_BLOCK_TYPE_METADATA)
block += v->data_blocks;
/*
* For RS(M, N), the continuous FEC data is divided into blocks of N
* bytes. Since block size may not be divisible by N, the last block
* is zero padded when decoding.
*
* Each byte of the block is covered by a different RS(M, N) code,
* and each code is interleaved over N blocks to make it less likely
* that bursty corruption will leave us in unrecoverable state.
*/
offset = block << v->data_dev_block_bits;
res = offset;
div64_u64(res, v->fec->rounds << v->data_dev_block_bits);
/*
* The base RS block we can feed to the interleaver to find out all
* blocks required for decoding.
*/
rsb = offset - res * (v->fec->rounds << v->data_dev_block_bits);
/*
* Locating erasures is slow, so attempt to recover the block without
* them first. Do a second attempt with erasures if the corruption is
* bad enough.
*/
r = fec_decode_rsb(v, io, fio, rsb, offset, false);
if (r < 0) {
r = fec_decode_rsb(v, io, fio, rsb, offset, true);
if (r < 0)
return r;
}
if (dest)
memcpy(dest, fio->output, 1 << v->data_dev_block_bits);
else if (iter) {
fio->output_pos = 0;
r = verity_for_bv_block(v, io, iter, fec_bv_copy);
}
return r;
}
/*
* Clean up per-bio data.
*/
void verity_fec_finish_io(struct dm_verity_io *io)
{
unsigned n;
struct dm_verity_fec *f = io->v->fec;
struct dm_verity_fec_io *fio = fec_io(io);
if (!verity_fec_is_enabled(io->v))
return;
mempool_free(fio->rs, f->rs_pool);
fec_for_each_prealloc_buffer(n)
mempool_free(fio->bufs[n], f->prealloc_pool);
fec_for_each_extra_buffer(fio, n)
mempool_free(fio->bufs[n], f->extra_pool);
mempool_free(fio->output, f->output_pool);
}
/*
* Initialize per-bio data.
*/
void verity_fec_init_io(struct dm_verity_io *io)
{
struct dm_verity_fec_io *fio = fec_io(io);
if (!verity_fec_is_enabled(io->v))
return;
fio->rs = NULL;
memset(fio->bufs, 0, sizeof(fio->bufs));
fio->nbufs = 0;
fio->output = NULL;
}
/*
* Append feature arguments and values to the status table.
*/
unsigned verity_fec_status_table(struct dm_verity *v, unsigned sz,
char *result, unsigned maxlen)
{
if (!verity_fec_is_enabled(v))
return sz;
DMEMIT(" " DM_VERITY_OPT_FEC_DEV " %s "
DM_VERITY_OPT_FEC_BLOCKS " %llu "
DM_VERITY_OPT_FEC_START " %llu "
DM_VERITY_OPT_FEC_ROOTS " %d",
v->fec->dev->name,
(unsigned long long)v->fec->blocks,
(unsigned long long)v->fec->start,
v->fec->roots);
return sz;
}
void verity_fec_dtr(struct dm_verity *v)
{
struct dm_verity_fec *f = v->fec;
struct kobject *kobj = &f->kobj_holder.kobj;
if (!verity_fec_is_enabled(v))
goto out;
mempool_destroy(f->rs_pool);
mempool_destroy(f->prealloc_pool);
mempool_destroy(f->extra_pool);
kmem_cache_destroy(f->cache);
if (f->data_bufio)
dm_bufio_client_destroy(f->data_bufio);
if (f->bufio)
dm_bufio_client_destroy(f->bufio);
if (f->dev)
dm_put_device(v->ti, f->dev);
if (kobj->state_initialized) {
kobject_put(kobj);
wait_for_completion(dm_get_completion_from_kobject(kobj));
}
out:
kfree(f);
v->fec = NULL;
}
static void *fec_rs_alloc(gfp_t gfp_mask, void *pool_data)
{
struct dm_verity *v = (struct dm_verity *)pool_data;
return init_rs(8, 0x11d, 0, 1, v->fec->roots);
}
static void fec_rs_free(void *element, void *pool_data)
{
struct rs_control *rs = (struct rs_control *)element;
if (rs)
free_rs(rs);
}
bool verity_is_fec_opt_arg(const char *arg_name)
{
return (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_DEV) ||
!strcasecmp(arg_name, DM_VERITY_OPT_FEC_BLOCKS) ||
!strcasecmp(arg_name, DM_VERITY_OPT_FEC_START) ||
!strcasecmp(arg_name, DM_VERITY_OPT_FEC_ROOTS));
}
int verity_fec_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
unsigned *argc, const char *arg_name)
{
int r;
struct dm_target *ti = v->ti;
const char *arg_value;
unsigned long long num_ll;
unsigned char num_c;
char dummy;
if (!*argc) {
ti->error = "FEC feature arguments require a value";
return -EINVAL;
}
arg_value = dm_shift_arg(as);
(*argc)--;
if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_DEV)) {
r = dm_get_device(ti, arg_value, FMODE_READ, &v->fec->dev);
if (r) {
ti->error = "FEC device lookup failed";
return r;
}
} else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_BLOCKS)) {
if (sscanf(arg_value, "%llu%c", &num_ll, &dummy) != 1 ||
((sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
>> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll)) {
ti->error = "Invalid " DM_VERITY_OPT_FEC_BLOCKS;
return -EINVAL;
}
v->fec->blocks = num_ll;
} else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_START)) {
if (sscanf(arg_value, "%llu%c", &num_ll, &dummy) != 1 ||
((sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT)) >>
(v->data_dev_block_bits - SECTOR_SHIFT) != num_ll)) {
ti->error = "Invalid " DM_VERITY_OPT_FEC_START;
return -EINVAL;
}
v->fec->start = num_ll;
} else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_ROOTS)) {
if (sscanf(arg_value, "%hhu%c", &num_c, &dummy) != 1 || !num_c ||
num_c < (DM_VERITY_FEC_RSM - DM_VERITY_FEC_MAX_RSN) ||
num_c > (DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN)) {
ti->error = "Invalid " DM_VERITY_OPT_FEC_ROOTS;
return -EINVAL;
}
v->fec->roots = num_c;
} else {
ti->error = "Unrecognized verity FEC feature request";
return -EINVAL;
}
return 0;
}
static ssize_t corrected_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
struct dm_verity_fec *f = container_of(kobj, struct dm_verity_fec,
kobj_holder.kobj);
return sprintf(buf, "%d\n", atomic_read(&f->corrected));
}
static struct kobj_attribute attr_corrected = __ATTR_RO(corrected);
static struct attribute *fec_attrs[] = {
&attr_corrected.attr,
NULL
};
static struct kobj_type fec_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.default_attrs = fec_attrs
};
/*
* Allocate dm_verity_fec for v->fec. Must be called before verity_fec_ctr.
*/
int verity_fec_ctr_alloc(struct dm_verity *v)
{
struct dm_verity_fec *f;
f = kzalloc(sizeof(struct dm_verity_fec), GFP_KERNEL);
if (!f) {
v->ti->error = "Cannot allocate FEC structure";
return -ENOMEM;
}
v->fec = f;
return 0;
}
/*
* Validate arguments and preallocate memory. Must be called after arguments
* have been parsed using verity_fec_parse_opt_args.
*/
int verity_fec_ctr(struct dm_verity *v)
{
int r;
struct dm_verity_fec *f = v->fec;
struct dm_target *ti = v->ti;
struct mapped_device *md = dm_table_get_md(ti->table);
u64 hash_blocks;
if (!verity_fec_is_enabled(v)) {
verity_fec_dtr(v);
return 0;
}
/* Create a kobject and sysfs attributes */
init_completion(&f->kobj_holder.completion);
r = kobject_init_and_add(&f->kobj_holder.kobj, &fec_ktype,
&disk_to_dev(dm_disk(md))->kobj, "%s", "fec");
if (r) {
ti->error = "Cannot create kobject";
return r;
}
/*
* FEC is computed over data blocks, possible metadata, and
* hash blocks. In other words, FEC covers total of fec_blocks
* blocks consisting of the following:
*
* data blocks | hash blocks | metadata (optional)
*
* We allow metadata after hash blocks to support a use case
* where all data is stored on the same device and FEC covers
* the entire area.
*
* If metadata is included, we require it to be available on the
* hash device after the hash blocks.
*/
hash_blocks = v->hash_blocks - v->hash_start;
/*
* Require matching block sizes for data and hash devices for
* simplicity.
*/
if (v->data_dev_block_bits != v->hash_dev_block_bits) {
ti->error = "Block sizes must match to use FEC";
return -EINVAL;
}
if (!f->roots) {
ti->error = "Missing " DM_VERITY_OPT_FEC_ROOTS;
return -EINVAL;
}
f->rsn = DM_VERITY_FEC_RSM - f->roots;
if (!f->blocks) {
ti->error = "Missing " DM_VERITY_OPT_FEC_BLOCKS;
return -EINVAL;
}
f->rounds = f->blocks;
if (sector_div(f->rounds, f->rsn))
f->rounds++;
/*
* Due to optional metadata, f->blocks can be larger than
* data_blocks and hash_blocks combined.
*/
if (f->blocks < v->data_blocks + hash_blocks || !f->rounds) {
ti->error = "Invalid " DM_VERITY_OPT_FEC_BLOCKS;
return -EINVAL;
}
/*
* Metadata is accessed through the hash device, so we require
* it to be large enough.
*/
f->hash_blocks = f->blocks - v->data_blocks;
if (dm_bufio_get_device_size(v->bufio) < f->hash_blocks) {
ti->error = "Hash device is too small for "
DM_VERITY_OPT_FEC_BLOCKS;
return -E2BIG;
}
f->bufio = dm_bufio_client_create(f->dev->bdev,
1 << v->data_dev_block_bits,
1, 0, NULL, NULL);
if (IS_ERR(f->bufio)) {
ti->error = "Cannot initialize FEC bufio client";
return PTR_ERR(f->bufio);
}
if (dm_bufio_get_device_size(f->bufio) <
((f->start + f->rounds * f->roots) >> v->data_dev_block_bits)) {
ti->error = "FEC device is too small";
return -E2BIG;
}
f->data_bufio = dm_bufio_client_create(v->data_dev->bdev,
1 << v->data_dev_block_bits,
1, 0, NULL, NULL);
if (IS_ERR(f->data_bufio)) {
ti->error = "Cannot initialize FEC data bufio client";
return PTR_ERR(f->data_bufio);
}
if (dm_bufio_get_device_size(f->data_bufio) < v->data_blocks) {
ti->error = "Data device is too small";
return -E2BIG;
}
/* Preallocate an rs_control structure for each worker thread */
f->rs_pool = mempool_create(num_online_cpus(), fec_rs_alloc,
fec_rs_free, (void *) v);
if (!f->rs_pool) {
ti->error = "Cannot allocate RS pool";
return -ENOMEM;
}
f->cache = kmem_cache_create("dm_verity_fec_buffers",
f->rsn << DM_VERITY_FEC_BUF_RS_BITS,
0, 0, NULL);
if (!f->cache) {
ti->error = "Cannot create FEC buffer cache";
return -ENOMEM;
}
/* Preallocate DM_VERITY_FEC_BUF_PREALLOC buffers for each thread */
f->prealloc_pool = mempool_create_slab_pool(num_online_cpus() *
DM_VERITY_FEC_BUF_PREALLOC,
f->cache);
if (!f->prealloc_pool) {
ti->error = "Cannot allocate FEC buffer prealloc pool";
return -ENOMEM;
}
f->extra_pool = mempool_create_slab_pool(0, f->cache);
if (!f->extra_pool) {
ti->error = "Cannot allocate FEC buffer extra pool";
return -ENOMEM;
}
/* Preallocate an output buffer for each thread */
f->output_pool = mempool_create_kmalloc_pool(num_online_cpus(),
1 << v->data_dev_block_bits);
if (!f->output_pool) {
ti->error = "Cannot allocate FEC output pool";
return -ENOMEM;
}
/* Reserve space for our per-bio data */
ti->per_bio_data_size += sizeof(struct dm_verity_fec_io);
return 0;
}

155
drivers/md/dm-verity-fec.h Normal file
View file

@ -0,0 +1,155 @@
/*
* Copyright (C) 2015 Google, Inc.
*
* Author: Sami Tolvanen <samitolvanen@google.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#ifndef DM_VERITY_FEC_H
#define DM_VERITY_FEC_H
#include "dm.h"
#include "dm-verity.h"
#include <linux/rslib.h>
/* Reed-Solomon(M, N) parameters */
#define DM_VERITY_FEC_RSM 255
#define DM_VERITY_FEC_MAX_RSN 253
#define DM_VERITY_FEC_MIN_RSN 231 /* ~10% space overhead */
/* buffers for deinterleaving and decoding */
#define DM_VERITY_FEC_BUF_PREALLOC 1 /* buffers to preallocate */
#define DM_VERITY_FEC_BUF_RS_BITS 4 /* 1 << RS blocks per buffer */
/* we need buffers for at most 1 << block size RS blocks */
#define DM_VERITY_FEC_BUF_MAX \
(1 << (PAGE_SHIFT - DM_VERITY_FEC_BUF_RS_BITS))
#define DM_VERITY_OPT_FEC_DEV "use_fec_from_device"
#define DM_VERITY_OPT_FEC_BLOCKS "fec_blocks"
#define DM_VERITY_OPT_FEC_START "fec_start"
#define DM_VERITY_OPT_FEC_ROOTS "fec_roots"
/* configuration */
struct dm_verity_fec {
struct dm_dev *dev; /* parity data device */
struct dm_bufio_client *data_bufio; /* for data dev access */
struct dm_bufio_client *bufio; /* for parity data access */
sector_t start; /* parity data start in blocks */
sector_t blocks; /* number of blocks covered */
sector_t rounds; /* number of interleaving rounds */
sector_t hash_blocks; /* blocks covered after v->hash_start */
unsigned char roots; /* number of parity bytes, M-N of RS(M, N) */
unsigned char rsn; /* N of RS(M, N) */
mempool_t *rs_pool; /* mempool for fio->rs */
mempool_t *prealloc_pool; /* mempool for preallocated buffers */
mempool_t *extra_pool; /* mempool for extra buffers */
mempool_t *output_pool; /* mempool for output */
struct kmem_cache *cache; /* cache for buffers */
atomic_t corrected; /* corrected errors */
struct dm_kobject_holder kobj_holder; /* for sysfs attributes */
};
/* per-bio data */
struct dm_verity_fec_io {
struct rs_control *rs; /* Reed-Solomon state */
int erasures[DM_VERITY_FEC_MAX_RSN]; /* erasures for decode_rs8 */
u8 *bufs[DM_VERITY_FEC_BUF_MAX]; /* bufs for deinterleaving */
unsigned nbufs; /* number of buffers allocated */
u8 *output; /* buffer for corrected output */
size_t output_pos;
};
#ifdef CONFIG_DM_VERITY_FEC
/* each feature parameter requires a value */
#define DM_VERITY_OPTS_FEC 8
extern bool verity_fec_is_enabled(struct dm_verity *v);
extern int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
enum verity_block_type type, sector_t block,
u8 *dest, struct bvec_iter *iter);
extern unsigned verity_fec_status_table(struct dm_verity *v, unsigned sz,
char *result, unsigned maxlen);
extern void verity_fec_finish_io(struct dm_verity_io *io);
extern void verity_fec_init_io(struct dm_verity_io *io);
extern bool verity_is_fec_opt_arg(const char *arg_name);
extern int verity_fec_parse_opt_args(struct dm_arg_set *as,
struct dm_verity *v, unsigned *argc,
const char *arg_name);
extern void verity_fec_dtr(struct dm_verity *v);
extern int verity_fec_ctr_alloc(struct dm_verity *v);
extern int verity_fec_ctr(struct dm_verity *v);
#else /* !CONFIG_DM_VERITY_FEC */
#define DM_VERITY_OPTS_FEC 0
static inline bool verity_fec_is_enabled(struct dm_verity *v)
{
return false;
}
static inline int verity_fec_decode(struct dm_verity *v,
struct dm_verity_io *io,
enum verity_block_type type,
sector_t block, u8 *dest,
struct bvec_iter *iter)
{
return -EOPNOTSUPP;
}
static inline unsigned verity_fec_status_table(struct dm_verity *v,
unsigned sz, char *result,
unsigned maxlen)
{
return sz;
}
static inline void verity_fec_finish_io(struct dm_verity_io *io)
{
}
static inline void verity_fec_init_io(struct dm_verity_io *io)
{
}
static inline bool verity_is_fec_opt_arg(const char *arg_name)
{
return false;
}
static inline int verity_fec_parse_opt_args(struct dm_arg_set *as,
struct dm_verity *v,
unsigned *argc,
const char *arg_name)
{
return -EINVAL;
}
static inline void verity_fec_dtr(struct dm_verity *v)
{
}
static inline int verity_fec_ctr_alloc(struct dm_verity *v)
{
return 0;
}
static inline int verity_fec_ctr(struct dm_verity *v)
{
return 0;
}
#endif /* CONFIG_DM_VERITY_FEC */
#endif /* DM_VERITY_FEC_H */

View file

@ -14,12 +14,11 @@
* access behavior.
*/
#include "dm-bufio.h"
#include "dm-verity.h"
#include "dm-verity-fec.h"
#include <linux/module.h>
#include <linux/device-mapper.h>
#include <linux/reboot.h>
#include <crypto/hash.h>
#define DM_MSG_PREFIX "verity"
@ -28,83 +27,18 @@
#define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
#define DM_VERITY_MAX_LEVELS 63
#define DM_VERITY_MAX_CORRUPTED_ERRS 100
#define DM_VERITY_OPT_LOGGING "ignore_corruption"
#define DM_VERITY_OPT_RESTART "restart_on_corruption"
#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC)
static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
enum verity_mode {
DM_VERITY_MODE_EIO,
DM_VERITY_MODE_LOGGING,
DM_VERITY_MODE_RESTART
};
enum verity_block_type {
DM_VERITY_BLOCK_TYPE_DATA,
DM_VERITY_BLOCK_TYPE_METADATA
};
struct dm_verity {
struct dm_dev *data_dev;
struct dm_dev *hash_dev;
struct dm_target *ti;
struct dm_bufio_client *bufio;
char *alg_name;
struct crypto_shash *tfm;
u8 *root_digest; /* digest of the root block */
u8 *salt; /* salt: its size is salt_size */
unsigned salt_size;
sector_t data_start; /* data offset in 512-byte sectors */
sector_t hash_start; /* hash start in blocks */
sector_t data_blocks; /* the number of data blocks */
sector_t hash_blocks; /* the number of hash blocks */
unsigned char data_dev_block_bits; /* log2(data blocksize) */
unsigned char hash_dev_block_bits; /* log2(hash blocksize) */
unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
unsigned char levels; /* the number of tree levels */
unsigned char version;
unsigned digest_size; /* digest size for the current hash algorithm */
unsigned shash_descsize;/* the size of temporary space for crypto */
int hash_failed; /* set to 1 if hash of any block failed */
enum verity_mode mode; /* mode for handling verification errors */
unsigned corrupted_errs;/* Number of errors for corrupted blocks */
struct workqueue_struct *verify_wq;
/* starting blocks for each tree level. 0 is the lowest level. */
sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
};
struct dm_verity_io {
struct dm_verity *v;
/* original values of bio->bi_end_io and bio->bi_private */
bio_end_io_t *orig_bi_end_io;
void *orig_bi_private;
sector_t block;
unsigned n_blocks;
struct bvec_iter iter;
struct work_struct work;
/*
* Three variably-size fields follow this struct:
*
* u8 hash_desc[v->shash_descsize];
* u8 real_digest[v->digest_size];
* u8 want_digest[v->digest_size];
*
* To access them use: io_hash_desc(), io_real_digest() and io_want_digest().
*/
};
struct dm_verity_prefetch_work {
struct work_struct work;
struct dm_verity *v;
@ -112,21 +46,6 @@ struct dm_verity_prefetch_work {
unsigned n_blocks;
};
static struct shash_desc *io_hash_desc(struct dm_verity *v, struct dm_verity_io *io)
{
return (struct shash_desc *)(io + 1);
}
static u8 *io_real_digest(struct dm_verity *v, struct dm_verity_io *io)
{
return (u8 *)(io + 1) + v->shash_descsize;
}
static u8 *io_want_digest(struct dm_verity *v, struct dm_verity_io *io)
{
return (u8 *)(io + 1) + v->shash_descsize + v->digest_size;
}
/*
* Auxiliary structure appended to each dm-bufio buffer. If the value
* hash_verified is nonzero, hash of the block has been verified.
@ -173,6 +92,84 @@ static sector_t verity_position_at_level(struct dm_verity *v, sector_t block,
return block >> (level * v->hash_per_block_bits);
}
/*
* Wrapper for crypto_shash_init, which handles verity salting.
*/
static int verity_hash_init(struct dm_verity *v, struct shash_desc *desc)
{
int r;
desc->tfm = v->tfm;
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
r = crypto_shash_init(desc);
if (unlikely(r < 0)) {
DMERR("crypto_shash_init failed: %d", r);
return r;
}
if (likely(v->version >= 1)) {
r = crypto_shash_update(desc, v->salt, v->salt_size);
if (unlikely(r < 0)) {
DMERR("crypto_shash_update failed: %d", r);
return r;
}
}
return 0;
}
static int verity_hash_update(struct dm_verity *v, struct shash_desc *desc,
const u8 *data, size_t len)
{
int r = crypto_shash_update(desc, data, len);
if (unlikely(r < 0))
DMERR("crypto_shash_update failed: %d", r);
return r;
}
static int verity_hash_final(struct dm_verity *v, struct shash_desc *desc,
u8 *digest)
{
int r;
if (unlikely(!v->version)) {
r = crypto_shash_update(desc, v->salt, v->salt_size);
if (r < 0) {
DMERR("crypto_shash_update failed: %d", r);
return r;
}
}
r = crypto_shash_final(desc, digest);
if (unlikely(r < 0))
DMERR("crypto_shash_final failed: %d", r);
return r;
}
int verity_hash(struct dm_verity *v, struct shash_desc *desc,
const u8 *data, size_t len, u8 *digest)
{
int r;
r = verity_hash_init(v, desc);
if (unlikely(r < 0))
return r;
r = verity_hash_update(v, desc, data, len);
if (unlikely(r < 0))
return r;
return verity_hash_final(v, desc, digest);
}
static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
sector_t *hash_block, unsigned *offset)
{
@ -246,17 +243,17 @@ out:
* Verify hash of a metadata block pertaining to the specified data block
* ("block" argument) at a specified level ("level" argument).
*
* On successful return, io_want_digest(v, io) contains the hash value for
* a lower tree level or for the data block (if we're at the lowest leve).
* On successful return, verity_io_want_digest(v, io) contains the hash value
* for a lower tree level or for the data block (if we're at the lowest level).
*
* If "skip_unverified" is true, unverified buffer is skipped and 1 is returned.
* If "skip_unverified" is false, unverified buffer is hashed and verified
* against current value of io_want_digest(v, io).
* against current value of verity_io_want_digest(v, io).
*/
static int verity_verify_level(struct dm_verity_io *io, sector_t block,
int level, bool skip_unverified)
static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
sector_t block, int level, bool skip_unverified,
u8 *want_digest)
{
struct dm_verity *v = io->v;
struct dm_buffer *buf;
struct buffer_aux *aux;
u8 *data;
@ -273,172 +270,185 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
aux = dm_bufio_get_aux_data(buf);
if (!aux->hash_verified) {
struct shash_desc *desc;
u8 *result;
if (skip_unverified) {
r = 1;
goto release_ret_r;
}
desc = io_hash_desc(v, io);
desc->tfm = v->tfm;
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
r = crypto_shash_init(desc);
if (r < 0) {
DMERR("crypto_shash_init failed: %d", r);
r = verity_hash(v, verity_io_hash_desc(v, io),
data, 1 << v->hash_dev_block_bits,
verity_io_real_digest(v, io));
if (unlikely(r < 0))
goto release_ret_r;
}
if (likely(v->version >= 1)) {
r = crypto_shash_update(desc, v->salt, v->salt_size);
if (r < 0) {
DMERR("crypto_shash_update failed: %d", r);
goto release_ret_r;
}
}
r = crypto_shash_update(desc, data, 1 << v->hash_dev_block_bits);
if (r < 0) {
DMERR("crypto_shash_update failed: %d", r);
goto release_ret_r;
}
if (!v->version) {
r = crypto_shash_update(desc, v->salt, v->salt_size);
if (r < 0) {
DMERR("crypto_shash_update failed: %d", r);
goto release_ret_r;
}
}
result = io_real_digest(v, io);
r = crypto_shash_final(desc, result);
if (r < 0) {
DMERR("crypto_shash_final failed: %d", r);
goto release_ret_r;
}
if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA,
hash_block)) {
r = -EIO;
goto release_ret_r;
}
} else
if (likely(memcmp(verity_io_real_digest(v, io), want_digest,
v->digest_size) == 0))
aux->hash_verified = 1;
else if (verity_fec_decode(v, io,
DM_VERITY_BLOCK_TYPE_METADATA,
hash_block, data, NULL) == 0)
aux->hash_verified = 1;
else if (verity_handle_err(v,
DM_VERITY_BLOCK_TYPE_METADATA,
hash_block)) {
r = -EIO;
goto release_ret_r;
}
}
data += offset;
memcpy(io_want_digest(v, io), data, v->digest_size);
dm_bufio_release(buf);
return 0;
memcpy(want_digest, data, v->digest_size);
r = 0;
release_ret_r:
dm_bufio_release(buf);
return r;
}
/*
* Find a hash for a given block, write it to digest and verify the integrity
* of the hash tree if necessary.
*/
int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
sector_t block, u8 *digest, bool *is_zero)
{
int r = 0, i;
if (likely(v->levels)) {
/*
* First, we try to get the requested hash for
* the current block. If the hash block itself is
* verified, zero is returned. If it isn't, this
* function returns 1 and we fall back to whole
* chain verification.
*/
r = verity_verify_level(v, io, block, 0, true, digest);
if (likely(r <= 0))
goto out;
}
memcpy(digest, v->root_digest, v->digest_size);
for (i = v->levels - 1; i >= 0; i--) {
r = verity_verify_level(v, io, block, i, false, digest);
if (unlikely(r))
goto out;
}
out:
if (!r && v->zero_digest)
*is_zero = !memcmp(v->zero_digest, digest, v->digest_size);
else
*is_zero = false;
return r;
}
/*
* Calls function process for 1 << v->data_dev_block_bits bytes in the bio_vec
* starting from iter.
*/
int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
struct bvec_iter *iter,
int (*process)(struct dm_verity *v,
struct dm_verity_io *io, u8 *data,
size_t len))
{
unsigned todo = 1 << v->data_dev_block_bits;
struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_bio_data_size);
do {
int r;
u8 *page;
unsigned len;
struct bio_vec bv = bio_iter_iovec(bio, *iter);
page = kmap_atomic(bv.bv_page);
len = bv.bv_len;
if (likely(len >= todo))
len = todo;
r = process(v, io, page + bv.bv_offset, len);
kunmap_atomic(page);
if (r < 0)
return r;
bio_advance_iter(bio, iter, len);
todo -= len;
} while (todo);
return 0;
}
static int verity_bv_hash_update(struct dm_verity *v, struct dm_verity_io *io,
u8 *data, size_t len)
{
return verity_hash_update(v, verity_io_hash_desc(v, io), data, len);
}
static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
u8 *data, size_t len)
{
memset(data, 0, len);
return 0;
}
/*
* Verify one "dm_verity_io" structure.
*/
static int verity_verify_io(struct dm_verity_io *io)
{
bool is_zero;
struct dm_verity *v = io->v;
struct bio *bio = dm_bio_from_per_bio_data(io,
v->ti->per_bio_data_size);
struct bvec_iter start;
unsigned b;
int i;
for (b = 0; b < io->n_blocks; b++) {
struct shash_desc *desc;
u8 *result;
int r;
unsigned todo;
struct shash_desc *desc = verity_io_hash_desc(v, io);
if (likely(v->levels)) {
r = verity_hash_for_block(v, io, io->block + b,
verity_io_want_digest(v, io),
&is_zero);
if (unlikely(r < 0))
return r;
if (is_zero) {
/*
* First, we try to get the requested hash for
* the current block. If the hash block itself is
* verified, zero is returned. If it isn't, this
* function returns 0 and we fall back to whole
* chain verification.
* If we expect a zero block, don't validate, just
* return zeros.
*/
int r = verity_verify_level(io, io->block + b, 0, true);
if (likely(!r))
goto test_block_hash;
if (r < 0)
r = verity_for_bv_block(v, io, &io->iter,
verity_bv_zero);
if (unlikely(r < 0))
return r;
continue;
}
memcpy(io_want_digest(v, io), v->root_digest, v->digest_size);
for (i = v->levels - 1; i >= 0; i--) {
int r = verity_verify_level(io, io->block + b, i, false);
if (unlikely(r))
return r;
}
test_block_hash:
desc = io_hash_desc(v, io);
desc->tfm = v->tfm;
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
r = crypto_shash_init(desc);
if (r < 0) {
DMERR("crypto_shash_init failed: %d", r);
r = verity_hash_init(v, desc);
if (unlikely(r < 0))
return r;
}
if (likely(v->version >= 1)) {
r = crypto_shash_update(desc, v->salt, v->salt_size);
if (r < 0) {
DMERR("crypto_shash_update failed: %d", r);
return r;
}
}
todo = 1 << v->data_dev_block_bits;
do {
u8 *page;
unsigned len;
struct bio_vec bv = bio_iter_iovec(bio, io->iter);
page = kmap_atomic(bv.bv_page);
len = bv.bv_len;
if (likely(len >= todo))
len = todo;
r = crypto_shash_update(desc, page + bv.bv_offset, len);
kunmap_atomic(page);
if (r < 0) {
DMERR("crypto_shash_update failed: %d", r);
return r;
}
bio_advance_iter(bio, &io->iter, len);
todo -= len;
} while (todo);
if (!v->version) {
r = crypto_shash_update(desc, v->salt, v->salt_size);
if (r < 0) {
DMERR("crypto_shash_update failed: %d", r);
return r;
}
}
result = io_real_digest(v, io);
r = crypto_shash_final(desc, result);
if (r < 0) {
DMERR("crypto_shash_final failed: %d", r);
start = io->iter;
r = verity_for_bv_block(v, io, &io->iter, verity_bv_hash_update);
if (unlikely(r < 0))
return r;
}
if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
io->block + b))
return -EIO;
}
r = verity_hash_final(v, desc, verity_io_real_digest(v, io));
if (unlikely(r < 0))
return r;
if (likely(memcmp(verity_io_real_digest(v, io),
verity_io_want_digest(v, io), v->digest_size) == 0))
continue;
else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
io->block + b, NULL, &start) == 0)
continue;
else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
io->block + b))
return -EIO;
}
return 0;
@ -453,9 +463,10 @@ static void verity_finish_io(struct dm_verity_io *io, int error)
struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_bio_data_size);
bio->bi_end_io = io->orig_bi_end_io;
bio->bi_private = io->orig_bi_private;
bio->bi_error = error;
verity_fec_finish_io(io);
bio_endio(bio);
}
@ -470,7 +481,7 @@ static void verity_end_io(struct bio *bio)
{
struct dm_verity_io *io = bio->bi_private;
if (bio->bi_error) {
if (bio->bi_error && !verity_fec_is_enabled(io->v)) {
verity_finish_io(io, bio->bi_error);
return;
}
@ -566,7 +577,6 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
io = dm_per_bio_data(bio, ti->per_bio_data_size);
io->v = v;
io->orig_bi_end_io = bio->bi_end_io;
io->orig_bi_private = bio->bi_private;
io->block = bio->bi_iter.bi_sector >> (v->data_dev_block_bits - SECTOR_SHIFT);
io->n_blocks = bio->bi_iter.bi_size >> v->data_dev_block_bits;
@ -574,6 +584,8 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
bio->bi_private = io;
io->iter = bio->bi_iter;
verity_fec_init_io(io);
verity_submit_prefetch(v, io);
generic_make_request(bio);
@ -588,6 +600,7 @@ static void verity_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
struct dm_verity *v = ti->private;
unsigned args = 0;
unsigned sz = 0;
unsigned x;
@ -614,8 +627,17 @@ static void verity_status(struct dm_target *ti, status_type_t type,
else
for (x = 0; x < v->salt_size; x++)
DMEMIT("%02x", v->salt[x]);
if (v->mode != DM_VERITY_MODE_EIO)
args++;
if (verity_fec_is_enabled(v))
args += DM_VERITY_OPTS_FEC;
if (v->zero_digest)
args++;
if (!args)
return;
DMEMIT(" %u", args);
if (v->mode != DM_VERITY_MODE_EIO) {
DMEMIT(" 1 ");
DMEMIT(" ");
switch (v->mode) {
case DM_VERITY_MODE_LOGGING:
DMEMIT(DM_VERITY_OPT_LOGGING);
@ -627,6 +649,9 @@ static void verity_status(struct dm_target *ti, status_type_t type,
BUG();
}
}
if (v->zero_digest)
DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
sz = verity_fec_status_table(v, sz, result, maxlen);
break;
}
}
@ -677,6 +702,7 @@ static void verity_dtr(struct dm_target *ti)
kfree(v->salt);
kfree(v->root_digest);
kfree(v->zero_digest);
if (v->tfm)
crypto_free_shash(v->tfm);
@ -689,9 +715,94 @@ static void verity_dtr(struct dm_target *ti)
if (v->data_dev)
dm_put_device(ti, v->data_dev);
verity_fec_dtr(v);
kfree(v);
}
static int verity_alloc_zero_digest(struct dm_verity *v)
{
int r = -ENOMEM;
struct shash_desc *desc;
u8 *zero_data;
v->zero_digest = kmalloc(v->digest_size, GFP_KERNEL);
if (!v->zero_digest)
return r;
desc = kmalloc(v->shash_descsize, GFP_KERNEL);
if (!desc)
return r; /* verity_dtr will free zero_digest */
zero_data = kzalloc(1 << v->data_dev_block_bits, GFP_KERNEL);
if (!zero_data)
goto out;
r = verity_hash(v, desc, zero_data, 1 << v->data_dev_block_bits,
v->zero_digest);
out:
kfree(desc);
kfree(zero_data);
return r;
}
static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
{
int r;
unsigned argc;
struct dm_target *ti = v->ti;
const char *arg_name;
static struct dm_arg _args[] = {
{0, DM_VERITY_OPTS_MAX, "Invalid number of feature args"},
};
r = dm_read_arg_group(_args, as, &argc, &ti->error);
if (r)
return -EINVAL;
if (!argc)
return 0;
do {
arg_name = dm_shift_arg(as);
argc--;
if (!strcasecmp(arg_name, DM_VERITY_OPT_LOGGING)) {
v->mode = DM_VERITY_MODE_LOGGING;
continue;
} else if (!strcasecmp(arg_name, DM_VERITY_OPT_RESTART)) {
v->mode = DM_VERITY_MODE_RESTART;
continue;
} else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) {
r = verity_alloc_zero_digest(v);
if (r) {
ti->error = "Cannot allocate zero digest";
return r;
}
continue;
} else if (verity_is_fec_opt_arg(arg_name)) {
r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
if (r)
return r;
continue;
}
ti->error = "Unrecognized verity feature request";
return -EINVAL;
} while (argc && !r);
return r;
}
/*
* Target parameters:
* <version> The current format is version 1.
@ -710,18 +821,13 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
struct dm_verity *v;
struct dm_arg_set as;
const char *opt_string;
unsigned int num, opt_params;
unsigned int num;
unsigned long long num_ll;
int r;
int i;
sector_t hash_position;
char dummy;
static struct dm_arg _args[] = {
{0, 1, "Invalid number of feature args"},
};
v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
if (!v) {
ti->error = "Cannot allocate verity structure";
@ -730,6 +836,10 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
ti->private = v;
v->ti = ti;
r = verity_fec_ctr_alloc(v);
if (r)
goto bad;
if ((dm_table_get_mode(ti->table) & ~FMODE_READ)) {
ti->error = "Device must be readonly";
r = -EINVAL;
@ -866,29 +976,9 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
as.argc = argc;
as.argv = argv;
r = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
if (r)
r = verity_parse_opt_args(&as, v);
if (r < 0)
goto bad;
while (opt_params) {
opt_params--;
opt_string = dm_shift_arg(&as);
if (!opt_string) {
ti->error = "Not enough feature arguments";
r = -EINVAL;
goto bad;
}
if (!strcasecmp(opt_string, DM_VERITY_OPT_LOGGING))
v->mode = DM_VERITY_MODE_LOGGING;
else if (!strcasecmp(opt_string, DM_VERITY_OPT_RESTART))
v->mode = DM_VERITY_MODE_RESTART;
else {
ti->error = "Invalid feature arguments";
r = -EINVAL;
goto bad;
}
}
}
v->hash_per_block_bits =
@ -938,8 +1028,6 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
ti->per_bio_data_size = roundup(sizeof(struct dm_verity_io) + v->shash_descsize + v->digest_size * 2, __alignof__(struct dm_verity_io));
/* WQ_UNBOUND greatly improves performance when running on ramdisk */
v->verify_wq = alloc_workqueue("kverityd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND, num_online_cpus());
if (!v->verify_wq) {
@ -948,6 +1036,16 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
ti->per_bio_data_size = sizeof(struct dm_verity_io) +
v->shash_descsize + v->digest_size * 2;
r = verity_fec_ctr(v);
if (r)
goto bad;
ti->per_bio_data_size = roundup(ti->per_bio_data_size,
__alignof__(struct dm_verity_io));
return 0;
bad:
@ -958,7 +1056,7 @@ bad:
static struct target_type verity_target = {
.name = "verity",
.version = {1, 2, 0},
.version = {1, 3, 0},
.module = THIS_MODULE,
.ctr = verity_ctr,
.dtr = verity_dtr,

129
drivers/md/dm-verity.h Normal file
View file

@ -0,0 +1,129 @@
/*
* Copyright (C) 2012 Red Hat, Inc.
* Copyright (C) 2015 Google, Inc.
*
* Author: Mikulas Patocka <mpatocka@redhat.com>
*
* Based on Chromium dm-verity driver (C) 2011 The Chromium OS Authors
*
* This file is released under the GPLv2.
*/
#ifndef DM_VERITY_H
#define DM_VERITY_H
#include "dm-bufio.h"
#include <linux/device-mapper.h>
#include <crypto/hash.h>
#define DM_VERITY_MAX_LEVELS 63
enum verity_mode {
DM_VERITY_MODE_EIO,
DM_VERITY_MODE_LOGGING,
DM_VERITY_MODE_RESTART
};
enum verity_block_type {
DM_VERITY_BLOCK_TYPE_DATA,
DM_VERITY_BLOCK_TYPE_METADATA
};
struct dm_verity_fec;
struct dm_verity {
struct dm_dev *data_dev;
struct dm_dev *hash_dev;
struct dm_target *ti;
struct dm_bufio_client *bufio;
char *alg_name;
struct crypto_shash *tfm;
u8 *root_digest; /* digest of the root block */
u8 *salt; /* salt: its size is salt_size */
u8 *zero_digest; /* digest for a zero block */
unsigned salt_size;
sector_t data_start; /* data offset in 512-byte sectors */
sector_t hash_start; /* hash start in blocks */
sector_t data_blocks; /* the number of data blocks */
sector_t hash_blocks; /* the number of hash blocks */
unsigned char data_dev_block_bits; /* log2(data blocksize) */
unsigned char hash_dev_block_bits; /* log2(hash blocksize) */
unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
unsigned char levels; /* the number of tree levels */
unsigned char version;
unsigned digest_size; /* digest size for the current hash algorithm */
unsigned shash_descsize;/* the size of temporary space for crypto */
int hash_failed; /* set to 1 if hash of any block failed */
enum verity_mode mode; /* mode for handling verification errors */
unsigned corrupted_errs;/* Number of errors for corrupted blocks */
struct workqueue_struct *verify_wq;
/* starting blocks for each tree level. 0 is the lowest level. */
sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
struct dm_verity_fec *fec; /* forward error correction */
};
struct dm_verity_io {
struct dm_verity *v;
/* original value of bio->bi_end_io */
bio_end_io_t *orig_bi_end_io;
sector_t block;
unsigned n_blocks;
struct bvec_iter iter;
struct work_struct work;
/*
* Three variably-size fields follow this struct:
*
* u8 hash_desc[v->shash_descsize];
* u8 real_digest[v->digest_size];
* u8 want_digest[v->digest_size];
*
* To access them use: verity_io_hash_desc(), verity_io_real_digest()
* and verity_io_want_digest().
*/
};
static inline struct shash_desc *verity_io_hash_desc(struct dm_verity *v,
struct dm_verity_io *io)
{
return (struct shash_desc *)(io + 1);
}
static inline u8 *verity_io_real_digest(struct dm_verity *v,
struct dm_verity_io *io)
{
return (u8 *)(io + 1) + v->shash_descsize;
}
static inline u8 *verity_io_want_digest(struct dm_verity *v,
struct dm_verity_io *io)
{
return (u8 *)(io + 1) + v->shash_descsize + v->digest_size;
}
static inline u8 *verity_io_digest_end(struct dm_verity *v,
struct dm_verity_io *io)
{
return verity_io_want_digest(v, io) + v->digest_size;
}
extern int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
struct bvec_iter *iter,
int (*process)(struct dm_verity *v,
struct dm_verity_io *io,
u8 *data, size_t len));
extern int verity_hash(struct dm_verity *v, struct shash_desc *desc,
const u8 *data, size_t len, u8 *digest);
extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
sector_t block, u8 *digest, bool *is_zero);
#endif /* DM_VERITY_H */

153
drivers/misc/uid_stat.c Normal file
View file

@ -0,0 +1,153 @@
/* drivers/misc/uid_stat.c
*
* Copyright (C) 2008 - 2009 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/atomic.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/stat.h>
#include <linux/uid_stat.h>
#include <net/activity_stats.h>
static DEFINE_SPINLOCK(uid_lock);
static LIST_HEAD(uid_list);
static struct proc_dir_entry *parent;
struct uid_stat {
struct list_head link;
uid_t uid;
atomic_t tcp_rcv;
atomic_t tcp_snd;
};
static struct uid_stat *find_uid_stat(uid_t uid) {
struct uid_stat *entry;
list_for_each_entry(entry, &uid_list, link) {
if (entry->uid == uid) {
return entry;
}
}
return NULL;
}
static int uid_stat_atomic_int_show(struct seq_file *m, void *v)
{
unsigned int bytes;
atomic_t *counter = m->private;
bytes = (unsigned int) (atomic_read(counter) + INT_MIN);
seq_printf(m, "%u\n", bytes);
return seq_has_overflowed(m) ? -ENOSPC : 0;
}
static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file)
{
return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode));
}
static const struct file_operations uid_stat_read_atomic_int_fops = {
.open = uid_stat_read_atomic_int_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
/* Create a new entry for tracking the specified uid. */
static struct uid_stat *create_stat(uid_t uid) {
struct uid_stat *new_uid;
/* Create the uid stat struct and append it to the list. */
new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC);
if (!new_uid)
return NULL;
new_uid->uid = uid;
/* Counters start at INT_MIN, so we can track 4GB of network traffic. */
atomic_set(&new_uid->tcp_rcv, INT_MIN);
atomic_set(&new_uid->tcp_snd, INT_MIN);
list_add_tail(&new_uid->link, &uid_list);
return new_uid;
}
static void create_stat_proc(struct uid_stat *new_uid)
{
char uid_s[32];
struct proc_dir_entry *entry;
sprintf(uid_s, "%d", new_uid->uid);
entry = proc_mkdir(uid_s, parent);
/* Keep reference to uid_stat so we know what uid to read stats from. */
proc_create_data("tcp_snd", S_IRUGO, entry,
&uid_stat_read_atomic_int_fops, &new_uid->tcp_snd);
proc_create_data("tcp_rcv", S_IRUGO, entry,
&uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv);
}
static struct uid_stat *find_or_create_uid_stat(uid_t uid)
{
struct uid_stat *entry;
unsigned long flags;
spin_lock_irqsave(&uid_lock, flags);
entry = find_uid_stat(uid);
if (entry) {
spin_unlock_irqrestore(&uid_lock, flags);
return entry;
}
entry = create_stat(uid);
spin_unlock_irqrestore(&uid_lock, flags);
if (entry)
create_stat_proc(entry);
return entry;
}
int uid_stat_tcp_snd(uid_t uid, int size) {
struct uid_stat *entry;
activity_stats_update();
entry = find_or_create_uid_stat(uid);
if (!entry)
return -1;
atomic_add(size, &entry->tcp_snd);
return 0;
}
int uid_stat_tcp_rcv(uid_t uid, int size) {
struct uid_stat *entry;
activity_stats_update();
entry = find_or_create_uid_stat(uid);
if (!entry)
return -1;
atomic_add(size, &entry->tcp_rcv);
return 0;
}
static int __init uid_stat_init(void)
{
parent = proc_mkdir("uid_stat", NULL);
if (!parent) {
pr_err("uid_stat: failed to create proc entry\n");
return -1;
}
return 0;
}
__initcall(uid_stat_init);

View file

@ -68,3 +68,15 @@ config MMC_TEST
This driver is only of interest to those developing or
testing a host driver. Most people should say N here.
config MMC_SIMULATE_MAX_SPEED
bool "Turn on maximum speed control per block device"
depends on MMC_BLOCK
help
Say Y here to enable MMC device speed limiting. Used to test and
simulate the behavior of the system when confronted with a slow MMC.
Enables max_read_speed, max_write_speed and cache_size attributes to
control the write or read maximum KB/second speed behaviors.
If unsure, say N here.

View file

@ -38,7 +38,6 @@
#include <linux/pm_runtime.h>
#include <linux/ioprio.h>
#define CREATE_TRACE_POINTS
#include <trace/events/mmc.h>
#include <linux/mmc/ioctl.h>
@ -329,6 +328,249 @@ out:
return ret;
}
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
static int max_read_speed, max_write_speed, cache_size = 4;
module_param(max_read_speed, int, S_IRUSR | S_IRGRP);
MODULE_PARM_DESC(max_read_speed, "maximum KB/s read speed 0=off");
module_param(max_write_speed, int, S_IRUSR | S_IRGRP);
MODULE_PARM_DESC(max_write_speed, "maximum KB/s write speed 0=off");
module_param(cache_size, int, S_IRUSR | S_IRGRP);
MODULE_PARM_DESC(cache_size, "MB high speed memory or SLC cache");
/*
* helper macros and expectations:
* size - unsigned long number of bytes
* jiffies - unsigned long HZ timestamp difference
* speed - unsigned KB/s transfer rate
*/
#define size_and_speed_to_jiffies(size, speed) \
((size) * HZ / (speed) / 1024UL)
#define jiffies_and_speed_to_size(jiffies, speed) \
(((speed) * (jiffies) * 1024UL) / HZ)
#define jiffies_and_size_to_speed(jiffies, size) \
((size) * HZ / (jiffies) / 1024UL)
/* Limits to report warning */
/* jiffies_and_size_to_speed(10*HZ, queue_max_hw_sectors(q) * 512UL) ~ 25 */
#define MIN_SPEED(q) 250 /* 10 times faster than a floppy disk */
#define MAX_SPEED(q) jiffies_and_size_to_speed(1, queue_max_sectors(q) * 512UL)
#define speed_valid(speed) ((speed) > 0)
static const char off[] = "off\n";
static int max_speed_show(int speed, char *buf)
{
if (speed)
return scnprintf(buf, PAGE_SIZE, "%uKB/s\n", speed);
else
return scnprintf(buf, PAGE_SIZE, off);
}
static int max_speed_store(const char *buf, struct request_queue *q)
{
unsigned int limit, set = 0;
if (!strncasecmp(off, buf, sizeof(off) - 2))
return set;
if (kstrtouint(buf, 0, &set) || (set > INT_MAX))
return -EINVAL;
if (set == 0)
return set;
limit = MAX_SPEED(q);
if (set > limit)
pr_warn("max speed %u ineffective above %u\n", set, limit);
limit = MIN_SPEED(q);
if (set < limit)
pr_warn("max speed %u painful below %u\n", set, limit);
return set;
}
static ssize_t max_write_speed_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
int ret = max_speed_show(atomic_read(&md->queue.max_write_speed), buf);
mmc_blk_put(md);
return ret;
}
static ssize_t max_write_speed_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
int set = max_speed_store(buf, md->queue.queue);
if (set < 0) {
mmc_blk_put(md);
return set;
}
atomic_set(&md->queue.max_write_speed, set);
mmc_blk_put(md);
return count;
}
static const DEVICE_ATTR(max_write_speed, S_IRUGO | S_IWUSR,
max_write_speed_show, max_write_speed_store);
static ssize_t max_read_speed_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
int ret = max_speed_show(atomic_read(&md->queue.max_read_speed), buf);
mmc_blk_put(md);
return ret;
}
static ssize_t max_read_speed_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
int set = max_speed_store(buf, md->queue.queue);
if (set < 0) {
mmc_blk_put(md);
return set;
}
atomic_set(&md->queue.max_read_speed, set);
mmc_blk_put(md);
return count;
}
static const DEVICE_ATTR(max_read_speed, S_IRUGO | S_IWUSR,
max_read_speed_show, max_read_speed_store);
static ssize_t cache_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
struct mmc_queue *mq = &md->queue;
int cache_size = atomic_read(&mq->cache_size);
int ret;
if (!cache_size)
ret = scnprintf(buf, PAGE_SIZE, off);
else {
int speed = atomic_read(&mq->max_write_speed);
if (!speed_valid(speed))
ret = scnprintf(buf, PAGE_SIZE, "%uMB\n", cache_size);
else { /* We accept race between cache_jiffies and cache_used */
unsigned long size = jiffies_and_speed_to_size(
jiffies - mq->cache_jiffies, speed);
long used = atomic_long_read(&mq->cache_used);
if (size >= used)
size = 0;
else
size = (used - size) * 100 / cache_size
/ 1024UL / 1024UL;
ret = scnprintf(buf, PAGE_SIZE, "%uMB %lu%% used\n",
cache_size, size);
}
}
mmc_blk_put(md);
return ret;
}
static ssize_t cache_size_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct mmc_blk_data *md;
unsigned int set = 0;
if (strncasecmp(off, buf, sizeof(off) - 2)
&& (kstrtouint(buf, 0, &set) || (set > INT_MAX)))
return -EINVAL;
md = mmc_blk_get(dev_to_disk(dev));
atomic_set(&md->queue.cache_size, set);
mmc_blk_put(md);
return count;
}
static const DEVICE_ATTR(cache_size, S_IRUGO | S_IWUSR,
cache_size_show, cache_size_store);
/* correct for write-back */
static long mmc_blk_cache_used(struct mmc_queue *mq, unsigned long waitfor)
{
long used = 0;
int speed = atomic_read(&mq->max_write_speed);
if (speed_valid(speed)) {
unsigned long size = jiffies_and_speed_to_size(
waitfor - mq->cache_jiffies, speed);
used = atomic_long_read(&mq->cache_used);
if (size >= used)
used = 0;
else
used -= size;
}
atomic_long_set(&mq->cache_used, used);
mq->cache_jiffies = waitfor;
return used;
}
static void mmc_blk_simulate_delay(
struct mmc_queue *mq,
struct request *req,
unsigned long waitfor)
{
int max_speed;
if (!req)
return;
max_speed = (rq_data_dir(req) == READ)
? atomic_read(&mq->max_read_speed)
: atomic_read(&mq->max_write_speed);
if (speed_valid(max_speed)) {
unsigned long bytes = blk_rq_bytes(req);
if (rq_data_dir(req) != READ) {
int cache_size = atomic_read(&mq->cache_size);
if (cache_size) {
unsigned long size = cache_size * 1024L * 1024L;
long used = mmc_blk_cache_used(mq, waitfor);
used += bytes;
atomic_long_set(&mq->cache_used, used);
bytes = 0;
if (used > size)
bytes = used - size;
}
}
waitfor += size_and_speed_to_jiffies(bytes, max_speed);
if (time_is_after_jiffies(waitfor)) {
long msecs = jiffies_to_msecs(waitfor - jiffies);
if (likely(msecs > 0))
msleep(msecs);
}
}
}
#else
#define mmc_blk_simulate_delay(mq, req, waitfor)
#endif
static ssize_t
num_wr_reqs_to_start_packing_show(struct device *dev,
struct device_attribute *attr, char *buf)
@ -1870,6 +2112,23 @@ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
}
end_req:
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
else if (atomic_read(&mq->cache_size)) {
long used = mmc_blk_cache_used(mq, jiffies);
if (used) {
int speed = atomic_read(&mq->max_write_speed);
if (speed_valid(speed)) {
unsigned long msecs = jiffies_to_msecs(
size_and_speed_to_jiffies(
used, speed));
if (msecs)
msleep(msecs);
}
}
}
#endif
blk_end_request_all(req, ret);
return ret ? 0 : 1;
@ -3346,6 +3605,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
struct mmc_async_req *areq;
const u8 packed_nr = 2;
u8 reqs = 0;
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
unsigned long waitfor = jiffies;
#endif
if (!rqc && !mq->mqrq_prev->req)
return 0;
@ -3396,6 +3658,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
*/
mmc_blk_reset_success(md, type);
mmc_blk_simulate_delay(mq, rqc, waitfor);
if (mmc_packed_cmd(mq_rq->cmd_type)) {
ret = mmc_blk_end_packed_req(mq_rq);
break;
@ -3968,6 +4232,14 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
card->ext_csd.boot_ro_lockable)
device_remove_file(disk_to_dev(md->disk),
&md->power_ro_lock);
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
device_remove_file(disk_to_dev(md->disk),
&dev_attr_max_write_speed);
device_remove_file(disk_to_dev(md->disk),
&dev_attr_max_read_speed);
device_remove_file(disk_to_dev(md->disk),
&dev_attr_cache_size);
#endif
del_gendisk(md->disk);
}
@ -4003,6 +4275,24 @@ static int mmc_add_disk(struct mmc_blk_data *md)
ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);
if (ret)
goto force_ro_fail;
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
atomic_set(&md->queue.max_write_speed, max_write_speed);
ret = device_create_file(disk_to_dev(md->disk),
&dev_attr_max_write_speed);
if (ret)
goto max_write_speed_fail;
atomic_set(&md->queue.max_read_speed, max_read_speed);
ret = device_create_file(disk_to_dev(md->disk),
&dev_attr_max_read_speed);
if (ret)
goto max_read_speed_fail;
atomic_set(&md->queue.cache_size, cache_size);
atomic_long_set(&md->queue.cache_used, 0);
md->queue.cache_jiffies = jiffies;
ret = device_create_file(disk_to_dev(md->disk), &dev_attr_cache_size);
if (ret)
goto cache_size_fail;
#endif
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
card->ext_csd.boot_ro_lockable) {
@ -4056,6 +4346,14 @@ no_pack_for_random_fails:
num_wr_reqs_to_start_packing_fail:
device_remove_file(disk_to_dev(md->disk), &md->power_ro_lock);
power_ro_lock_fail:
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
device_remove_file(disk_to_dev(md->disk), &dev_attr_cache_size);
cache_size_fail:
device_remove_file(disk_to_dev(md->disk), &dev_attr_max_read_speed);
max_read_speed_fail:
device_remove_file(disk_to_dev(md->disk), &dev_attr_max_write_speed);
max_write_speed_fail:
#endif
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
force_ro_fail:
del_gendisk(md->disk);

View file

@ -77,6 +77,14 @@ struct mmc_queue {
int (*err_check_fn) (struct mmc_card *, struct mmc_async_req *);
void (*packed_test_fn) (struct request_queue *, struct mmc_queue_req *);
void (*cmdq_shutdown)(struct mmc_queue *);
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
atomic_t max_write_speed;
atomic_t max_read_speed;
atomic_t cache_size;
/* i/o tracking */
atomic_long_t cache_used;
unsigned long cache_jiffies;
#endif
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,

View file

@ -33,6 +33,7 @@
#include <linux/pm.h>
#include <linux/jiffies.h>
#define CREATE_TRACE_POINTS
#include <trace/events/mmc.h>
#include <linux/mmc/card.h>
@ -51,6 +52,11 @@
#include "sd_ops.h"
#include "sdio_ops.h"
EXPORT_TRACEPOINT_SYMBOL_GPL(mmc_blk_erase_start);
EXPORT_TRACEPOINT_SYMBOL_GPL(mmc_blk_erase_end);
EXPORT_TRACEPOINT_SYMBOL_GPL(mmc_blk_rw_start);
EXPORT_TRACEPOINT_SYMBOL_GPL(mmc_blk_rw_end);
/* If the device is not responding */
#define MMC_CORE_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */

View file

@ -22,6 +22,7 @@
#include "core.h"
#include "bus.h"
#include "host.h"
#include "sd.h"
#include "sdio_bus.h"
#include "mmc_ops.h"

View file

@ -150,7 +150,7 @@ config PPPOL2TP
if TTY
config PPPOLAC
bool "PPP on L2TP Access Concentrator"
tristate "PPP on L2TP Access Concentrator"
depends on PPP && INET
help
L2TP (RFC 2661) is a tunneling protocol widely used in virtual private
@ -159,7 +159,7 @@ config PPPOLAC
fairly simple and suited for clients.
config PPPOPNS
bool "PPP on PPTP Network Server"
tristate "PPP on PPTP Network Server"
depends on PPP && INET
help
PPTP (RFC 2637) is a tunneling protocol widely used in virtual private

View file

@ -206,11 +206,10 @@ static void pppolac_xmit_core(struct work_struct *delivery_work)
while ((skb = skb_dequeue(&delivery_queue))) {
struct sock *sk_udp = skb->sk;
struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
struct msghdr msg = {
.msg_iov = (struct iovec *)&iov,
.msg_iovlen = 1,
.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
};
struct msghdr msg = { 0 };
iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iov, 1,
skb->len);
sk_udp->sk_prot->sendmsg(sk_udp, &msg, skb->len);
kfree_skb(skb);
}

View file

@ -189,11 +189,10 @@ static void pppopns_xmit_core(struct work_struct *delivery_work)
while ((skb = skb_dequeue(&delivery_queue))) {
struct sock *sk_raw = skb->sk;
struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
struct msghdr msg = {
.msg_iov = (struct iovec *)&iov,
.msg_iovlen = 1,
.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
};
struct msghdr msg = { 0 };
iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iov, 1,
skb->len);
sk_raw->sk_prot->sendmsg(sk_raw, &msg, skb->len);
kfree_skb(skb);
}

View file

@ -448,12 +448,19 @@ static void acc_hid_close(struct hid_device *hid)
{
}
static int acc_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
__u8 *buf, size_t len, unsigned char rtype, int reqtype)
{
return 0;
}
static struct hid_ll_driver acc_hid_ll_driver = {
.parse = acc_hid_parse,
.start = acc_hid_start,
.stop = acc_hid_stop,
.open = acc_hid_open,
.close = acc_hid_close,
.raw_request = acc_hid_raw_request,
};
static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev,

View file

@ -583,6 +583,11 @@ static void audio_disable(struct usb_function *f)
usb_ep_disable(audio->in_ep);
}
static void audio_free_func(struct usb_function *f)
{
/* no-op */
}
/*-------------------------------------------------------------------------*/
static void audio_build_desc(struct audio_dev *audio)
@ -827,6 +832,7 @@ static struct audio_dev _audio_dev = {
.set_alt = audio_set_alt,
.setup = audio_setup,
.disable = audio_disable,
.free_func = audio_free_func,
},
.lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock),
.idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs),

View file

@ -158,7 +158,7 @@ static struct usb_interface_descriptor ptp_interface_desc = {
.bInterfaceProtocol = 1,
};
static struct usb_endpoint_descriptor mtp_superspeed_in_desc = {
static struct usb_endpoint_descriptor mtp_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@ -166,8 +166,8 @@ static struct usb_endpoint_descriptor mtp_superspeed_in_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor mtp_superspeed_in_comp_desc = {
.bLength = sizeof(mtp_superspeed_in_comp_desc),
static struct usb_ss_ep_comp_descriptor mtp_ss_in_comp_desc = {
.bLength = sizeof(mtp_ss_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
@ -175,7 +175,8 @@ static struct usb_ss_ep_comp_descriptor mtp_superspeed_in_comp_desc = {
/* .bmAttributes = 0, */
};
static struct usb_endpoint_descriptor mtp_superspeed_out_desc = {
static struct usb_endpoint_descriptor mtp_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
@ -183,8 +184,8 @@ static struct usb_endpoint_descriptor mtp_superspeed_out_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor mtp_superspeed_out_comp_desc = {
.bLength = sizeof(mtp_superspeed_out_comp_desc),
static struct usb_ss_ep_comp_descriptor mtp_ss_out_comp_desc = {
.bLength = sizeof(mtp_ss_out_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
@ -231,8 +232,8 @@ static struct usb_endpoint_descriptor mtp_intr_desc = {
.bInterval = 6,
};
static struct usb_ss_ep_comp_descriptor mtp_superspeed_intr_comp_desc = {
.bLength = sizeof(mtp_superspeed_intr_comp_desc),
static struct usb_ss_ep_comp_descriptor mtp_intr_ss_comp_desc = {
.bLength = sizeof(mtp_intr_ss_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 3 values can be tweaked if necessary */
@ -259,12 +260,12 @@ static struct usb_descriptor_header *hs_mtp_descs[] = {
static struct usb_descriptor_header *ss_mtp_descs[] = {
(struct usb_descriptor_header *) &mtp_interface_desc,
(struct usb_descriptor_header *) &mtp_superspeed_in_desc,
(struct usb_descriptor_header *) &mtp_superspeed_in_comp_desc,
(struct usb_descriptor_header *) &mtp_superspeed_out_desc,
(struct usb_descriptor_header *) &mtp_superspeed_out_comp_desc,
(struct usb_descriptor_header *) &mtp_ss_in_desc,
(struct usb_descriptor_header *) &mtp_ss_in_comp_desc,
(struct usb_descriptor_header *) &mtp_ss_out_desc,
(struct usb_descriptor_header *) &mtp_ss_out_comp_desc,
(struct usb_descriptor_header *) &mtp_intr_desc,
(struct usb_descriptor_header *) &mtp_superspeed_intr_comp_desc,
(struct usb_descriptor_header *) &mtp_intr_ss_comp_desc,
NULL,
};
@ -286,12 +287,12 @@ static struct usb_descriptor_header *hs_ptp_descs[] = {
static struct usb_descriptor_header *ss_ptp_descs[] = {
(struct usb_descriptor_header *) &ptp_interface_desc,
(struct usb_descriptor_header *) &mtp_superspeed_in_desc,
(struct usb_descriptor_header *) &mtp_superspeed_in_comp_desc,
(struct usb_descriptor_header *) &mtp_superspeed_out_desc,
(struct usb_descriptor_header *) &mtp_superspeed_out_comp_desc,
(struct usb_descriptor_header *) &mtp_ss_in_desc,
(struct usb_descriptor_header *) &mtp_ss_in_comp_desc,
(struct usb_descriptor_header *) &mtp_ss_out_desc,
(struct usb_descriptor_header *) &mtp_ss_out_comp_desc,
(struct usb_descriptor_header *) &mtp_intr_desc,
(struct usb_descriptor_header *) &mtp_superspeed_intr_comp_desc,
(struct usb_descriptor_header *) &mtp_intr_ss_comp_desc,
NULL,
};
@ -352,7 +353,7 @@ struct ext_mtp_desc mtp_ext_config_desc = {
.dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)),
.bcdVersion = __constant_cpu_to_le16(0x0100),
.wIndex = __constant_cpu_to_le16(4),
.bCount = __constant_cpu_to_le16(1),
.bCount = 1,
},
.function = {
.bFirstInterfaceNumber = 0,
@ -395,6 +396,8 @@ struct mtp_instance {
struct usb_function_instance func_inst;
const char *name;
struct mtp_dev *dev;
char mtp_ext_compat_id[16];
struct usb_os_desc mtp_os_desc;
};
/* temporary variable used between mtp_open() and mtp_gadget_bind() */
@ -1389,6 +1392,7 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f)
struct mtp_dev *dev = func_to_mtp(f);
int id;
int ret;
struct mtp_instance *fi_mtp;
dev->cdev = cdev;
DBG(cdev, "mtp_function_bind dev: %p\n", dev);
@ -1406,6 +1410,18 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f)
mtp_string_defs[INTERFACE_STRING_INDEX].id = ret;
mtp_interface_desc.iInterface = ret;
}
fi_mtp = container_of(f->fi, struct mtp_instance, func_inst);
if (cdev->use_os_string) {
f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
GFP_KERNEL);
if (!f->os_desc_table)
return -ENOMEM;
f->os_desc_n = 1;
f->os_desc_table[0].os_desc = &fi_mtp->mtp_os_desc;
}
/* allocate endpoints */
ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc,
&mtp_fullspeed_out_desc, &mtp_intr_desc);
@ -1419,18 +1435,32 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f)
mtp_highspeed_out_desc.bEndpointAddress =
mtp_fullspeed_out_desc.bEndpointAddress;
}
/* support super speed hardware */
if (gadget_is_superspeed(c->cdev->gadget)) {
unsigned max_burst;
/* Calculate bMaxBurst, we know packet size is 1024 */
max_burst = min_t(unsigned, MTP_BULK_BUFFER_SIZE / 1024, 15);
mtp_ss_in_desc.bEndpointAddress =
mtp_fullspeed_in_desc.bEndpointAddress;
mtp_ss_in_comp_desc.bMaxBurst = max_burst;
mtp_ss_out_desc.bEndpointAddress =
mtp_fullspeed_out_desc.bEndpointAddress;
mtp_ss_out_comp_desc.bMaxBurst = max_burst;
}
/* support super speed hardware */
if (gadget_is_superspeed(c->cdev->gadget)) {
mtp_superspeed_in_desc.bEndpointAddress =
mtp_ss_in_desc.bEndpointAddress =
mtp_fullspeed_in_desc.bEndpointAddress;
mtp_superspeed_out_desc.bEndpointAddress =
mtp_ss_out_desc.bEndpointAddress =
mtp_fullspeed_out_desc.bEndpointAddress;
}
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
f->name, dev->ep_in->name, dev->ep_out->name);
gadget_is_superspeed(c->cdev->gadget) ? "super" :
(gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full"),
f->name, dev->ep_in->name, dev->ep_out->name);
return 0;
}
@ -1449,6 +1479,8 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
mtp_request_free(req, dev->ep_intr);
dev->state = STATE_OFFLINE;
dev->is_ptp = false;
kfree(f->os_desc_table);
f->os_desc_n = 0;
}
static int mtp_function_set_alt(struct usb_function *f,
@ -1748,6 +1780,7 @@ static void mtp_free_inst(struct usb_function_instance *fi)
fi_mtp = to_fi_mtp(fi);
kfree(fi_mtp->name);
mtp_cleanup();
kfree(fi_mtp->mtp_os_desc.group.default_groups);
kfree(fi_mtp);
}
@ -1755,6 +1788,8 @@ struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config)
{
struct mtp_instance *fi_mtp;
int ret = 0;
struct usb_os_desc *descs[1];
char *names[1];
fi_mtp = kzalloc(sizeof(*fi_mtp), GFP_KERNEL);
if (!fi_mtp)
@ -1762,6 +1797,13 @@ struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config)
fi_mtp->func_inst.set_inst_name = mtp_set_inst_name;
fi_mtp->func_inst.free_func_inst = mtp_free_inst;
fi_mtp->mtp_os_desc.ext_compat_id = fi_mtp->mtp_ext_compat_id;
INIT_LIST_HEAD(&fi_mtp->mtp_os_desc.ext_prop);
descs[0] = &fi_mtp->mtp_os_desc;
names[0] = "MTP";
usb_os_desc_prepare_interf_dir(&fi_mtp->func_inst.group, 1,
descs, names, THIS_MODULE);
if (mtp_config) {
ret = mtp_setup_configfs(fi_mtp);
if (ret) {

View file

@ -11,4 +11,4 @@ menuconfig ADF_FBDEV
menuconfig ADF_MEMBLOCK
depends on ADF
depends on HAVE_MEMBLOCK
tristate "Helper for using memblocks as buffers in ADF drivers"
bool "Helper for using memblocks as buffers in ADF drivers"

View file

@ -2,13 +2,15 @@ ccflags-y := -Idrivers/staging/android
CFLAGS_adf.o := -I$(src)
obj-$(CONFIG_ADF) += adf.o \
obj-$(CONFIG_ADF) += adf_core.o
adf_core-y := adf.o \
adf_client.o \
adf_fops.o \
adf_format.o \
adf_sysfs.o
obj-$(CONFIG_COMPAT) += adf_fops32.o
adf_core-$(CONFIG_COMPAT) += adf_fops32.o
obj-$(CONFIG_ADF_FBDEV) += adf_fbdev.o

View file

@ -199,6 +199,7 @@ if MISC_FILESYSTEMS
source "fs/adfs/Kconfig"
source "fs/affs/Kconfig"
source "fs/ecryptfs/Kconfig"
source "fs/sdcardfs/Kconfig"
source "fs/hfs/Kconfig"
source "fs/hfsplus/Kconfig"
source "fs/befs/Kconfig"

View file

@ -3,7 +3,7 @@
#
# 14 Sep 2000, Christoph Hellwig <hch@infradead.org>
# Rewritten to use lists instead of if-statements.
#
#
obj-y := open.o read_write.o file_table.o super.o \
char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
@ -59,7 +59,7 @@ obj-y += devpts/
obj-$(CONFIG_PROFILING) += dcookies.o
obj-$(CONFIG_DLM) += dlm/
# Do not add any filesystems before this line
obj-$(CONFIG_FSCACHE) += fscache/
obj-$(CONFIG_REISERFS_FS) += reiserfs/
@ -81,6 +81,7 @@ obj-$(CONFIG_ISO9660_FS) += isofs/
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+
obj-$(CONFIG_HFS_FS) += hfs/
obj-$(CONFIG_ECRYPT_FS) += ecryptfs/
obj-$(CONFIG_SDCARD_FS) += sdcardfs/
obj-$(CONFIG_VXFS_FS) += freevxfs/
obj-$(CONFIG_NFS_FS) += nfs/
obj-$(CONFIG_EXPORTFS) += exportfs/

View file

@ -651,7 +651,7 @@ static unsigned long randomize_stack_top(unsigned long stack_top)
if ((current->flags & PF_RANDOMIZE) &&
!(current->personality & ADDR_NO_RANDOMIZE)) {
random_variable = (unsigned long) get_random_int();
random_variable = get_random_long();
random_variable &= STACK_RND_MASK;
random_variable <<= PAGE_SHIFT;
}

View file

@ -3020,6 +3020,7 @@ char *d_absolute_path(const struct path *path,
return ERR_PTR(error);
return res;
}
EXPORT_SYMBOL(d_absolute_path);
/*
* same as __d_path but appends "(deleted)" for unlinked files.

View file

@ -702,6 +702,8 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
struct fsnotify_group *group;
struct inode *inode;
struct path path;
struct path alteredpath;
struct path *canonical_path = &path;
struct fd f;
int ret;
unsigned flags = 0;
@ -741,13 +743,22 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
if (ret)
goto fput_and_out;
/* support stacked filesystems */
if(path.dentry && path.dentry->d_op) {
if (path.dentry->d_op->d_canonical_path) {
path.dentry->d_op->d_canonical_path(path.dentry, &alteredpath);
canonical_path = &alteredpath;
path_put(&path);
}
}
/* inode held in place by reference to path; group by fget on fd */
inode = path.dentry->d_inode;
inode = canonical_path->dentry->d_inode;
group = f.file->private_data;
/* create/update an inode mark */
ret = inotify_update_watch(group, inode, mask);
path_put(&path);
path_put(canonical_path);
fput_and_out:
fdput(f);
return ret;

View file

@ -554,7 +554,7 @@ static int ramoops_parse_dt(struct platform_device *pdev,
static int ramoops_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ramoops_platform_data *pdata = platform_get_drvdata(pdev);
struct ramoops_platform_data *pdata = pdev->dev.platform_data;
struct ramoops_context *cxt = &oops_cxt;
size_t dump_mem_sz;
phys_addr_t paddr;
@ -666,7 +666,6 @@ static int ramoops_probe(struct platform_device *pdev)
cxt->size, (unsigned long long)cxt->phys_addr,
cxt->ecc_info.ecc_size, cxt->ecc_info.block_size);
platform_set_drvdata(pdev, pdata);
return 0;
fail_buf:

13
fs/sdcardfs/Kconfig Normal file
View file

@ -0,0 +1,13 @@
config SDCARD_FS
tristate "sdcard file system"
depends on CONFIGFS_FS
default n
help
Sdcardfs is based on Wrapfs file system.
config SDCARD_FS_FADV_NOACTIVE
bool "sdcardfs fadvise noactive support"
depends on FADV_NOACTIVE
default y
help
Sdcardfs supports fadvise noactive mode.

7
fs/sdcardfs/Makefile Normal file
View file

@ -0,0 +1,7 @@
SDCARDFS_VERSION="0.1"
EXTRA_CFLAGS += -DSDCARDFS_VERSION=\"$(SDCARDFS_VERSION)\"
obj-$(CONFIG_SDCARD_FS) += sdcardfs.o
sdcardfs-y := dentry.o file.o inode.o main.o super.o lookup.o mmap.o packagelist.o derived_perm.o

182
fs/sdcardfs/dentry.c Normal file
View file

@ -0,0 +1,182 @@
/*
* fs/sdcardfs/dentry.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
#include "linux/ctype.h"
/*
* returns: -ERRNO if error (returned to user)
* 0: tell VFS to invalidate dentry
* 1: dentry is valid
*/
static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
{
int err = 1;
struct path parent_lower_path, lower_path;
struct dentry *parent_dentry = NULL;
struct dentry *parent_lower_dentry = NULL;
struct dentry *lower_cur_parent_dentry = NULL;
struct dentry *lower_dentry = NULL;
if (flags & LOOKUP_RCU)
return -ECHILD;
spin_lock(&dentry->d_lock);
if (IS_ROOT(dentry)) {
spin_unlock(&dentry->d_lock);
return 1;
}
spin_unlock(&dentry->d_lock);
/* check uninitialized obb_dentry and
* whether the base obbpath has been changed or not */
if (is_obbpath_invalid(dentry)) {
d_drop(dentry);
return 0;
}
parent_dentry = dget_parent(dentry);
sdcardfs_get_lower_path(parent_dentry, &parent_lower_path);
sdcardfs_get_real_lower(dentry, &lower_path);
parent_lower_dentry = parent_lower_path.dentry;
lower_dentry = lower_path.dentry;
lower_cur_parent_dentry = dget_parent(lower_dentry);
spin_lock(&lower_dentry->d_lock);
if (d_unhashed(lower_dentry)) {
spin_unlock(&lower_dentry->d_lock);
d_drop(dentry);
err = 0;
goto out;
}
spin_unlock(&lower_dentry->d_lock);
if (parent_lower_dentry != lower_cur_parent_dentry) {
d_drop(dentry);
err = 0;
goto out;
}
if (dentry < lower_dentry) {
spin_lock(&dentry->d_lock);
spin_lock(&lower_dentry->d_lock);
} else {
spin_lock(&lower_dentry->d_lock);
spin_lock(&dentry->d_lock);
}
if (dentry->d_name.len != lower_dentry->d_name.len) {
__d_drop(dentry);
err = 0;
} else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name,
dentry->d_name.len) != 0) {
__d_drop(dentry);
err = 0;
}
if (dentry < lower_dentry) {
spin_unlock(&lower_dentry->d_lock);
spin_unlock(&dentry->d_lock);
} else {
spin_unlock(&dentry->d_lock);
spin_unlock(&lower_dentry->d_lock);
}
out:
dput(parent_dentry);
dput(lower_cur_parent_dentry);
sdcardfs_put_lower_path(parent_dentry, &parent_lower_path);
sdcardfs_put_real_lower(dentry, &lower_path);
return err;
}
static void sdcardfs_d_release(struct dentry *dentry)
{
/* release and reset the lower paths */
if(has_graft_path(dentry)) {
sdcardfs_put_reset_orig_path(dentry);
}
sdcardfs_put_reset_lower_path(dentry);
free_dentry_private_data(dentry);
return;
}
static int sdcardfs_hash_ci(const struct dentry *dentry,
struct qstr *qstr)
{
/*
* This function is copy of vfat_hashi.
* FIXME Should we support national language?
* Refer to vfat_hashi()
* struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io;
*/
const unsigned char *name;
unsigned int len;
unsigned long hash;
name = qstr->name;
//len = vfat_striptail_len(qstr);
len = qstr->len;
hash = init_name_hash();
while (len--)
//hash = partial_name_hash(nls_tolower(t, *name++), hash);
hash = partial_name_hash(tolower(*name++), hash);
qstr->hash = end_name_hash(hash);
return 0;
}
/*
* Case insensitive compare of two vfat names.
*/
static int sdcardfs_cmp_ci(const struct dentry *parent,
const struct dentry *dentry,
unsigned int len, const char *str, const struct qstr *name)
{
/* This function is copy of vfat_cmpi */
// FIXME Should we support national language?
//struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
//unsigned int alen, blen;
/* A filename cannot end in '.' or we treat it like it has none */
/*
alen = vfat_striptail_len(name);
blen = __vfat_striptail_len(len, str);
if (alen == blen) {
if (nls_strnicmp(t, name->name, str, alen) == 0)
return 0;
}
*/
if (name->len == len) {
if (strncasecmp(name->name, str, len) == 0)
return 0;
}
return 1;
}
const struct dentry_operations sdcardfs_ci_dops = {
.d_revalidate = sdcardfs_d_revalidate,
.d_release = sdcardfs_d_release,
.d_hash = sdcardfs_hash_ci,
.d_compare = sdcardfs_cmp_ci,
.d_canonical_path = sdcardfs_get_real_lower,
};

265
fs/sdcardfs/derived_perm.c Normal file
View file

@ -0,0 +1,265 @@
/*
* fs/sdcardfs/derived_perm.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
/* copy derived state from parent inode */
static void inherit_derived_state(struct inode *parent, struct inode *child)
{
struct sdcardfs_inode_info *pi = SDCARDFS_I(parent);
struct sdcardfs_inode_info *ci = SDCARDFS_I(child);
ci->perm = PERM_INHERIT;
ci->userid = pi->userid;
ci->d_uid = pi->d_uid;
ci->under_android = pi->under_android;
}
/* helper function for derived state */
void setup_derived_state(struct inode *inode, perm_t perm,
userid_t userid, uid_t uid, bool under_android)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
info->perm = perm;
info->userid = userid;
info->d_uid = uid;
info->under_android = under_android;
}
/* 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)
{
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);
appid_t appid;
/* 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).
*/
inherit_derived_state(parent->d_inode, dentry->d_inode);
/* Derive custom permissions based on parent and current node */
switch (parent_info->perm) {
case PERM_INHERIT:
/* 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);
break;
case PERM_ROOT:
/* Assume masked off by default. */
if (!strcasecmp(newdentry->d_name.name, "Android")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID;
info->under_android = true;
}
break;
case PERM_ANDROID:
if (!strcasecmp(newdentry->d_name.name, "data")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_DATA;
} else if (!strcasecmp(newdentry->d_name.name, "obb")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_OBB;
/* Single OBB directory is always shared */
} else if (!strcasecmp(newdentry->d_name.name, "media")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_MEDIA;
}
break;
case PERM_ANDROID_DATA:
case PERM_ANDROID_OBB:
case PERM_ANDROID_MEDIA:
appid = get_appid(sbi->pkgl_id, newdentry->d_name.name);
if (appid != 0) {
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
}
break;
}
}
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
{
get_derived_permission_new(parent, dentry, dentry);
}
void get_derive_permissions_recursive(struct dentry *parent) {
struct dentry *dentry;
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
if (dentry && 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);
}
}
}
/* main function for updating derived permission */
inline void update_derived_permission_lock(struct dentry *dentry)
{
struct dentry *parent;
if(!dentry || !dentry->d_inode) {
printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
return;
}
/* FIXME:
* 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);
} else {
parent = dget_parent(dentry);
if(parent) {
get_derived_permission(parent, dentry);
dput(parent);
}
}
fix_derived_permission(dentry->d_inode);
mutex_unlock(&dentry->d_inode->i_mutex);
}
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_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
if(parent_info->perm == PERM_ANDROID &&
!strcasecmp(dentry->d_name.name, "obb")) {
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
if(!(sbi->options.multiuser == false
&& parent_info->userid == 0)) {
ret = 1;
}
}
dput(parent);
return ret;
}
int is_obbpath_invalid(struct dentry *dent)
{
int ret = 0;
struct sdcardfs_dentry_info *di = SDCARDFS_D(dent);
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb);
char *path_buf, *obbpath_s;
/* check the base obbpath has been changed.
* this routine can check an uninitialized obb dentry as well.
* regarding the uninitialized obb, refer to the sdcardfs_mkdir() */
spin_lock(&di->lock);
if(di->orig_path.dentry) {
if(!di->lower_path.dentry) {
ret = 1;
} else {
path_get(&di->lower_path);
//lower_parent = lock_parent(lower_path->dentry);
path_buf = kmalloc(PATH_MAX, GFP_ATOMIC);
if(!path_buf) {
ret = 1;
printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__);
} 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)) {
ret = 1;
}
kfree(path_buf);
}
//unlock_dir(lower_parent);
path_put(&di->lower_path);
}
}
spin_unlock(&di->lock);
return ret;
}
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_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
spin_lock(&SDCARDFS_D(dentry)->lock);
if (sbi->options.multiuser) {
if(parent_info->perm == PERM_PRE_ROOT &&
!strcasecmp(dentry->d_name.name, "obb")) {
ret = 1;
}
} else if (parent_info->perm == PERM_ANDROID &&
!strcasecmp(dentry->d_name.name, "obb")) {
ret = 1;
}
spin_unlock(&SDCARDFS_D(dentry)->lock);
return ret;
}
/* The lower_path will be stored to the dentry's orig_path
* and the base obbpath will be copyed to the lower_path variable.
* if an error returned, there's no change in the lower_path
* returns: -ERRNO if error (0: no error) */
int setup_obb_dentry(struct dentry *dentry, struct path *lower_path)
{
int err = 0;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
struct path obbpath;
/* A local obb dentry must have its own orig_path to support rmdir
* and mkdir of itself. Usually, we expect that the sbi->obbpath
* is avaiable on this stage. */
sdcardfs_set_orig_path(dentry, lower_path);
err = kern_path(sbi->obbpath_s,
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath);
if(!err) {
/* the obbpath base has been found */
printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n");
pathcpy(lower_path, &obbpath);
} else {
/* if the sbi->obbpath is not available, we can optionally
* setup the lower_path with its orig_path.
* but, the current implementation just returns an error
* because the sdcard daemon also regards this case as
* a lookup fail. */
printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n");
}
return err;
}

356
fs/sdcardfs/file.c Normal file
View file

@ -0,0 +1,356 @@
/*
* fs/sdcardfs/file.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
#include <linux/backing-dev.h>
#endif
static ssize_t sdcardfs_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int err;
struct file *lower_file;
struct dentry *dentry = file->f_path.dentry;
#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
struct backing_dev_info *bdi;
#endif
lower_file = sdcardfs_lower_file(file);
#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
if (file->f_mode & FMODE_NOACTIVE) {
if (!(lower_file->f_mode & FMODE_NOACTIVE)) {
bdi = lower_file->f_mapping->backing_dev_info;
lower_file->f_ra.ra_pages = bdi->ra_pages * 2;
spin_lock(&lower_file->f_lock);
lower_file->f_mode |= FMODE_NOACTIVE;
spin_unlock(&lower_file->f_lock);
}
}
#endif
err = vfs_read(lower_file, buf, count, ppos);
/* update our inode atime upon a successful lower read */
if (err >= 0)
fsstack_copy_attr_atime(d_inode(dentry),
file_inode(lower_file));
return err;
}
static ssize_t sdcardfs_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int err;
struct file *lower_file;
struct dentry *dentry = file->f_path.dentry;
/* check disk space */
if (!check_min_free_space(dentry, count, 0)) {
printk(KERN_INFO "No minimum free space.\n");
return -ENOSPC;
}
lower_file = sdcardfs_lower_file(file);
err = vfs_write(lower_file, buf, count, ppos);
/* update our inode times+sizes upon a successful lower write */
if (err >= 0) {
fsstack_copy_inode_size(d_inode(dentry),
file_inode(lower_file));
fsstack_copy_attr_times(d_inode(dentry),
file_inode(lower_file));
}
return err;
}
static int sdcardfs_readdir(struct file *file, struct dir_context *ctx)
{
int err;
struct file *lower_file = NULL;
struct dentry *dentry = file->f_path.dentry;
lower_file = sdcardfs_lower_file(file);
lower_file->f_pos = file->f_pos;
err = iterate_dir(lower_file, ctx);
file->f_pos = lower_file->f_pos;
if (err >= 0) /* copy the atime */
fsstack_copy_attr_atime(d_inode(dentry),
file_inode(lower_file));
return err;
}
static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long err = -ENOTTY;
struct file *lower_file;
lower_file = sdcardfs_lower_file(file);
/* XXX: use vfs_ioctl if/when VFS exports it */
if (!lower_file || !lower_file->f_op)
goto out;
if (lower_file->f_op->unlocked_ioctl)
err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
out:
return err;
}
#ifdef CONFIG_COMPAT
static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long err = -ENOTTY;
struct file *lower_file;
lower_file = sdcardfs_lower_file(file);
/* XXX: use vfs_ioctl if/when VFS exports it */
if (!lower_file || !lower_file->f_op)
goto out;
if (lower_file->f_op->compat_ioctl)
err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
out:
return err;
}
#endif
static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma)
{
int err = 0;
bool willwrite;
struct file *lower_file;
const struct vm_operations_struct *saved_vm_ops = NULL;
/* this might be deferred to mmap's writepage */
willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
/*
* File systems which do not implement ->writepage may use
* generic_file_readonly_mmap as their ->mmap op. If you call
* generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL.
* But we cannot call the lower ->mmap op, so we can't tell that
* writeable mappings won't work. Therefore, our only choice is to
* check if the lower file system supports the ->writepage, and if
* not, return EINVAL (the same error that
* generic_file_readonly_mmap returns in that case).
*/
lower_file = sdcardfs_lower_file(file);
if (willwrite && !lower_file->f_mapping->a_ops->writepage) {
err = -EINVAL;
printk(KERN_ERR "sdcardfs: lower file system does not "
"support writeable mmap\n");
goto out;
}
/*
* find and save lower vm_ops.
*
* XXX: the VFS should have a cleaner way of finding the lower vm_ops
*/
if (!SDCARDFS_F(file)->lower_vm_ops) {
err = lower_file->f_op->mmap(lower_file, vma);
if (err) {
printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err);
goto out;
}
saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */
err = do_munmap(current->mm, vma->vm_start,
vma->vm_end - vma->vm_start);
if (err) {
printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err);
goto out;
}
}
/*
* Next 3 lines are all I need from generic_file_mmap. I definitely
* don't want its test for ->readpage which returns -ENOEXEC.
*/
file_accessed(file);
vma->vm_ops = &sdcardfs_vm_ops;
file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */
if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */
SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops;
out:
return err;
}
static int sdcardfs_open(struct inode *inode, struct file *file)
{
int err = 0;
struct file *lower_file = NULL;
struct path lower_path;
struct dentry *dentry = file->f_path.dentry;
struct dentry *parent = dget_parent(dentry);
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
/* don't open unhashed/deleted files */
if (d_unhashed(dentry)) {
err = -ENOENT;
goto out_err;
}
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.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);
err = -EACCES;
goto out_err;
}
/* save current_cred and override it */
OVERRIDE_CRED(sbi, saved_cred);
file->private_data =
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
if (!SDCARDFS_F(file)) {
err = -ENOMEM;
goto out_revert_cred;
}
/* open lower object and link sdcardfs's file struct to lower's */
sdcardfs_get_lower_path(file->f_path.dentry, &lower_path);
lower_file = dentry_open(&lower_path, file->f_flags, current_cred());
path_put(&lower_path);
if (IS_ERR(lower_file)) {
err = PTR_ERR(lower_file);
lower_file = sdcardfs_lower_file(file);
if (lower_file) {
sdcardfs_set_lower_file(file, NULL);
fput(lower_file); /* fput calls dput for lower_dentry */
}
} else {
sdcardfs_set_lower_file(file, lower_file);
}
if (err)
kfree(SDCARDFS_F(file));
else {
sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode));
}
out_revert_cred:
REVERT_CRED(saved_cred);
out_err:
dput(parent);
return err;
}
static int sdcardfs_flush(struct file *file, fl_owner_t id)
{
int err = 0;
struct file *lower_file = NULL;
lower_file = sdcardfs_lower_file(file);
if (lower_file && lower_file->f_op && lower_file->f_op->flush) {
filemap_write_and_wait(file->f_mapping);
err = lower_file->f_op->flush(lower_file, id);
}
return err;
}
/* release all lower object references & free the file info structure */
static int sdcardfs_file_release(struct inode *inode, struct file *file)
{
struct file *lower_file;
lower_file = sdcardfs_lower_file(file);
if (lower_file) {
sdcardfs_set_lower_file(file, NULL);
fput(lower_file);
}
kfree(SDCARDFS_F(file));
return 0;
}
static int sdcardfs_fsync(struct file *file, loff_t start, loff_t end,
int datasync)
{
int err;
struct file *lower_file;
struct path lower_path;
struct dentry *dentry = file->f_path.dentry;
err = __generic_file_fsync(file, start, end, datasync);
if (err)
goto out;
lower_file = sdcardfs_lower_file(file);
sdcardfs_get_lower_path(dentry, &lower_path);
err = vfs_fsync_range(lower_file, start, end, datasync);
sdcardfs_put_lower_path(dentry, &lower_path);
out:
return err;
}
static int sdcardfs_fasync(int fd, struct file *file, int flag)
{
int err = 0;
struct file *lower_file = NULL;
lower_file = sdcardfs_lower_file(file);
if (lower_file->f_op && lower_file->f_op->fasync)
err = lower_file->f_op->fasync(fd, lower_file, flag);
return err;
}
const struct file_operations sdcardfs_main_fops = {
.llseek = generic_file_llseek,
.read = sdcardfs_read,
.write = sdcardfs_write,
.unlocked_ioctl = sdcardfs_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = sdcardfs_compat_ioctl,
#endif
.mmap = sdcardfs_mmap,
.open = sdcardfs_open,
.flush = sdcardfs_flush,
.release = sdcardfs_file_release,
.fsync = sdcardfs_fsync,
.fasync = sdcardfs_fasync,
};
/* trimmed directory options */
const struct file_operations sdcardfs_dir_fops = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate = sdcardfs_readdir,
.unlocked_ioctl = sdcardfs_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = sdcardfs_compat_ioctl,
#endif
.open = sdcardfs_open,
.release = sdcardfs_file_release,
.flush = sdcardfs_flush,
.fsync = sdcardfs_fsync,
.fasync = sdcardfs_fasync,
};

802
fs/sdcardfs/inode.c Normal file
View file

@ -0,0 +1,802 @@
/*
* fs/sdcardfs/inode.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
{
struct cred * cred;
const struct cred * old_cred;
cred = prepare_creds();
if (!cred)
return NULL;
cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid);
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
old_cred = override_creds(cred);
return old_cred;
}
/* Do not directly use this function, use REVERT_CRED() instead. */
void revert_fsids(const struct cred * old_cred)
{
const struct cred * cur_cred;
cur_cred = current->cred;
revert_creds(old_cred);
put_cred(cur_cred);
}
static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
umode_t mode, bool want_excl)
{
int err;
struct dentry *lower_dentry;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
const struct cred *saved_cred = NULL;
if(!check_caller_access_to_name(dir, dentry->d_name.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);
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
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);
if (err)
goto out;
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid);
if (err)
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
out:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED(saved_cred);
out_eacces:
return err;
}
#if 0
static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry)
{
struct dentry *lower_old_dentry;
struct dentry *lower_new_dentry;
struct dentry *lower_dir_dentry;
u64 file_size_save;
int err;
struct path lower_old_path, lower_new_path;
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
file_size_save = i_size_read(d_inode(old_dentry));
sdcardfs_get_lower_path(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_dir_dentry = lock_parent(lower_new_dentry);
err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry),
lower_new_dentry, NULL);
if (err || !d_inode(lower_new_dentry))
goto out;
err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path);
if (err)
goto out;
fsstack_copy_attr_times(dir, d_inode(lower_new_dentry));
fsstack_copy_inode_size(dir, d_inode(lower_new_dentry));
set_nlink(d_inode(old_dentry),
sdcardfs_lower_inode(d_inode(old_dentry))->i_nlink);
i_size_write(d_inode(new_dentry), file_size_save);
out:
unlock_dir(lower_dir_dentry);
sdcardfs_put_lower_path(old_dentry, &lower_old_path);
sdcardfs_put_lower_path(new_dentry, &lower_new_path);
REVERT_CRED();
return err;
}
#endif
static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
{
int err;
struct dentry *lower_dentry;
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)) {
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);
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
dget(lower_dentry);
lower_dir_dentry = lock_parent(lower_dentry);
err = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
/*
* Note: unlinking on top of NFS can cause silly-renamed files.
* Trying to delete such files results in EBUSY from NFS
* below. Silly-renamed files will get deleted by NFS later on, so
* we just need to detect them here and treat such EBUSY errors as
* if the upper file was successfully deleted.
*/
if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED)
err = 0;
if (err)
goto out;
fsstack_copy_attr_times(dir, lower_dir_inode);
fsstack_copy_inode_size(dir, lower_dir_inode);
set_nlink(d_inode(dentry),
sdcardfs_lower_inode(d_inode(dentry))->i_nlink);
d_inode(dentry)->i_ctime = dir->i_ctime;
d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */
out:
unlock_dir(lower_dir_dentry);
dput(lower_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED(saved_cred);
out_eacces:
return err;
}
#if 0
static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry,
const char *symname)
{
int err;
struct dentry *lower_dentry;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
lower_parent_dentry = lock_parent(lower_dentry);
err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname);
if (err)
goto out;
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
if (err)
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
out:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED();
return err;
}
#endif
static int touch(char *abs_path, mode_t mode) {
struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode);
if (IS_ERR(filp)) {
if (PTR_ERR(filp) == -EEXIST) {
return 0;
}
else {
printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n",
abs_path, PTR_ERR(filp));
return PTR_ERR(filp);
}
}
filp_close(filp, current->files);
return 0;
}
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 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;
if(!check_caller_access_to_name(dir, dentry->d_name.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);
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
/* check disk space */
if (!check_min_free_space(dentry, 0, 1)) {
printk(KERN_INFO "sdcardfs: No minimum free space.\n");
err = -ENOSPC;
goto out_revert;
}
/* the lower_dentry is negative here */
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
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)
goto out;
/* if it is a local obb dentry, setup it with the base obbpath */
if(need_graft_path(dentry)) {
err = setup_obb_dentry(dentry, &lower_path);
if(err) {
/* if the sbi->obbpath is not available, the lower_path won't be
* changed by setup_obb_dentry() but the lower path is saved to
* its orig_path. this dentry will be revalidated later.
* but now, the lower_path should be NULL */
sdcardfs_put_reset_lower_path(dentry);
/* the newly created lower path which saved to its orig_path or
* the lower_path is the base obbpath.
* therefore, an additional path_get is required */
path_get(&lower_path);
} else
make_nomedia_in_obb = 1;
}
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
if (err)
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"))
&& (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);
if (touch_err) {
printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n",
nomedia_fullpath, touch_err);
kfree(nomedia_fullpath);
goto out;
}
kfree(nomedia_fullpath);
}
out:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
out_revert:
REVERT_CRED(saved_cred);
out_eacces:
return err;
}
static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct dentry *lower_dentry;
struct dentry *lower_dir_dentry;
int err;
struct path lower_path;
const struct cred *saved_cred = NULL;
if(!check_caller_access_to_name(dir, dentry->d_name.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);
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
/* 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_dir_dentry = lock_parent(lower_dentry);
err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
if (err)
goto out;
d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */
if (d_inode(dentry))
clear_nlink(d_inode(dentry));
fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry));
fsstack_copy_inode_size(dir, d_inode(lower_dir_dentry));
set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink);
out:
unlock_dir(lower_dir_dentry);
sdcardfs_put_real_lower(dentry, &lower_path);
REVERT_CRED(saved_cred);
out_eacces:
return err;
}
#if 0
static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
dev_t dev)
{
int err;
struct dentry *lower_dentry;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
lower_parent_dentry = lock_parent(lower_dentry);
err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev);
if (err)
goto out;
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
if (err)
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
out:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED();
return err;
}
#endif
/*
* The locking rules in sdcardfs_rename are complex. We could use a simpler
* superblock-level name-space lock for renames and copy-ups.
*/
static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
int err = 0;
struct dentry *lower_old_dentry = NULL;
struct dentry *lower_new_dentry = NULL;
struct dentry *lower_old_dir_dentry = NULL;
struct dentry *lower_new_dir_dentry = 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)) {
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);
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred);
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_old_dir_dentry = dget_parent(lower_old_dentry);
lower_new_dir_dentry = dget_parent(lower_new_dentry);
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
/* source should not be ancestor of target */
if (trap == lower_old_dentry) {
err = -EINVAL;
goto out;
}
/* target should not be ancestor of source */
if (trap == lower_new_dentry) {
err = -ENOTEMPTY;
goto out;
}
err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry,
d_inode(lower_new_dir_dentry), lower_new_dentry,
NULL, 0);
if (err)
goto out;
/* Copy attrs from lower dir, but i_uid/i_gid */
sdcardfs_copy_and_fix_attrs(new_dir, d_inode(lower_new_dir_dentry));
fsstack_copy_inode_size(new_dir, d_inode(lower_new_dir_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);
out:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dput(lower_old_dir_dentry);
dput(lower_new_dir_dentry);
sdcardfs_put_real_lower(old_dentry, &lower_old_path);
sdcardfs_put_lower_path(new_dentry, &lower_new_path);
REVERT_CRED(saved_cred);
out_eacces:
return err;
}
#if 0
static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
{
int err;
struct dentry *lower_dentry;
struct path lower_path;
/* XXX readlink does not requires overriding credential */
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
if (!d_inode(lower_dentry)->i_op ||
!d_inode(lower_dentry)->i_op->readlink) {
err = -EINVAL;
goto out;
}
err = d_inode(lower_dentry)->i_op->readlink(lower_dentry,
buf, bufsiz);
if (err < 0)
goto out;
fsstack_copy_attr_atime(d_inode(dentry), d_inode(lower_dentry));
out:
sdcardfs_put_lower_path(dentry, &lower_path);
return err;
}
#endif
#if 0
static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie)
{
char *buf;
int len = PAGE_SIZE, err;
mm_segment_t old_fs;
/* This is freed by the put_link method assuming a successful call. */
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
buf = ERR_PTR(-ENOMEM);
return buf;
}
/* read the symlink, and then we will follow it */
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sdcardfs_readlink(dentry, buf, len);
set_fs(old_fs);
if (err < 0) {
kfree(buf);
buf = ERR_PTR(err);
} else {
buf[err] = '\0';
}
return *cookie = buf;
}
#endif
static int sdcardfs_permission(struct inode *inode, int mask)
{
int err;
/*
* Permission check on sdcardfs inode.
* Calling process should have AID_SDCARD_RW permission
*/
err = generic_permission(inode, mask);
/* XXX
* Original sdcardfs code calls inode_permission(lower_inode,.. )
* for checking inode permission. But doing such things here seems
* duplicated work, because the functions called after this func,
* such as vfs_create, vfs_unlink, vfs_rename, and etc,
* does exactly same thing, i.e., they calls inode_permission().
* So we just let they do the things.
* If there are any security hole, just uncomment following if block.
*/
#if 0
if (!err) {
/*
* Permission check on lower_inode(=EXT4).
* we check it with AID_MEDIA_RW permission
*/
struct inode *lower_inode;
OVERRIDE_CRED(SDCARDFS_SB(inode->sb));
lower_inode = sdcardfs_lower_inode(inode);
err = inode_permission(lower_inode, mask);
REVERT_CRED();
}
#endif
return err;
}
static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
{
int err;
struct dentry *lower_dentry;
struct inode *inode;
struct inode *lower_inode;
struct path lower_path;
struct iattr lower_ia;
struct dentry *parent;
inode = d_inode(dentry);
/*
* 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);
/* 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)) {
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);
err = -EACCES;
}
dput(parent);
}
if (err)
goto out_err;
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
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);
lower_ia.ia_valid &= ~(ATTR_UID | ATTR_GID | ATTR_MODE);
/*
* If shrinking, first truncate upper level to cancel writing dirty
* pages beyond the new eof; and also if its' maxbytes is more
* limiting (fail with -EFBIG before making any change to the lower
* level). There is no need to vmtruncate the upper level
* afterwards in the other cases: we fsstack_copy_inode_size from
* the lower level.
*/
if (current->mm)
down_write(&current->mm->mmap_sem);
if (ia->ia_valid & ATTR_SIZE) {
err = inode_newsize_ok(inode, ia->ia_size);
if (err) {
if (current->mm)
up_write(&current->mm->mmap_sem);
goto out;
}
truncate_setsize(inode, ia->ia_size);
}
/*
* mode change is for clearing setuid/setgid bits. Allow lower fs
* to interpret this in its own way.
*/
if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
lower_ia.ia_valid &= ~ATTR_MODE;
/* notify the (possibly copied-up) lower inode */
/*
* Note: we use d_inode(lower_dentry), because lower_inode may be
* unlinked (no inode->i_sb and i_ino==0. This happens if someone
* 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 */
NULL);
mutex_unlock(&d_inode(lower_dentry)->i_mutex);
if (current->mm)
up_write(&current->mm->mmap_sem);
if (err)
goto out;
/* get attributes from the lower inode and update derived permissions */
sdcardfs_copy_and_fix_attrs(inode, lower_inode);
/*
* Not running fsstack_copy_inode_size(inode, lower_inode), because
* VFS should update our inode size, and notify_change on
* lower_inode should update its size.
*/
out:
sdcardfs_put_lower_path(dentry, &lower_path);
out_err:
return err;
}
static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
struct dentry *lower_dentry;
struct inode *inode;
struct inode *lower_inode;
struct path lower_path;
struct dentry *parent;
parent = dget_parent(dentry);
if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.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);
dput(parent);
return -EACCES;
}
dput(parent);
inode = d_inode(dentry);
sdcardfs_get_lower_path(dentry, &lower_path);
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);
sdcardfs_put_lower_path(dentry, &lower_path);
return 0;
}
const struct inode_operations sdcardfs_symlink_iops = {
.permission = sdcardfs_permission,
.setattr = sdcardfs_setattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
* These methods are *NOT* perfectly tested.
.readlink = sdcardfs_readlink,
.follow_link = sdcardfs_follow_link,
.put_link = kfree_put_link,
*/
};
const struct inode_operations sdcardfs_dir_iops = {
.create = sdcardfs_create,
.lookup = sdcardfs_lookup,
#if 0
.permission = sdcardfs_permission,
#endif
.unlink = sdcardfs_unlink,
.mkdir = sdcardfs_mkdir,
.rmdir = sdcardfs_rmdir,
.rename = sdcardfs_rename,
.setattr = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
* These methods are *NOT* perfectly tested.
.symlink = sdcardfs_symlink,
.link = sdcardfs_link,
.mknod = sdcardfs_mknod,
*/
};
const struct inode_operations sdcardfs_main_iops = {
.permission = sdcardfs_permission,
.setattr = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
};

384
fs/sdcardfs/lookup.c Normal file
View file

@ -0,0 +1,384 @@
/*
* fs/sdcardfs/lookup.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
#include "linux/delay.h"
/* The dentry cache is just so we have properly sized dentries */
static struct kmem_cache *sdcardfs_dentry_cachep;
int sdcardfs_init_dentry_cache(void)
{
sdcardfs_dentry_cachep =
kmem_cache_create("sdcardfs_dentry",
sizeof(struct sdcardfs_dentry_info),
0, SLAB_RECLAIM_ACCOUNT, NULL);
return sdcardfs_dentry_cachep ? 0 : -ENOMEM;
}
void sdcardfs_destroy_dentry_cache(void)
{
if (sdcardfs_dentry_cachep)
kmem_cache_destroy(sdcardfs_dentry_cachep);
}
void free_dentry_private_data(struct dentry *dentry)
{
if (!dentry || !dentry->d_fsdata)
return;
kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata);
dentry->d_fsdata = NULL;
}
/* allocate new dentry private data */
int new_dentry_private_data(struct dentry *dentry)
{
struct sdcardfs_dentry_info *info = SDCARDFS_D(dentry);
/* use zalloc to init dentry_info.lower_path */
info = kmem_cache_zalloc(sdcardfs_dentry_cachep, GFP_ATOMIC);
if (!info)
return -ENOMEM;
spin_lock_init(&info->lock);
dentry->d_fsdata = info;
return 0;
}
struct inode_data {
struct inode *lower_inode;
userid_t id;
};
static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/)
{
struct inode *current_lower_inode = sdcardfs_lower_inode(inode);
userid_t current_userid = SDCARDFS_I(inode)->userid;
if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode &&
current_userid == ((struct inode_data *)candidate_data)->id)
return 1; /* found a match */
else
return 0; /* no match */
}
static int sdcardfs_inode_set(struct inode *inode, void *lower_inode)
{
/* we do actual inode initialization in sdcardfs_iget */
return 0;
}
struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, userid_t id)
{
struct sdcardfs_inode_info *info;
struct inode_data data;
struct inode *inode; /* the new inode to return */
int err;
data.id = id;
data.lower_inode = lower_inode;
inode = iget5_locked(sb, /* our superblock */
/*
* hashval: we use inode number, but we can
* also use "(unsigned long)lower_inode"
* instead.
*/
lower_inode->i_ino, /* hashval */
sdcardfs_inode_test, /* inode comparison function */
sdcardfs_inode_set, /* inode init function */
&data); /* data passed to test+set fxns */
if (!inode) {
err = -EACCES;
iput(lower_inode);
return ERR_PTR(err);
}
/* if found a cached inode, then just return it */
if (!(inode->i_state & I_NEW))
return inode;
/* initialize new inode */
info = SDCARDFS_I(inode);
inode->i_ino = lower_inode->i_ino;
if (!igrab(lower_inode)) {
err = -ESTALE;
return ERR_PTR(err);
}
sdcardfs_set_lower_inode(inode, lower_inode);
inode->i_version++;
/* use different set of inode ops for symlinks & directories */
if (S_ISDIR(lower_inode->i_mode))
inode->i_op = &sdcardfs_dir_iops;
else if (S_ISLNK(lower_inode->i_mode))
inode->i_op = &sdcardfs_symlink_iops;
else
inode->i_op = &sdcardfs_main_iops;
/* use different set of file ops for directories */
if (S_ISDIR(lower_inode->i_mode))
inode->i_fop = &sdcardfs_dir_fops;
else
inode->i_fop = &sdcardfs_main_fops;
inode->i_mapping->a_ops = &sdcardfs_aops;
inode->i_atime.tv_sec = 0;
inode->i_atime.tv_nsec = 0;
inode->i_mtime.tv_sec = 0;
inode->i_mtime.tv_nsec = 0;
inode->i_ctime.tv_sec = 0;
inode->i_ctime.tv_nsec = 0;
/* properly initialize special inodes */
if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
init_special_inode(inode, lower_inode->i_mode,
lower_inode->i_rdev);
/* all well, copy inode attributes */
sdcardfs_copy_and_fix_attrs(inode, lower_inode);
fsstack_copy_inode_size(inode, lower_inode);
unlock_new_inode(inode);
return inode;
}
/*
* Connect a sdcardfs inode dentry/inode with several lower ones. This is
* the classic stackable file system "vnode interposition" action.
*
* @dentry: sdcardfs's dentry which interposes on lower one
* @sb: sdcardfs's super_block
* @lower_path: the lower path (caller does path_get/put)
*/
int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
struct path *lower_path, userid_t id)
{
int err = 0;
struct inode *inode;
struct inode *lower_inode;
struct super_block *lower_sb;
lower_inode = lower_path->dentry->d_inode;
lower_sb = sdcardfs_lower_super(sb);
/* check that the lower file system didn't cross a mount point */
if (lower_inode->i_sb != lower_sb) {
err = -EXDEV;
goto out;
}
/*
* We allocate our new inode below by calling sdcardfs_iget,
* which will initialize some of the new inode's fields
*/
/* inherit lower inode number for sdcardfs's inode */
inode = sdcardfs_iget(sb, lower_inode, id);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto out;
}
d_add(dentry, inode);
update_derived_permission_lock(dentry);
out:
return err;
}
/*
* Main driver function for sdcardfs's lookup.
*
* Returns: NULL (ok), ERR_PTR if an error occurred.
* Fills in lower_parent_path with <dentry,mnt> on success.
*/
static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
unsigned int flags, struct path *lower_parent_path, userid_t id)
{
int err = 0;
struct vfsmount *lower_dir_mnt;
struct dentry *lower_dir_dentry = NULL;
struct dentry *lower_dentry;
const char *name;
struct path lower_path;
struct qstr this;
struct sdcardfs_sb_info *sbi;
sbi = SDCARDFS_SB(dentry->d_sb);
/* must initialize dentry operations */
d_set_d_op(dentry, &sdcardfs_ci_dops);
if (IS_ROOT(dentry))
goto out;
name = dentry->d_name.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,
&lower_path);
/* no error: handle positive dentries */
if (!err) {
/* check if the dentry is an obb dentry
* if true, the lower_inode must be replaced with
* the inode of the graft path */
if(need_graft_path(dentry)) {
/* setup_obb_dentry()
* The lower_path will be stored to the dentry's orig_path
* and the base obbpath will be copyed to the lower_path variable.
* if an error returned, there's no change in the lower_path
* returns: -ERRNO if error (0: no error) */
err = setup_obb_dentry(dentry, &lower_path);
if(err) {
/* if the sbi->obbpath is not available, we can optionally
* setup the lower_path with its orig_path.
* but, the current implementation just returns an error
* because the sdcard daemon also regards this case as
* a lookup fail. */
printk(KERN_INFO "sdcardfs: base obbpath is not available\n");
sdcardfs_put_reset_orig_path(dentry);
goto out;
}
}
sdcardfs_set_lower_path(dentry, &lower_path);
err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id);
if (err) /* path_put underlying path on error */
sdcardfs_put_reset_lower_path(dentry);
goto out;
}
/*
* We don't consider ENOENT an error, and we want to return a
* negative dentry.
*/
if (err && err != -ENOENT)
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);
if (lower_dentry)
goto setup_lower;
lower_dentry = d_alloc(lower_dir_dentry, &this);
if (!lower_dentry) {
err = -ENOMEM;
goto out;
}
d_add(lower_dentry, NULL); /* instantiate and hash */
setup_lower:
lower_path.dentry = lower_dentry;
lower_path.mnt = mntget(lower_dir_mnt);
sdcardfs_set_lower_path(dentry, &lower_path);
/*
* If the intent is to create a file, then don't return an error, so
* the VFS will continue the process of making this negative dentry
* into a positive one.
*/
if (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET))
err = 0;
out:
return ERR_PTR(err);
}
/*
* On success:
* fills dentry object appropriate values and returns NULL.
* On fail (== error)
* returns error ptr
*
* @dir : Parent inode. It is locked (dir->i_mutex)
* @dentry : Target dentry to lookup. we should set each of fields.
* (dentry->d_name is initialized already)
* @nd : nameidata of parent inode
*/
struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
struct dentry *ret = NULL, *parent;
struct path lower_parent_path;
int err = 0;
const struct cred *saved_cred = NULL;
parent = dget_parent(dentry);
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.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",
__func__, dentry->d_name.name, current->comm);
goto out_err;
}
/* save current_cred and override it */
OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred);
sdcardfs_get_lower_path(parent, &lower_parent_path);
/* allocate dentry private data. We free it in ->d_release */
err = new_dentry_private_data(dentry);
if (err) {
ret = ERR_PTR(err);
goto out;
}
ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid);
if (IS_ERR(ret))
{
goto out;
}
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);
get_derived_permission(parent, dentry);
fix_derived_permission(dentry->d_inode);
mutex_unlock(&dentry->d_inode->i_mutex);
}
/* update parent directory's atime */
fsstack_copy_attr_atime(parent->d_inode,
sdcardfs_lower_inode(parent->d_inode));
out:
sdcardfs_put_lower_path(parent, &lower_parent_path);
REVERT_CRED(saved_cred);
out_err:
dput(parent);
return ret;
}

402
fs/sdcardfs/main.c Normal file
View file

@ -0,0 +1,402 @@
/*
* fs/sdcardfs/main.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
#include <linux/module.h>
#include <linux/types.h>
#include <linux/parser.h>
enum {
Opt_fsuid,
Opt_fsgid,
Opt_gid,
Opt_debug,
Opt_lower_fs,
Opt_mask,
Opt_multiuser, // May need?
Opt_userid,
Opt_reserved_mb,
Opt_err,
};
static const match_table_t sdcardfs_tokens = {
{Opt_fsuid, "fsuid=%u"},
{Opt_fsgid, "fsgid=%u"},
{Opt_gid, "gid=%u"},
{Opt_debug, "debug"},
{Opt_mask, "mask=%u"},
{Opt_userid, "userid=%d"},
{Opt_multiuser, "multiuser"},
{Opt_reserved_mb, "reserved_mb=%u"},
{Opt_err, NULL}
};
static int parse_options(struct super_block *sb, char *options, int silent,
int *debug, struct sdcardfs_mount_options *opts)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
/* 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;
opts->multiuser = false;
opts->fs_user_id = 0;
opts->gid = 0;
/* by default, 0MB is reserved */
opts->reserved_mb = 0;
*debug = 0;
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_fsuid:
if (match_int(&args[0], &option))
return 0;
opts->fs_low_uid = option;
break;
case Opt_fsgid:
if (match_int(&args[0], &option))
return 0;
opts->fs_low_gid = option;
break;
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
opts->gid = option;
break;
case Opt_userid:
if (match_int(&args[0], &option))
return 0;
opts->fs_user_id = option;
break;
case Opt_mask:
if (match_int(&args[0], &option))
return 0;
opts->mask = option;
break;
case Opt_multiuser:
opts->multiuser = true;
break;
case Opt_reserved_mb:
if (match_int(&args[0], &option))
return 0;
opts->reserved_mb = option;
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 - uid:%d\n",
opts->fs_low_uid);
printk( KERN_INFO "sdcardfs : options - gid:%d\n",
opts->fs_low_gid);
}
return 0;
}
#if 0
/*
* our custom d_alloc_root work-alike
*
* we can't use d_alloc_root if we want to use our own interpose function
* unchanged, so we simply call our own "fake" d_alloc_root
*/
static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb)
{
struct dentry *ret = NULL;
if (sb) {
static const struct qstr name = {
.name = "/",
.len = 1
};
ret = d_alloc(NULL, &name);
if (ret) {
d_set_d_op(ret, &sdcardfs_ci_dops);
ret->d_sb = sb;
ret->d_parent = ret;
}
}
return ret;
}
#endif
DEFINE_MUTEX(sdcardfs_super_list_lock);
LIST_HEAD(sdcardfs_super_list);
EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock);
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)
{
int err = 0;
int debug;
struct super_block *lower_sb;
struct path lower_path;
struct sdcardfs_sb_info *sb_info;
struct inode *inode;
printk(KERN_INFO "sdcardfs version 2.0\n");
if (!dev_name) {
printk(KERN_ERR
"sdcardfs: read_super: missing dev_name argument\n");
err = -EINVAL;
goto out;
}
printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
/* parse lower path */
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
&lower_path);
if (err) {
printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name);
goto out;
}
/* allocate superblock private data */
sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL);
if (!SDCARDFS_SB(sb)) {
printk(KERN_CRIT "sdcardfs: read_super: out of memory\n");
err = -ENOMEM;
goto out_free;
}
sb_info = sb->s_fs_info;
/* parse options */
err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
if (err) {
printk(KERN_ERR "sdcardfs: invalid options\n");
goto out_freesbi;
}
/* set the lower superblock field of upper superblock */
lower_sb = lower_path.dentry->d_sb;
atomic_inc(&lower_sb->s_active);
sdcardfs_set_lower_super(sb, lower_sb);
/* inherit maxbytes from lower file system */
sb->s_maxbytes = lower_sb->s_maxbytes;
/*
* Our c/m/atime granularity is 1 ns because we may stack on file
* systems whose granularity is as good.
*/
sb->s_time_gran = 1;
sb->s_magic = SDCARDFS_SUPER_MAGIC;
sb->s_op = &sdcardfs_sops;
/* get a new inode and allocate our root dentry */
inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto out_sput;
}
sb->s_root = d_make_root(inode);
if (!sb->s_root) {
err = -ENOMEM;
goto out_iput;
}
d_set_d_op(sb->s_root, &sdcardfs_ci_dops);
/* link the upper and lower dentries */
sb->s_root->d_fsdata = NULL;
err = new_dentry_private_data(sb->s_root);
if (err)
goto out_freeroot;
/* set the lower dentries for s_root */
sdcardfs_set_lower_path(sb->s_root, &lower_path);
/*
* No need to call interpose because we already have a positive
* dentry, which was instantiated by d_make_root. Just need to
* d_rehash it.
*/
d_rehash(sb->s_root);
/* setup permission policy */
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);
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);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
fix_derived_permission(sb->s_root->d_inode);
sb_info->sb = sb;
list_add(&sb_info->list, &sdcardfs_super_list);
mutex_unlock(&sdcardfs_super_list_lock);
if (!silent)
printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n",
dev_name, lower_sb->s_type->name);
goto out; /* all is well */
/* no longer needed: free_dentry_private_data(sb->s_root); */
out_freeroot:
dput(sb->s_root);
out_iput:
iput(inode);
out_sput:
/* drop refs we took earlier */
atomic_dec(&lower_sb->s_active);
out_freesbi:
kfree(SDCARDFS_SB(sb));
sb->s_fs_info = NULL;
out_free:
path_put(&lower_path);
out:
return err;
}
/* 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))
{
int error;
struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
if (IS_ERR(s))
return ERR_CAST(s);
s->s_flags = flags;
error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0);
if (error) {
deactivate_locked_super(s);
return ERR_PTR(error);
}
s->s_flags |= MS_ACTIVE;
return dget(s->s_root);
}
struct dentry *sdcardfs_mount(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);
}
void sdcardfs_kill_sb(struct super_block *sb) {
struct sdcardfs_sb_info *sbi;
if (sb->s_magic == SDCARDFS_SUPER_MAGIC) {
sbi = SDCARDFS_SB(sb);
mutex_lock(&sdcardfs_super_list_lock);
list_del(&sbi->list);
mutex_unlock(&sdcardfs_super_list_lock);
}
generic_shutdown_super(sb);
}
static struct file_system_type sdcardfs_fs_type = {
.owner = THIS_MODULE,
.name = SDCARDFS_NAME,
.mount = sdcardfs_mount,
.kill_sb = sdcardfs_kill_sb,
.fs_flags = 0,
};
static int __init init_sdcardfs_fs(void)
{
int err;
pr_info("Registering sdcardfs " SDCARDFS_VERSION "\n");
err = sdcardfs_init_inode_cache();
if (err)
goto out;
err = sdcardfs_init_dentry_cache();
if (err)
goto out;
err = packagelist_init();
if (err)
goto out;
err = register_filesystem(&sdcardfs_fs_type);
out:
if (err) {
sdcardfs_destroy_inode_cache();
sdcardfs_destroy_dentry_cache();
packagelist_exit();
}
return err;
}
static void __exit exit_sdcardfs_fs(void)
{
sdcardfs_destroy_inode_cache();
sdcardfs_destroy_dentry_cache();
packagelist_exit();
unregister_filesystem(&sdcardfs_fs_type);
pr_info("Completed sdcardfs module unload\n");
}
MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University"
" (http://www.fsl.cs.sunysb.edu/)");
MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION
" (http://wrapfs.filesystems.org/)");
MODULE_LICENSE("GPL");
module_init(init_sdcardfs_fs);
module_exit(exit_sdcardfs_fs);

81
fs/sdcardfs/mmap.c Normal file
View file

@ -0,0 +1,81 @@
/*
* fs/sdcardfs/mmap.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
int err;
struct file *file, *lower_file;
const struct vm_operations_struct *lower_vm_ops;
struct vm_area_struct lower_vma;
memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
file = lower_vma.vm_file;
lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
BUG_ON(!lower_vm_ops);
lower_file = sdcardfs_lower_file(file);
/*
* XXX: vm_ops->fault may be called in parallel. Because we have to
* resort to temporarily changing the vma->vm_file to point to the
* lower file, a concurrent invocation of sdcardfs_fault could see a
* different value. In this workaround, we keep a different copy of
* the vma structure in our stack, so we never expose a different
* value of the vma->vm_file called to us, even temporarily. A
* better fix would be to change the calling semantics of ->fault to
* take an explicit file pointer.
*/
lower_vma.vm_file = lower_file;
err = lower_vm_ops->fault(&lower_vma, vmf);
return err;
}
static ssize_t sdcardfs_direct_IO(struct kiocb *iocb,
struct iov_iter *iter, loff_t pos)
{
/*
* This function returns zero on purpose in order to support direct IO.
* __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null.
*
* However, this function won't be called by certain file operations
* including generic fs functions. * reads and writes are delivered to
* the lower file systems and the direct IOs will be handled by them.
*
* NOTE: exceptionally, on the recent kernels (since Linux 3.8.x),
* swap_writepage invokes this function directly.
*/
printk(KERN_INFO "%s, operation is not supported\n", __func__);
return 0;
}
/*
* XXX: the default address_space_ops for sdcardfs is empty. We cannot set
* our inode->i_mapping->a_ops to NULL because too many code paths expect
* the a_ops vector to be non-NULL.
*/
const struct address_space_operations sdcardfs_aops = {
/* empty on purpose */
.direct_IO = sdcardfs_direct_IO,
};
const struct vm_operations_struct sdcardfs_vm_ops = {
.fault = sdcardfs_fault,
};

37
fs/sdcardfs/multiuser.h Normal file
View file

@ -0,0 +1,37 @@
/*
* fs/sdcardfs/multiuser.h
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#define MULTIUSER_APP_PER_USER_RANGE 100000
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 appid_t multiuser_get_app_id(uid_t uid) {
return uid % MULTIUSER_APP_PER_USER_RANGE;
}
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);
}

437
fs/sdcardfs/packagelist.c Normal file
View file

@ -0,0 +1,437 @@
/*
* fs/sdcardfs/packagelist.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
#include <linux/hashtable.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#define STRING_BUF_SIZE (512)
struct hashtable_entry {
struct hlist_node hlist;
void *key;
unsigned int value;
};
struct sb_list {
struct super_block *sb;
struct list_head list;
};
struct packagelist_data {
DECLARE_HASHTABLE(package_to_appid,8);
struct mutex hashtable_lock;
};
static struct packagelist_data *pkgl_data_all;
static struct kmem_cache *hashtable_entry_cachep;
static unsigned int str_hash(const char *key) {
int i;
unsigned int h = strlen(key);
char *data = (char *)key;
for (i = 0; i < strlen(key); i++) {
h = h * 31 + *data;
data++;
}
return h;
}
appid_t get_appid(void *pkgl_id, const char *app_name)
{
struct packagelist_data *pkgl_dat = pkgl_data_all;
struct hashtable_entry *hash_cur;
unsigned int hash = str_hash(app_name);
appid_t ret_id;
mutex_lock(&pkgl_dat->hashtable_lock);
hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
if (!strcasecmp(app_name, hash_cur->key)) {
ret_id = (appid_t)hash_cur->value;
mutex_unlock(&pkgl_dat->hashtable_lock);
return ret_id;
}
}
mutex_unlock(&pkgl_dat->hashtable_lock);
return 0;
}
/* Kernel has already enforced everything we returned through
* derive_permissions_locked(), so this is used to lock down access
* even further, such as enforcing that apps hold sdcard_rw. */
int check_caller_access_to_name(struct inode *parent_node, const char* name) {
/* Always block security-sensitive files at root */
if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) {
if (!strcasecmp(name, "autorun.inf")
|| !strcasecmp(name, ".android_secure")
|| !strcasecmp(name, "android_secure")) {
return 0;
}
}
/* Root always has access; access for any other UIDs should always
* be controlled through packages.list. */
if (from_kuid(&init_user_ns, current_fsuid()) == 0) {
return 1;
}
/* No extra permissions to enforce */
return 1;
}
/* This function is used when file opening. The open flags must be
* checked before calling check_caller_access_to_name() */
int open_flags_to_access_mode(int open_flags) {
if((open_flags & O_ACCMODE) == O_RDONLY) {
return 0; /* R_OK */
} else if ((open_flags & O_ACCMODE) == O_WRONLY) {
return 1; /* W_OK */
} else {
/* Probably O_RDRW, but treat as default to be safe */
return 1; /* R_OK | W_OK */
}
}
static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key,
unsigned int value)
{
struct hashtable_entry *hash_cur;
struct hashtable_entry *new_entry;
unsigned int hash = str_hash(key);
hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
if (!strcasecmp(key, hash_cur->key)) {
hash_cur->value = value;
return 0;
}
}
new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL);
if (!new_entry)
return -ENOMEM;
new_entry->key = kstrdup(key, GFP_KERNEL);
new_entry->value = value;
hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash);
return 0;
}
static void fixup_perms(struct super_block *sb) {
if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) {
mutex_lock(&sb->s_root->d_inode->i_mutex);
get_derive_permissions_recursive(sb->s_root);
mutex_unlock(&sb->s_root->d_inode->i_mutex);
}
}
static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key,
unsigned int value) {
int ret;
struct sdcardfs_sb_info *sbinfo;
mutex_lock(&sdcardfs_super_list_lock);
mutex_lock(&pkgl_dat->hashtable_lock);
ret = insert_str_to_int_lock(pkgl_dat, key, value);
mutex_unlock(&pkgl_dat->hashtable_lock);
list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
if (sbinfo) {
fixup_perms(sbinfo->sb);
}
}
mutex_unlock(&sdcardfs_super_list_lock);
return ret;
}
static void remove_str_to_int_lock(struct hashtable_entry *h_entry) {
kfree(h_entry->key);
hash_del(&h_entry->hlist);
kmem_cache_free(hashtable_entry_cachep, h_entry);
}
static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key)
{
struct sdcardfs_sb_info *sbinfo;
struct hashtable_entry *hash_cur;
unsigned int hash = str_hash(key);
mutex_lock(&sdcardfs_super_list_lock);
mutex_lock(&pkgl_dat->hashtable_lock);
hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
if (!strcasecmp(key, hash_cur->key)) {
remove_str_to_int_lock(hash_cur);
break;
}
}
mutex_unlock(&pkgl_dat->hashtable_lock);
list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
if (sbinfo) {
fixup_perms(sbinfo->sb);
}
}
mutex_unlock(&sdcardfs_super_list_lock);
return;
}
static void remove_all_hashentrys(struct packagelist_data *pkgl_dat)
{
struct hashtable_entry *hash_cur;
struct hlist_node *h_t;
int i;
mutex_lock(&pkgl_dat->hashtable_lock);
hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist)
remove_str_to_int_lock(hash_cur);
mutex_unlock(&pkgl_dat->hashtable_lock);
hash_init(pkgl_dat->package_to_appid);
}
static struct packagelist_data * packagelist_create(void)
{
struct packagelist_data *pkgl_dat;
pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO);
if (!pkgl_dat) {
printk(KERN_ERR "sdcardfs: Failed to create hash\n");
return ERR_PTR(-ENOMEM);
}
mutex_init(&pkgl_dat->hashtable_lock);
hash_init(pkgl_dat->package_to_appid);
return pkgl_dat;
}
static void packagelist_destroy(struct packagelist_data *pkgl_dat)
{
remove_all_hashentrys(pkgl_dat);
printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
kfree(pkgl_dat);
}
struct package_appid {
struct config_item item;
int add_pid;
};
static inline struct package_appid *to_package_appid(struct config_item *item)
{
return item ? container_of(item, struct package_appid, item) : NULL;
}
static ssize_t package_appid_attr_show(struct config_item *item,
char *page)
{
ssize_t count;
count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name));
return count;
}
static ssize_t package_appid_attr_store(struct config_item *item,
const char *page, size_t count)
{
struct package_appid *package_appid = to_package_appid(item);
unsigned long tmp;
char *p = (char *) page;
int ret;
tmp = simple_strtoul(p, &p, 10);
if (!p || (*p && (*p != '\n')))
return -EINVAL;
if (tmp > INT_MAX)
return -ERANGE;
ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp);
package_appid->add_pid = tmp;
if (ret)
return ret;
return count;
}
static struct configfs_attribute package_appid_attr_add_pid = {
.ca_owner = THIS_MODULE,
.ca_name = "appid",
.ca_mode = S_IRUGO | S_IWUGO,
.show = package_appid_attr_show,
.store = package_appid_attr_store,
};
static struct configfs_attribute *package_appid_attrs[] = {
&package_appid_attr_add_pid,
NULL,
};
static void package_appid_release(struct config_item *item)
{
printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name);
/* item->ci_name is freed already, so we rely on the dentry */
remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name);
kfree(to_package_appid(item));
}
static struct configfs_item_operations package_appid_item_ops = {
.release = package_appid_release,
};
static struct config_item_type package_appid_type = {
.ct_item_ops = &package_appid_item_ops,
.ct_attrs = package_appid_attrs,
.ct_owner = THIS_MODULE,
};
struct sdcardfs_packages {
struct config_group group;
};
static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item)
{
return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL;
}
static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name)
{
struct package_appid *package_appid;
package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL);
if (!package_appid)
return ERR_PTR(-ENOMEM);
config_item_init_type_name(&package_appid->item, name,
&package_appid_type);
package_appid->add_pid = 0;
return &package_appid->item;
}
static ssize_t packages_attr_show(struct config_item *item,
char *page)
{
struct hashtable_entry *hash_cur;
struct hlist_node *h_t;
int i;
int count = 0;
mutex_lock(&pkgl_data_all->hashtable_lock);
hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist)
count += snprintf(page + count, PAGE_SIZE - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value);
mutex_unlock(&pkgl_data_all->hashtable_lock);
return count;
}
static struct configfs_attribute sdcardfs_packages_attr_description = {
.ca_owner = THIS_MODULE,
.ca_name = "packages_gid.list",
.ca_mode = S_IRUGO,
.show = packages_attr_show,
};
static struct configfs_attribute *sdcardfs_packages_attrs[] = {
&sdcardfs_packages_attr_description,
NULL,
};
static void sdcardfs_packages_release(struct config_item *item)
{
printk(KERN_INFO "sdcardfs: destroyed something?\n");
kfree(to_sdcardfs_packages(item));
}
static struct configfs_item_operations sdcardfs_packages_item_ops = {
.release = sdcardfs_packages_release,
};
/*
* Note that, since no extra work is required on ->drop_item(),
* no ->drop_item() is provided.
*/
static struct configfs_group_operations sdcardfs_packages_group_ops = {
.make_item = sdcardfs_packages_make_item,
};
static struct config_item_type sdcardfs_packages_type = {
.ct_item_ops = &sdcardfs_packages_item_ops,
.ct_group_ops = &sdcardfs_packages_group_ops,
.ct_attrs = sdcardfs_packages_attrs,
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem sdcardfs_packages_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "sdcardfs",
.ci_type = &sdcardfs_packages_type,
},
},
};
static int configfs_sdcardfs_init(void)
{
int ret;
struct configfs_subsystem *subsys = &sdcardfs_packages_subsys;
config_group_init(&subsys->su_group);
mutex_init(&subsys->su_mutex);
ret = configfs_register_subsystem(subsys);
if (ret) {
printk(KERN_ERR "Error %d while registering subsystem %s\n",
ret,
subsys->su_group.cg_item.ci_namebuf);
}
return ret;
}
static void configfs_sdcardfs_exit(void)
{
configfs_unregister_subsystem(&sdcardfs_packages_subsys);
}
int packagelist_init(void)
{
hashtable_entry_cachep =
kmem_cache_create("packagelist_hashtable_entry",
sizeof(struct hashtable_entry), 0, 0, NULL);
if (!hashtable_entry_cachep) {
printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n");
return -ENOMEM;
}
pkgl_data_all = packagelist_create();
configfs_sdcardfs_init();
return 0;
}
void packagelist_exit(void)
{
configfs_sdcardfs_exit();
packagelist_destroy(pkgl_data_all);
if (hashtable_entry_cachep)
kmem_cache_destroy(hashtable_entry_cachep);
}

530
fs/sdcardfs/sdcardfs.h Normal file
View file

@ -0,0 +1,530 @@
/*
* fs/sdcardfs/sdcardfs.h
*
* The sdcardfs v2.0
* This file system replaces the sdcard daemon on Android
* On version 2.0, some of the daemon functions have been ported
* to support the multi-user concepts of Android 4.4
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#ifndef _SDCARDFS_H_
#define _SDCARDFS_H_
#include <linux/dcache.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/seq_file.h>
#include <linux/statfs.h>
#include <linux/fs_stack.h>
#include <linux/magic.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/security.h>
#include <linux/string.h>
#include <linux/list.h>
#include "multiuser.h"
/* the file system name */
#define SDCARDFS_NAME "sdcardfs"
/* sdcardfs root inode number */
#define SDCARDFS_ROOT_INO 1
/* useful for tracking code reachability */
#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__)
#define SDCARDFS_DIRENT_SIZE 256
/* temporary static uid settings for development */
#define AID_ROOT 0 /* uid for accessing /mnt/sdcard & extSdcard */
#define AID_MEDIA_RW 1023 /* internal media storage write access */
#define AID_SDCARD_RW 1015 /* external storage write access */
#define AID_SDCARD_R 1028 /* external storage read access */
#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_PACKAGE_INFO 1027
#define fix_derived_permission(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));\
} while (0)
/* OVERRIDE_CRED() and REVERT_CRED()
* OVERRID_CRED()
* backup original task->cred
* and modifies task->cred->fsuid/fsgid to specified value.
* REVERT_CRED()
* restore original task->cred->fsuid/fsgid.
* 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); \
if (!saved_cred) { return -ENOMEM; }
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \
saved_cred = override_fsids(sdcardfs_sbi); \
if (!saved_cred) { return ERR_PTR(-ENOMEM); }
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
#define DEBUG_CRED() \
printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \
__FUNCTION__, __LINE__, \
(int)current->cred->fsuid, \
(int)current->cred->fsgid);
/* Android 5.0 support */
/* Permission mode for a specific node. Controls how file permissions
* are derived for children nodes. */
typedef enum {
/* Nothing special; this node should just inherit from its parent. */
PERM_INHERIT,
/* This node is one level above a normal root; used for legacy layouts
* which use the first level to represent user_id. */
PERM_PRE_ROOT,
/* This node is "/" */
PERM_ROOT,
/* This node is "/Android" */
PERM_ANDROID,
/* This node is "/Android/data" */
PERM_ANDROID_DATA,
/* This node is "/Android/obb" */
PERM_ANDROID_OBB,
/* This node is "/Android/media" */
PERM_ANDROID_MEDIA,
} perm_t;
struct sdcardfs_sb_info;
struct sdcardfs_mount_options;
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi);
/* Do not directly use this function, use REVERT_CRED() instead. */
void revert_fsids(const struct cred * old_cred);
/* operations vectors defined in specific files */
extern const struct file_operations sdcardfs_main_fops;
extern const struct file_operations sdcardfs_dir_fops;
extern const struct inode_operations sdcardfs_main_iops;
extern const struct inode_operations sdcardfs_dir_iops;
extern const struct inode_operations sdcardfs_symlink_iops;
extern const struct super_operations sdcardfs_sops;
extern const struct dentry_operations sdcardfs_ci_dops;
extern const struct address_space_operations sdcardfs_aops, sdcardfs_dummy_aops;
extern const struct vm_operations_struct sdcardfs_vm_ops;
extern int sdcardfs_init_inode_cache(void);
extern void sdcardfs_destroy_inode_cache(void);
extern int sdcardfs_init_dentry_cache(void);
extern void sdcardfs_destroy_dentry_cache(void);
extern int new_dentry_private_data(struct dentry *dentry);
extern void free_dentry_private_data(struct dentry *dentry);
extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags);
extern struct inode *sdcardfs_iget(struct super_block *sb,
struct inode *lower_inode, userid_t id);
extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
struct path *lower_path, userid_t id);
/* file private data */
struct sdcardfs_file_info {
struct file *lower_file;
const struct vm_operations_struct *lower_vm_ops;
};
/* sdcardfs inode data in memory */
struct sdcardfs_inode_info {
struct inode *lower_inode;
/* state derived based on current position in hierachy */
perm_t perm;
userid_t userid;
uid_t d_uid;
bool under_android;
struct inode vfs_inode;
};
/* sdcardfs dentry data in memory */
struct sdcardfs_dentry_info {
spinlock_t lock; /* protects lower_path */
struct path lower_path;
struct path orig_path;
};
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;
};
/* sdcardfs super-block data in memory */
struct sdcardfs_sb_info {
struct super_block *sb;
struct super_block *lower_sb;
/* derived perm policy : some of options have been added
* to sdcardfs_mount_options (Android 4.4 support) */
struct sdcardfs_mount_options options;
spinlock_t lock; /* protects obbpath */
char *obbpath_s;
struct path obbpath;
void *pkgl_id;
struct list_head list;
};
/*
* inode to private data
*
* Since we use containers and the struct inode is _inside_ the
* sdcardfs_inode_info structure, SDCARDFS_I will always (given a non-NULL
* inode pointer), return a valid non-NULL pointer.
*/
static inline struct sdcardfs_inode_info *SDCARDFS_I(const struct inode *inode)
{
return container_of(inode, struct sdcardfs_inode_info, vfs_inode);
}
/* dentry to private data */
#define SDCARDFS_D(dent) ((struct sdcardfs_dentry_info *)(dent)->d_fsdata)
/* superblock to private data */
#define SDCARDFS_SB(super) ((struct sdcardfs_sb_info *)(super)->s_fs_info)
/* file to private Data */
#define SDCARDFS_F(file) ((struct sdcardfs_file_info *)((file)->private_data))
/* file to lower file */
static inline struct file *sdcardfs_lower_file(const struct file *f)
{
return SDCARDFS_F(f)->lower_file;
}
static inline void sdcardfs_set_lower_file(struct file *f, struct file *val)
{
SDCARDFS_F(f)->lower_file = val;
}
/* inode to lower inode. */
static inline struct inode *sdcardfs_lower_inode(const struct inode *i)
{
return SDCARDFS_I(i)->lower_inode;
}
static inline void sdcardfs_set_lower_inode(struct inode *i, struct inode *val)
{
SDCARDFS_I(i)->lower_inode = val;
}
/* superblock to lower superblock */
static inline struct super_block *sdcardfs_lower_super(
const struct super_block *sb)
{
return SDCARDFS_SB(sb)->lower_sb;
}
static inline void sdcardfs_set_lower_super(struct super_block *sb,
struct super_block *val)
{
SDCARDFS_SB(sb)->lower_sb = val;
}
/* path based (dentry/mnt) macros */
static inline void pathcpy(struct path *dst, const struct path *src)
{
dst->dentry = src->dentry;
dst->mnt = src->mnt;
}
/* sdcardfs_get_pname functions calls path_get()
* therefore, the caller must call "proper" path_put functions
*/
#define SDCARDFS_DENT_FUNC(pname) \
static inline void sdcardfs_get_##pname(const struct dentry *dent, \
struct path *pname) \
{ \
spin_lock(&SDCARDFS_D(dent)->lock); \
pathcpy(pname, &SDCARDFS_D(dent)->pname); \
path_get(pname); \
spin_unlock(&SDCARDFS_D(dent)->lock); \
return; \
} \
static inline void sdcardfs_put_##pname(const struct dentry *dent, \
struct path *pname) \
{ \
path_put(pname); \
return; \
} \
static inline void sdcardfs_set_##pname(const struct dentry *dent, \
struct path *pname) \
{ \
spin_lock(&SDCARDFS_D(dent)->lock); \
pathcpy(&SDCARDFS_D(dent)->pname, pname); \
spin_unlock(&SDCARDFS_D(dent)->lock); \
return; \
} \
static inline void sdcardfs_reset_##pname(const struct dentry *dent) \
{ \
spin_lock(&SDCARDFS_D(dent)->lock); \
SDCARDFS_D(dent)->pname.dentry = NULL; \
SDCARDFS_D(dent)->pname.mnt = NULL; \
spin_unlock(&SDCARDFS_D(dent)->lock); \
return; \
} \
static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \
{ \
struct path pname; \
spin_lock(&SDCARDFS_D(dent)->lock); \
if(SDCARDFS_D(dent)->pname.dentry) { \
pathcpy(&pname, &SDCARDFS_D(dent)->pname); \
SDCARDFS_D(dent)->pname.dentry = NULL; \
SDCARDFS_D(dent)->pname.mnt = NULL; \
spin_unlock(&SDCARDFS_D(dent)->lock); \
path_put(&pname); \
} else \
spin_unlock(&SDCARDFS_D(dent)->lock); \
return; \
}
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) {
/* 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
* the user boundary enforcement for the default view. The UIDs
* assigned to app directories are still multiuser aware. */
return AID_SDCARD_RW;
} else {
return multiuser_get_uid(info->userid, sb_info->options.gid);
}
}
static inline int get_mode(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;
if (info->perm == PERM_PRE_ROOT) {
/* Top of multi-user view should always be visible to ensure
* secondary users can traverse inside. */
visible_mode = 0711;
} else if (info->under_android) {
/* 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) {
visible_mode = visible_mode & ~0006;
} else {
visible_mode = visible_mode & ~0007;
}
}
owner_mode = info->lower_inode->i_mode & 0700;
filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
return filtered_mode;
}
static inline int has_graft_path(const struct dentry *dent)
{
int ret = 0;
spin_lock(&SDCARDFS_D(dent)->lock);
if (SDCARDFS_D(dent)->orig_path.dentry != NULL)
ret = 1;
spin_unlock(&SDCARDFS_D(dent)->lock);
return ret;
}
static inline void sdcardfs_get_real_lower(const struct dentry *dent,
struct path *real_lower)
{
/* in case of a local obb dentry
* the orig_path should be returned
*/
if(has_graft_path(dent))
sdcardfs_get_orig_path(dent, real_lower);
else
sdcardfs_get_lower_path(dent, real_lower);
}
static inline void sdcardfs_put_real_lower(const struct dentry *dent,
struct path *real_lower)
{
if(has_graft_path(dent))
sdcardfs_put_orig_path(dent, real_lower);
else
sdcardfs_put_lower_path(dent, real_lower);
}
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 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);
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 update_derived_permission_lock(struct dentry *dentry);
extern int need_graft_path(struct dentry *dentry);
extern int is_base_obbpath(struct dentry *dentry);
extern int is_obbpath_invalid(struct dentry *dentry);
extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path);
/* locking helpers */
static inline struct dentry *lock_parent(struct dentry *dentry)
{
struct dentry *dir = dget_parent(dentry);
mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT);
return dir;
}
static inline void unlock_dir(struct dentry *dir)
{
mutex_unlock(&d_inode(dir)->i_mutex);
dput(dir);
}
static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t mode)
{
int err;
struct dentry *dent;
struct iattr attrs;
struct path parent;
dent = kern_path_locked(path_s, &parent);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
if (err == -EEXIST)
err = 0;
goto out_unlock;
}
err = vfs_mkdir(d_inode(parent.dentry), dent, mode);
if (err) {
if (err == -EEXIST)
err = 0;
goto out_dput;
}
attrs.ia_uid = make_kuid(&init_user_ns, uid);
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);
mutex_unlock(&d_inode(dent)->i_mutex);
out_dput:
dput(dent);
out_unlock:
/* parent dentry locked by lookup_create */
mutex_unlock(&d_inode(parent.dentry)->i_mutex);
path_put(&parent);
return err;
}
/*
* Return 1, if a disk has enough free space, otherwise 0.
* We assume that any files can not be overwritten.
*/
static inline int check_min_free_space(struct dentry *dentry, size_t size, int dir)
{
int err;
struct path lower_path;
struct kstatfs statfs;
u64 avail;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
if (sbi->options.reserved_mb) {
/* Get fs stat of lower filesystem. */
sdcardfs_get_lower_path(dentry, &lower_path);
err = vfs_statfs(&lower_path, &statfs);
sdcardfs_put_lower_path(dentry, &lower_path);
if (unlikely(err))
return 0;
/* Invalid statfs informations. */
if (unlikely(statfs.f_bsize == 0))
return 0;
/* if you are checking directory, set size to f_bsize. */
if (unlikely(dir))
size = statfs.f_bsize;
/* available size */
avail = statfs.f_bavail * statfs.f_bsize;
/* not enough space */
if ((u64)size > avail)
return 0;
/* enough space */
if ((avail - size) > (sbi->options.reserved_mb * 1024 * 1024))
return 1;
return 0;
} else
return 1;
}
/* Copies attrs and maintains sdcardfs managed attrs */
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_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_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;
set_nlink(dest, src->i_nlink);
}
#endif /* not _SDCARDFS_H_ */

222
fs/sdcardfs/super.c Normal file
View file

@ -0,0 +1,222 @@
/*
* fs/sdcardfs/super.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
/*
* The inode cache is used with alloc_inode for both our inode info and the
* vfs inode.
*/
static struct kmem_cache *sdcardfs_inode_cachep;
/* final actions when unmounting a file system */
static void sdcardfs_put_super(struct super_block *sb)
{
struct sdcardfs_sb_info *spd;
struct super_block *s;
spd = SDCARDFS_SB(sb);
if (!spd)
return;
if(spd->obbpath_s) {
kfree(spd->obbpath_s);
path_put(&spd->obbpath);
}
/* decrement lower super references */
s = sdcardfs_lower_super(sb);
sdcardfs_set_lower_super(sb, NULL);
atomic_dec(&s->s_active);
kfree(spd);
sb->s_fs_info = NULL;
}
static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
int err;
struct path lower_path;
u32 min_blocks;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
sdcardfs_get_lower_path(dentry, &lower_path);
err = vfs_statfs(&lower_path, buf);
sdcardfs_put_lower_path(dentry, &lower_path);
if (sbi->options.reserved_mb) {
/* Invalid statfs informations. */
if (buf->f_bsize == 0) {
printk(KERN_ERR "Returned block size is zero.\n");
return -EINVAL;
}
min_blocks = ((sbi->options.reserved_mb * 1024 * 1024)/buf->f_bsize);
buf->f_blocks -= min_blocks;
if (buf->f_bavail > min_blocks)
buf->f_bavail -= min_blocks;
else
buf->f_bavail = 0;
/* Make reserved blocks invisiable to media storage */
buf->f_bfree = buf->f_bavail;
}
/* set return buf to our f/s to avoid confusing user-level utils */
buf->f_type = SDCARDFS_SUPER_MAGIC;
return err;
}
/*
* @flags: numeric mount options
* @options: mount options string
*/
static int sdcardfs_remount_fs(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)) != 0) {
printk(KERN_ERR
"sdcardfs: remount flags 0x%x unsupported\n", *flags);
err = -EINVAL;
}
return err;
}
/*
* Called by iput() when the inode reference count reached zero
* and the inode is not hashed anywhere. Used to clear anything
* that needs to be, before the inode is completely destroyed and put
* on the inode free list.
*/
static void sdcardfs_evict_inode(struct inode *inode)
{
struct inode *lower_inode;
truncate_inode_pages(&inode->i_data, 0);
clear_inode(inode);
/*
* Decrement a reference to a lower_inode, which was incremented
* by our read_inode when it was created initially.
*/
lower_inode = sdcardfs_lower_inode(inode);
sdcardfs_set_lower_inode(inode, NULL);
iput(lower_inode);
}
static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
{
struct sdcardfs_inode_info *i;
i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL);
if (!i)
return NULL;
/* memset everything up to the inode to 0 */
memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode));
i->vfs_inode.i_version = 1;
return &i->vfs_inode;
}
static void sdcardfs_destroy_inode(struct inode *inode)
{
kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
}
/* sdcardfs inode cache constructor */
static void init_once(void *obj)
{
struct sdcardfs_inode_info *i = obj;
inode_init_once(&i->vfs_inode);
}
int sdcardfs_init_inode_cache(void)
{
int err = 0;
sdcardfs_inode_cachep =
kmem_cache_create("sdcardfs_inode_cache",
sizeof(struct sdcardfs_inode_info), 0,
SLAB_RECLAIM_ACCOUNT, init_once);
if (!sdcardfs_inode_cachep)
err = -ENOMEM;
return err;
}
/* sdcardfs inode cache destructor */
void sdcardfs_destroy_inode_cache(void)
{
if (sdcardfs_inode_cachep)
kmem_cache_destroy(sdcardfs_inode_cachep);
}
/*
* Used only in nfs, to kill any pending RPC tasks, so that subsequent
* code can actually succeed and won't leave tasks that need handling.
*/
static void sdcardfs_umount_begin(struct super_block *sb)
{
struct super_block *lower_sb;
lower_sb = sdcardfs_lower_super(sb);
if (lower_sb && lower_sb->s_op && lower_sb->s_op->umount_begin)
lower_sb->s_op->umount_begin(lower_sb);
}
static int sdcardfs_show_options(struct seq_file *m, struct dentry *root)
{
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
struct sdcardfs_mount_options *opts = &sbi->options;
if (opts->fs_low_uid != 0)
seq_printf(m, ",uid=%u", opts->fs_low_uid);
if (opts->fs_low_gid != 0)
seq_printf(m, ",gid=%u", opts->fs_low_gid);
if (opts->multiuser)
seq_printf(m, ",multiuser");
if (opts->reserved_mb != 0)
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
return 0;
};
const struct super_operations sdcardfs_sops = {
.put_super = sdcardfs_put_super,
.statfs = sdcardfs_statfs,
.remount_fs = sdcardfs_remount_fs,
.evict_inode = sdcardfs_evict_inode,
.umount_begin = sdcardfs_umount_begin,
.show_options = sdcardfs_show_options,
.alloc_inode = sdcardfs_alloc_inode,
.destroy_inode = sdcardfs_destroy_inode,
.drop_inode = generic_delete_inode,
};

View file

@ -161,6 +161,7 @@ struct dentry_operations {
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool);
struct inode *(*d_select_inode)(struct dentry *, unsigned);
void (*d_canonical_path)(const struct dentry *, struct path *);
struct dentry *(*d_real)(struct dentry *, struct inode *);
} ____cacheline_aligned;

View file

@ -75,6 +75,8 @@ extern struct dentry *user_path_create(int, const char __user *, struct path *,
extern void done_path_create(struct path *, struct dentry *);
extern struct dentry *kern_path_locked(const char *, struct path *);
extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
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);

View file

@ -34,6 +34,7 @@ extern const struct file_operations random_fops, urandom_fops;
#endif
unsigned int get_random_int(void);
unsigned long get_random_long(void);
unsigned long randomize_range(unsigned long start, unsigned long end, unsigned long len);
u32 prandom_u32(void);

View file

@ -47,10 +47,6 @@ struct linger {
struct msghdr {
void *msg_name; /* ptr to socket address structure */
int msg_namelen; /* size of socket address structure */
#if defined(CONFIG_PPPOLAC) || defined(CONFIG_PPPOPNS)
struct iovec *msg_iov; /* scatter/gather array */
__kernel_size_t msg_iovlen; /* # elements in msg_iov */
#endif
struct iov_iter msg_iter; /* data */
void *msg_control; /* ancillary data */
__kernel_size_t msg_controllen; /* ancillary data buffer length */

View file

@ -21,7 +21,12 @@
#define MAX_SUSPEND_ABORT_LEN 256
void log_wakeup_reason(int irq);
void log_suspend_abort_reason(const char *fmt, ...);
int check_wakeup_reason(int irq);
#ifdef CONFIG_SUSPEND
void log_suspend_abort_reason(const char *fmt, ...);
#else
static inline void log_suspend_abort_reason(const char *fmt, ...) { }
#endif
#endif /* _LINUX_WAKEUP_REASON_H */

View file

@ -135,7 +135,7 @@ TRACE_EVENT(cpu_frequency_limits,
TP_fast_assign(
__entry->min_freq = min_freq;
__entry->max_freq = min_freq;
__entry->max_freq = max_freq;
__entry->cpu_id = cpu_id;
),

View file

@ -52,6 +52,8 @@
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
#define SDCARDFS_SUPER_MAGIC 0xb550ca10
#define SMB_SUPER_MAGIC 0x517B
#define CGROUP_SUPER_MAGIC 0x27e0eb

View file

@ -1,6 +1,7 @@
config SUSPEND
bool "Suspend to RAM and standby"
depends on ARCH_SUSPEND_POSSIBLE
select RTC_LIB
default y
---help---
Allow the system to enter sleep states in which main memory is

View file

@ -121,7 +121,7 @@ static unsigned long soft_lockup_nmi_warn;
unsigned int __read_mostly hardlockup_panic =
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE;
#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI
static unsigned long hardlockup_allcpu_dumped;
static unsigned long __maybe_unused hardlockup_allcpu_dumped;
#endif
/*
* We may not want to enable hard lockup detection by default in all cases,

View file

@ -2634,6 +2634,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
return 0;
}
EXPORT_SYMBOL(do_munmap);
int vm_munmap(unsigned long start, size_t len)
{