Initial port of sdcardfs
authorDaniel Campello <campello@google.com>
Fri, 19 Jun 2015 21:31:25 +0000 (14:31 -0700)
committerStricted <info@stricted.net>
Thu, 11 Oct 2018 16:03:12 +0000 (18:03 +0200)
Change-Id: I5b5772a2bbff9f3a7dda641644630a7b8afacec0

 Conflicts:
include/linux/namei.h

fixed-by: vlw <vlwwwwww@gmail.com>

15 files changed:
fs/sdcardfs/Kconfig [new file with mode: 0644]
fs/sdcardfs/Makefile [new file with mode: 0644]
fs/sdcardfs/dentry.c [new file with mode: 0644]
fs/sdcardfs/derived_perm.c [new file with mode: 0644]
fs/sdcardfs/file.c [new file with mode: 0644]
fs/sdcardfs/hashtable.h [new file with mode: 0644]
fs/sdcardfs/inode.c [new file with mode: 0644]
fs/sdcardfs/lookup.c [new file with mode: 0644]
fs/sdcardfs/main.c [new file with mode: 0644]
fs/sdcardfs/mmap.c [new file with mode: 0644]
fs/sdcardfs/multiuser.h [new file with mode: 0644]
fs/sdcardfs/packagelist.c [new file with mode: 0644]
fs/sdcardfs/sdcardfs.h [new file with mode: 0644]
fs/sdcardfs/strtok.h [new file with mode: 0644]
fs/sdcardfs/super.c [new file with mode: 0644]

diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig
new file mode 100644 (file)
index 0000000..657f495
--- /dev/null
@@ -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
diff --git a/fs/sdcardfs/Makefile b/fs/sdcardfs/Makefile
new file mode 100644 (file)
index 0000000..b84fbb2
--- /dev/null
@@ -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
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
new file mode 100644 (file)
index 0000000..4572a54
--- /dev/null
@@ -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,
+};
+
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
new file mode 100644 (file)
index 0000000..00c33a4
--- /dev/null
@@ -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;
+}
+
+
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
new file mode 100644 (file)
index 0000000..bcacb94
--- /dev/null
@@ -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,
+};
diff --git a/fs/sdcardfs/hashtable.h b/fs/sdcardfs/hashtable.h
new file mode 100644 (file)
index 0000000..1e770f3
--- /dev/null
@@ -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
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
new file mode 100644 (file)
index 0000000..e8ed042
--- /dev/null
@@ -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,
+};
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
new file mode 100644 (file)
index 0000000..c0b1237
--- /dev/null
@@ -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;
+}
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
new file mode 100644 (file)
index 0000000..860bfbc
--- /dev/null
@@ -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, 00664);
+                               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);
diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c
new file mode 100644 (file)
index 0000000..c807d7f
--- /dev/null
@@ -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,
+};
diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h
new file mode 100644 (file)
index 0000000..923ba10
--- /dev/null
@@ -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);
+}
+
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
new file mode 100644 (file)
index 0000000..c786d8f
--- /dev/null
@@ -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);
+}
+
+
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
new file mode 100644 (file)
index 0000000..90f8b24
--- /dev/null
@@ -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_ */
diff --git a/fs/sdcardfs/strtok.h b/fs/sdcardfs/strtok.h
new file mode 100644 (file)
index 0000000..50ab25a
--- /dev/null
@@ -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 */
+}
+
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
new file mode 100644 (file)
index 0000000..1d206c8
--- /dev/null
@@ -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,
+};