/* * There is no need to lock the esdfs_super_info's rwsem as there is no * way anyone can have a reference to the superblock at this point in time. */ static int esdfs_read_super(struct super_block *sb, const char *dev_name, void *raw_data, int silent) { int err = 0; struct super_block *lower_sb; struct path lower_path; struct esdfs_sb_info *sbi; struct inode *inode; if (!dev_name) { esdfs_msg(sb, KERN_ERR, "missing dev_name argument\n"); err = -EINVAL; goto out; } /* parse lower path */ err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &lower_path); if (err) { esdfs_msg(sb, KERN_ERR, "error accessing lower directory '%s'\n", dev_name); goto out; } /* allocate superblock private data */ sb->s_fs_info = kzalloc(sizeof(struct esdfs_sb_info), GFP_KERNEL); sbi = ESDFS_SB(sb); if (!sbi) { esdfs_msg(sb, KERN_CRIT, "read_super: out of memory\n"); err = -ENOMEM; goto out_pput; } /* set defaults and then parse the mount options */ memcpy(&sbi->lower_perms, &esdfs_perms_table[ESDFS_PERMS_LOWER_DEFAULT], sizeof(struct esdfs_perms)); memcpy(&sbi->upper_perms, &esdfs_perms_table[ESDFS_PERMS_UPPER_LEGACY], sizeof(struct esdfs_perms)); err = parse_options(sb, (char *)raw_data); if (err) goto out_free; /* set the lower superblock field of upper superblock */ lower_sb = lower_path.dentry->d_sb; atomic_inc(&lower_sb->s_active); esdfs_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_op = &esdfs_sops; /* get a new inode and allocate our root dentry */ inode = esdfs_iget(sb, lower_path.dentry->d_inode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_sput; } sb->s_root = d_make_root(inode); if (!sb->s_root) { err = -ENOMEM; goto out_iput; } d_set_d_op(sb->s_root, &esdfs_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; /* if get here: cannot have error */ /* set the lower dentries for s_root */ esdfs_set_lower_path(sb->s_root, &lower_path); #ifdef CONFIG_SECURITY_SELINUX security_secctx_to_secid(ESDFS_LOWER_SECCTX, strlen(ESDFS_LOWER_SECCTX), &sbi->lower_secid); #endif /* * 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); if (!silent) esdfs_msg(sb, KERN_INFO, "mounted on top of %s type %s\n", dev_name, lower_sb->s_type->name); if (!ESDFS_DERIVE_PERMS(sbi)) goto out; /* let user know that we ignore this option in derived mode */ if (memcmp(&sbi->upper_perms, &esdfs_perms_table[ESDFS_PERMS_UPPER_LEGACY], sizeof(struct esdfs_perms))) esdfs_msg(sb, KERN_WARNING, "'upper' mount option ignored in derived mode\n"); /* all derived modes start with the same, basic root */ memcpy(&sbi->upper_perms, &esdfs_perms_table[ESDFS_PERMS_UPPER_DERIVED], sizeof(struct esdfs_perms)); /* * In Android 3.0 all user conent in the emulated storage tree was * stored in /data/media. Android 4.2 introduced multi-user support, * which required that the primary user's content be migrated from * /data/media to /data/media/0. The framework then uses bind mounts * to create per-process namespaces to isolate each user's tree at * /data/media/N. This approach of having each user in a common root * is now considered "legacy" by the sdcard service. */ if (test_opt(sbi, DERIVE_LEGACY)) { ESDFS_I(inode)->tree = ESDFS_TREE_ROOT_LEGACY; sbi->obb_parent = dget(sb->s_root); /* * Android 4.4 reorganized this sturcture yet again, so that the * primary user's content was again at the root. Secondary users' * content is found in Android/user/N. Emulated internal storage still * seems to use the legacy tree, but secondary external storage uses * this method. */ } else if (test_opt(sbi, DERIVE_UNIFIED)) ESDFS_I(inode)->tree = ESDFS_TREE_ROOT; /* * Later versions of Android organize user content using quantum * entanglement, which has a low probability of being supported by * this driver. */ else esdfs_msg(sb, KERN_WARNING, "unsupported derived permissions mode\n"); /* initialize root inode */ esdfs_derive_perms(sb->s_root); goto out; out_freeroot: dput(sb->s_root); out_iput: iput(inode); out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); out_free: kfree(ESDFS_SB(sb)); sb->s_fs_info = NULL; out_pput: path_put(&lower_path); out: return err; }
static void esdfs_destroy_inode(struct inode *inode) { kmem_cache_free(esdfs_inode_cachep, ESDFS_I(inode)); }