Initial port of sdcardfs

Change-Id: I5b5772a2bbff9f3a7dda641644630a7b8afacec0
This commit is contained in:
Daniel Campello 2015-06-19 14:31:25 -07:00 committed by LuK1337
parent ec57310b8e
commit ebe0f70f8d
19 changed files with 4124 additions and 2 deletions

View File

@ -196,6 +196,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 \
@ -60,7 +60,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/
@ -85,6 +85,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/

18
fs/sdcardfs/Kconfig Executable file
View File

@ -0,0 +1,18 @@
config SDCARD_FS
tristate "sdcard file system"
depends on EXPERIMENTAL
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.
config SDCARD_FS_CI_SEARCH
tristate "sdcardfs case-insensitive search support"
depends on SDCARD_FS
default y

7
fs/sdcardfs/Makefile Executable 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 Executable 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, struct nameidata *nd)
{
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 (nd && nd->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,
const struct inode *inode, 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 inode *pinode,
const struct dentry *dentry, const struct inode *inode,
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,
};

290
fs/sdcardfs/derived_perm.c Executable file
View File

@ -0,0 +1,290 @@
/*
* 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->d_gid = pi->d_gid;
ci->d_mode = pi->d_mode;
}
/* helper function for derived state */
void setup_derived_state(struct inode *inode, perm_t perm,
userid_t userid, uid_t uid, gid_t gid, mode_t mode)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
info->perm = perm;
info->userid = userid;
info->d_uid = uid;
info->d_gid = gid;
info->d_mode = mode;
}
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
{
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);
//printk(KERN_INFO "sdcardfs: derived: %s, %s, %d\n", parent->d_name.name,
// dentry->d_name.name, parent_info->perm);
if (sbi->options.derive == DERIVE_NONE) {
return;
}
/* Derive custom permissions based on parent and current node */
switch (parent_info->perm) {
case PERM_INHERIT:
/* Already inherited above */
break;
case PERM_LEGACY_PRE_ROOT:
/* Legacy internal layout places users at top level */
info->perm = PERM_ROOT;
info->userid = simple_strtoul(dentry->d_name.name, NULL, 10);
break;
case PERM_ROOT:
/* Assume masked off by default. */
info->d_mode = 00770;
if (!strcasecmp(dentry->d_name.name, "Android")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID;
info->d_mode = 00771;
} else if (sbi->options.split_perms) {
if (!strcasecmp(dentry->d_name.name, "DCIM")
|| !strcasecmp(dentry->d_name.name, "Pictures")) {
info->d_gid = AID_SDCARD_PICS;
} else if (!strcasecmp(dentry->d_name.name, "Alarms")
|| !strcasecmp(dentry->d_name.name, "Movies")
|| !strcasecmp(dentry->d_name.name, "Music")
|| !strcasecmp(dentry->d_name.name, "Notifications")
|| !strcasecmp(dentry->d_name.name, "Podcasts")
|| !strcasecmp(dentry->d_name.name, "Ringtones")) {
info->d_gid = AID_SDCARD_AV;
}
}
break;
case PERM_ANDROID:
if (!strcasecmp(dentry->d_name.name, "data")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_DATA;
info->d_mode = 00771;
} else if (!strcasecmp(dentry->d_name.name, "obb")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_OBB;
info->d_mode = 00771;
// FIXME : this feature will be implemented later.
/* Single OBB directory is always shared */
} else if (!strcasecmp(dentry->d_name.name, "user")) {
/* User directories must only be accessible to system, protected
* by sdcard_all. Zygote will bind mount the appropriate user-
* specific path. */
info->perm = PERM_ANDROID_USER;
info->d_gid = AID_SDCARD_ALL;
info->d_mode = 00770;
}
break;
/* same policy will be applied on PERM_ANDROID_DATA
* and PERM_ANDROID_OBB */
case PERM_ANDROID_DATA:
case PERM_ANDROID_OBB:
appid = get_appid(sbi->pkgl_id, dentry->d_name.name);
if (appid != 0) {
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
}
info->d_mode = 00770;
break;
case PERM_ANDROID_USER:
/* Root of a secondary user */
info->perm = PERM_ROOT;
info->userid = simple_strtoul(dentry->d_name.name, NULL, 10);
info->d_gid = AID_SDCARD_R;
info->d_mode = 00771;
break;
}
}
/* main function for updating derived permission */
inline void update_derived_permission(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
*/
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);
}
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.derive == DERIVE_UNIFIED
&& 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);
/* DERIVED_LEGACY */
if(parent_info->perm == PERM_LEGACY_PRE_ROOT &&
!strcasecmp(dentry->d_name.name, "obb")) {
ret = 1;
}
/* DERIVED_UNIFIED :/Android/obb is the base obbpath */
else if (parent_info->perm == PERM_ANDROID &&
!strcasecmp(dentry->d_name.name, "obb")) {
if((sbi->options.derive == DERIVE_UNIFIED
&& parent_info->userid == 0)) {
ret = 1;
}
}
spin_unlock(&SDCARDFS_D(dentry)->lock);
dput(parent);
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;
}

357
fs/sdcardfs/file.c Executable file
View File

