123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- /*
- * fs/sdcardfs/main.c
- *
- * Copyright (c) 2013 Samsung Electronics Co. Ltd
- * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
- * Sunghwan Yun, Sungjong Seo
- *
- * This program has been developed as a stackable file system based on
- * the WrapFS which written by
- *
- * Copyright (c) 1998-2011 Erez Zadok
- * Copyright (c) 2009 Shrikar Archak
- * Copyright (c) 2003-2011 Stony Brook University
- * Copyright (c) 2003-2011 The Research Foundation of SUNY
- *
- * This file is dual licensed. It may be redistributed and/or modified
- * under the terms of the Apache 2.0 License OR version 2 of the GNU
- * General Public License.
- */
- #include "sdcardfs.h"
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/parser.h>
- enum {
- Opt_fsuid,
- Opt_fsgid,
- Opt_gid,
- Opt_debug,
- Opt_mask,
- Opt_multiuser,
- Opt_userid,
- Opt_reserved_mb,
- Opt_gid_derivation,
- Opt_default_normal,
- Opt_unshared_obb,
- Opt_nocache,
- Opt_err,
- };
- static const match_table_t sdcardfs_tokens = {
- {Opt_fsuid, "fsuid=%u"},
- {Opt_fsgid, "fsgid=%u"},
- {Opt_gid, "gid=%u"},
- {Opt_debug, "debug"},
- {Opt_mask, "mask=%u"},
- {Opt_userid, "userid=%d"},
- {Opt_multiuser, "multiuser"},
- {Opt_gid_derivation, "derive_gid"},
- {Opt_default_normal, "default_normal"},
- {Opt_unshared_obb, "unshared_obb"},
- {Opt_reserved_mb, "reserved_mb=%u"},
- {Opt_nocache, "nocache"},
- {Opt_err, NULL}
- };
- static int parse_options(struct super_block *sb, char *options, int silent,
- int *debug, struct sdcardfs_vfsmount_options *vfsopts,
- struct sdcardfs_mount_options *opts)
- {
- char *p;
- substring_t args[MAX_OPT_ARGS];
- int option;
- /* by default, we use AID_MEDIA_RW as uid, gid */
- opts->fs_low_uid = AID_MEDIA_RW;
- opts->fs_low_gid = AID_MEDIA_RW;
- vfsopts->mask = 0;
- opts->multiuser = false;
- opts->fs_user_id = 0;
- vfsopts->gid = 0;
- /* by default, 0MB is reserved */
- opts->reserved_mb = 0;
- /* by default, gid derivation is off */
- opts->gid_derivation = false;
- opts->default_normal = false;
- opts->nocache = false;
- *debug = 0;
- if (!options)
- return 0;
- while ((p = strsep(&options, ",")) != NULL) {
- int token;
- if (!*p)
- continue;
- token = match_token(p, sdcardfs_tokens, args);
- switch (token) {
- case Opt_debug:
- *debug = 1;
- break;
- case Opt_fsuid:
- if (match_int(&args[0], &option))
- return 0;
- opts->fs_low_uid = option;
- break;
- case Opt_fsgid:
- if (match_int(&args[0], &option))
- return 0;
- opts->fs_low_gid = option;
- break;
- case Opt_gid:
- if (match_int(&args[0], &option))
- return 0;
- vfsopts->gid = option;
- break;
- case Opt_userid:
- if (match_int(&args[0], &option))
- return 0;
- opts->fs_user_id = option;
- break;
- case Opt_mask:
- if (match_int(&args[0], &option))
- return 0;
- vfsopts->mask = option;
- break;
- case Opt_multiuser:
- opts->multiuser = true;
- break;
- case Opt_reserved_mb:
- if (match_int(&args[0], &option))
- return 0;
- opts->reserved_mb = option;
- break;
- case Opt_gid_derivation:
- opts->gid_derivation = true;
- break;
- case Opt_default_normal:
- opts->default_normal = true;
- break;
- case Opt_unshared_obb:
- opts->unshared_obb = true;
- break;
- case Opt_nocache:
- opts->nocache = true;
- break;
- /* unknown option */
- default:
- if (!silent)
- pr_err("Unrecognized mount option \"%s\" or missing value", p);
- return -EINVAL;
- }
- }
- if (*debug) {
- pr_info("sdcardfs : options - debug:%d\n", *debug);
- pr_info("sdcardfs : options - uid:%d\n",
- opts->fs_low_uid);
- pr_info("sdcardfs : options - gid:%d\n",
- opts->fs_low_gid);
- }
- return 0;
- }
- int parse_options_remount(struct super_block *sb, char *options, int silent,
- struct sdcardfs_vfsmount_options *vfsopts)
- {
- char *p;
- substring_t args[MAX_OPT_ARGS];
- int option;
- int debug;
- if (!options)
- return 0;
- while ((p = strsep(&options, ",")) != NULL) {
- int token;
- if (!*p)
- continue;
- token = match_token(p, sdcardfs_tokens, args);
- switch (token) {
- case Opt_debug:
- debug = 1;
- break;
- case Opt_gid:
- if (match_int(&args[0], &option))
- return 0;
- vfsopts->gid = option;
- break;
- case Opt_mask:
- if (match_int(&args[0], &option))
- return 0;
- vfsopts->mask = option;
- break;
- case Opt_unshared_obb:
- case Opt_default_normal:
- case Opt_multiuser:
- case Opt_userid:
- case Opt_fsuid:
- case Opt_fsgid:
- case Opt_reserved_mb:
- case Opt_gid_derivation:
- if (!silent)
- pr_warn("Option \"%s\" can't be changed during remount\n", p);
- break;
- /* unknown option */
- default:
- if (!silent)
- pr_err("Unrecognized mount option \"%s\" or missing value", p);
- return -EINVAL;
- }
- }
- if (debug) {
- pr_info("sdcardfs : options - debug:%d\n", debug);
- pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid);
- pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask);
- }
- return 0;
- }
- #if 0
- /*
- * our custom d_alloc_root work-alike
- *
- * we can't use d_alloc_root if we want to use our own interpose function
- * unchanged, so we simply call our own "fake" d_alloc_root
- */
- static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb)
- {
- struct dentry *ret = NULL;
- if (sb) {
- static const struct qstr name = QSTR_INIT("/", 1);
- ret = d_alloc(NULL, &name);
- if (ret) {
- d_set_d_op(ret, &sdcardfs_ci_dops);
- ret->d_sb = sb;
- ret->d_parent = ret;
- }
- }
- return ret;
- }
- #endif
- DEFINE_MUTEX(sdcardfs_super_list_lock);
- EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock);
- LIST_HEAD(sdcardfs_super_list);
- EXPORT_SYMBOL_GPL(sdcardfs_super_list);
- /*
- * There is no need to lock the sdcardfs_super_info's rwsem as there is no
- * way anyone can have a reference to the superblock at this point in time.
- */
- static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
- const char *dev_name, void *raw_data, int silent)
- {
- int err = 0;
- int debug;
- struct super_block *lower_sb;
- struct path lower_path;
- struct sdcardfs_sb_info *sb_info;
- struct sdcardfs_vfsmount_options *mnt_opt = mnt->data;
- struct inode *inode;
- pr_info("sdcardfs version 2.0\n");
- if (!dev_name) {
- pr_err("sdcardfs: read_super: missing dev_name argument\n");
- err = -EINVAL;
- goto out;
- }
- pr_info("sdcardfs: dev_name -> %s\n", dev_name);
- pr_info("sdcardfs: options -> %s\n", (char *)raw_data);
- pr_info("sdcardfs: mnt -> %p\n", mnt);
- /* parse lower path */
- err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
- &lower_path);
- if (err) {
- pr_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)) {
- pr_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, mnt_opt, &sb_info->options);
- if (err) {
- pr_err("sdcardfs: invalid options\n");
- goto out_freesbi;
- }
- /* set the lower superblock field of upper superblock */
- lower_sb = lower_path.dentry->d_sb;
- atomic_inc(&lower_sb->s_active);
- sdcardfs_set_lower_super(sb, lower_sb);
- sb->s_stack_depth = lower_sb->s_stack_depth + 1;
- if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
- pr_err("sdcardfs: maximum fs stacking depth exceeded\n");
- err = -EINVAL;
- goto out_sput;
- }
- /* inherit maxbytes from lower file system */
- sb->s_maxbytes = lower_sb->s_maxbytes;
- /*
- * Our c/m/atime granularity is 1 ns because we may stack on file
- * systems whose granularity is as good.
- */
- sb->s_time_gran = 1;
- sb->s_magic = SDCARDFS_SUPER_MAGIC;
- sb->s_op = &sdcardfs_sops;
- /* get a new inode and allocate our root dentry */
- inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out_sput;
- }
- sb->s_root = d_make_root(inode);
- if (!sb->s_root) {
- err = -ENOMEM;
- goto out_sput;
- }
- d_set_d_op(sb->s_root, &sdcardfs_ci_dops);
- /* link the upper and lower dentries */
- sb->s_root->d_fsdata = NULL;
- err = new_dentry_private_data(sb->s_root);
- if (err)
- goto out_freeroot;
- /* set the lower dentries for s_root */
- sdcardfs_set_lower_path(sb->s_root, &lower_path);
- /*
- * No need to call interpose because we already have a positive
- * dentry, which was instantiated by d_make_root. Just need to
- * d_rehash it.
- */
- d_rehash(sb->s_root);
- /* setup permission policy */
- sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
- mutex_lock(&sdcardfs_super_list_lock);
- if (sb_info->options.multiuser) {
- setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT,
- sb_info->options.fs_user_id, AID_ROOT);
- snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
- } else {
- setup_derived_state(sb->s_root->d_inode, PERM_ROOT,
- sb_info->options.fs_user_id, AID_ROOT);
- snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
- }
- fixup_tmp_permissions(sb->s_root->d_inode);
- sb_info->sb = sb;
- list_add(&sb_info->list, &sdcardfs_super_list);
- mutex_unlock(&sdcardfs_super_list_lock);
- if (!silent)
- pr_info("sdcardfs: mounted on top of %s type %s\n",
- dev_name, lower_sb->s_type->name);
- goto out; /* all is well */
- /* no longer needed: free_dentry_private_data(sb->s_root); */
- out_freeroot:
- dput(sb->s_root);
- sb->s_root = NULL;
- out_sput:
- /* drop refs we took earlier */
- atomic_dec(&lower_sb->s_active);
- out_freesbi:
- kfree(SDCARDFS_SB(sb));
- sb->s_fs_info = NULL;
- out_free:
- path_put(&lower_path);
- out:
- return err;
- }
- struct sdcardfs_mount_private {
- struct vfsmount *mnt;
- const char *dev_name;
- void *raw_data;
- };
- static int __sdcardfs_fill_super(
- struct super_block *sb,
- void *_priv, int silent)
- {
- struct sdcardfs_mount_private *priv = _priv;
- return sdcardfs_read_super(priv->mnt,
- sb, priv->dev_name, priv->raw_data, silent);
- }
- static struct dentry *sdcardfs_mount(struct vfsmount *mnt,
- struct file_system_type *fs_type, int flags,
- const char *dev_name, void *raw_data)
- {
- struct sdcardfs_mount_private priv = {
- .mnt = mnt,
- .dev_name = dev_name,
- .raw_data = raw_data
- };
- return mount_nodev(fs_type, flags,
- &priv, __sdcardfs_fill_super);
- }
- static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *raw_data)
- {
- WARN(1, "sdcardfs does not support mount. Use mount2.\n");
- return ERR_PTR(-EINVAL);
- }
- void *sdcardfs_alloc_mnt_data(void)
- {
- return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
- }
- void sdcardfs_kill_sb(struct super_block *sb)
- {
- struct sdcardfs_sb_info *sbi;
- if (sb->s_magic == SDCARDFS_SUPER_MAGIC && sb->s_fs_info) {
- sbi = SDCARDFS_SB(sb);
- mutex_lock(&sdcardfs_super_list_lock);
- list_del(&sbi->list);
- mutex_unlock(&sdcardfs_super_list_lock);
- }
- kill_anon_super(sb);
- }
- static struct file_system_type sdcardfs_fs_type = {
- .owner = THIS_MODULE,
- .name = SDCARDFS_NAME,
- .mount = sdcardfs_mount_wrn,
- .mount2 = sdcardfs_mount,
- .alloc_mnt_data = sdcardfs_alloc_mnt_data,
- .kill_sb = sdcardfs_kill_sb,
- .fs_flags = 0,
- };
- MODULE_ALIAS_FS(SDCARDFS_NAME);
- 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");
- }
- /* Original wrapfs authors */
- MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)");
- /* Original sdcardfs authors */
- MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics");
- /* Current maintainer */
- MODULE_AUTHOR("Daniel Rosenberg, Google");
- MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION);
- MODULE_LICENSE("GPL");
- module_init(init_sdcardfs_fs);
- module_exit(exit_sdcardfs_fs);
|