@ -0,0 +1,357 @@
/*
* 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(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
return err;
}
static ssize_t sdcardfs_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int err = 0;
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(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
fsstack_copy_attr_times(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
}
return err;
}
static int sdcardfs_readdir(struct file *file, void *dirent, filldir_t filldir)
{
int err = 0;
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 = vfs_readdir(lower_file, filldir, dirent);
file->f_pos = lower_file->f_pos;
if (err >= 0) /* copy the atime */
fsstack_copy_attr_atime(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
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;
vma->vm_flags |= VM_CAN_NONLINEAR;
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;
int has_rw;
/* don't open unhashed/deleted files */
if (d_unhashed(dentry)) {
err = -ENOENT;
goto out_err;
}
has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name,
sbi->options.derive,
open_flags_to_access_mode(file->f_flags), has_rw)) {
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.dentry, lower_path.mnt,
file->f_flags, current_cred());
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 {
fsstack_copy_attr_all(inode, sdcardfs_lower_inode(inode));
fix_derived_permission(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)
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, int datasync)
{
int err;
struct file *lower_file;
struct path lower_path;
struct dentry *dentry = file->f_path.dentry;
lower_file = sdcardfs_lower_file(file);
sdcardfs_get_lower_path(dentry, &lower_path);
err = vfs_fsync(lower_file, datasync);
sdcardfs_put_lower_path(dentry, &lower_path);
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,
.readdir = 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,
};

190
fs/sdcardfs/hashtable.h Executable file
View File

@ -0,0 +1,190 @@
/*
* Statically sized hash table implementation
* (C) 2012 Sasha Levin <levinsasha928@gmail.com>
*/
#ifndef _LINUX_HASHTABLE_H
#define _LINUX_HASHTABLE_H
#include <linux/list.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/hash.h>
#include <linux/rculist.h>
#define DEFINE_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)] = \
{ [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
#define DECLARE_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)]
#define HASH_SIZE(name) (ARRAY_SIZE(name))
#define HASH_BITS(name) ilog2(HASH_SIZE(name))
/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */
#define hash_min(val, bits) \
(sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits))
static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
{
unsigned int i;
for (i = 0; i < sz; i++)
INIT_HLIST_HEAD(&ht[i]);
}
/**
* hash_init - initialize a hash table
* @hashtable: hashtable to be initialized
*
* Calculates the size of the hashtable from the given parameter, otherwise
* same as hash_init_size.
*
* This has to be a macro since HASH_BITS() will not work on pointers since
* it calculates the size during preprocessing.
*/
#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable))
/**
* hash_add - add an object to a hashtable
* @hashtable: hashtable to add to
* @node: the &struct hlist_node of the object to be added
* @key: the key of the object to be added
*/
#define hash_add(hashtable, node, key) \
hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
/**
* hash_add_rcu - add an object to a rcu enabled hashtable
* @hashtable: hashtable to add to
* @node: the &struct hlist_node of the object to be added
* @key: the key of the object to be added
*/
#define hash_add_rcu(hashtable, node, key) \
hlist_add_head_rcu(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
/**
* hash_hashed - check whether an object is in any hashtable
* @node: the &struct hlist_node of the object to be checked
*/
static inline bool hash_hashed(struct hlist_node *node)
{
return !hlist_unhashed(node);
}
static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz)
{
unsigned int i;
for (i = 0; i < sz; i++)
if (!hlist_empty(&ht[i]))
return false;
return true;
}
/**
* hash_empty - check whether a hashtable is empty
* @hashtable: hashtable to check
*
* This has to be a macro since HASH_BITS() will not work on pointers since
* it calculates the size during preprocessing.
*/
#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable))
/**
* hash_del - remove an object from a hashtable
* @node: &struct hlist_node of the object to remove
*/
static inline void hash_del(struct hlist_node *node)
{
hlist_del_init(node);
}
/**
* hash_del_rcu - remove an object from a rcu enabled hashtable
* @node: &struct hlist_node of the object to remove
*/
static inline void hash_del_rcu(struct hlist_node *node)
{
hlist_del_init_rcu(node);
}
/**
* hash_for_each - iterate over a hashtable
* @name: hashtable to iterate
* @bkt: integer to use as bucket loop cursor
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each(name, bkt, obj, member, pos) \
for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
(bkt)++)\
hlist_for_each_entry(obj, pos, &name[bkt], member)
/**
* hash_for_each_rcu - iterate over a rcu enabled hashtable
* @name: hashtable to iterate
* @bkt: integer to use as bucket loop cursor
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each_rcu(name, bkt, obj, member) \
for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
(bkt)++)\
hlist_for_each_entry_rcu(obj, &name[bkt], member)
/**
* hash_for_each_safe - iterate over a hashtable safe against removal of
* hash entry
* @name: hashtable to iterate
* @bkt: integer to use as bucket loop cursor
* @tmp: a &struct used for temporary storage
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each_safe(name, bkt, tmp, obj, member, pos) \
for ((bkt) = 0, obj = NULL; (bkt) < HASH_SIZE(name);\
(bkt)++)\
hlist_for_each_entry_safe(obj, pos, tmp, &name[bkt], member)
/**
* hash_for_each_possible - iterate over all possible objects hashing to the
* same bucket
* @name: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible(name, obj, member, key, pos) \
hlist_for_each_entry(obj, pos, &name[hash_min(key, HASH_BITS(name))], member)
/**
* hash_for_each_possible_rcu - iterate over all possible objects hashing to the
* same bucket in an rcu enabled hashtable
* in a rcu enabled hashtable
* @name: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible_rcu(name, obj, member, key) \
hlist_for_each_entry_rcu(obj, &name[hash_min(key, HASH_BITS(name))],\
member)
/**
* hash_for_each_possible_safe - iterate over all possible objects hashing to the
* same bucket safe against removals
* @name: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @tmp: a &struct used for temporary storage
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible_safe(name, obj, tmp, member, key) \
hlist_for_each_entry_safe(obj, tmp,\
&name[hash_min(key, HASH_BITS(name))], member)
#endif

886
fs/sdcardfs/inode.c Executable file
View File

@ -0,0 +1,886 @@
/*
* 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 = sbi->options.fs_low_uid;
cred->fsgid = 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,
int mode, struct nameidata *nd)
{
int err = 0;
struct dentry *lower_dentry;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path, saved_path;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
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);
err = mnt_want_write(lower_path.mnt);
if (err)
goto out_unlock;
pathcpy(&saved_path, &nd->path);
pathcpy(&nd->path, &lower_path);
/* set last 16bytes of mode field to 0664 */
mode = (mode & S_IFMT) | 00664;
err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, nd);
pathcpy(&nd->path, &saved_path);
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, lower_parent_dentry->d_inode);
out:
mnt_drop_write(lower_path.mnt);
out_unlock:
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(old_dentry->d_inode);
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 = mnt_want_write(lower_new_path.mnt);
if (err)
goto out_unlock;
err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
lower_new_dentry);
if (err || !lower_new_dentry->d_inode)
goto out;
err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path);
if (err)
goto out;
fsstack_copy_attr_times(dir, lower_new_dentry->d_inode);
fsstack_copy_inode_size(dir, lower_new_dentry->d_inode);
old_dentry->d_inode->i_nlink =
sdcardfs_lower_inode(old_dentry->d_inode)->i_nlink;
i_size_write(new_dentry->d_inode, file_size_save);
out:
mnt_drop_write(lower_new_path.mnt);
out_unlock:
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;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
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 = mnt_want_write(lower_path.mnt);
if (err)
goto out_unlock;
err = vfs_unlink(lower_dir_inode, lower_dentry);
/*
* 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);
dentry->d_inode->i_nlink =
sdcardfs_lower_inode(dentry->d_inode)->i_nlink;
dentry->d_inode->i_ctime = dir->i_ctime;
d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */
out:
mnt_drop_write(lower_path.mnt);
out_unlock:
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 = 0;
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 = mnt_want_write(lower_path.mnt);
if (err)
goto out_unlock;
err = vfs_symlink(lower_parent_dentry->d_inode, 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, lower_parent_dentry->d_inode);
out:
mnt_drop_write(lower_path.mnt);
out_unlock:
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, int mode)
{
int err = 0;
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;
int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
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);
err = mnt_want_write(lower_path.mnt);
if (err)
goto out_unlock;
/* set last 16bytes of mode field to 0775 */
mode = (mode & S_IFMT) | 00775;
err = vfs_mkdir(lower_parent_dentry->d_inode, 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);
if (err)
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);
/* update number of links on parent directory */
dir->i_nlink = sdcardfs_lower_inode(dir)->i_nlink;
if ((sbi->options.derive == DERIVE_UNIFIED) && (!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:
mnt_drop_write(lower_path.mnt);
out_unlock:
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;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
//char *path_s = NULL;
int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
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 = mnt_want_write(lower_path.mnt);
if (err)
goto out_unlock;
err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
if (err)
goto out;
d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */
if (dentry->d_inode)
clear_nlink(dentry->d_inode);
fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode);
dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
out:
mnt_drop_write(lower_path.mnt);
out_unlock:
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, int mode,
dev_t dev)
{
int err = 0;
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 = mnt_want_write(lower_path.mnt);
if (err)
goto out_unlock;
err = vfs_mknod(lower_parent_dentry->d_inode, 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, lower_parent_dentry->d_inode);
out:
mnt_drop_write(lower_path.mnt);
out_unlock:
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;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(old_dentry->d_sb);
const struct cred *saved_cred = NULL;
int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name,
sbi->options.derive, 1, has_rw) ||
!check_caller_access_to_name(new_dir, new_dentry->d_name.name,
sbi->options.derive, 1, has_rw)) {
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 = mnt_want_write(lower_old_path.mnt);
if (err)
goto out;
err = mnt_want_write(lower_new_path.mnt);
if (err)
goto out_drop_old_write;
err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
lower_new_dir_dentry->d_inode, lower_new_dentry);
if (err)
goto out_err;
/* Copy attrs from lower dir, but i_uid/i_gid */
fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode);
fix_derived_permission(new_dir);
if (new_dir != old_dir) {
fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode);
fix_derived_permission(old_dir);
/* update the derived permission of the old_dentry
* with its new parent
*/
new_parent = dget_parent(new_dentry);
if(new_parent) {
if(old_dentry->d_inode) {
get_derived_permission(new_parent, old_dentry);
fix_derived_permission(old_dentry->d_inode);
}
dput(new_parent);
}
}
out_err:
mnt_drop_write(lower_new_path.mnt);
out_drop_old_write:
mnt_drop_write(lower_old_path.mnt);
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 (!lower_dentry->d_inode->i_op ||
!lower_dentry->d_inode->i_op->readlink) {
err = -EINVAL;
goto out;
}
err = lower_dentry->d_inode->i_op->readlink(lower_dentry,
buf, bufsiz);
if (err < 0)
goto out;
fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode);
out:
sdcardfs_put_lower_path(dentry, &lower_path);
return err;
}
#endif
#if 0
static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
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);
goto out;
}
/* 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';
}
out:
nd_set_link(nd, buf);
return NULL;
}
#endif
#if 0
/* this @nd *IS* still used */
static void sdcardfs_put_link(struct dentry *dentry, struct nameidata *nd,
void *cookie)
{
char *buf = nd_get_link(nd);
if (!IS_ERR(buf)) /* free the char* */
kfree(buf);
}
#endif
static int sdcardfs_permission(struct inode *inode, int mask, unsigned int flags)
{
int err;
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
/*
* Permission check on sdcardfs inode.
* Calling process should have AID_SDCARD_RW permission
*/
err = generic_permission(inode, mask, 0, inode->i_op->check_acl);
/* 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_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;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
parent = dget_parent(dentry);
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name,
sbi->options.derive, 0, 0)) {
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 = dentry->d_inode;
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
lower_inode = sdcardfs_lower_inode(inode);
fsstack_copy_attr_all(inode, lower_inode);
fsstack_copy_inode_size(inode, lower_inode);
/* if the dentry has been moved from other location
* so, on this stage, its derived permission must be
* rechecked from its private field.
*/
fix_derived_permission(inode);
generic_fillattr(inode, stat);
sdcardfs_put_lower_path(dentry, &lower_path);
return 0;
}
static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
{
int err = 0;
struct dentry *lower_dentry;
struct inode *inode;
struct inode *lower_inode;
struct path lower_path;
struct iattr lower_ia;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
struct dentry *parent;
int has_rw;
inode = dentry->d_inode;
/*
* 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 */
has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
parent = dget_parent(dentry);
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name,
sbi->options.derive, 1, has_rw)) {
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 lower_dentry->d_inode, 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(&lower_dentry->d_inode->i_mutex);
err = notify_change(lower_dentry, &lower_ia); /* note: lower_ia */
mutex_unlock(&lower_dentry->d_inode->i_mutex);
if (current->mm)
up_write(&current->mm->mmap_sem);
if (err)
goto out;
/* get attributes from the lower inode */
fsstack_copy_attr_all(inode, lower_inode);
/* update derived permission of the upper inode */
fix_derived_permission(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;
}
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 = sdcardfs_put_link,
*/
};
const struct inode_operations sdcardfs_dir_iops = {
.create = sdcardfs_create,
.lookup = sdcardfs_lookup,
.permission = sdcardfs_permission,
.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,
};

386
fs/sdcardfs/lookup.c Executable file
View File

@ -0,0 +1,386 @@
/*
* 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;
}
static int sdcardfs_inode_test(struct inode *inode, void *candidate_lower_inode)
{
struct inode *current_lower_inode = sdcardfs_lower_inode(inode);
if (current_lower_inode == (struct inode *)candidate_lower_inode)
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;
}
static struct inode *sdcardfs_iget(struct super_block *sb,
struct inode *lower_inode)
{
struct sdcardfs_inode_info *info;
struct inode *inode; /* the new inode to return */
int err;
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 */
lower_inode); /* 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 */
fsstack_copy_attr_all(inode, lower_inode);
fsstack_copy_inode_size(inode, lower_inode);
fix_derived_permission(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)
{
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);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto out;
}
d_add(dentry, inode);
update_derived_permission(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,
struct nameidata *nd, struct path *lower_parent_path)
{
int err = 0;
struct vfsmount *lower_dir_mnt;
struct dentry *lower_dir_dentry = NULL;
struct dentry *lower_dentry;
const char *name;
struct nameidata lower_nd;
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 */
if (sbi->options.lower_fs == LOWER_FS_EXT4) {
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name,
LOOKUP_CASE_INSENSITIVE, &lower_nd);
} else if (sbi->options.lower_fs == LOWER_FS_FAT) {
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
&lower_nd);
}
/* 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_nd.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_nd.path);
err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_nd.path);
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 (nd) {
if (nd->flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET))
err = 0;
} else
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,
struct nameidata *nd)
{
struct dentry *ret = NULL, *parent;
struct path lower_parent_path;
int err = 0;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
parent = dget_parent(dentry);
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name,
sbi->options.derive, 0, 0)) {
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, nd, &lower_parent_path);
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 */
get_derived_permission(parent, dentry);
fix_derived_permission(dentry->d_inode);
}
/* 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;
}

425
fs/sdcardfs/main.c Executable file
View File

@ -0,0 +1,425 @@
/*
* 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_uid,
Opt_gid,
Opt_wgid,
Opt_debug,
Opt_split,
Opt_derive,
Opt_lower_fs,
Opt_reserved_mb,
Opt_err,
};
static const match_table_t sdcardfs_tokens = {
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
{Opt_wgid, "wgid=%u"},
{Opt_debug, "debug"},
{Opt_split, "split"},
{Opt_derive, "derive=%s"},
{Opt_lower_fs, "lower_fs=%s"},
{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;
char *string_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;
/* by default, we use AID_SDCARD_RW as write_gid */
opts->write_gid = AID_SDCARD_RW;
/* default permission policy
* (DERIVE_NONE | DERIVE_LEGACY | DERIVE_UNIFIED) */
opts->derive = DERIVE_NONE;
opts->split_perms = 0;
/* by default, we use LOWER_FS_EXT4 as lower fs type */
opts->lower_fs = LOWER_FS_EXT4;
/* 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_uid:
if (match_int(&args[0], &option))
return 0;
opts->fs_low_uid = option;
break;
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
opts->fs_low_gid = option;
break;
case Opt_wgid:
if (match_int(&args[0], &option))
return 0;
opts->write_gid = option;
break;
case Opt_split:
opts->split_perms=1;
break;
case Opt_derive:
string_option = match_strdup(&args[0]);
if (!strcmp("none", string_option)) {
opts->derive = DERIVE_NONE;
} else if (!strcmp("legacy", string_option)) {
opts->derive = DERIVE_LEGACY;
} else if (!strcmp("unified", string_option)) {
opts->derive = DERIVE_UNIFIED;
} else {
kfree(string_option);
goto invalid_option;
}
kfree(string_option);
break;
case Opt_lower_fs:
string_option = match_strdup(&args[0]);
if (!strcmp("ext4", string_option)) {
opts->lower_fs = LOWER_FS_EXT4;
} else if (!strcmp("fat", string_option)) {
opts->lower_fs = LOWER_FS_FAT;
} else {
kfree(string_option);
goto invalid_option;
}
kfree(string_option);
break;
case Opt_reserved_mb:
if (match_int(&args[0], &option))
return 0;
opts->reserved_mb = option;
break;
/* unknown option */
default:
invalid_option:
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;
}
/*
* 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;
}
/*
* 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;
void *pkgl_id;
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;
}
if (sb_info->options.derive != DERIVE_NONE) {
pkgl_id = packagelist_create(sb_info->options.write_gid);
if(IS_ERR(pkgl_id))
goto out_freesbi;
else
sb_info->pkgl_id = pkgl_id;
}
/* 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;
/* see comment next to the definition of sdcardfs_d_alloc_root */
sb->s_root = sdcardfs_d_alloc_root(sb);
if (!sb->s_root) {
err = -ENOMEM;
goto out_sput;
}
/* 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);
/* call interpose to create the upper level inode */
err = sdcardfs_interpose(sb->s_root, sb, &lower_path);
if (!err) {
/* setup permission policy */
switch(sb_info->options.derive) {
case DERIVE_NONE:
setup_derived_state(sb->s_root->d_inode,
PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775);
sb_info->obbpath_s = NULL;
break;
case DERIVE_LEGACY:
/* Legacy behavior used to support internal multiuser layout which
* places user_id at the top directory level, with the actual roots
* just below that. Shared OBB path is also at top level. */
setup_derived_state(sb->s_root->d_inode,
PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771);
/* initialize the obbpath string and lookup the path
* sb_info->obb_path will be deactivated by path_put
* on sdcardfs_put_super */
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
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);
if(err)
printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n",
__func__,__LINE__, sb_info->obbpath_s);
break;
case DERIVE_UNIFIED:
/* Unified multiuser layout which places secondary user_id under
* /Android/user and shared OBB path under /Android/obb. */
setup_derived_state(sb->s_root->d_inode,
PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771);
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
break;
}
fix_derived_permission(sb->s_root->d_inode);
if (!silent)
printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n",
dev_name, lower_sb->s_type->name);
goto out;
}
/* else error: fall through */
free_dentry_private_data(sb->s_root);
out_freeroot:
dput(sb->s_root);
out_sput:
/* drop refs we took earlier */
atomic_dec(&lower_sb->s_active);
packagelist_destroy(sb_info->pkgl_id);
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, 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);
}
static struct file_system_type sdcardfs_fs_type = {
.owner = THIS_MODULE,
.name = SDCARDFS_NAME,
.mount = sdcardfs_mount,
.kill_sb = generic_shutdown_super,
.fs_flags = FS_REVAL_DOT,
};
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);

82
fs/sdcardfs/mmap.c Executable file
View File

@ -0,0 +1,82 @@
/*
* 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(int rw, struct kiocb *iocb,
const struct iovec *iov, loff_t offset,
unsigned long nr_segs)
{
/*
* 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 Executable 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);
}

458
fs/sdcardfs/packagelist.c Executable file
View File

@ -0,0 +1,458 @@
/*
* 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 "strtok.h"
#include "hashtable.h"
#include <linux/syscalls.h>
#include <linux/kthread.h>
#include <linux/inotify.h>
#include <linux/delay.h>
#define STRING_BUF_SIZE (512)
struct hashtable_entry {
struct hlist_node hlist;
void *key;
int value;
};
struct packagelist_data {
DECLARE_HASHTABLE(package_to_appid,8);
DECLARE_HASHTABLE(appid_with_rw,7);
struct mutex hashtable_lock;
struct task_struct *thread_id;
gid_t write_gid;
char *strtok_last;
char read_buf[STRING_BUF_SIZE];
char event_buf[STRING_BUF_SIZE];
char app_name_buf[STRING_BUF_SIZE];
char gids_buf[STRING_BUF_SIZE];
};
static struct kmem_cache *hashtable_entry_cachep;
/* Path to system-provided mapping of package name to appIds */
static const char* const kpackageslist_file = "/data/system/packages.list";
/* Supplementary groups to execute with */
static const gid_t kgroups[1] = { AID_PACKAGE_INFO };
static unsigned int str_hash(void *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;
}
static int contain_appid_key(struct packagelist_data *pkgl_dat, void *appid) {
struct hashtable_entry *hash_cur;
struct hlist_node *h_n;
hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, (unsigned int)appid, h_n)
if (appid == hash_cur->key)
return 1;
return 0;
}
/* Return if the calling UID holds sdcard_rw. */
int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) {
struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id;
appid_t appid;
int ret;
/* No additional permissions enforcement */
if (derive == DERIVE_NONE) {
return 1;
}
appid = multiuser_get_app_id(current_fsuid());
mutex_lock(&pkgl_dat->hashtable_lock);
ret = contain_appid_key(pkgl_dat, (void *)appid);
mutex_unlock(&pkgl_dat->hashtable_lock);
return ret;
}
appid_t get_appid(void *pkgl_id, const char *app_name)
{
struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id;
struct hashtable_entry *hash_cur;
struct hlist_node *h_n;
unsigned int hash = str_hash((void *)app_name);
appid_t ret_id;
//printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash);
mutex_lock(&pkgl_dat->hashtable_lock);
hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) {
//printk(KERN_INFO "sdcardfs: %s: %s\n", __func__, (char *)hash_cur->key);
if (!strcasecmp(app_name, hash_cur->key)) {
ret_id = (appid_t)hash_cur->value;
mutex_unlock(&pkgl_dat->hashtable_lock);
//printk(KERN_INFO "=> app_id: %d\n", (int)ret_id);
return ret_id;
}
}
mutex_unlock(&pkgl_dat->hashtable_lock);
//printk(KERN_INFO "=> app_id: %d\n", 0);
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,
derive_t derive, int w_ok, int has_rw) {
/* 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;
}
}
/* No additional permissions enforcement */
if (derive == DERIVE_NONE) {
return 1;
}
/* Root always has access; access for any other UIDs should always
* be controlled through packages.list. */
if (current_fsuid() == 0) {
return 1;
}
/* If asking to write, verify that caller either owns the
* parent or holds sdcard_rw. */
if (w_ok) {
if (parent_node &&
(current_fsuid() == SDCARDFS_I(parent_node)->d_uid)) {
return 1;
}
return has_rw;
}
/* 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(struct packagelist_data *pkgl_dat, void *key, int value) {
struct hashtable_entry *hash_cur;
struct hashtable_entry *new_entry;
struct hlist_node *h_n;
unsigned int hash = str_hash(key);
//printk(KERN_INFO "sdcardfs: %s: %s: %d, %u\n", __func__, (char *)key, value, hash);
hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) {
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 remove_str_to_int(struct hashtable_entry *h_entry) {
//printk(KERN_INFO "sdcardfs: %s: %s: %d\n", __func__, (char *)h_entry->key, h_entry->value);
kfree(h_entry->key);
kmem_cache_free(hashtable_entry_cachep, h_entry);
}
static int insert_int_to_null(struct packagelist_data *pkgl_dat, void *key, int value) {
struct hashtable_entry *hash_cur;
struct hashtable_entry *new_entry;
struct hlist_node *h_n;
//printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value);
hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist,
(unsigned int)key, h_n) {
if (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 = key;
new_entry->value = value;
hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist,
(unsigned int)new_entry->key);
return 0;
}
static void remove_int_to_null(struct hashtable_entry *h_entry) {
//printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)h_entry->key, h_entry->value);
kmem_cache_free(hashtable_entry_cachep, h_entry);
}
static void remove_all_hashentrys(struct packagelist_data *pkgl_dat)
{
struct hashtable_entry *hash_cur;
struct hlist_node *h_n;
struct hlist_node *h_t;
int i;
hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist, h_n)
remove_str_to_int(hash_cur);
hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist, h_n)
remove_int_to_null(hash_cur);
hash_init(pkgl_dat->package_to_appid);
hash_init(pkgl_dat->appid_with_rw);
}
static int read_package_list(struct packagelist_data *pkgl_dat) {
int ret;
int fd;
int read_amount;
printk(KERN_INFO "sdcardfs: read_package_list\n");
mutex_lock(&pkgl_dat->hashtable_lock);
remove_all_hashentrys(pkgl_dat);
fd = sys_open(kpackageslist_file, O_RDONLY, 0);
if (fd < 0) {
printk(KERN_ERR "sdcardfs: failed to open package list\n");
mutex_unlock(&pkgl_dat->hashtable_lock);
return fd;
}
while ((read_amount = sys_read(fd, pkgl_dat->read_buf,
sizeof(pkgl_dat->read_buf))) > 0) {
int appid;
char *token;
int one_line_len = 0;
int additional_read;
unsigned long ret_gid;
while (one_line_len < read_amount) {
if (pkgl_dat->read_buf[one_line_len] == '\n') {
one_line_len++;
break;
}
one_line_len++;
}
additional_read = read_amount - one_line_len;
if (additional_read > 0)
sys_lseek(fd, -additional_read, SEEK_CUR);
if (sscanf(pkgl_dat->read_buf, "%s %d %*d %*s %*s %s",
pkgl_dat->app_name_buf, &appid,
pkgl_dat->gids_buf) == 3) {
ret = insert_str_to_int(pkgl_dat, pkgl_dat->app_name_buf, appid);
if (ret) {
sys_close(fd);
mutex_unlock(&pkgl_dat->hashtable_lock);
return ret;
}
token = strtok_r(pkgl_dat->gids_buf, ",", &pkgl_dat->strtok_last);
while (token != NULL) {
if (!kstrtoul(token, 10, &ret_gid) &&
(ret_gid == pkgl_dat->write_gid)) {
ret = insert_int_to_null(pkgl_dat, (void *)appid, 1);
if (ret) {
sys_close(fd);
mutex_unlock(&pkgl_dat->hashtable_lock);
return ret;
}
break;
}
token = strtok_r(NULL, ",", &pkgl_dat->strtok_last);
}
}
}
sys_close(fd);
mutex_unlock(&pkgl_dat->hashtable_lock);
return 0;
}
static int packagelist_reader(void *thread_data)
{
struct packagelist_data *pkgl_dat = (struct packagelist_data *)thread_data;
struct inotify_event *event;
bool active = false;
int event_pos;
int event_size;
int res = 0;
int nfd;
allow_signal(SIGINT);
nfd = sys_inotify_init();
if (nfd < 0) {
printk(KERN_ERR "sdcardfs: inotify_init failed: %d\n", nfd);
return nfd;
}
while (!kthread_should_stop()) {
if (signal_pending(current)) {
ssleep(1);
continue;
}
if (!active) {
res = sys_inotify_add_watch(nfd, kpackageslist_file, IN_DELETE_SELF);
if (res < 0) {
if (res == -ENOENT || res == -EACCES) {
/* Framework may not have created yet, sleep and retry */
printk(KERN_ERR "sdcardfs: missing packages.list; retrying\n");
ssleep(2);
printk(KERN_ERR "sdcardfs: missing packages.list_end; retrying\n");
continue;
} else {
printk(KERN_ERR "sdcardfs: inotify_add_watch failed: %d\n", res);
goto interruptable_sleep;
}
}
/* Watch above will tell us about any future changes, so
* read the current state. */
res = read_package_list(pkgl_dat);
if (res) {
printk(KERN_ERR "sdcardfs: read_package_list failed: %d\n", res);
goto interruptable_sleep;
}
active = true;
}
event_pos = 0;
res = sys_read(nfd, pkgl_dat->event_buf, sizeof(pkgl_dat->event_buf));
if (res < (int) sizeof(*event)) {
if (res == -EINTR)
continue;
printk(KERN_ERR "sdcardfs: failed to read inotify event: %d\n", res);
goto interruptable_sleep;
}
while (res >= (int) sizeof(*event)) {
event = (struct inotify_event *) (pkgl_dat->event_buf + event_pos);
printk(KERN_INFO "sdcardfs: inotify event: %08x\n", event->mask);
if ((event->mask & IN_IGNORED) == IN_IGNORED) {
/* Previously watched file was deleted, probably due to move
* that swapped in new data; re-arm the watch and read. */
active = false;
}
event_size = sizeof(*event) + event->len;
res -= event_size;
event_pos += event_size;
}
continue;
interruptable_sleep:
set_current_state(TASK_INTERRUPTIBLE);
schedule();
}
flush_signals(current);
sys_close(nfd);
return res;
}
void * packagelist_create(gid_t write_gid)
{
struct packagelist_data *pkgl_dat;
struct task_struct *packagelist_thread;
pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO);
if (!pkgl_dat) {
printk(KERN_ERR "sdcardfs: creating kthread failed\n");
return ERR_PTR(-ENOMEM);
}
mutex_init(&pkgl_dat->hashtable_lock);
hash_init(pkgl_dat->package_to_appid);
hash_init(pkgl_dat->appid_with_rw);
pkgl_dat->write_gid = write_gid;
packagelist_thread = kthread_run(packagelist_reader, (void *)pkgl_dat, "pkgld");
if (IS_ERR(packagelist_thread)) {
printk(KERN_ERR "sdcardfs: creating kthread failed\n");
kfree(pkgl_dat);
return packagelist_thread;
}
pkgl_dat->thread_id = packagelist_thread;
printk(KERN_INFO "sdcardfs: created packagelist pkgld/%d\n",
(int)pkgl_dat->thread_id->pid);
return (void *)pkgl_dat;
}
void packagelist_destroy(void *pkgl_id)
{
struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id;
pid_t pkgl_pid = pkgl_dat->thread_id->pid;
force_sig_info(SIGINT, SEND_SIG_PRIV, pkgl_dat->thread_id);
kthread_stop(pkgl_dat->thread_id);
remove_all_hashentrys(pkgl_dat);
printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld/%d\n", (int)pkgl_pid);
kfree(pkgl_dat);
}
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;
}
return 0;
}
void packagelist_exit(void)
{
if (hashtable_entry_cachep)
kmem_cache_destroy(hashtable_entry_cachep);
}

493
fs/sdcardfs/sdcardfs.h Executable file
View File

@ -0,0 +1,493 @@
/*
* 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 "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 = SDCARDFS_I(x)->d_uid; \
(x)->i_gid = SDCARDFS_I(x)->d_gid; \
(x)->i_mode = ((x)->i_mode & S_IFMT) | SDCARDFS_I(x)->d_mode;\
} 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 4.4 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_LEGACY_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/user" */
PERM_ANDROID_USER,
} perm_t;
/* Permissions structure to derive */
typedef enum {
DERIVE_NONE,
DERIVE_LEGACY,
DERIVE_UNIFIED,
} derive_t;
typedef enum {
LOWER_FS_EXT4,
LOWER_FS_FAT,
} lower_fs_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,
struct nameidata *nd);
extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
struct path *lower_path);
/* 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
* caution: d_mode does not include file types
*/
perm_t perm;
userid_t userid;
uid_t d_uid;
gid_t d_gid;
mode_t d_mode;
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;
gid_t write_gid;
int split_perms;
derive_t derive;
lower_fs_t lower_fs;
unsigned int reserved_mb;
};
/* sdcardfs super-block data in memory */
struct sdcardfs_sb_info {
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;
};
/*
* 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 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);
}
/* for packagelist.c */
extern int get_caller_has_rw_locked(void *pkgl_id, derive_t derive);
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,
derive_t derive, int w_ok, int has_rw);
extern int open_flags_to_access_mode(int open_flags);
extern void * packagelist_create(gid_t write_gid);
extern void packagelist_destroy(void *pkgl_id);
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, gid_t gid, mode_t mode);
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
extern void update_derived_permission(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(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
return dir;
}
static inline void unlock_dir(struct dentry *dir)
{
mutex_unlock(&dir->d_inode->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 nameidata nd;
err = kern_path_parent(path_s, &nd);
if (err) {
if (err == -EEXIST)
err = 0;
goto out;
}
dent = lookup_create(&nd, 1);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
if (err == -EEXIST)
err = 0;
goto out_unlock;
}
err = vfs_mkdir(nd.path.dentry->d_inode, dent, mode);
if (err) {
if (err == -EEXIST)
err = 0;
goto out_dput;
}
attrs.ia_uid = uid;
attrs.ia_gid = gid;
attrs.ia_valid = ATTR_UID | ATTR_GID;
mutex_lock(&dent->d_inode->i_mutex);
notify_change(dent, &attrs);
mutex_unlock(&dent->d_inode->i_mutex);
out_dput:
dput(dent);
out_unlock:
/* parent dentry locked by lookup_create */
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
path_put(&nd.path);
out:
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;
}
#endif /* not _SDCARDFS_H_ */

75
fs/sdcardfs/strtok.h Executable file
View File

@ -0,0 +1,75 @@
/*
* fs/sdcardfs/strtok.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.
*/
static char *
strtok_r(char *s, const char *delim, char **last)
{
char *spanp;
int c, sc;
char *tok;
/* if (s == NULL && (s = *last) == NULL)
return NULL; */
if (s == NULL) {
s = *last;
if (s == NULL)
return NULL;
}
/*
* Skip (span) leading delimiters (s += strspn(s, delim), sort of).
*/
cont:
c = *s++;
for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
if (c == sc)
goto cont;
}
if (c == 0) { /* no non-delimiter characters */
*last = NULL;
return NULL;
}
tok = s - 1;
/*
* Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
* Note that delim must have one NUL; we stop if we see that, too.
*/
for (;;) {
c = *s++;
spanp = (char *)delim;
do {
sc = *spanp++;
if (sc == c) {
if (c == 0)
s = NULL;
else
s[-1] = 0;
*last = s;
return tok;
}
} while (sc != 0);
}
/* NOTREACHED */
}

229
fs/sdcardfs/super.c Executable file
View File

@ -0,0 +1,229 @@
/*
* 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);
if(spd->pkgl_id)
packagelist_destroy(spd->pkgl_id);
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);
end_writeback(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 vfsmount *mnt)
{
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(mnt->mnt_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->derive == DERIVE_NONE)
seq_printf(m, ",derive=none");
else if (opts->derive == DERIVE_LEGACY)
seq_printf(m, ",derive=legacy");
else if (opts->derive == DERIVE_UNIFIED)
seq_printf(m, ",derive=unified");
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

@ -55,6 +55,9 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
#define LOOKUP_JUMPED 0x1000
#define LOOKUP_ROOT 0x2000
#define LOOKUP_EMPTY 0x4000
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
#define LOOKUP_CASE_INSENSITIVE 0x8000
#endif
extern int user_path_at(int, const char __user *, unsigned, struct path *);
extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);

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