Restore sdcardfs feature
4da740cRevert "ANDROID: fscrypt: add key removal notifier chain"9b733ddRevert "ANDROID: sdcardfs: Enable modular sdcardfs"3b7ef78Revert "ANDROID: vfs: Add setattr2 for filesystems with per mount permissions"7df42051Revert "ANDROID: vfs: fix export symbol type"633920fRevert "ANDROID: vfs: Add permission2 for filesystems with per mount permissions"d60170fRevert "ANDROID: vfs: fix export symbol types"fc411cfRevert "ANDROID: vfs: add d_canonical_path for stacked filesystem support"0fb8b79Revert "ANDROID: fs: Restore vfs_path_lookup() export"7d8d128ANDROID: sdcardfs: remove sdcardfs from system` Change-Id: I675db7148a6a349d1ed630b506f26e77f55924cd Signed-off-by: Srinivasarao P <spathi@codeaurora.org>
This commit is contained in:
@@ -237,6 +237,7 @@ source "fs/orangefs/Kconfig"
|
|||||||
source "fs/adfs/Kconfig"
|
source "fs/adfs/Kconfig"
|
||||||
source "fs/affs/Kconfig"
|
source "fs/affs/Kconfig"
|
||||||
source "fs/ecryptfs/Kconfig"
|
source "fs/ecryptfs/Kconfig"
|
||||||
|
source "fs/sdcardfs/Kconfig"
|
||||||
source "fs/hfs/Kconfig"
|
source "fs/hfs/Kconfig"
|
||||||
source "fs/hfsplus/Kconfig"
|
source "fs/hfsplus/Kconfig"
|
||||||
source "fs/befs/Kconfig"
|
source "fs/befs/Kconfig"
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ obj-$(CONFIG_ISO9660_FS) += isofs/
|
|||||||
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+
|
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+
|
||||||
obj-$(CONFIG_HFS_FS) += hfs/
|
obj-$(CONFIG_HFS_FS) += hfs/
|
||||||
obj-$(CONFIG_ECRYPT_FS) += ecryptfs/
|
obj-$(CONFIG_ECRYPT_FS) += ecryptfs/
|
||||||
|
obj-$(CONFIG_SDCARD_FS) += sdcardfs/
|
||||||
obj-$(CONFIG_VXFS_FS) += freevxfs/
|
obj-$(CONFIG_VXFS_FS) += freevxfs/
|
||||||
obj-$(CONFIG_NFS_FS) += nfs/
|
obj-$(CONFIG_NFS_FS) += nfs/
|
||||||
obj-$(CONFIG_EXPORTFS) += exportfs/
|
obj-$(CONFIG_EXPORTFS) += exportfs/
|
||||||
|
|||||||
14
fs/attr.c
14
fs/attr.c
@@ -223,7 +223,7 @@ EXPORT_SYMBOL(setattr_copy);
|
|||||||
* the file open for write, as there can be no conflicting delegation in
|
* the file open for write, as there can be no conflicting delegation in
|
||||||
* that case.
|
* that case.
|
||||||
*/
|
*/
|
||||||
int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
|
int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
umode_t mode = inode->i_mode;
|
umode_t mode = inode->i_mode;
|
||||||
@@ -247,7 +247,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
|
|||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
if (!inode_owner_or_capable(inode)) {
|
if (!inode_owner_or_capable(inode)) {
|
||||||
error = inode_permission(inode, MAY_WRITE);
|
error = inode_permission2(mnt, inode, MAY_WRITE);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@@ -330,7 +330,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (inode->i_op->setattr)
|
if (mnt && inode->i_op->setattr2)
|
||||||
|
error = inode->i_op->setattr2(mnt, dentry, attr);
|
||||||
|
else if (inode->i_op->setattr)
|
||||||
error = inode->i_op->setattr(dentry, attr);
|
error = inode->i_op->setattr(dentry, attr);
|
||||||
else
|
else
|
||||||
error = simple_setattr(dentry, attr);
|
error = simple_setattr(dentry, attr);
|
||||||
@@ -343,4 +345,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
|
|||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(notify_change2);
|
||||||
|
|
||||||
|
int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
|
||||||
|
{
|
||||||
|
return notify_change2(NULL, dentry, attr, delegated_inode);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(notify_change);
|
EXPORT_SYMBOL(notify_change);
|
||||||
|
|||||||
@@ -742,7 +742,7 @@ void do_coredump(const siginfo_t *siginfo)
|
|||||||
goto close_fail;
|
goto close_fail;
|
||||||
if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
|
if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
|
||||||
goto close_fail;
|
goto close_fail;
|
||||||
if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))
|
if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file))
|
||||||
goto close_fail;
|
goto close_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -867,12 +867,34 @@ static int check_for_busy_inodes(struct super_block *sb,
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BLOCKING_NOTIFIER_HEAD(fscrypt_key_removal_notifiers);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register a function to be executed when the FS_IOC_REMOVE_ENCRYPTION_KEY
|
||||||
|
* ioctl has removed a key and is about to try evicting inodes.
|
||||||
|
*/
|
||||||
|
int fscrypt_register_key_removal_notifier(struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return blocking_notifier_chain_register(&fscrypt_key_removal_notifiers,
|
||||||
|
nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fscrypt_register_key_removal_notifier);
|
||||||
|
|
||||||
|
int fscrypt_unregister_key_removal_notifier(struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return blocking_notifier_chain_unregister(&fscrypt_key_removal_notifiers,
|
||||||
|
nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fscrypt_unregister_key_removal_notifier);
|
||||||
|
|
||||||
static int try_to_lock_encrypted_files(struct super_block *sb,
|
static int try_to_lock_encrypted_files(struct super_block *sb,
|
||||||
struct fscrypt_master_key *mk)
|
struct fscrypt_master_key *mk)
|
||||||
{
|
{
|
||||||
int err1;
|
int err1;
|
||||||
int err2;
|
int err2;
|
||||||
|
|
||||||
|
blocking_notifier_call_chain(&fscrypt_key_removal_notifiers, 0, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An inode can't be evicted while it is dirty or has dirty pages.
|
* An inode can't be evicted while it is dirty or has dirty pages.
|
||||||
* Thus, we first have to clean the inodes in ->mk_decrypted_inodes.
|
* Thus, we first have to clean the inodes in ->mk_decrypted_inodes.
|
||||||
|
|||||||
@@ -1310,7 +1310,7 @@ EXPORT_SYMBOL(flush_old_exec);
|
|||||||
void would_dump(struct linux_binprm *bprm, struct file *file)
|
void would_dump(struct linux_binprm *bprm, struct file *file)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
if (inode_permission(inode, MAY_READ) < 0) {
|
if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) {
|
||||||
struct user_namespace *old, *user_ns;
|
struct user_namespace *old, *user_ns;
|
||||||
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
|
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ void set_fs_pwd(struct fs_struct *fs, const struct path *path)
|
|||||||
if (old_pwd.dentry)
|
if (old_pwd.dentry)
|
||||||
path_put(&old_pwd);
|
path_put(&old_pwd);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(set_fs_pwd);
|
||||||
|
|
||||||
static inline int replace_path(struct path *p, const struct path *old, const struct path *new)
|
static inline int replace_path(struct path *p, const struct path *old, const struct path *new)
|
||||||
{
|
{
|
||||||
@@ -90,6 +91,7 @@ void free_fs_struct(struct fs_struct *fs)
|
|||||||
path_put(&fs->pwd);
|
path_put(&fs->pwd);
|
||||||
kmem_cache_free(fs_cachep, fs);
|
kmem_cache_free(fs_cachep, fs);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(free_fs_struct);
|
||||||
|
|
||||||
void exit_fs(struct task_struct *tsk)
|
void exit_fs(struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
@@ -128,6 +130,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
|
|||||||
}
|
}
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(copy_fs_struct);
|
||||||
|
|
||||||
int unshare_fs_struct(void)
|
int unshare_fs_struct(void)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1803,7 +1803,7 @@ int dentry_needs_remove_privs(struct dentry *dentry)
|
|||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __remove_privs(struct dentry *dentry, int kill)
|
static int __remove_privs(struct vfsmount *mnt, struct dentry *dentry, int kill)
|
||||||
{
|
{
|
||||||
struct iattr newattrs;
|
struct iattr newattrs;
|
||||||
|
|
||||||
@@ -1812,7 +1812,7 @@ static int __remove_privs(struct dentry *dentry, int kill)
|
|||||||
* Note we call this on write, so notify_change will not
|
* Note we call this on write, so notify_change will not
|
||||||
* encounter any conflicting delegations:
|
* encounter any conflicting delegations:
|
||||||
*/
|
*/
|
||||||
return notify_change(dentry, &newattrs, NULL);
|
return notify_change2(mnt, dentry, &newattrs, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1839,7 +1839,7 @@ int file_remove_privs(struct file *file)
|
|||||||
if (kill < 0)
|
if (kill < 0)
|
||||||
return kill;
|
return kill;
|
||||||
if (kill)
|
if (kill)
|
||||||
error = __remove_privs(dentry, kill);
|
error = __remove_privs(file->f_path.mnt, dentry, kill);
|
||||||
if (!error)
|
if (!error)
|
||||||
inode_has_no_xattr(inode);
|
inode_has_no_xattr(inode);
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ extern void __init chrdev_init(void);
|
|||||||
* namei.c
|
* namei.c
|
||||||
*/
|
*/
|
||||||
extern int user_path_mountpoint_at(int, const char __user *, unsigned int, struct path *);
|
extern int user_path_mountpoint_at(int, const char __user *, unsigned int, struct path *);
|
||||||
extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
|
|
||||||
const char *, unsigned int, struct path *);
|
|
||||||
long do_mknodat(int dfd, const char __user *filename, umode_t mode,
|
long do_mknodat(int dfd, const char __user *filename, umode_t mode,
|
||||||
unsigned int dev);
|
unsigned int dev);
|
||||||
long do_mkdirat(int dfd, const char __user *pathname, umode_t mode);
|
long do_mkdirat(int dfd, const char __user *pathname, umode_t mode);
|
||||||
|
|||||||
189
fs/namei.c
189
fs/namei.c
@@ -382,9 +382,11 @@ EXPORT_SYMBOL(generic_permission);
|
|||||||
* flag in inode->i_opflags, that says "this has not special
|
* flag in inode->i_opflags, that says "this has not special
|
||||||
* permission function, use the fast case".
|
* permission function, use the fast case".
|
||||||
*/
|
*/
|
||||||
static inline int do_inode_permission(struct inode *inode, int mask)
|
static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||||
{
|
{
|
||||||
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
|
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
|
||||||
|
if (likely(mnt && inode->i_op->permission2))
|
||||||
|
return inode->i_op->permission2(mnt, inode, mask);
|
||||||
if (likely(inode->i_op->permission))
|
if (likely(inode->i_op->permission))
|
||||||
return inode->i_op->permission(inode, mask);
|
return inode->i_op->permission(inode, mask);
|
||||||
|
|
||||||
@@ -417,7 +419,8 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inode_permission - Check for access rights to a given inode
|
* inode_permission2 - Check for access rights to a given inode
|
||||||
|
* @mnt:
|
||||||
* @inode: Inode to check permission on
|
* @inode: Inode to check permission on
|
||||||
* @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
|
* @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
|
||||||
*
|
*
|
||||||
@@ -427,7 +430,7 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
|
|||||||
*
|
*
|
||||||
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
|
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
|
||||||
*/
|
*/
|
||||||
int inode_permission(struct inode *inode, int mask)
|
int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
@@ -451,7 +454,7 @@ int inode_permission(struct inode *inode, int mask)
|
|||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = do_inode_permission(inode, mask);
|
retval = do_inode_permission(mnt, inode, mask);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
@@ -459,7 +462,14 @@ int inode_permission(struct inode *inode, int mask)
|
|||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
return security_inode_permission(inode, mask);
|
retval = security_inode_permission(inode, mask);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(inode_permission2);
|
||||||
|
|
||||||
|
int inode_permission(struct inode *inode, int mask)
|
||||||
|
{
|
||||||
|
return inode_permission2(NULL, inode, mask);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(inode_permission);
|
EXPORT_SYMBOL(inode_permission);
|
||||||
|
|
||||||
@@ -1778,13 +1788,13 @@ static struct dentry *lookup_slow(const struct qstr *name,
|
|||||||
static inline int may_lookup(struct nameidata *nd)
|
static inline int may_lookup(struct nameidata *nd)
|
||||||
{
|
{
|
||||||
if (nd->flags & LOOKUP_RCU) {
|
if (nd->flags & LOOKUP_RCU) {
|
||||||
int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
|
int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
|
||||||
if (err != -ECHILD)
|
if (err != -ECHILD)
|
||||||
return err;
|
return err;
|
||||||
if (unlazy_walk(nd))
|
if (unlazy_walk(nd))
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
}
|
}
|
||||||
return inode_permission(nd->inode, MAY_EXEC);
|
return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int handle_dots(struct nameidata *nd, int type)
|
static inline int handle_dots(struct nameidata *nd, int type)
|
||||||
@@ -2540,8 +2550,8 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(vfs_path_lookup);
|
EXPORT_SYMBOL(vfs_path_lookup);
|
||||||
|
|
||||||
static int lookup_one_len_common(const char *name, struct dentry *base,
|
static int lookup_one_len_common(const char *name, struct vfsmount *mnt,
|
||||||
int len, struct qstr *this)
|
struct dentry *base, int len, struct qstr *this)
|
||||||
{
|
{
|
||||||
this->name = name;
|
this->name = name;
|
||||||
this->len = len;
|
this->len = len;
|
||||||
@@ -2569,7 +2579,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return inode_permission(base->d_inode, MAY_EXEC);
|
return inode_permission2(mnt, base->d_inode, MAY_EXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2593,7 +2603,7 @@ struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len
|
|||||||
|
|
||||||
WARN_ON_ONCE(!inode_is_locked(base->d_inode));
|
WARN_ON_ONCE(!inode_is_locked(base->d_inode));
|
||||||
|
|
||||||
err = lookup_one_len_common(name, base, len, &this);
|
err = lookup_one_len_common(name, NULL, base, len, &this);
|
||||||
if (err)
|
if (err)
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
|
|
||||||
@@ -2612,7 +2622,7 @@ EXPORT_SYMBOL(try_lookup_one_len);
|
|||||||
*
|
*
|
||||||
* The caller must hold base->i_mutex.
|
* The caller must hold base->i_mutex.
|
||||||
*/
|
*/
|
||||||
struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len)
|
||||||
{
|
{
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
struct qstr this;
|
struct qstr this;
|
||||||
@@ -2620,13 +2630,19 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
|||||||
|
|
||||||
WARN_ON_ONCE(!inode_is_locked(base->d_inode));
|
WARN_ON_ONCE(!inode_is_locked(base->d_inode));
|
||||||
|
|
||||||
err = lookup_one_len_common(name, base, len, &this);
|
err = lookup_one_len_common(name, mnt, base, len, &this);
|
||||||
if (err)
|
if (err)
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
|
|
||||||
dentry = lookup_dcache(&this, base, 0);
|
dentry = lookup_dcache(&this, base, 0);
|
||||||
return dentry ? dentry : __lookup_slow(&this, base, 0);
|
return dentry ? dentry : __lookup_slow(&this, base, 0);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(lookup_one_len2);
|
||||||
|
|
||||||
|
struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
||||||
|
{
|
||||||
|
return lookup_one_len2(name, NULL, base, len);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(lookup_one_len);
|
EXPORT_SYMBOL(lookup_one_len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2648,7 +2664,7 @@ struct dentry *lookup_one_len_unlocked(const char *name,
|
|||||||
int err;
|
int err;
|
||||||
struct dentry *ret;
|
struct dentry *ret;
|
||||||
|
|
||||||
err = lookup_one_len_common(name, base, len, &this);
|
err = lookup_one_len_common(name, NULL, base, len, &this);
|
||||||
if (err)
|
if (err)
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
|
|
||||||
@@ -2872,7 +2888,7 @@ EXPORT_SYMBOL(__check_sticky);
|
|||||||
* 11. We don't allow removal of NFS sillyrenamed files; it's handled by
|
* 11. We don't allow removal of NFS sillyrenamed files; it's handled by
|
||||||
* nfs_async_unlink().
|
* nfs_async_unlink().
|
||||||
*/
|
*/
|
||||||
static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *victim, bool isdir)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_backing_inode(victim);
|
struct inode *inode = d_backing_inode(victim);
|
||||||
int error;
|
int error;
|
||||||
@@ -2889,7 +2905,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
|||||||
|
|
||||||
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
|
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
|
||||||
|
|
||||||
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
if (IS_APPEND(dir))
|
if (IS_APPEND(dir))
|
||||||
@@ -2921,7 +2937,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
|||||||
* 4. We should have write and exec permissions on dir
|
* 4. We should have write and exec permissions on dir
|
||||||
* 5. We can't do it if dir is immutable (done in permission())
|
* 5. We can't do it if dir is immutable (done in permission())
|
||||||
*/
|
*/
|
||||||
static inline int may_create(struct inode *dir, struct dentry *child)
|
static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child)
|
||||||
{
|
{
|
||||||
struct user_namespace *s_user_ns;
|
struct user_namespace *s_user_ns;
|
||||||
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
|
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
|
||||||
@@ -2933,7 +2949,7 @@ static inline int may_create(struct inode *dir, struct dentry *child)
|
|||||||
if (!kuid_has_mapping(s_user_ns, current_fsuid()) ||
|
if (!kuid_has_mapping(s_user_ns, current_fsuid()) ||
|
||||||
!kgid_has_mapping(s_user_ns, current_fsgid()))
|
!kgid_has_mapping(s_user_ns, current_fsgid()))
|
||||||
return -EOVERFLOW;
|
return -EOVERFLOW;
|
||||||
return inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2980,10 +2996,10 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(unlock_rename);
|
EXPORT_SYMBOL(unlock_rename);
|
||||||
|
|
||||||
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry,
|
||||||
bool want_excl)
|
umode_t mode, bool want_excl)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(mnt, dir, dentry);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@@ -2999,14 +3015,21 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_create2);
|
||||||
|
|
||||||
|
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
|
bool want_excl)
|
||||||
|
{
|
||||||
|
return vfs_create2(NULL, dir, dentry, mode, want_excl);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_create);
|
EXPORT_SYMBOL(vfs_create);
|
||||||
|
|
||||||
int vfs_mkobj(struct dentry *dentry, umode_t mode,
|
int vfs_mkobj2(struct vfsmount *mnt, struct dentry *dentry, umode_t mode,
|
||||||
int (*f)(struct dentry *, umode_t, void *),
|
int (*f)(struct dentry *, umode_t, void *),
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
struct inode *dir = dentry->d_parent->d_inode;
|
struct inode *dir = dentry->d_parent->d_inode;
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(mnt, dir, dentry);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@@ -3020,6 +3043,15 @@ int vfs_mkobj(struct dentry *dentry, umode_t mode,
|
|||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_mkobj2);
|
||||||
|
|
||||||
|
|
||||||
|
int vfs_mkobj(struct dentry *dentry, umode_t mode,
|
||||||
|
int (*f)(struct dentry *, umode_t, void *),
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
return vfs_mkobj2(NULL, dentry, mode, f, arg);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_mkobj);
|
EXPORT_SYMBOL(vfs_mkobj);
|
||||||
|
|
||||||
bool may_open_dev(const struct path *path)
|
bool may_open_dev(const struct path *path)
|
||||||
@@ -3031,6 +3063,7 @@ bool may_open_dev(const struct path *path)
|
|||||||
static int may_open(const struct path *path, int acc_mode, int flag)
|
static int may_open(const struct path *path, int acc_mode, int flag)
|
||||||
{
|
{
|
||||||
struct dentry *dentry = path->dentry;
|
struct dentry *dentry = path->dentry;
|
||||||
|
struct vfsmount *mnt = path->mnt;
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
@@ -3055,7 +3088,7 @@ static int may_open(const struct path *path, int acc_mode, int flag)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = inode_permission(inode, MAY_OPEN | acc_mode);
|
error = inode_permission2(mnt, inode, MAY_OPEN | acc_mode);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@@ -3090,7 +3123,7 @@ static int handle_truncate(struct file *filp)
|
|||||||
if (!error)
|
if (!error)
|
||||||
error = security_path_truncate(path);
|
error = security_path_truncate(path);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
error = do_truncate(path->dentry, 0,
|
error = do_truncate2(path->mnt, path->dentry, 0,
|
||||||
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
|
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
|
||||||
filp);
|
filp);
|
||||||
}
|
}
|
||||||
@@ -3117,7 +3150,7 @@ static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t m
|
|||||||
!kgid_has_mapping(s_user_ns, current_fsgid()))
|
!kgid_has_mapping(s_user_ns, current_fsgid()))
|
||||||
return -EOVERFLOW;
|
return -EOVERFLOW;
|
||||||
|
|
||||||
error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
|
error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@@ -3527,7 +3560,8 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag)
|
|||||||
int error;
|
int error;
|
||||||
|
|
||||||
/* we want directory to be writable */
|
/* we want directory to be writable */
|
||||||
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
error = inode_permission2(ERR_PTR(-EOPNOTSUPP), dir,
|
||||||
|
MAY_WRITE | MAY_EXEC);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
error = -EOPNOTSUPP;
|
error = -EOPNOTSUPP;
|
||||||
@@ -3781,9 +3815,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(user_path_create);
|
EXPORT_SYMBOL(user_path_create);
|
||||||
|
|
||||||
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(mnt, dir, dentry);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@@ -3807,6 +3841,12 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
|||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_mknod2);
|
||||||
|
|
||||||
|
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||||
|
{
|
||||||
|
return vfs_mknod2(NULL, dir, dentry, mode, dev);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_mknod);
|
EXPORT_SYMBOL(vfs_mknod);
|
||||||
|
|
||||||
static int may_mknod(umode_t mode)
|
static int may_mknod(umode_t mode)
|
||||||
@@ -3849,12 +3889,12 @@ long do_mknodat(int dfd, const char __user *filename, umode_t mode,
|
|||||||
goto out;
|
goto out;
|
||||||
switch (mode & S_IFMT) {
|
switch (mode & S_IFMT) {
|
||||||
case 0: case S_IFREG:
|
case 0: case S_IFREG:
|
||||||
error = vfs_create(path.dentry->d_inode,dentry,mode,true);
|
error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true);
|
||||||
if (!error)
|
if (!error)
|
||||||
ima_post_path_mknod(dentry);
|
ima_post_path_mknod(dentry);
|
||||||
break;
|
break;
|
||||||
case S_IFCHR: case S_IFBLK:
|
case S_IFCHR: case S_IFBLK:
|
||||||
error = vfs_mknod(path.dentry->d_inode,dentry,mode,
|
error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode,
|
||||||
new_decode_dev(dev));
|
new_decode_dev(dev));
|
||||||
break;
|
break;
|
||||||
case S_IFIFO: case S_IFSOCK:
|
case S_IFIFO: case S_IFSOCK:
|
||||||
@@ -3881,9 +3921,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
|
|||||||
return do_mknodat(AT_FDCWD, filename, mode, dev);
|
return do_mknodat(AT_FDCWD, filename, mode, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(mnt, dir, dentry);
|
||||||
unsigned max_links = dir->i_sb->s_max_links;
|
unsigned max_links = dir->i_sb->s_max_links;
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
@@ -3905,6 +3945,12 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||||||
fsnotify_mkdir(dir, dentry);
|
fsnotify_mkdir(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_mkdir2);
|
||||||
|
|
||||||
|
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
|
{
|
||||||
|
return vfs_mkdir2(NULL, dir, dentry, mode);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_mkdir);
|
EXPORT_SYMBOL(vfs_mkdir);
|
||||||
|
|
||||||
long do_mkdirat(int dfd, const char __user *pathname, umode_t mode)
|
long do_mkdirat(int dfd, const char __user *pathname, umode_t mode)
|
||||||
@@ -3923,7 +3969,7 @@ long do_mkdirat(int dfd, const char __user *pathname, umode_t mode)
|
|||||||
mode &= ~current_umask();
|
mode &= ~current_umask();
|
||||||
error = security_path_mkdir(&path, dentry, mode);
|
error = security_path_mkdir(&path, dentry, mode);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
|
error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode);
|
||||||
done_path_create(&path, dentry);
|
done_path_create(&path, dentry);
|
||||||
if (retry_estale(error, lookup_flags)) {
|
if (retry_estale(error, lookup_flags)) {
|
||||||
lookup_flags |= LOOKUP_REVAL;
|
lookup_flags |= LOOKUP_REVAL;
|
||||||
@@ -3942,9 +3988,9 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
|
|||||||
return do_mkdirat(AT_FDCWD, pathname, mode);
|
return do_mkdirat(AT_FDCWD, pathname, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int error = may_delete(dir, dentry, 1);
|
int error = may_delete(mnt, dir, dentry, 1);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@@ -3979,6 +4025,12 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||||||
d_delete(dentry);
|
d_delete(dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_rmdir2);
|
||||||
|
|
||||||
|
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
|
{
|
||||||
|
return vfs_rmdir2(NULL, dir, dentry);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_rmdir);
|
EXPORT_SYMBOL(vfs_rmdir);
|
||||||
|
|
||||||
long do_rmdir(int dfd, const char __user *pathname)
|
long do_rmdir(int dfd, const char __user *pathname)
|
||||||
@@ -4024,7 +4076,7 @@ long do_rmdir(int dfd, const char __user *pathname)
|
|||||||
error = security_path_rmdir(&path, dentry);
|
error = security_path_rmdir(&path, dentry);
|
||||||
if (error)
|
if (error)
|
||||||
goto exit3;
|
goto exit3;
|
||||||
error = vfs_rmdir(path.dentry->d_inode, dentry);
|
error = vfs_rmdir2(path.mnt, path.dentry->d_inode, dentry);
|
||||||
exit3:
|
exit3:
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
exit2:
|
exit2:
|
||||||
@@ -4063,10 +4115,10 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
|
|||||||
* be appropriate for callers that expect the underlying filesystem not
|
* be appropriate for callers that expect the underlying filesystem not
|
||||||
* to be NFS exported.
|
* to be NFS exported.
|
||||||
*/
|
*/
|
||||||
int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
|
int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
|
||||||
{
|
{
|
||||||
struct inode *target = dentry->d_inode;
|
struct inode *target = dentry->d_inode;
|
||||||
int error = may_delete(dir, dentry, 0);
|
int error = may_delete(mnt, dir, dentry, 0);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@@ -4101,6 +4153,12 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate
|
|||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_unlink2);
|
||||||
|
|
||||||
|
int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
|
||||||
|
{
|
||||||
|
return vfs_unlink2(NULL, dir, dentry, delegated_inode);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_unlink);
|
EXPORT_SYMBOL(vfs_unlink);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4146,7 +4204,7 @@ long do_unlinkat(int dfd, struct filename *name)
|
|||||||
error = security_path_unlink(&path, dentry);
|
error = security_path_unlink(&path, dentry);
|
||||||
if (error)
|
if (error)
|
||||||
goto exit2;
|
goto exit2;
|
||||||
error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode);
|
error = vfs_unlink2(path.mnt, path.dentry->d_inode, dentry, &delegated_inode);
|
||||||
exit2:
|
exit2:
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
}
|
}
|
||||||
@@ -4196,9 +4254,9 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
|
|||||||
return do_unlinkat(AT_FDCWD, getname(pathname));
|
return do_unlinkat(AT_FDCWD, getname(pathname));
|
||||||
}
|
}
|
||||||
|
|
||||||
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(mnt, dir, dentry);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@@ -4215,6 +4273,12 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
|||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_symlink2);
|
||||||
|
|
||||||
|
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
||||||
|
{
|
||||||
|
return vfs_symlink2(NULL, dir, dentry, oldname);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_symlink);
|
EXPORT_SYMBOL(vfs_symlink);
|
||||||
|
|
||||||
long do_symlinkat(const char __user *oldname, int newdfd,
|
long do_symlinkat(const char __user *oldname, int newdfd,
|
||||||
@@ -4237,7 +4301,7 @@ long do_symlinkat(const char __user *oldname, int newdfd,
|
|||||||
|
|
||||||
error = security_path_symlink(&path, dentry, from->name);
|
error = security_path_symlink(&path, dentry, from->name);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
|
error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name);
|
||||||
done_path_create(&path, dentry);
|
done_path_create(&path, dentry);
|
||||||
if (retry_estale(error, lookup_flags)) {
|
if (retry_estale(error, lookup_flags)) {
|
||||||
lookup_flags |= LOOKUP_REVAL;
|
lookup_flags |= LOOKUP_REVAL;
|
||||||
@@ -4278,7 +4342,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
|
|||||||
* be appropriate for callers that expect the underlying filesystem not
|
* be appropriate for callers that expect the underlying filesystem not
|
||||||
* to be NFS exported.
|
* to be NFS exported.
|
||||||
*/
|
*/
|
||||||
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
|
int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
|
||||||
{
|
{
|
||||||
struct inode *inode = old_dentry->d_inode;
|
struct inode *inode = old_dentry->d_inode;
|
||||||
unsigned max_links = dir->i_sb->s_max_links;
|
unsigned max_links = dir->i_sb->s_max_links;
|
||||||
@@ -4287,7 +4351,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
|||||||
if (!inode)
|
if (!inode)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
error = may_create(dir, new_dentry);
|
error = may_create(mnt, dir, new_dentry);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@@ -4337,6 +4401,12 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
|||||||
fsnotify_link(dir, inode, new_dentry);
|
fsnotify_link(dir, inode, new_dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_link2);
|
||||||
|
|
||||||
|
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
|
||||||
|
{
|
||||||
|
return vfs_link2(NULL, old_dentry, dir, new_dentry, delegated_inode);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_link);
|
EXPORT_SYMBOL(vfs_link);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4392,7 +4462,7 @@ int do_linkat(int olddfd, const char __user *oldname, int newdfd,
|
|||||||
error = security_path_link(old_path.dentry, &new_path, new_dentry);
|
error = security_path_link(old_path.dentry, &new_path, new_dentry);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_dput;
|
goto out_dput;
|
||||||
error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
|
error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
|
||||||
out_dput:
|
out_dput:
|
||||||
done_path_create(&new_path, new_dentry);
|
done_path_create(&new_path, new_dentry);
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
@@ -4474,7 +4544,8 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
|
|||||||
* ->i_mutex on parents, which works but leads to some truly excessive
|
* ->i_mutex on parents, which works but leads to some truly excessive
|
||||||
* locking].
|
* locking].
|
||||||
*/
|
*/
|
||||||
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
int vfs_rename2(struct vfsmount *mnt,
|
||||||
|
struct inode *old_dir, struct dentry *old_dentry,
|
||||||
struct inode *new_dir, struct dentry *new_dentry,
|
struct inode *new_dir, struct dentry *new_dentry,
|
||||||
struct inode **delegated_inode, unsigned int flags)
|
struct inode **delegated_inode, unsigned int flags)
|
||||||
{
|
{
|
||||||
@@ -4489,19 +4560,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
if (source == target)
|
if (source == target)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error = may_delete(old_dir, old_dentry, is_dir);
|
error = may_delete(mnt, old_dir, old_dentry, is_dir);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
error = may_create(new_dir, new_dentry);
|
error = may_create(mnt, new_dir, new_dentry);
|
||||||
} else {
|
} else {
|
||||||
new_is_dir = d_is_dir(new_dentry);
|
new_is_dir = d_is_dir(new_dentry);
|
||||||
|
|
||||||
if (!(flags & RENAME_EXCHANGE))
|
if (!(flags & RENAME_EXCHANGE))
|
||||||
error = may_delete(new_dir, new_dentry, is_dir);
|
error = may_delete(mnt, new_dir, new_dentry, is_dir);
|
||||||
else
|
else
|
||||||
error = may_delete(new_dir, new_dentry, new_is_dir);
|
error = may_delete(mnt, new_dir, new_dentry, new_is_dir);
|
||||||
}
|
}
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@@ -4515,12 +4586,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
*/
|
*/
|
||||||
if (new_dir != old_dir) {
|
if (new_dir != old_dir) {
|
||||||
if (is_dir) {
|
if (is_dir) {
|
||||||
error = inode_permission(source, MAY_WRITE);
|
error = inode_permission2(mnt, source, MAY_WRITE);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
if ((flags & RENAME_EXCHANGE) && new_is_dir) {
|
if ((flags & RENAME_EXCHANGE) && new_is_dir) {
|
||||||
error = inode_permission(target, MAY_WRITE);
|
error = inode_permission2(mnt, target, MAY_WRITE);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@@ -4597,6 +4668,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_rename2);
|
||||||
|
|
||||||
|
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
|
struct inode *new_dir, struct dentry *new_dentry,
|
||||||
|
struct inode **delegated_inode, unsigned int flags)
|
||||||
|
{
|
||||||
|
return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry, delegated_inode, flags);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_rename);
|
EXPORT_SYMBOL(vfs_rename);
|
||||||
|
|
||||||
static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
|
static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
|
||||||
@@ -4710,7 +4789,7 @@ static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
|
|||||||
&new_path, new_dentry, flags);
|
&new_path, new_dentry, flags);
|
||||||
if (error)
|
if (error)
|
||||||
goto exit5;
|
goto exit5;
|
||||||
error = vfs_rename(old_path.dentry->d_inode, old_dentry,
|
error = vfs_rename2(old_path.mnt, old_path.dentry->d_inode, old_dentry,
|
||||||
new_path.dentry->d_inode, new_dentry,
|
new_path.dentry->d_inode, new_dentry,
|
||||||
&delegated_inode, flags);
|
&delegated_inode, flags);
|
||||||
exit5:
|
exit5:
|
||||||
@@ -4761,7 +4840,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
|
|||||||
|
|
||||||
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
|
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(NULL, dir, dentry);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
|||||||
@@ -490,7 +490,7 @@ static int fanotify_find_path(int dfd, const char __user *filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* you can only watch an inode if you have read permissions on it */
|
/* you can only watch an inode if you have read permissions on it */
|
||||||
ret = inode_permission(path->dentry->d_inode, MAY_READ);
|
ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
|
||||||
if (ret)
|
if (ret)
|
||||||
path_put(path);
|
path_put(path);
|
||||||
out:
|
out:
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
/* you can only watch an inode if you have read permissions on it */
|
/* you can only watch an inode if you have read permissions on it */
|
||||||
error = inode_permission(path->dentry->d_inode, MAY_READ);
|
error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
|
||||||
if (error)
|
if (error)
|
||||||
path_put(path);
|
path_put(path);
|
||||||
return error;
|
return error;
|
||||||
@@ -702,6 +702,8 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
|
|||||||
struct fsnotify_group *group;
|
struct fsnotify_group *group;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct path path;
|
struct path path;
|
||||||
|
struct path alteredpath;
|
||||||
|
struct path *canonical_path = &path;
|
||||||
struct fd f;
|
struct fd f;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned flags = 0;
|
unsigned flags = 0;
|
||||||
@@ -747,13 +749,22 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto fput_and_out;
|
goto fput_and_out;
|
||||||
|
|
||||||
|
/* support stacked filesystems */
|
||||||
|
if(path.dentry && path.dentry->d_op) {
|
||||||
|
if (path.dentry->d_op->d_canonical_path) {
|
||||||
|
path.dentry->d_op->d_canonical_path(&path, &alteredpath);
|
||||||
|
canonical_path = &alteredpath;
|
||||||
|
path_put(&path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* inode held in place by reference to path; group by fget on fd */
|
/* inode held in place by reference to path; group by fget on fd */
|
||||||
inode = path.dentry->d_inode;
|
inode = canonical_path->dentry->d_inode;
|
||||||
group = f.file->private_data;
|
group = f.file->private_data;
|
||||||
|
|
||||||
/* create/update an inode mark */
|
/* create/update an inode mark */
|
||||||
ret = inotify_update_watch(group, inode, mask);
|
ret = inotify_update_watch(group, inode, mask);
|
||||||
path_put(&path);
|
path_put(canonical_path);
|
||||||
fput_and_out:
|
fput_and_out:
|
||||||
fdput(f);
|
fdput(f);
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
36
fs/open.c
36
fs/open.c
@@ -34,8 +34,8 @@
|
|||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length,
|
||||||
struct file *filp)
|
unsigned int time_attrs, struct file *filp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct iattr newattrs;
|
struct iattr newattrs;
|
||||||
@@ -60,17 +60,24 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
|||||||
|
|
||||||
inode_lock(dentry->d_inode);
|
inode_lock(dentry->d_inode);
|
||||||
/* Note any delegations or leases have already been broken: */
|
/* Note any delegations or leases have already been broken: */
|
||||||
ret = notify_change(dentry, &newattrs, NULL);
|
ret = notify_change2(mnt, dentry, &newattrs, NULL);
|
||||||
inode_unlock(dentry->d_inode);
|
inode_unlock(dentry->d_inode);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
||||||
|
struct file *filp)
|
||||||
|
{
|
||||||
|
return do_truncate2(NULL, dentry, length, time_attrs, filp);
|
||||||
|
}
|
||||||
|
|
||||||
long vfs_truncate(const struct path *path, loff_t length)
|
long vfs_truncate(const struct path *path, loff_t length)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
struct vfsmount *mnt;
|
||||||
long error;
|
long error;
|
||||||
|
|
||||||
inode = path->dentry->d_inode;
|
inode = path->dentry->d_inode;
|
||||||
|
mnt = path->mnt;
|
||||||
|
|
||||||
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
|
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
|
||||||
if (S_ISDIR(inode->i_mode))
|
if (S_ISDIR(inode->i_mode))
|
||||||
@@ -82,7 +89,7 @@ long vfs_truncate(const struct path *path, loff_t length)
|
|||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
error = inode_permission(inode, MAY_WRITE);
|
error = inode_permission2(mnt, inode, MAY_WRITE);
|
||||||
if (error)
|
if (error)
|
||||||
goto mnt_drop_write_and_out;
|
goto mnt_drop_write_and_out;
|
||||||
|
|
||||||
@@ -106,7 +113,7 @@ long vfs_truncate(const struct path *path, loff_t length)
|
|||||||
if (!error)
|
if (!error)
|
||||||
error = security_path_truncate(path);
|
error = security_path_truncate(path);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = do_truncate(path->dentry, length, 0, NULL);
|
error = do_truncate2(mnt, path->dentry, length, 0, NULL);
|
||||||
|
|
||||||
put_write_and_out:
|
put_write_and_out:
|
||||||
put_write_access(inode);
|
put_write_access(inode);
|
||||||
@@ -155,6 +162,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
|
|||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
|
struct vfsmount *mnt;
|
||||||
struct fd f;
|
struct fd f;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
@@ -171,6 +179,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
|
|||||||
small = 0;
|
small = 0;
|
||||||
|
|
||||||
dentry = f.file->f_path.dentry;
|
dentry = f.file->f_path.dentry;
|
||||||
|
mnt = f.file->f_path.mnt;
|
||||||
inode = dentry->d_inode;
|
inode = dentry->d_inode;
|
||||||
error = -EINVAL;
|
error = -EINVAL;
|
||||||
if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
|
if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
|
||||||
@@ -191,7 +200,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
|
|||||||
if (!error)
|
if (!error)
|
||||||
error = security_path_truncate(&f.file->f_path);
|
error = security_path_truncate(&f.file->f_path);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
|
error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
|
||||||
sb_end_write(inode->i_sb);
|
sb_end_write(inode->i_sb);
|
||||||
out_putf:
|
out_putf:
|
||||||
fdput(f);
|
fdput(f);
|
||||||
@@ -350,6 +359,7 @@ long do_faccessat(int dfd, const char __user *filename, int mode)
|
|||||||
struct cred *override_cred;
|
struct cred *override_cred;
|
||||||
struct path path;
|
struct path path;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
struct vfsmount *mnt;
|
||||||
int res;
|
int res;
|
||||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||||
|
|
||||||
@@ -399,6 +409,7 @@ long do_faccessat(int dfd, const char __user *filename, int mode)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
inode = d_backing_inode(path.dentry);
|
inode = d_backing_inode(path.dentry);
|
||||||
|
mnt = path.mnt;
|
||||||
|
|
||||||
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
|
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
|
||||||
/*
|
/*
|
||||||
@@ -410,7 +421,7 @@ long do_faccessat(int dfd, const char __user *filename, int mode)
|
|||||||
goto out_path_release;
|
goto out_path_release;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = inode_permission(inode, mode | MAY_ACCESS);
|
res = inode_permission2(mnt, inode, mode | MAY_ACCESS);
|
||||||
/* SuS v2 requires we report a read only fs too */
|
/* SuS v2 requires we report a read only fs too */
|
||||||
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
|
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
|
||||||
goto out_path_release;
|
goto out_path_release;
|
||||||
@@ -459,7 +470,7 @@ int ksys_chdir(const char __user *filename)
|
|||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||||
if (error)
|
if (error)
|
||||||
goto dput_and_out;
|
goto dput_and_out;
|
||||||
|
|
||||||
@@ -493,7 +504,8 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
|
|||||||
if (!d_can_lookup(f.file->f_path.dentry))
|
if (!d_can_lookup(f.file->f_path.dentry))
|
||||||
goto out_putf;
|
goto out_putf;
|
||||||
|
|
||||||
error = inode_permission(file_inode(f.file), MAY_EXEC | MAY_CHDIR);
|
error = inode_permission2(f.file->f_path.mnt, file_inode(f.file),
|
||||||
|
MAY_EXEC | MAY_CHDIR);
|
||||||
if (!error)
|
if (!error)
|
||||||
set_fs_pwd(current->fs, &f.file->f_path);
|
set_fs_pwd(current->fs, &f.file->f_path);
|
||||||
out_putf:
|
out_putf:
|
||||||
@@ -512,7 +524,7 @@ int ksys_chroot(const char __user *filename)
|
|||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||||
if (error)
|
if (error)
|
||||||
goto dput_and_out;
|
goto dput_and_out;
|
||||||
|
|
||||||
@@ -557,7 +569,7 @@ static int chmod_common(const struct path *path, umode_t mode)
|
|||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
|
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
|
||||||
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||||
error = notify_change(path->dentry, &newattrs, &delegated_inode);
|
error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
@@ -648,7 +660,7 @@ static int chown_common(const struct path *path, uid_t user, gid_t group)
|
|||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
error = security_path_chown(path, uid, gid);
|
error = security_path_chown(path, uid, gid);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = notify_change(path->dentry, &newattrs, &delegated_inode);
|
error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
error = break_deleg_wait(&delegated_inode);
|
error = break_deleg_wait(&delegated_inode);
|
||||||
|
|||||||
@@ -461,6 +461,8 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(vfs_read);
|
EXPORT_SYMBOL_GPL(vfs_read);
|
||||||
|
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_read);
|
||||||
|
|
||||||
static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
|
static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
|
||||||
{
|
{
|
||||||
struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len };
|
struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len };
|
||||||
@@ -560,6 +562,8 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(vfs_write);
|
EXPORT_SYMBOL_GPL(vfs_write);
|
||||||
|
|
||||||
|
EXPORT_SYMBOL_GPL(vfs_write);
|
||||||
|
|
||||||
static inline loff_t file_pos_read(struct file *file)
|
static inline loff_t file_pos_read(struct file *file)
|
||||||
{
|
{
|
||||||
return file->f_mode & FMODE_STREAM ? 0 : file->f_pos;
|
return file->f_mode & FMODE_STREAM ? 0 : file->f_pos;
|
||||||
|
|||||||
13
fs/sdcardfs/Kconfig
Normal file
13
fs/sdcardfs/Kconfig
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
config SDCARD_FS
|
||||||
|
tristate "sdcard file system"
|
||||||
|
depends on CONFIGFS_FS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Sdcardfs is based on Wrapfs file system.
|
||||||
|
|
||||||
|
config SDCARD_FS_FADV_NOACTIVE
|
||||||
|
bool "sdcardfs fadvise noactive support"
|
||||||
|
depends on FADV_NOACTIVE
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Sdcardfs supports fadvise noactive mode.
|
||||||
7
fs/sdcardfs/Makefile
Normal file
7
fs/sdcardfs/Makefile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
SDCARDFS_VERSION="0.1"
|
||||||
|
|
||||||
|
EXTRA_CFLAGS += -DSDCARDFS_VERSION=\"$(SDCARDFS_VERSION)\"
|
||||||
|
|
||||||
|
obj-$(CONFIG_SDCARD_FS) += sdcardfs.o
|
||||||
|
|
||||||
|
sdcardfs-y := dentry.o file.o inode.o main.o super.o lookup.o mmap.o packagelist.o derived_perm.o
|
||||||
196
fs/sdcardfs/dentry.c
Normal file
196
fs/sdcardfs/dentry.c
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* fs/sdcardfs/dentry.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||||
|
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||||
|
* Sunghwan Yun, Sungjong Seo
|
||||||
|
*
|
||||||
|
* This program has been developed as a stackable file system based on
|
||||||
|
* the WrapFS which written by
|
||||||
|
*
|
||||||
|
* Copyright (c) 1998-2011 Erez Zadok
|
||||||
|
* Copyright (c) 2009 Shrikar Archak
|
||||||
|
* Copyright (c) 2003-2011 Stony Brook University
|
||||||
|
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||||
|
*
|
||||||
|
* This file is dual licensed. It may be redistributed and/or modified
|
||||||
|
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||||
|
* General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sdcardfs.h"
|
||||||
|
#include "linux/ctype.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* returns: -ERRNO if error (returned to user)
|
||||||
|
* 0: tell VFS to invalidate dentry
|
||||||
|
* 1: dentry is valid
|
||||||
|
*/
|
||||||
|
static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
|
{
|
||||||
|
int err = 1;
|
||||||
|
struct path parent_lower_path, lower_path;
|
||||||
|
struct dentry *parent_dentry = NULL;
|
||||||
|
struct dentry *parent_lower_dentry = NULL;
|
||||||
|
struct dentry *lower_cur_parent_dentry = NULL;
|
||||||
|
struct dentry *lower_dentry = NULL;
|
||||||
|
struct inode *inode;
|
||||||
|
struct sdcardfs_inode_data *data;
|
||||||
|
|
||||||
|
if (flags & LOOKUP_RCU)
|
||||||
|
return -ECHILD;
|
||||||
|
|
||||||
|
spin_lock(&dentry->d_lock);
|
||||||
|
if (IS_ROOT(dentry)) {
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
|
||||||
|
/* check uninitialized obb_dentry and
|
||||||
|
* whether the base obbpath has been changed or not
|
||||||
|
*/
|
||||||
|
if (is_obbpath_invalid(dentry)) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) {
|
||||||
|
err = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
|
||||||
|
if (err == 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(&lower_dentry->d_lock);
|
||||||
|
if (d_unhashed(lower_dentry)) {
|
||||||
|
spin_unlock(&lower_dentry->d_lock);
|
||||||
|
err = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
spin_unlock(&lower_dentry->d_lock);
|
||||||
|
|
||||||
|
if (parent_lower_dentry != lower_cur_parent_dentry) {
|
||||||
|
err = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dentry < lower_dentry) {
|
||||||
|
spin_lock(&dentry->d_lock);
|
||||||
|
spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||||
|
} else {
|
||||||
|
spin_lock(&lower_dentry->d_lock);
|
||||||
|
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (!err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* If our top's inode is gone, we may be out of date */
|
||||||
|
inode = igrab(d_inode(dentry));
|
||||||
|
if (inode) {
|
||||||
|
data = top_data_get(SDCARDFS_I(inode));
|
||||||
|
if (!data || data->abandoned) {
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
if (data)
|
||||||
|
data_put(data);
|
||||||
|
iput(inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1 = delete, 0 = cache */
|
||||||
|
static int sdcardfs_d_delete(const struct dentry *d)
|
||||||
|
{
|
||||||
|
return SDCARDFS_SB(d->d_sb)->options.nocache ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdcardfs_d_release(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
if (!dentry || !dentry->d_fsdata)
|
||||||
|
return;
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_hash_ci(const struct dentry *dentry,
|
||||||
|
struct qstr *qstr)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This function is copy of vfat_hashi.
|
||||||
|
* FIXME Should we support national language?
|
||||||
|
* Refer to vfat_hashi()
|
||||||
|
* struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io;
|
||||||
|
*/
|
||||||
|
const unsigned char *name;
|
||||||
|
unsigned int len;
|
||||||
|
unsigned long hash;
|
||||||
|
|
||||||
|
name = qstr->name;
|
||||||
|
len = qstr->len;
|
||||||
|
|
||||||
|
hash = init_name_hash(dentry);
|
||||||
|
while (len--)
|
||||||
|
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 *dentry,
|
||||||
|
unsigned int len, const char *str, const struct qstr *name)
|
||||||
|
{
|
||||||
|
/* FIXME Should we support national language? */
|
||||||
|
|
||||||
|
if (name->len == len) {
|
||||||
|
if (str_n_case_eq(name->name, str, len))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdcardfs_canonical_path(const struct path *path,
|
||||||
|
struct path *actual_path)
|
||||||
|
{
|
||||||
|
sdcardfs_get_real_lower(path->dentry, actual_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct dentry_operations sdcardfs_ci_dops = {
|
||||||
|
.d_revalidate = sdcardfs_d_revalidate,
|
||||||
|
.d_delete = sdcardfs_d_delete,
|
||||||
|
.d_release = sdcardfs_d_release,
|
||||||
|
.d_hash = sdcardfs_hash_ci,
|
||||||
|
.d_compare = sdcardfs_cmp_ci,
|
||||||
|
.d_canonical_path = sdcardfs_canonical_path,
|
||||||
|
};
|
||||||
|
|
||||||
477
fs/sdcardfs/derived_perm.c
Normal file
477
fs/sdcardfs/derived_perm.c
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
/*
|
||||||
|
* 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->data->perm = PERM_INHERIT;
|
||||||
|
ci->data->userid = pi->data->userid;
|
||||||
|
ci->data->d_uid = pi->data->d_uid;
|
||||||
|
ci->data->under_android = pi->data->under_android;
|
||||||
|
ci->data->under_cache = pi->data->under_cache;
|
||||||
|
ci->data->under_obb = pi->data->under_obb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper function for derived state */
|
||||||
|
void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
|
||||||
|
uid_t uid)
|
||||||
|
{
|
||||||
|
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
|
||||||
|
|
||||||
|
info->data->perm = perm;
|
||||||
|
info->data->userid = userid;
|
||||||
|
info->data->d_uid = uid;
|
||||||
|
info->data->under_android = false;
|
||||||
|
info->data->under_cache = false;
|
||||||
|
info->data->under_obb = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* While renaming, there is a point where we want the path from dentry,
|
||||||
|
* but the name from newdentry
|
||||||
|
*/
|
||||||
|
void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
|
||||||
|
const struct qstr *name)
|
||||||
|
{
|
||||||
|
struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
|
||||||
|
struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent));
|
||||||
|
struct sdcardfs_inode_data *parent_data = parent_info->data;
|
||||||
|
appid_t appid;
|
||||||
|
unsigned long user_num;
|
||||||
|
int err;
|
||||||
|
struct qstr q_Android = QSTR_LITERAL("Android");
|
||||||
|
struct qstr q_data = QSTR_LITERAL("data");
|
||||||
|
struct qstr q_sandbox = QSTR_LITERAL("sandbox");
|
||||||
|
struct qstr q_obb = QSTR_LITERAL("obb");
|
||||||
|
struct qstr q_media = QSTR_LITERAL("media");
|
||||||
|
struct qstr q_cache = QSTR_LITERAL("cache");
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
* These values are used by our custom permission call instead
|
||||||
|
* of using the inode permissions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
inherit_derived_state(d_inode(parent), d_inode(dentry));
|
||||||
|
|
||||||
|
/* Files don't get special labels */
|
||||||
|
if (!S_ISDIR(d_inode(dentry)->i_mode)) {
|
||||||
|
set_top(info, parent_info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Derive custom permissions based on parent and current node */
|
||||||
|
switch (parent_data->perm) {
|
||||||
|
case PERM_INHERIT:
|
||||||
|
case PERM_ANDROID_PACKAGE_CACHE:
|
||||||
|
set_top(info, parent_info);
|
||||||
|
break;
|
||||||
|
case PERM_PRE_ROOT:
|
||||||
|
/* Legacy internal layout places users at top level */
|
||||||
|
info->data->perm = PERM_ROOT;
|
||||||
|
err = kstrtoul(name->name, 10, &user_num);
|
||||||
|
if (err)
|
||||||
|
info->data->userid = 0;
|
||||||
|
else
|
||||||
|
info->data->userid = user_num;
|
||||||
|
break;
|
||||||
|
case PERM_ROOT:
|
||||||
|
/* Assume masked off by default. */
|
||||||
|
if (qstr_case_eq(name, &q_Android)) {
|
||||||
|
/* App-specific directories inside; let anyone traverse */
|
||||||
|
info->data->perm = PERM_ANDROID;
|
||||||
|
info->data->under_android = true;
|
||||||
|
} else {
|
||||||
|
set_top(info, parent_info);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID:
|
||||||
|
if (qstr_case_eq(name, &q_data)) {
|
||||||
|
/* App-specific directories inside; let anyone traverse */
|
||||||
|
info->data->perm = PERM_ANDROID_DATA;
|
||||||
|
} else if (qstr_case_eq(name, &q_sandbox)) {
|
||||||
|
/* App-specific directories inside; let anyone traverse */
|
||||||
|
info->data->perm = PERM_ANDROID_DATA;
|
||||||
|
} else if (qstr_case_eq(name, &q_obb)) {
|
||||||
|
/* App-specific directories inside; let anyone traverse */
|
||||||
|
info->data->perm = PERM_ANDROID_OBB;
|
||||||
|
info->data->under_obb = true;
|
||||||
|
/* Single OBB directory is always shared */
|
||||||
|
} else if (qstr_case_eq(name, &q_media)) {
|
||||||
|
/* App-specific directories inside; let anyone traverse */
|
||||||
|
info->data->perm = PERM_ANDROID_MEDIA;
|
||||||
|
} else {
|
||||||
|
set_top(info, parent_info);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_OBB:
|
||||||
|
case PERM_ANDROID_DATA:
|
||||||
|
case PERM_ANDROID_MEDIA:
|
||||||
|
info->data->perm = PERM_ANDROID_PACKAGE;
|
||||||
|
appid = get_appid(name->name);
|
||||||
|
if (appid != 0 && !is_excluded(name->name, parent_data->userid))
|
||||||
|
info->data->d_uid =
|
||||||
|
multiuser_get_uid(parent_data->userid, appid);
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_PACKAGE:
|
||||||
|
if (qstr_case_eq(name, &q_cache)) {
|
||||||
|
info->data->perm = PERM_ANDROID_PACKAGE_CACHE;
|
||||||
|
info->data->under_cache = true;
|
||||||
|
}
|
||||||
|
set_top(info, parent_info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
|
||||||
|
{
|
||||||
|
get_derived_permission_new(parent, dentry, &dentry->d_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static appid_t get_type(const char *name)
|
||||||
|
{
|
||||||
|
const char *ext = strrchr(name, '.');
|
||||||
|
appid_t id;
|
||||||
|
|
||||||
|
if (ext && ext[0]) {
|
||||||
|
ext = &ext[1];
|
||||||
|
id = get_ext_gid(ext);
|
||||||
|
return id?:AID_MEDIA_RW;
|
||||||
|
}
|
||||||
|
return AID_MEDIA_RW;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fixup_lower_ownership(struct dentry *dentry, const char *name)
|
||||||
|
{
|
||||||
|
struct path path;
|
||||||
|
struct inode *inode;
|
||||||
|
struct inode *delegated_inode = NULL;
|
||||||
|
int error;
|
||||||
|
struct sdcardfs_inode_info *info;
|
||||||
|
struct sdcardfs_inode_data *info_d;
|
||||||
|
struct sdcardfs_inode_data *info_top;
|
||||||
|
perm_t perm;
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
uid_t uid = sbi->options.fs_low_uid;
|
||||||
|
gid_t gid = sbi->options.fs_low_gid;
|
||||||
|
struct iattr newattrs;
|
||||||
|
|
||||||
|
if (!sbi->options.gid_derivation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
info = SDCARDFS_I(d_inode(dentry));
|
||||||
|
info_d = info->data;
|
||||||
|
perm = info_d->perm;
|
||||||
|
if (info_d->under_obb) {
|
||||||
|
perm = PERM_ANDROID_OBB;
|
||||||
|
} else if (info_d->under_cache) {
|
||||||
|
perm = PERM_ANDROID_PACKAGE_CACHE;
|
||||||
|
} else if (perm == PERM_INHERIT) {
|
||||||
|
info_top = top_data_get(info);
|
||||||
|
perm = info_top->perm;
|
||||||
|
data_put(info_top);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (perm) {
|
||||||
|
case PERM_ROOT:
|
||||||
|
case PERM_ANDROID:
|
||||||
|
case PERM_ANDROID_DATA:
|
||||||
|
case PERM_ANDROID_MEDIA:
|
||||||
|
case PERM_ANDROID_PACKAGE:
|
||||||
|
case PERM_ANDROID_PACKAGE_CACHE:
|
||||||
|
uid = multiuser_get_uid(info_d->userid, uid);
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_OBB:
|
||||||
|
uid = AID_MEDIA_OBB;
|
||||||
|
break;
|
||||||
|
case PERM_PRE_ROOT:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (perm) {
|
||||||
|
case PERM_ROOT:
|
||||||
|
case PERM_ANDROID:
|
||||||
|
case PERM_ANDROID_DATA:
|
||||||
|
case PERM_ANDROID_MEDIA:
|
||||||
|
if (S_ISDIR(d_inode(dentry)->i_mode))
|
||||||
|
gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
|
||||||
|
else
|
||||||
|
gid = multiuser_get_uid(info_d->userid, get_type(name));
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_OBB:
|
||||||
|
gid = AID_MEDIA_OBB;
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_PACKAGE:
|
||||||
|
if (uid_is_app(info_d->d_uid))
|
||||||
|
gid = multiuser_get_ext_gid(info_d->d_uid);
|
||||||
|
else
|
||||||
|
gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_PACKAGE_CACHE:
|
||||||
|
if (uid_is_app(info_d->d_uid))
|
||||||
|
gid = multiuser_get_ext_cache_gid(info_d->d_uid);
|
||||||
|
else
|
||||||
|
gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
|
||||||
|
break;
|
||||||
|
case PERM_PRE_ROOT:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdcardfs_get_lower_path(dentry, &path);
|
||||||
|
inode = d_inode(path.dentry);
|
||||||
|
if (d_inode(path.dentry)->i_gid.val != gid || d_inode(path.dentry)->i_uid.val != uid) {
|
||||||
|
retry_deleg:
|
||||||
|
newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE;
|
||||||
|
newattrs.ia_uid = make_kuid(current_user_ns(), uid);
|
||||||
|
newattrs.ia_gid = make_kgid(current_user_ns(), gid);
|
||||||
|
if (!S_ISDIR(inode->i_mode))
|
||||||
|
newattrs.ia_valid |=
|
||||||
|
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
|
||||||
|
inode_lock(inode);
|
||||||
|
error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid);
|
||||||
|
if (!error)
|
||||||
|
error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode);
|
||||||
|
inode_unlock(inode);
|
||||||
|
if (delegated_inode) {
|
||||||
|
error = break_deleg_wait(&delegated_inode);
|
||||||
|
if (!error)
|
||||||
|
goto retry_deleg;
|
||||||
|
}
|
||||||
|
if (error)
|
||||||
|
pr_debug("sdcardfs: Failed to touch up lower fs gid/uid for %s\n", name);
|
||||||
|
}
|
||||||
|
sdcardfs_put_lower_path(dentry, &path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int descendant_may_need_fixup(struct sdcardfs_inode_data *data,
|
||||||
|
struct limit_search *limit)
|
||||||
|
{
|
||||||
|
if (data->perm == PERM_ROOT)
|
||||||
|
return (limit->flags & BY_USERID) ?
|
||||||
|
data->userid == limit->userid : 1;
|
||||||
|
if (data->perm == PERM_PRE_ROOT || data->perm == PERM_ANDROID)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int needs_fixup(perm_t perm)
|
||||||
|
{
|
||||||
|
if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB
|
||||||
|
|| perm == PERM_ANDROID_MEDIA)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit, int depth)
|
||||||
|
{
|
||||||
|
struct dentry *child;
|
||||||
|
struct sdcardfs_inode_info *info;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All paths will terminate their recursion on hitting PERM_ANDROID_OBB,
|
||||||
|
* PERM_ANDROID_MEDIA, or PERM_ANDROID_DATA. This happens at a depth of
|
||||||
|
* at most 3.
|
||||||
|
*/
|
||||||
|
WARN(depth > 3, "%s: Max expected depth exceeded!\n", __func__);
|
||||||
|
spin_lock_nested(&dentry->d_lock, depth);
|
||||||
|
if (!d_inode(dentry)) {
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info = SDCARDFS_I(d_inode(dentry));
|
||||||
|
|
||||||
|
if (needs_fixup(info->data->perm)) {
|
||||||
|
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||||
|
spin_lock_nested(&child->d_lock, depth + 1);
|
||||||
|
if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) {
|
||||||
|
if (d_inode(child)) {
|
||||||
|
get_derived_permission(dentry, child);
|
||||||
|
fixup_tmp_permissions(d_inode(child));
|
||||||
|
spin_unlock(&child->d_lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&child->d_lock);
|
||||||
|
}
|
||||||
|
} else if (descendant_may_need_fixup(info->data, limit)) {
|
||||||
|
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||||
|
__fixup_perms_recursive(child, limit, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit)
|
||||||
|
{
|
||||||
|
__fixup_perms_recursive(dentry, limit, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* main function for updating derived permission */
|
||||||
|
inline void update_derived_permission_lock(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
struct dentry *parent;
|
||||||
|
|
||||||
|
if (!dentry || !d_inode(dentry)) {
|
||||||
|
pr_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)) {
|
||||||
|
parent = dget_parent(dentry);
|
||||||
|
if (parent) {
|
||||||
|
get_derived_permission(parent, dentry);
|
||||||
|
dput(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixup_tmp_permissions(d_inode(dentry));
|
||||||
|
}
|
||||||
|
|
||||||
|
int need_graft_path(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct dentry *parent = dget_parent(dentry);
|
||||||
|
struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent));
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
struct qstr obb = QSTR_LITERAL("obb");
|
||||||
|
|
||||||
|
if (!sbi->options.unshared_obb &&
|
||||||
|
parent_info->data->perm == PERM_ANDROID &&
|
||||||
|
qstr_case_eq(&dentry->d_name, &obb)) {
|
||||||
|
|
||||||
|
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
|
||||||
|
if (!(sbi->options.multiuser == false
|
||||||
|
&& parent_info->data->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;
|
||||||
|
int need_put = 0;
|
||||||
|
struct path lower_path;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
path_buf = kmalloc(PATH_MAX, GFP_ATOMIC);
|
||||||
|
if (!path_buf) {
|
||||||
|
ret = 1;
|
||||||
|
pr_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) ||
|
||||||
|
!str_case_eq(sbi->obbpath_s, obbpath_s)) {
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
kfree(path_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pathcpy(&lower_path, &di->lower_path);
|
||||||
|
need_put = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&di->lock);
|
||||||
|
if (need_put)
|
||||||
|
path_put(&lower_path);
|
||||||
|
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(d_inode(parent));
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
struct qstr q_obb = QSTR_LITERAL("obb");
|
||||||
|
|
||||||
|
spin_lock(&SDCARDFS_D(dentry)->lock);
|
||||||
|
if (sbi->options.multiuser) {
|
||||||
|
if (parent_info->data->perm == PERM_PRE_ROOT &&
|
||||||
|
qstr_case_eq(&dentry->d_name, &q_obb)) {
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
} else if (parent_info->data->perm == PERM_ANDROID &&
|
||||||
|
qstr_case_eq(&dentry->d_name, &q_obb)) {
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
spin_unlock(&SDCARDFS_D(dentry)->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The lower_path will be stored to the dentry's orig_path
|
||||||
|
* and the base obbpath will be copyed to the lower_path variable.
|
||||||
|
* if an error returned, there's no change in the lower_path
|
||||||
|
* returns: -ERRNO if error (0: no error)
|
||||||
|
*/
|
||||||
|
int setup_obb_dentry(struct dentry *dentry, struct path *lower_path)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
struct path obbpath;
|
||||||
|
|
||||||
|
/* A local obb dentry must have its own orig_path to support rmdir
|
||||||
|
* and mkdir of itself. Usually, we expect that the sbi->obbpath
|
||||||
|
* is avaiable on this stage.
|
||||||
|
*/
|
||||||
|
sdcardfs_set_orig_path(dentry, lower_path);
|
||||||
|
|
||||||
|
err = kern_path(sbi->obbpath_s,
|
||||||
|
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath);
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
/* the obbpath base has been found */
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
pr_info("sdcardfs: the sbi->obbpath is not available\n");
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
467
fs/sdcardfs/file.c
Normal file
467
fs/sdcardfs/file.c
Normal file
@@ -0,0 +1,467 @@
|
|||||||
|
/*
|
||||||
|
* fs/sdcardfs/file.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||||
|
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||||
|
* Sunghwan Yun, Sungjong Seo
|
||||||
|
*
|
||||||
|
* This program has been developed as a stackable file system based on
|
||||||
|
* the WrapFS which written by
|
||||||
|
*
|
||||||
|
* Copyright (c) 1998-2011 Erez Zadok
|
||||||
|
* Copyright (c) 2009 Shrikar Archak
|
||||||
|
* Copyright (c) 2003-2011 Stony Brook University
|
||||||
|
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||||
|
*
|
||||||
|
* This file is dual licensed. It may be redistributed and/or modified
|
||||||
|
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||||
|
* General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sdcardfs.h"
|
||||||
|
#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
|
||||||
|
#include <linux/backing-dev.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static ssize_t sdcardfs_read(struct file *file, char __user *buf,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct file *lower_file;
|
||||||
|
struct dentry *dentry = file->f_path.dentry;
|
||||||
|
#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
|
||||||
|
struct backing_dev_info *bdi;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
|
||||||
|
if (file->f_mode & FMODE_NOACTIVE) {
|
||||||
|
if (!(lower_file->f_mode & FMODE_NOACTIVE)) {
|
||||||
|
bdi = lower_file->f_mapping->backing_dev_info;
|
||||||
|
lower_file->f_ra.ra_pages = bdi->ra_pages * 2;
|
||||||
|
spin_lock(&lower_file->f_lock);
|
||||||
|
lower_file->f_mode |= FMODE_NOACTIVE;
|
||||||
|
spin_unlock(&lower_file->f_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
err = vfs_read(lower_file, buf, count, ppos);
|
||||||
|
/* update our inode atime upon a successful lower read */
|
||||||
|
if (err >= 0)
|
||||||
|
fsstack_copy_attr_atime(d_inode(dentry),
|
||||||
|
file_inode(lower_file));
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t sdcardfs_write(struct file *file, const char __user *buf,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct file *lower_file;
|
||||||
|
struct dentry *dentry = file->f_path.dentry;
|
||||||
|
struct inode *inode = d_inode(dentry);
|
||||||
|
|
||||||
|
/* check disk space */
|
||||||
|
if (!check_min_free_space(dentry, count, 0)) {
|
||||||
|
pr_err("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) {
|
||||||
|
if (sizeof(loff_t) > sizeof(long))
|
||||||
|
inode_lock(inode);
|
||||||
|
fsstack_copy_inode_size(inode, file_inode(lower_file));
|
||||||
|
fsstack_copy_attr_times(inode, file_inode(lower_file));
|
||||||
|
if (sizeof(loff_t) > sizeof(long))
|
||||||
|
inode_unlock(inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_readdir(struct file *file, struct dir_context *ctx)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct file *lower_file = NULL;
|
||||||
|
struct dentry *dentry = file->f_path.dentry;
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
|
||||||
|
lower_file->f_pos = file->f_pos;
|
||||||
|
err = iterate_dir(lower_file, ctx);
|
||||||
|
file->f_pos = lower_file->f_pos;
|
||||||
|
if (err >= 0) /* copy the atime */
|
||||||
|
fsstack_copy_attr_atime(d_inode(dentry),
|
||||||
|
file_inode(lower_file));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd,
|
||||||
|
unsigned long arg)
|
||||||
|
{
|
||||||
|
long err = -ENOTTY;
|
||||||
|
struct file *lower_file;
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
struct dentry *dentry = file->f_path.dentry;
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
|
||||||
|
/* XXX: use vfs_ioctl if/when VFS exports it */
|
||||||
|
if (!lower_file || !lower_file->f_op)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data);
|
||||||
|
if (!saved_cred) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lower_file->f_op->unlocked_ioctl)
|
||||||
|
err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
|
||||||
|
|
||||||
|
/* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */
|
||||||
|
if (!err)
|
||||||
|
sdcardfs_copy_and_fix_attrs(file_inode(file),
|
||||||
|
file_inode(lower_file));
|
||||||
|
revert_fsids(saved_cred);
|
||||||
|
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;
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
struct dentry *dentry = file->f_path.dentry;
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
|
||||||
|
/* XXX: use vfs_ioctl if/when VFS exports it */
|
||||||
|
if (!lower_file || !lower_file->f_op)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data);
|
||||||
|
if (!saved_cred) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lower_file->f_op->compat_ioctl)
|
||||||
|
err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
|
||||||
|
|
||||||
|
revert_fsids(saved_cred);
|
||||||
|
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;
|
||||||
|
pr_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) {
|
||||||
|
pr_err("sdcardfs: lower mmap failed %d\n", err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Next 3 lines are all I need from generic_file_mmap. I definitely
|
||||||
|
* don't want its test for ->readpage which returns -ENOEXEC.
|
||||||
|
*/
|
||||||
|
file_accessed(file);
|
||||||
|
vma->vm_ops = &sdcardfs_vm_ops;
|
||||||
|
|
||||||
|
file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */
|
||||||
|
if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */
|
||||||
|
SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops;
|
||||||
|
vma->vm_private_data = file;
|
||||||
|
get_file(lower_file);
|
||||||
|
vma->vm_file = lower_file;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct file *lower_file = NULL;
|
||||||
|
struct path lower_path;
|
||||||
|
struct dentry *dentry = file->f_path.dentry;
|
||||||
|
struct dentry *parent = dget_parent(dentry);
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
|
/* don't open unhashed/deleted files */
|
||||||
|
if (d_unhashed(dentry)) {
|
||||||
|
err = -ENOENT;
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||||
|
err = -EACCES;
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(sbi, SDCARDFS_I(inode)->data);
|
||||||
|
if (!saved_cred) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
file->private_data =
|
||||||
|
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
|
||||||
|
if (!SDCARDFS_F(file)) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out_revert_cred;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open lower object and link sdcardfs's file struct to lower's */
|
||||||
|
sdcardfs_get_lower_path(file->f_path.dentry, &lower_path);
|
||||||
|
lower_file = dentry_open(&lower_path, file->f_flags, current_cred());
|
||||||
|
path_put(&lower_path);
|
||||||
|
if (IS_ERR(lower_file)) {
|
||||||
|
err = PTR_ERR(lower_file);
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
if (lower_file) {
|
||||||
|
sdcardfs_set_lower_file(file, NULL);
|
||||||
|
fput(lower_file); /* fput calls dput for lower_dentry */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sdcardfs_set_lower_file(file, lower_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
kfree(SDCARDFS_F(file));
|
||||||
|
else
|
||||||
|
sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode));
|
||||||
|
|
||||||
|
out_revert_cred:
|
||||||
|
revert_fsids(saved_cred);
|
||||||
|
out_err:
|
||||||
|
dput(parent);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_flush(struct file *file, fl_owner_t id)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct file *lower_file = NULL;
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
if (lower_file && lower_file->f_op && lower_file->f_op->flush) {
|
||||||
|
filemap_write_and_wait(file->f_mapping);
|
||||||
|
err = lower_file->f_op->flush(lower_file, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* release all lower object references & free the file info structure */
|
||||||
|
static int sdcardfs_file_release(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
struct file *lower_file;
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
if (lower_file) {
|
||||||
|
sdcardfs_set_lower_file(file, NULL);
|
||||||
|
fput(lower_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(SDCARDFS_F(file));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_fsync(struct file *file, loff_t start, loff_t end,
|
||||||
|
int datasync)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct file *lower_file;
|
||||||
|
struct path lower_path;
|
||||||
|
struct dentry *dentry = file->f_path.dentry;
|
||||||
|
|
||||||
|
err = __generic_file_fsync(file, start, end, datasync);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
|
err = vfs_fsync_range(lower_file, start, end, datasync);
|
||||||
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_fasync(int fd, struct file *file, int flag)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct file *lower_file = NULL;
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
if (lower_file->f_op && lower_file->f_op->fasync)
|
||||||
|
err = lower_file->f_op->fasync(fd, lower_file, flag);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sdcardfs cannot use generic_file_llseek as ->llseek, because it would
|
||||||
|
* only set the offset of the upper file. So we have to implement our
|
||||||
|
* own method to set both the upper and lower file offsets
|
||||||
|
* consistently.
|
||||||
|
*/
|
||||||
|
static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct file *lower_file;
|
||||||
|
|
||||||
|
err = generic_file_llseek(file, offset, whence);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
err = generic_file_llseek(lower_file, offset, whence);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sdcardfs read_iter, redirect modified iocb to lower read_iter
|
||||||
|
*/
|
||||||
|
ssize_t sdcardfs_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct file *file = iocb->ki_filp, *lower_file;
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
if (!lower_file->f_op->read_iter) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_file(lower_file); /* prevent lower_file from being released */
|
||||||
|
iocb->ki_filp = lower_file;
|
||||||
|
err = lower_file->f_op->read_iter(iocb, iter);
|
||||||
|
iocb->ki_filp = file;
|
||||||
|
fput(lower_file);
|
||||||
|
/* update upper inode atime as needed */
|
||||||
|
if (err >= 0 || err == -EIOCBQUEUED)
|
||||||
|
fsstack_copy_attr_atime(file->f_path.dentry->d_inode,
|
||||||
|
file_inode(lower_file));
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sdcardfs write_iter, redirect modified iocb to lower write_iter
|
||||||
|
*/
|
||||||
|
ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct file *file = iocb->ki_filp, *lower_file;
|
||||||
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
|
||||||
|
lower_file = sdcardfs_lower_file(file);
|
||||||
|
if (!lower_file->f_op->write_iter) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_file(lower_file); /* prevent lower_file from being released */
|
||||||
|
iocb->ki_filp = lower_file;
|
||||||
|
err = lower_file->f_op->write_iter(iocb, iter);
|
||||||
|
iocb->ki_filp = file;
|
||||||
|
fput(lower_file);
|
||||||
|
/* update upper inode times/sizes as needed */
|
||||||
|
if (err >= 0 || err == -EIOCBQUEUED) {
|
||||||
|
if (sizeof(loff_t) > sizeof(long))
|
||||||
|
inode_lock(inode);
|
||||||
|
fsstack_copy_inode_size(inode, file_inode(lower_file));
|
||||||
|
fsstack_copy_attr_times(inode, file_inode(lower_file));
|
||||||
|
if (sizeof(loff_t) > sizeof(long))
|
||||||
|
inode_unlock(inode);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
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,
|
||||||
|
.read_iter = sdcardfs_read_iter,
|
||||||
|
.write_iter = sdcardfs_write_iter,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* trimmed directory options */
|
||||||
|
const struct file_operations sdcardfs_dir_fops = {
|
||||||
|
.llseek = sdcardfs_file_llseek,
|
||||||
|
.read = generic_read_dir,
|
||||||
|
.iterate = sdcardfs_readdir,
|
||||||
|
.unlocked_ioctl = sdcardfs_unlocked_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl = sdcardfs_compat_ioctl,
|
||||||
|
#endif
|
||||||
|
.open = sdcardfs_open,
|
||||||
|
.release = sdcardfs_file_release,
|
||||||
|
.flush = sdcardfs_flush,
|
||||||
|
.fsync = sdcardfs_fsync,
|
||||||
|
.fasync = sdcardfs_fasync,
|
||||||
|
};
|
||||||
824
fs/sdcardfs/inode.c
Normal file
824
fs/sdcardfs/inode.c
Normal file
@@ -0,0 +1,824 @@
|
|||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
#include <linux/fs_struct.h>
|
||||||
|
#include <linux/ratelimit.h>
|
||||||
|
#include <linux/sched/task.h>
|
||||||
|
|
||||||
|
const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
|
||||||
|
struct sdcardfs_inode_data *data)
|
||||||
|
{
|
||||||
|
struct cred *cred;
|
||||||
|
const struct cred *old_cred;
|
||||||
|
uid_t uid;
|
||||||
|
|
||||||
|
cred = prepare_creds();
|
||||||
|
if (!cred)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (sbi->options.gid_derivation) {
|
||||||
|
if (data->under_obb)
|
||||||
|
uid = AID_MEDIA_OBB;
|
||||||
|
else
|
||||||
|
uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid);
|
||||||
|
} else {
|
||||||
|
uid = sbi->options.fs_low_uid;
|
||||||
|
}
|
||||||
|
cred->fsuid = make_kuid(&init_user_ns, uid);
|
||||||
|
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
|
||||||
|
|
||||||
|
old_cred = override_creds(cred);
|
||||||
|
|
||||||
|
return old_cred;
|
||||||
|
}
|
||||||
|
|
||||||
|
void revert_fsids(const struct cred *old_cred)
|
||||||
|
{
|
||||||
|
const struct cred *cur_cred;
|
||||||
|
|
||||||
|
cur_cred = current->cred;
|
||||||
|
revert_creds(old_cred);
|
||||||
|
put_cred(cur_cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
|
||||||
|
umode_t mode, bool want_excl)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct dentry *lower_dentry;
|
||||||
|
struct vfsmount *lower_dentry_mnt;
|
||||||
|
struct dentry *lower_parent_dentry = NULL;
|
||||||
|
struct path lower_path;
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
struct fs_struct *saved_fs;
|
||||||
|
struct fs_struct *copied_fs;
|
||||||
|
|
||||||
|
if (!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||||
|
err = -EACCES;
|
||||||
|
goto out_eacces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
|
||||||
|
SDCARDFS_I(dir)->data);
|
||||||
|
if (!saved_cred)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
|
lower_dentry = lower_path.dentry;
|
||||||
|
lower_dentry_mnt = lower_path.mnt;
|
||||||
|
lower_parent_dentry = lock_parent(lower_dentry);
|
||||||
|
|
||||||
|
if (d_is_positive(lower_dentry))
|
||||||
|
return -EEXIST;
|
||||||
|
|
||||||
|
/* set last 16bytes of mode field to 0664 */
|
||||||
|
mode = (mode & S_IFMT) | 00664;
|
||||||
|
|
||||||
|
/* temporarily change umask for lower fs write */
|
||||||
|
saved_fs = current->fs;
|
||||||
|
copied_fs = copy_fs_struct(current->fs);
|
||||||
|
if (!copied_fs) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
copied_fs->umask = 0;
|
||||||
|
task_lock(current);
|
||||||
|
current->fs = copied_fs;
|
||||||
|
task_unlock(current);
|
||||||
|
|
||||||
|
err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path,
|
||||||
|
SDCARDFS_I(dir)->data->userid);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
|
||||||
|
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
|
||||||
|
fixup_lower_ownership(dentry, dentry->d_name.name);
|
||||||
|
|
||||||
|
out:
|
||||||
|
task_lock(current);
|
||||||
|
current->fs = saved_fs;
|
||||||
|
task_unlock(current);
|
||||||
|
free_fs_struct(copied_fs);
|
||||||
|
out_unlock:
|
||||||
|
unlock_dir(lower_parent_dentry);
|
||||||
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
revert_fsids(saved_cred);
|
||||||
|
out_eacces:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct dentry *lower_dentry;
|
||||||
|
struct vfsmount *lower_mnt;
|
||||||
|
struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
|
||||||
|
struct dentry *lower_dir_dentry;
|
||||||
|
struct path lower_path;
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
|
if (!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||||
|
err = -EACCES;
|
||||||
|
goto out_eacces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
|
||||||
|
SDCARDFS_I(dir)->data);
|
||||||
|
if (!saved_cred)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
|
lower_dentry = lower_path.dentry;
|
||||||
|
lower_mnt = lower_path.mnt;
|
||||||
|
dget(lower_dentry);
|
||||||
|
lower_dir_dentry = lock_parent(lower_dentry);
|
||||||
|
|
||||||
|
err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: unlinking on top of NFS can cause silly-renamed files.
|
||||||
|
* Trying to delete such files results in EBUSY from NFS
|
||||||
|
* below. Silly-renamed files will get deleted by NFS later on, so
|
||||||
|
* we just need to detect them here and treat such EBUSY errors as
|
||||||
|
* if the upper file was successfully deleted.
|
||||||
|
*/
|
||||||
|
if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED)
|
||||||
|
err = 0;
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
fsstack_copy_attr_times(dir, lower_dir_inode);
|
||||||
|
fsstack_copy_inode_size(dir, lower_dir_inode);
|
||||||
|
set_nlink(d_inode(dentry),
|
||||||
|
sdcardfs_lower_inode(d_inode(dentry))->i_nlink);
|
||||||
|
d_inode(dentry)->i_ctime = dir->i_ctime;
|
||||||
|
d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */
|
||||||
|
out:
|
||||||
|
unlock_dir(lower_dir_dentry);
|
||||||
|
dput(lower_dentry);
|
||||||
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
revert_fsids(saved_cred);
|
||||||
|
out_eacces:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
pr_err("sdcardfs: failed to open(%s): %ld\n",
|
||||||
|
abs_path, PTR_ERR(filp));
|
||||||
|
return PTR_ERR(filp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filp_close(filp, current->files);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
int make_nomedia_in_obb = 0;
|
||||||
|
struct dentry *lower_dentry;
|
||||||
|
struct vfsmount *lower_mnt;
|
||||||
|
struct dentry *lower_parent_dentry = NULL;
|
||||||
|
struct dentry *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_data *pd = SDCARDFS_I(dir)->data;
|
||||||
|
int touch_err = 0;
|
||||||
|
struct fs_struct *saved_fs;
|
||||||
|
struct fs_struct *copied_fs;
|
||||||
|
struct qstr q_obb = QSTR_LITERAL("obb");
|
||||||
|
struct qstr q_data = QSTR_LITERAL("data");
|
||||||
|
|
||||||
|
if (!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||||
|
err = -EACCES;
|
||||||
|
goto out_eacces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
|
||||||
|
SDCARDFS_I(dir)->data);
|
||||||
|
if (!saved_cred)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* check disk space */
|
||||||
|
parent_dentry = dget_parent(dentry);
|
||||||
|
if (!check_min_free_space(parent_dentry, 0, 1)) {
|
||||||
|
pr_err("sdcardfs: No minimum free space.\n");
|
||||||
|
err = -ENOSPC;
|
||||||
|
dput(parent_dentry);
|
||||||
|
goto out_revert;
|
||||||
|
}
|
||||||
|
dput(parent_dentry);
|
||||||
|
|
||||||
|
/* the lower_dentry is negative here */
|
||||||
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
|
lower_dentry = lower_path.dentry;
|
||||||
|
lower_mnt = lower_path.mnt;
|
||||||
|
lower_parent_dentry = lock_parent(lower_dentry);
|
||||||
|
|
||||||
|
/* set last 16bytes of mode field to 0775 */
|
||||||
|
mode = (mode & S_IFMT) | 00775;
|
||||||
|
|
||||||
|
/* temporarily change umask for lower fs write */
|
||||||
|
saved_fs = current->fs;
|
||||||
|
copied_fs = copy_fs_struct(current->fs);
|
||||||
|
if (!copied_fs) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
unlock_dir(lower_parent_dentry);
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
copied_fs->umask = 0;
|
||||||
|
task_lock(current);
|
||||||
|
current->fs = copied_fs;
|
||||||
|
task_unlock(current);
|
||||||
|
|
||||||
|
err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
unlock_dir(lower_parent_dentry);
|
||||||
|
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, pd->userid);
|
||||||
|
if (err) {
|
||||||
|
unlock_dir(lower_parent_dentry);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
|
||||||
|
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
|
||||||
|
/* update number of links on parent directory */
|
||||||
|
set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
|
||||||
|
fixup_lower_ownership(dentry, dentry->d_name.name);
|
||||||
|
unlock_dir(lower_parent_dentry);
|
||||||
|
if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb))
|
||||||
|
&& (pd->perm == PERM_ANDROID) && (pd->userid == 0))
|
||||||
|
make_nomedia_in_obb = 1;
|
||||||
|
|
||||||
|
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
|
||||||
|
if (make_nomedia_in_obb ||
|
||||||
|
((pd->perm == PERM_ANDROID)
|
||||||
|
&& (qstr_case_eq(&dentry->d_name, &q_data)))) {
|
||||||
|
revert_fsids(saved_cred);
|
||||||
|
saved_cred = override_fsids(sbi,
|
||||||
|
SDCARDFS_I(d_inode(dentry))->data);
|
||||||
|
if (!saved_cred) {
|
||||||
|
pr_err("sdcardfs: failed to set up .nomedia in %s: %d\n",
|
||||||
|
lower_path.dentry->d_name.name,
|
||||||
|
-ENOMEM);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
set_fs_pwd(current->fs, &lower_path);
|
||||||
|
touch_err = touch(".nomedia", 0664);
|
||||||
|
if (touch_err) {
|
||||||
|
pr_err("sdcardfs: failed to create .nomedia in %s: %d\n",
|
||||||
|
lower_path.dentry->d_name.name,
|
||||||
|
touch_err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
task_lock(current);
|
||||||
|
current->fs = saved_fs;
|
||||||
|
task_unlock(current);
|
||||||
|
|
||||||
|
free_fs_struct(copied_fs);
|
||||||
|
out_unlock:
|
||||||
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
out_revert:
|
||||||
|
revert_fsids(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;
|
||||||
|
struct vfsmount *lower_mnt;
|
||||||
|
int err;
|
||||||
|
struct path lower_path;
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
|
if (!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||||
|
err = -EACCES;
|
||||||
|
goto out_eacces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
|
||||||
|
SDCARDFS_I(dir)->data);
|
||||||
|
if (!saved_cred)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* 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_mnt = lower_path.mnt;
|
||||||
|
lower_dir_dentry = lock_parent(lower_dentry);
|
||||||
|
|
||||||
|
err = vfs_rmdir2(lower_mnt, d_inode(lower_dir_dentry), lower_dentry);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */
|
||||||
|
if (d_inode(dentry))
|
||||||
|
clear_nlink(d_inode(dentry));
|
||||||
|
fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry));
|
||||||
|
fsstack_copy_inode_size(dir, d_inode(lower_dir_dentry));
|
||||||
|
set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink);
|
||||||
|
|
||||||
|
out:
|
||||||
|
unlock_dir(lower_dir_dentry);
|
||||||
|
sdcardfs_put_real_lower(dentry, &lower_path);
|
||||||
|
revert_fsids(saved_cred);
|
||||||
|
out_eacces:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
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 vfsmount *lower_mnt = NULL;
|
||||||
|
struct dentry *trap = NULL;
|
||||||
|
struct path lower_old_path, lower_new_path;
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
|
if (flags)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) ||
|
||||||
|
!check_caller_access_to_name(new_dir, &new_dentry->d_name)) {
|
||||||
|
err = -EACCES;
|
||||||
|
goto out_eacces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(SDCARDFS_SB(old_dir->i_sb),
|
||||||
|
SDCARDFS_I(new_dir)->data);
|
||||||
|
if (!saved_cred)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
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_mnt = lower_old_path.mnt;
|
||||||
|
lower_old_dir_dentry = dget_parent(lower_old_dentry);
|
||||||
|
lower_new_dir_dentry = dget_parent(lower_new_dentry);
|
||||||
|
|
||||||
|
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
||||||
|
/* source should not be ancestor of target */
|
||||||
|
if (trap == lower_old_dentry) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* target should not be ancestor of source */
|
||||||
|
if (trap == lower_new_dentry) {
|
||||||
|
err = -ENOTEMPTY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = vfs_rename2(lower_mnt,
|
||||||
|
d_inode(lower_old_dir_dentry), lower_old_dentry,
|
||||||
|
d_inode(lower_new_dir_dentry), lower_new_dentry,
|
||||||
|
NULL, 0);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Copy attrs from lower dir, but i_uid/i_gid */
|
||||||
|
sdcardfs_copy_and_fix_attrs(new_dir, d_inode(lower_new_dir_dentry));
|
||||||
|
fsstack_copy_inode_size(new_dir, d_inode(lower_new_dir_dentry));
|
||||||
|
|
||||||
|
if (new_dir != old_dir) {
|
||||||
|
sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry));
|
||||||
|
fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry));
|
||||||
|
}
|
||||||
|
get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name);
|
||||||
|
fixup_tmp_permissions(d_inode(old_dentry));
|
||||||
|
fixup_lower_ownership(old_dentry, new_dentry->d_name.name);
|
||||||
|
d_invalidate(old_dentry); /* Can't fixup ownership recursively :( */
|
||||||
|
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_fsids(saved_cred);
|
||||||
|
out_eacces:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct dentry *lower_dentry;
|
||||||
|
struct path lower_path;
|
||||||
|
/* XXX readlink does not requires overriding credential */
|
||||||
|
|
||||||
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
|
lower_dentry = lower_path.dentry;
|
||||||
|
if (!d_inode(lower_dentry)->i_op ||
|
||||||
|
!d_inode(lower_dentry)->i_op->readlink) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d_inode(lower_dentry)->i_op->readlink(lower_dentry,
|
||||||
|
buf, bufsiz);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
fsstack_copy_attr_atime(d_inode(dentry), d_inode(lower_dentry));
|
||||||
|
|
||||||
|
out:
|
||||||
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
int len = PAGE_SIZE, err;
|
||||||
|
mm_segment_t old_fs;
|
||||||
|
|
||||||
|
/* This is freed by the put_link method assuming a successful call. */
|
||||||
|
buf = kmalloc(len, GFP_KERNEL);
|
||||||
|
if (!buf) {
|
||||||
|
buf = ERR_PTR(-ENOMEM);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read the symlink, and then we will follow it */
|
||||||
|
old_fs = get_fs();
|
||||||
|
set_fs(KERNEL_DS);
|
||||||
|
err = sdcardfs_readlink(dentry, buf, len);
|
||||||
|
set_fs(old_fs);
|
||||||
|
if (err < 0) {
|
||||||
|
kfree(buf);
|
||||||
|
buf = ERR_PTR(err);
|
||||||
|
} else {
|
||||||
|
buf[err] = '\0';
|
||||||
|
}
|
||||||
|
return *cookie = buf;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int sdcardfs_permission_wrn(struct inode *inode, int mask)
|
||||||
|
{
|
||||||
|
WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_attrs(struct inode *dest, const struct inode *src)
|
||||||
|
{
|
||||||
|
dest->i_mode = src->i_mode;
|
||||||
|
dest->i_uid = src->i_uid;
|
||||||
|
dest->i_gid = src->i_gid;
|
||||||
|
dest->i_rdev = src->i_rdev;
|
||||||
|
dest->i_atime = src->i_atime;
|
||||||
|
dest->i_mtime = src->i_mtime;
|
||||||
|
dest->i_ctime = src->i_ctime;
|
||||||
|
dest->i_blkbits = src->i_blkbits;
|
||||||
|
dest->i_flags = src->i_flags;
|
||||||
|
#ifdef CONFIG_FS_POSIX_ACL
|
||||||
|
dest->i_acl = src->i_acl;
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SECURITY
|
||||||
|
dest->i_security = src->i_security;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct inode tmp;
|
||||||
|
struct sdcardfs_inode_data *top = top_data_get(SDCARDFS_I(inode));
|
||||||
|
|
||||||
|
if (IS_ERR(mnt))
|
||||||
|
return PTR_ERR(mnt);
|
||||||
|
|
||||||
|
if (!top)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Permission check on sdcardfs inode.
|
||||||
|
* Calling process should have AID_SDCARD_RW permission
|
||||||
|
* Since generic_permission only needs i_mode, i_uid,
|
||||||
|
* i_gid, and i_sb, we can create a fake inode to pass
|
||||||
|
* this information down in.
|
||||||
|
*
|
||||||
|
* The underlying code may attempt to take locks in some
|
||||||
|
* cases for features we're not using, but if that changes,
|
||||||
|
* locks must be dealt with to avoid undefined behavior.
|
||||||
|
*/
|
||||||
|
copy_attrs(&tmp, inode);
|
||||||
|
tmp.i_uid = make_kuid(&init_user_ns, top->d_uid);
|
||||||
|
tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, inode->i_sb, top));
|
||||||
|
tmp.i_mode = (inode->i_mode & S_IFMT)
|
||||||
|
| get_mode(mnt, SDCARDFS_I(inode), top);
|
||||||
|
data_put(top);
|
||||||
|
tmp.i_sb = inode->i_sb;
|
||||||
|
if (IS_POSIXACL(inode))
|
||||||
|
pr_warn("%s: This may be undefined behavior...\n", __func__);
|
||||||
|
err = generic_permission(&tmp, mask);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia)
|
||||||
|
{
|
||||||
|
WARN_RATELIMIT(1, "sdcardfs does not support setattr. User setattr2.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct dentry *lower_dentry;
|
||||||
|
struct vfsmount *lower_mnt;
|
||||||
|
struct inode *inode;
|
||||||
|
struct inode *lower_inode;
|
||||||
|
struct path lower_path;
|
||||||
|
struct iattr lower_ia;
|
||||||
|
struct dentry *parent;
|
||||||
|
struct inode tmp;
|
||||||
|
struct dentry tmp_d;
|
||||||
|
struct sdcardfs_inode_data *top;
|
||||||
|
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
|
inode = d_inode(dentry);
|
||||||
|
top = top_data_get(SDCARDFS_I(inode));
|
||||||
|
|
||||||
|
if (!top)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Permission check on sdcardfs inode.
|
||||||
|
* Calling process should have AID_SDCARD_RW permission
|
||||||
|
* Since generic_permission only needs i_mode, i_uid,
|
||||||
|
* i_gid, and i_sb, we can create a fake inode to pass
|
||||||
|
* this information down in.
|
||||||
|
*
|
||||||
|
* The underlying code may attempt to take locks in some
|
||||||
|
* cases for features we're not using, but if that changes,
|
||||||
|
* locks must be dealt with to avoid undefined behavior.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
copy_attrs(&tmp, inode);
|
||||||
|
tmp.i_uid = make_kuid(&init_user_ns, top->d_uid);
|
||||||
|
tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, dentry->d_sb, top));
|
||||||
|
tmp.i_mode = (inode->i_mode & S_IFMT)
|
||||||
|
| get_mode(mnt, SDCARDFS_I(inode), top);
|
||||||
|
tmp.i_size = i_size_read(inode);
|
||||||
|
data_put(top);
|
||||||
|
tmp.i_sb = inode->i_sb;
|
||||||
|
tmp_d.d_inode = &tmp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if user has permission to change dentry. We don't check if
|
||||||
|
* this user can change the lower inode: that should happen when
|
||||||
|
* calling notify_change on the lower inode.
|
||||||
|
*/
|
||||||
|
/* prepare our own lower struct iattr (with the lower file) */
|
||||||
|
memcpy(&lower_ia, ia, sizeof(lower_ia));
|
||||||
|
/* Allow touch updating timestamps. A previous permission check ensures
|
||||||
|
* we have write access. Changes to mode, owner, and group are ignored
|
||||||
|
*/
|
||||||
|
ia->ia_valid |= ATTR_FORCE;
|
||||||
|
err = setattr_prepare(&tmp_d, ia);
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
/* check the Android group ID */
|
||||||
|
parent = dget_parent(dentry);
|
||||||
|
if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name))
|
||||||
|
err = -EACCES;
|
||||||
|
dput(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(SDCARDFS_SB(dentry->d_sb),
|
||||||
|
SDCARDFS_I(inode)->data);
|
||||||
|
if (!saved_cred)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
|
lower_dentry = lower_path.dentry;
|
||||||
|
lower_mnt = lower_path.mnt;
|
||||||
|
lower_inode = sdcardfs_lower_inode(inode);
|
||||||
|
|
||||||
|
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 (ia->ia_valid & ATTR_SIZE) {
|
||||||
|
err = inode_newsize_ok(&tmp, ia->ia_size);
|
||||||
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
truncate_setsize(inode, ia->ia_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mode change is for clearing setuid/setgid bits. Allow lower fs
|
||||||
|
* to interpret this in its own way.
|
||||||
|
*/
|
||||||
|
if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
|
||||||
|
lower_ia.ia_valid &= ~ATTR_MODE;
|
||||||
|
|
||||||
|
/* notify the (possibly copied-up) lower inode */
|
||||||
|
/*
|
||||||
|
* Note: we use d_inode(lower_dentry), because lower_inode may be
|
||||||
|
* unlinked (no inode->i_sb and i_ino==0. This happens if someone
|
||||||
|
* tries to open(), unlink(), then ftruncate() a file.
|
||||||
|
*/
|
||||||
|
inode_lock(d_inode(lower_dentry));
|
||||||
|
err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */
|
||||||
|
NULL);
|
||||||
|
inode_unlock(d_inode(lower_dentry));
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* get attributes from the lower inode and update derived permissions */
|
||||||
|
sdcardfs_copy_and_fix_attrs(inode, lower_inode);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not running fsstack_copy_inode_size(inode, lower_inode), because
|
||||||
|
* VFS should update our inode size, and notify_change on
|
||||||
|
* lower_inode should update its size.
|
||||||
|
*/
|
||||||
|
|
||||||
|
out:
|
||||||
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
revert_fsids(saved_cred);
|
||||||
|
out_err:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode,
|
||||||
|
struct kstat *lower_stat, struct kstat *stat)
|
||||||
|
{
|
||||||
|
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
|
||||||
|
struct sdcardfs_inode_data *top = top_data_get(info);
|
||||||
|
struct super_block *sb = inode->i_sb;
|
||||||
|
|
||||||
|
if (!top)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
stat->dev = inode->i_sb->s_dev;
|
||||||
|
stat->ino = inode->i_ino;
|
||||||
|
stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top);
|
||||||
|
stat->nlink = inode->i_nlink;
|
||||||
|
stat->uid = make_kuid(&init_user_ns, top->d_uid);
|
||||||
|
stat->gid = make_kgid(&init_user_ns, get_gid(mnt, sb, top));
|
||||||
|
stat->rdev = inode->i_rdev;
|
||||||
|
stat->size = lower_stat->size;
|
||||||
|
stat->atime = lower_stat->atime;
|
||||||
|
stat->mtime = lower_stat->mtime;
|
||||||
|
stat->ctime = lower_stat->ctime;
|
||||||
|
stat->blksize = lower_stat->blksize;
|
||||||
|
stat->blocks = lower_stat->blocks;
|
||||||
|
data_put(top);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int sdcardfs_getattr(const struct path *path, struct kstat *stat,
|
||||||
|
u32 request_mask, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct vfsmount *mnt = path->mnt;
|
||||||
|
struct dentry *dentry = path->dentry;
|
||||||
|
struct kstat lower_stat;
|
||||||
|
struct path lower_path;
|
||||||
|
struct dentry *parent;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
parent = dget_parent(dentry);
|
||||||
|
if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||||
|
dput(parent);
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
dput(parent);
|
||||||
|
|
||||||
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
|
err = vfs_getattr(&lower_path, &lower_stat, request_mask, flags);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
sdcardfs_copy_and_fix_attrs(d_inode(dentry),
|
||||||
|
d_inode(lower_path.dentry));
|
||||||
|
err = sdcardfs_fillattr(mnt, d_inode(dentry), &lower_stat, stat);
|
||||||
|
out:
|
||||||
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct inode_operations sdcardfs_symlink_iops = {
|
||||||
|
.permission2 = sdcardfs_permission,
|
||||||
|
.setattr2 = sdcardfs_setattr,
|
||||||
|
/* XXX Following operations are implemented,
|
||||||
|
* but FUSE(sdcard) or FAT does not support them
|
||||||
|
* These methods are *NOT* perfectly tested.
|
||||||
|
.readlink = sdcardfs_readlink,
|
||||||
|
.follow_link = sdcardfs_follow_link,
|
||||||
|
.put_link = kfree_put_link,
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct inode_operations sdcardfs_dir_iops = {
|
||||||
|
.create = sdcardfs_create,
|
||||||
|
.lookup = sdcardfs_lookup,
|
||||||
|
.permission = sdcardfs_permission_wrn,
|
||||||
|
.permission2 = sdcardfs_permission,
|
||||||
|
.unlink = sdcardfs_unlink,
|
||||||
|
.mkdir = sdcardfs_mkdir,
|
||||||
|
.rmdir = sdcardfs_rmdir,
|
||||||
|
.rename = sdcardfs_rename,
|
||||||
|
.setattr = sdcardfs_setattr_wrn,
|
||||||
|
.setattr2 = sdcardfs_setattr,
|
||||||
|
.getattr = sdcardfs_getattr,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct inode_operations sdcardfs_main_iops = {
|
||||||
|
.permission = sdcardfs_permission_wrn,
|
||||||
|
.permission2 = sdcardfs_permission,
|
||||||
|
.setattr = sdcardfs_setattr_wrn,
|
||||||
|
.setattr2 = sdcardfs_setattr,
|
||||||
|
.getattr = sdcardfs_getattr,
|
||||||
|
};
|
||||||
468
fs/sdcardfs/lookup.c
Normal file
468
fs/sdcardfs/lookup.c
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
kmem_cache_destroy(sdcardfs_dentry_cachep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_dentry_private_data(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata);
|
||||||
|
dentry->d_fsdata = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate new dentry private data */
|
||||||
|
int new_dentry_private_data(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
struct sdcardfs_dentry_info *info = SDCARDFS_D(dentry);
|
||||||
|
|
||||||
|
/* use zalloc to init dentry_info.lower_path */
|
||||||
|
info = kmem_cache_zalloc(sdcardfs_dentry_cachep, GFP_ATOMIC);
|
||||||
|
if (!info)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spin_lock_init(&info->lock);
|
||||||
|
dentry->d_fsdata = info;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct inode_data {
|
||||||
|
struct inode *lower_inode;
|
||||||
|
userid_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/)
|
||||||
|
{
|
||||||
|
struct inode *current_lower_inode = sdcardfs_lower_inode(inode);
|
||||||
|
userid_t current_userid = SDCARDFS_I(inode)->data->userid;
|
||||||
|
|
||||||
|
if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode &&
|
||||||
|
current_userid == ((struct inode_data *)candidate_data)->id)
|
||||||
|
return 1; /* found a match */
|
||||||
|
else
|
||||||
|
return 0; /* no match */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_inode_set(struct inode *inode, void *lower_inode)
|
||||||
|
{
|
||||||
|
/* we do actual inode initialization in sdcardfs_iget */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, userid_t id)
|
||||||
|
{
|
||||||
|
struct sdcardfs_inode_info *info;
|
||||||
|
struct inode_data data;
|
||||||
|
struct inode *inode; /* the new inode to return */
|
||||||
|
|
||||||
|
if (!igrab(lower_inode))
|
||||||
|
return ERR_PTR(-ESTALE);
|
||||||
|
|
||||||
|
data.id = id;
|
||||||
|
data.lower_inode = lower_inode;
|
||||||
|
inode = iget5_locked(sb, /* our superblock */
|
||||||
|
/*
|
||||||
|
* hashval: we use inode number, but we can
|
||||||
|
* also use "(unsigned long)lower_inode"
|
||||||
|
* instead.
|
||||||
|
*/
|
||||||
|
lower_inode->i_ino, /* hashval */
|
||||||
|
sdcardfs_inode_test, /* inode comparison function */
|
||||||
|
sdcardfs_inode_set, /* inode init function */
|
||||||
|
&data); /* data passed to test+set fxns */
|
||||||
|
if (!inode) {
|
||||||
|
iput(lower_inode);
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
/* if found a cached inode, then just return it (after iput) */
|
||||||
|
if (!(inode->i_state & I_NEW)) {
|
||||||
|
iput(lower_inode);
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialize new inode */
|
||||||
|
info = SDCARDFS_I(inode);
|
||||||
|
|
||||||
|
inode->i_ino = lower_inode->i_ino;
|
||||||
|
sdcardfs_set_lower_inode(inode, lower_inode);
|
||||||
|
|
||||||
|
inode_inc_iversion_raw(inode);
|
||||||
|
|
||||||
|
/* use different set of inode ops for symlinks & directories */
|
||||||
|
if (S_ISDIR(lower_inode->i_mode))
|
||||||
|
inode->i_op = &sdcardfs_dir_iops;
|
||||||
|
else if (S_ISLNK(lower_inode->i_mode))
|
||||||
|
inode->i_op = &sdcardfs_symlink_iops;
|
||||||
|
else
|
||||||
|
inode->i_op = &sdcardfs_main_iops;
|
||||||
|
|
||||||
|
/* use different set of file ops for directories */
|
||||||
|
if (S_ISDIR(lower_inode->i_mode))
|
||||||
|
inode->i_fop = &sdcardfs_dir_fops;
|
||||||
|
else
|
||||||
|
inode->i_fop = &sdcardfs_main_fops;
|
||||||
|
|
||||||
|
inode->i_mapping->a_ops = &sdcardfs_aops;
|
||||||
|
|
||||||
|
inode->i_atime.tv_sec = 0;
|
||||||
|
inode->i_atime.tv_nsec = 0;
|
||||||
|
inode->i_mtime.tv_sec = 0;
|
||||||
|
inode->i_mtime.tv_nsec = 0;
|
||||||
|
inode->i_ctime.tv_sec = 0;
|
||||||
|
inode->i_ctime.tv_nsec = 0;
|
||||||
|
|
||||||
|
/* properly initialize special inodes */
|
||||||
|
if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
|
||||||
|
S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
|
||||||
|
init_special_inode(inode, lower_inode->i_mode,
|
||||||
|
lower_inode->i_rdev);
|
||||||
|
|
||||||
|
/* all well, copy inode attributes */
|
||||||
|
sdcardfs_copy_and_fix_attrs(inode, lower_inode);
|
||||||
|
fsstack_copy_inode_size(inode, lower_inode);
|
||||||
|
|
||||||
|
unlock_new_inode(inode);
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper interpose routine, called directly by ->lookup to handle
|
||||||
|
* spliced dentries.
|
||||||
|
*/
|
||||||
|
static struct dentry *__sdcardfs_interpose(struct dentry *dentry,
|
||||||
|
struct super_block *sb,
|
||||||
|
struct path *lower_path,
|
||||||
|
userid_t id)
|
||||||
|
{
|
||||||
|
struct inode *inode;
|
||||||
|
struct inode *lower_inode;
|
||||||
|
struct super_block *lower_sb;
|
||||||
|
struct dentry *ret_dentry;
|
||||||
|
|
||||||
|
lower_inode = d_inode(lower_path->dentry);
|
||||||
|
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) {
|
||||||
|
ret_dentry = ERR_PTR(-EXDEV);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We allocate our new inode below by calling sdcardfs_iget,
|
||||||
|
* which will initialize some of the new inode's fields
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* inherit lower inode number for sdcardfs's inode */
|
||||||
|
inode = sdcardfs_iget(sb, lower_inode, id);
|
||||||
|
if (IS_ERR(inode)) {
|
||||||
|
ret_dentry = ERR_CAST(inode);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret_dentry = d_splice_alias(inode, dentry);
|
||||||
|
dentry = ret_dentry ?: dentry;
|
||||||
|
if (!IS_ERR(dentry))
|
||||||
|
update_derived_permission_lock(dentry);
|
||||||
|
out:
|
||||||
|
return ret_dentry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connect an sdcardfs inode dentry/inode with several lower ones. This is
|
||||||
|
* the classic stackable file system "vnode interposition" action.
|
||||||
|
*
|
||||||
|
* @dentry: sdcardfs's dentry which interposes on lower one
|
||||||
|
* @sb: sdcardfs's super_block
|
||||||
|
* @lower_path: the lower path (caller does path_get/put)
|
||||||
|
*/
|
||||||
|
int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
|
||||||
|
struct path *lower_path, userid_t id)
|
||||||
|
{
|
||||||
|
struct dentry *ret_dentry;
|
||||||
|
|
||||||
|
ret_dentry = __sdcardfs_interpose(dentry, sb, lower_path, id);
|
||||||
|
return PTR_ERR(ret_dentry);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sdcardfs_name_data {
|
||||||
|
struct dir_context ctx;
|
||||||
|
const struct qstr *to_find;
|
||||||
|
char *name;
|
||||||
|
bool found;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sdcardfs_name_match(struct dir_context *ctx, const char *name,
|
||||||
|
int namelen, loff_t offset, u64 ino, unsigned int d_type)
|
||||||
|
{
|
||||||
|
struct sdcardfs_name_data *buf = container_of(ctx, struct sdcardfs_name_data, ctx);
|
||||||
|
struct qstr candidate = QSTR_INIT(name, namelen);
|
||||||
|
|
||||||
|
if (qstr_case_eq(buf->to_find, &candidate)) {
|
||||||
|
memcpy(buf->name, name, namelen);
|
||||||
|
buf->name[namelen] = 0;
|
||||||
|
buf->found = true;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main driver function for sdcardfs's lookup.
|
||||||
|
*
|
||||||
|
* Returns: NULL (ok), ERR_PTR if an error occurred.
|
||||||
|
* Fills in lower_parent_path with <dentry,mnt> on success.
|
||||||
|
*/
|
||||||
|
static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
|
||||||
|
unsigned int flags, struct path *lower_parent_path, userid_t id)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct vfsmount *lower_dir_mnt;
|
||||||
|
struct dentry *lower_dir_dentry = NULL;
|
||||||
|
struct dentry *lower_dentry;
|
||||||
|
const struct qstr *name;
|
||||||
|
struct path lower_path;
|
||||||
|
struct dentry *ret_dentry = NULL;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* now start the actual lookup procedure */
|
||||||
|
lower_dir_dentry = lower_parent_path->dentry;
|
||||||
|
lower_dir_mnt = lower_parent_path->mnt;
|
||||||
|
|
||||||
|
/* Use vfs_path_lookup to check if the dentry exists or not */
|
||||||
|
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0,
|
||||||
|
&lower_path);
|
||||||
|
/* check for other cases */
|
||||||
|
if (err == -ENOENT) {
|
||||||
|
struct file *file;
|
||||||
|
const struct cred *cred = current_cred();
|
||||||
|
|
||||||
|
struct sdcardfs_name_data buffer = {
|
||||||
|
.ctx.actor = sdcardfs_name_match,
|
||||||
|
.to_find = name,
|
||||||
|
.name = __getname(),
|
||||||
|
.found = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!buffer.name) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
file = dentry_open(lower_parent_path, O_RDONLY, cred);
|
||||||
|
if (IS_ERR(file)) {
|
||||||
|
err = PTR_ERR(file);
|
||||||
|
goto put_name;
|
||||||
|
}
|
||||||
|
err = iterate_dir(file, &buffer.ctx);
|
||||||
|
fput(file);
|
||||||
|
if (err)
|
||||||
|
goto put_name;
|
||||||
|
|
||||||
|
if (buffer.found)
|
||||||
|
err = vfs_path_lookup(lower_dir_dentry,
|
||||||
|
lower_dir_mnt,
|
||||||
|
buffer.name, 0,
|
||||||
|
&lower_path);
|
||||||
|
else
|
||||||
|
err = -ENOENT;
|
||||||
|
put_name:
|
||||||
|
__putname(buffer.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no error: handle positive dentries */
|
||||||
|
if (!err) {
|
||||||
|
found:
|
||||||
|
/* check if the dentry is an obb dentry
|
||||||
|
* if true, the lower_inode must be replaced with
|
||||||
|
* the inode of the graft path
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (need_graft_path(dentry)) {
|
||||||
|
|
||||||
|
/* setup_obb_dentry()
|
||||||
|
* The lower_path will be stored to the dentry's orig_path
|
||||||
|
* and the base obbpath will be copyed to the lower_path variable.
|
||||||
|
* if an error returned, there's no change in the lower_path
|
||||||
|
* returns: -ERRNO if error (0: no error)
|
||||||
|
*/
|
||||||
|
err = setup_obb_dentry(dentry, &lower_path);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
/* if the sbi->obbpath is not available, we can optionally
|
||||||
|
* setup the lower_path with its orig_path.
|
||||||
|
* but, the current implementation just returns an error
|
||||||
|
* because the sdcard daemon also regards this case as
|
||||||
|
* a lookup fail.
|
||||||
|
*/
|
||||||
|
pr_info("sdcardfs: base obbpath is not available\n");
|
||||||
|
sdcardfs_put_reset_orig_path(dentry);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sdcardfs_set_lower_path(dentry, &lower_path);
|
||||||
|
ret_dentry =
|
||||||
|
__sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id);
|
||||||
|
if (IS_ERR(ret_dentry)) {
|
||||||
|
err = PTR_ERR(ret_dentry);
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* get a (very likely) new negative dentry */
|
||||||
|
lower_dentry = lookup_one_len_unlocked(name->name,
|
||||||
|
lower_dir_dentry, name->len);
|
||||||
|
if (IS_ERR(lower_dentry)) {
|
||||||
|
err = PTR_ERR(lower_dentry);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
lower_path.dentry = lower_dentry;
|
||||||
|
lower_path.mnt = mntget(lower_dir_mnt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if someone sneakily filled in the dentry when
|
||||||
|
* we weren't looking. We'll check again in create.
|
||||||
|
*/
|
||||||
|
if (unlikely(d_inode_rcu(lower_dentry))) {
|
||||||
|
err = 0;
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdcardfs_set_lower_path(dentry, &lower_path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the intent is to create a file, then don't return an error, so
|
||||||
|
* the VFS will continue the process of making this negative dentry
|
||||||
|
* into a positive one.
|
||||||
|
*/
|
||||||
|
if (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET))
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (err)
|
||||||
|
return ERR_PTR(err);
|
||||||
|
return ret_dentry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On success:
|
||||||
|
* fills dentry object appropriate values and returns NULL.
|
||||||
|
* On fail (== error)
|
||||||
|
* returns error ptr
|
||||||
|
*
|
||||||
|
* @dir : Parent inode.
|
||||||
|
* @dentry : Target dentry to lookup. we should set each of fields.
|
||||||
|
* (dentry->d_name is initialized already)
|
||||||
|
* @nd : nameidata of parent inode
|
||||||
|
*/
|
||||||
|
struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
struct dentry *ret = NULL, *parent;
|
||||||
|
struct path lower_parent_path;
|
||||||
|
int err = 0;
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
|
parent = dget_parent(dentry);
|
||||||
|
|
||||||
|
if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||||
|
ret = ERR_PTR(-EACCES);
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
|
||||||
|
SDCARDFS_I(dir)->data);
|
||||||
|
if (!saved_cred) {
|
||||||
|
ret = ERR_PTR(-ENOMEM);
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdcardfs_get_lower_path(parent, &lower_parent_path);
|
||||||
|
|
||||||
|
/* allocate dentry private data. We free it in ->d_release */
|
||||||
|
err = new_dentry_private_data(dentry);
|
||||||
|
if (err) {
|
||||||
|
ret = ERR_PTR(err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path,
|
||||||
|
SDCARDFS_I(dir)->data->userid);
|
||||||
|
if (IS_ERR(ret))
|
||||||
|
goto out;
|
||||||
|
if (ret)
|
||||||
|
dentry = ret;
|
||||||
|
if (d_inode(dentry)) {
|
||||||
|
fsstack_copy_attr_times(d_inode(dentry),
|
||||||
|
sdcardfs_lower_inode(d_inode(dentry)));
|
||||||
|
/* get derived permission */
|
||||||
|
get_derived_permission(parent, dentry);
|
||||||
|
fixup_tmp_permissions(d_inode(dentry));
|
||||||
|
fixup_lower_ownership(dentry, dentry->d_name.name);
|
||||||
|
}
|
||||||
|
/* update parent directory's atime */
|
||||||
|
fsstack_copy_attr_atime(d_inode(parent),
|
||||||
|
sdcardfs_lower_inode(d_inode(parent)));
|
||||||
|
|
||||||
|
out:
|
||||||
|
sdcardfs_put_lower_path(parent, &lower_parent_path);
|
||||||
|
revert_fsids(saved_cred);
|
||||||
|
out_err:
|
||||||
|
dput(parent);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
518
fs/sdcardfs/main.c
Normal file
518
fs/sdcardfs/main.c
Normal file
@@ -0,0 +1,518 @@
|
|||||||
|
/*
|
||||||
|
* 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/fscrypt.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_nocache,
|
||||||
|
Opt_unshared_obb,
|
||||||
|
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_nocache:
|
||||||
|
opts->nocache = true;
|
||||||
|
break;
|
||||||
|
case Opt_unshared_obb:
|
||||||
|
opts->unshared_obb = 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 = {
|
||||||
|
.name = "/",
|
||||||
|
.len = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = d_alloc(NULL, &name);
|
||||||
|
if (ret) {
|
||||||
|
d_set_d_op(ret, &sdcardfs_ci_dops);
|
||||||
|
ret->d_sb = sb;
|
||||||
|
ret->d_parent = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DEFINE_MUTEX(sdcardfs_super_list_lock);
|
||||||
|
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, d_inode(lower_path.dentry), 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(d_inode(sb->s_root), 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(d_inode(sb->s_root), 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(d_inode(sb->s_root));
|
||||||
|
sb_info->sb = sb;
|
||||||
|
list_add(&sb_info->list, &sdcardfs_super_list);
|
||||||
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
|
||||||
|
sb_info->fscrypt_nb.notifier_call = sdcardfs_on_fscrypt_key_removed;
|
||||||
|
fscrypt_register_key_removal_notifier(&sb_info->fscrypt_nb);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
fscrypt_unregister_key_removal_notifier(&sbi->fscrypt_nb);
|
||||||
|
|
||||||
|
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);
|
||||||
87
fs/sdcardfs/mmap.c
Normal file
87
fs/sdcardfs/mmap.c
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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_fault *vmf)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct file *file;
|
||||||
|
const struct vm_operations_struct *lower_vm_ops;
|
||||||
|
|
||||||
|
file = (struct file *)vmf->vma->vm_private_data;
|
||||||
|
lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
|
||||||
|
BUG_ON(!lower_vm_ops);
|
||||||
|
|
||||||
|
err = lower_vm_ops->fault(vmf);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdcardfs_vm_open(struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
struct file *file = (struct file *)vma->vm_private_data;
|
||||||
|
|
||||||
|
get_file(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdcardfs_vm_close(struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
struct file *file = (struct file *)vma->vm_private_data;
|
||||||
|
|
||||||
|
fput(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_page_mkwrite(struct vm_fault *vmf)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct file *file;
|
||||||
|
const struct vm_operations_struct *lower_vm_ops;
|
||||||
|
|
||||||
|
file = (struct file *)vmf->vma->vm_private_data;
|
||||||
|
lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
|
||||||
|
BUG_ON(!lower_vm_ops);
|
||||||
|
if (!lower_vm_ops->page_mkwrite)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = lower_vm_ops->page_mkwrite(vmf);
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This function should never be called directly. We need it
|
||||||
|
* to exist, to get past a check in open_check_o_direct(),
|
||||||
|
* which is called from do_last().
|
||||||
|
*/
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct address_space_operations sdcardfs_aops = {
|
||||||
|
.direct_IO = sdcardfs_direct_IO,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct vm_operations_struct sdcardfs_vm_ops = {
|
||||||
|
.fault = sdcardfs_fault,
|
||||||
|
.page_mkwrite = sdcardfs_page_mkwrite,
|
||||||
|
.open = sdcardfs_vm_open,
|
||||||
|
.close = sdcardfs_vm_close,
|
||||||
|
};
|
||||||
53
fs/sdcardfs/multiuser.h
Normal file
53
fs/sdcardfs/multiuser.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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 AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
|
||||||
|
#define AID_APP_START 10000 /* first app user */
|
||||||
|
#define AID_APP_END 19999 /* last app user */
|
||||||
|
#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
|
||||||
|
#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
|
||||||
|
#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */
|
||||||
|
#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */
|
||||||
|
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
|
||||||
|
|
||||||
|
typedef uid_t userid_t;
|
||||||
|
typedef uid_t appid_t;
|
||||||
|
|
||||||
|
static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id)
|
||||||
|
{
|
||||||
|
return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool uid_is_app(uid_t uid)
|
||||||
|
{
|
||||||
|
appid_t appid = uid % AID_USER_OFFSET;
|
||||||
|
|
||||||
|
return appid >= AID_APP_START && appid <= AID_APP_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gid_t multiuser_get_ext_cache_gid(uid_t uid)
|
||||||
|
{
|
||||||
|
return uid - AID_APP_START + AID_EXT_CACHE_GID_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gid_t multiuser_get_ext_gid(uid_t uid)
|
||||||
|
{
|
||||||
|
return uid - AID_APP_START + AID_EXT_GID_START;
|
||||||
|
}
|
||||||
882
fs/sdcardfs/packagelist.c
Normal file
882
fs/sdcardfs/packagelist.c
Normal file
@@ -0,0 +1,882 @@
|
|||||||
|
/*
|
||||||
|
* fs/sdcardfs/packagelist.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||||
|
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||||
|
* Sunghwan Yun, Sungjong Seo
|
||||||
|
*
|
||||||
|
* This program has been developed as a stackable file system based on
|
||||||
|
* the WrapFS which written by
|
||||||
|
*
|
||||||
|
* Copyright (c) 1998-2011 Erez Zadok
|
||||||
|
* Copyright (c) 2009 Shrikar Archak
|
||||||
|
* Copyright (c) 2003-2011 Stony Brook University
|
||||||
|
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||||
|
*
|
||||||
|
* This file is dual licensed. It may be redistributed and/or modified
|
||||||
|
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||||
|
* General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sdcardfs.h"
|
||||||
|
#include <linux/hashtable.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/radix-tree.h>
|
||||||
|
#include <linux/dcache.h>
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include <linux/configfs.h>
|
||||||
|
|
||||||
|
struct hashtable_entry {
|
||||||
|
struct hlist_node hlist;
|
||||||
|
struct hlist_node dlist; /* for deletion cleanup */
|
||||||
|
struct qstr key;
|
||||||
|
atomic_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DEFINE_HASHTABLE(package_to_appid, 8);
|
||||||
|
static DEFINE_HASHTABLE(package_to_userid, 8);
|
||||||
|
static DEFINE_HASHTABLE(ext_to_groupid, 8);
|
||||||
|
|
||||||
|
|
||||||
|
static struct kmem_cache *hashtable_entry_cachep;
|
||||||
|
|
||||||
|
static unsigned int full_name_case_hash(const void *salt, const unsigned char *name, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned long hash = init_name_hash(salt);
|
||||||
|
|
||||||
|
while (len--)
|
||||||
|
hash = partial_name_hash(tolower(*name++), hash);
|
||||||
|
return end_name_hash(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void qstr_init(struct qstr *q, const char *name)
|
||||||
|
{
|
||||||
|
q->name = name;
|
||||||
|
q->len = strlen(q->name);
|
||||||
|
q->hash = full_name_case_hash(0, q->name, q->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int qstr_copy(const struct qstr *src, struct qstr *dest)
|
||||||
|
{
|
||||||
|
dest->name = kstrdup(src->name, GFP_KERNEL);
|
||||||
|
dest->hash_len = src->hash_len;
|
||||||
|
return !!dest->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static appid_t __get_appid(const struct qstr *key)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
unsigned int hash = key->hash;
|
||||||
|
appid_t ret_id;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
|
||||||
|
if (qstr_case_eq(key, &hash_cur->key)) {
|
||||||
|
ret_id = atomic_read(&hash_cur->value);
|
||||||
|
rcu_read_unlock();
|
||||||
|
return ret_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
appid_t get_appid(const char *key)
|
||||||
|
{
|
||||||
|
struct qstr q;
|
||||||
|
|
||||||
|
qstr_init(&q, key);
|
||||||
|
return __get_appid(&q);
|
||||||
|
}
|
||||||
|
|
||||||
|
static appid_t __get_ext_gid(const struct qstr *key)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
unsigned int hash = key->hash;
|
||||||
|
appid_t ret_id;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
|
||||||
|
if (qstr_case_eq(key, &hash_cur->key)) {
|
||||||
|
ret_id = atomic_read(&hash_cur->value);
|
||||||
|
rcu_read_unlock();
|
||||||
|
return ret_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
appid_t get_ext_gid(const char *key)
|
||||||
|
{
|
||||||
|
struct qstr q;
|
||||||
|
|
||||||
|
qstr_init(&q, key);
|
||||||
|
return __get_ext_gid(&q);
|
||||||
|
}
|
||||||
|
|
||||||
|
static appid_t __is_excluded(const struct qstr *app_name, userid_t user)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
unsigned int hash = app_name->hash;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
|
||||||
|
if (atomic_read(&hash_cur->value) == user &&
|
||||||
|
qstr_case_eq(app_name, &hash_cur->key)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
appid_t is_excluded(const char *key, userid_t user)
|
||||||
|
{
|
||||||
|
struct qstr q;
|
||||||
|
qstr_init(&q, key);
|
||||||
|
return __is_excluded(&q, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 struct qstr *name)
|
||||||
|
{
|
||||||
|
struct qstr q_autorun = QSTR_LITERAL("autorun.inf");
|
||||||
|
struct qstr q__android_secure = QSTR_LITERAL(".android_secure");
|
||||||
|
struct qstr q_android_secure = QSTR_LITERAL("android_secure");
|
||||||
|
|
||||||
|
/* Always block security-sensitive files at root */
|
||||||
|
if (parent_node && SDCARDFS_I(parent_node)->data->perm == PERM_ROOT) {
|
||||||
|
if (qstr_case_eq(name, &q_autorun)
|
||||||
|
|| qstr_case_eq(name, &q__android_secure)
|
||||||
|
|| qstr_case_eq(name, &q_android_secure)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Root always has access; access for any other UIDs should always
|
||||||
|
* be controlled through packages.list.
|
||||||
|
*/
|
||||||
|
if (from_kuid(&init_user_ns, current_fsuid()) == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* No extra permissions to enforce */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key,
|
||||||
|
appid_t value)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!ret)
|
||||||
|
return NULL;
|
||||||
|
INIT_HLIST_NODE(&ret->dlist);
|
||||||
|
INIT_HLIST_NODE(&ret->hlist);
|
||||||
|
|
||||||
|
if (!qstr_copy(key, &ret->key)) {
|
||||||
|
kmem_cache_free(hashtable_entry_cachep, ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_set(&ret->value, value);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int insert_packagelist_appid_entry_locked(const struct qstr *key, appid_t value)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
struct hashtable_entry *new_entry;
|
||||||
|
unsigned int hash = key->hash;
|
||||||
|
|
||||||
|
hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
|
||||||
|
if (qstr_case_eq(key, &hash_cur->key)) {
|
||||||
|
atomic_set(&hash_cur->value, value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_entry = alloc_hashtable_entry(key, value);
|
||||||
|
if (!new_entry)
|
||||||
|
return -ENOMEM;
|
||||||
|
hash_add_rcu(package_to_appid, &new_entry->hlist, hash);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int insert_ext_gid_entry_locked(const struct qstr *key, appid_t value)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
struct hashtable_entry *new_entry;
|
||||||
|
unsigned int hash = key->hash;
|
||||||
|
|
||||||
|
/* An extension can only belong to one gid */
|
||||||
|
hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
|
||||||
|
if (qstr_case_eq(key, &hash_cur->key))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
new_entry = alloc_hashtable_entry(key, value);
|
||||||
|
if (!new_entry)
|
||||||
|
return -ENOMEM;
|
||||||
|
hash_add_rcu(ext_to_groupid, &new_entry->hlist, hash);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int insert_userid_exclude_entry_locked(const struct qstr *key, userid_t value)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
struct hashtable_entry *new_entry;
|
||||||
|
unsigned int hash = key->hash;
|
||||||
|
|
||||||
|
/* Only insert if not already present */
|
||||||
|
hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
|
||||||
|
if (atomic_read(&hash_cur->value) == value &&
|
||||||
|
qstr_case_eq(key, &hash_cur->key))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
new_entry = alloc_hashtable_entry(key, value);
|
||||||
|
if (!new_entry)
|
||||||
|
return -ENOMEM;
|
||||||
|
hash_add_rcu(package_to_userid, &new_entry->hlist, hash);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fixup_all_perms_name(const struct qstr *key)
|
||||||
|
{
|
||||||
|
struct sdcardfs_sb_info *sbinfo;
|
||||||
|
struct limit_search limit = {
|
||||||
|
.flags = BY_NAME,
|
||||||
|
.name = QSTR_INIT(key->name, key->len),
|
||||||
|
};
|
||||||
|
list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
|
||||||
|
if (sbinfo_has_sdcard_magic(sbinfo))
|
||||||
|
fixup_perms_recursive(sbinfo->sb->s_root, &limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid)
|
||||||
|
{
|
||||||
|
struct sdcardfs_sb_info *sbinfo;
|
||||||
|
struct limit_search limit = {
|
||||||
|
.flags = BY_NAME | BY_USERID,
|
||||||
|
.name = QSTR_INIT(key->name, key->len),
|
||||||
|
.userid = userid,
|
||||||
|
};
|
||||||
|
list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
|
||||||
|
if (sbinfo_has_sdcard_magic(sbinfo))
|
||||||
|
fixup_perms_recursive(sbinfo->sb->s_root, &limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fixup_all_perms_userid(userid_t userid)
|
||||||
|
{
|
||||||
|
struct sdcardfs_sb_info *sbinfo;
|
||||||
|
struct limit_search limit = {
|
||||||
|
.flags = BY_USERID,
|
||||||
|
.userid = userid,
|
||||||
|
};
|
||||||
|
list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
|
||||||
|
if (sbinfo_has_sdcard_magic(sbinfo))
|
||||||
|
fixup_perms_recursive(sbinfo->sb->s_root, &limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int insert_packagelist_entry(const struct qstr *key, appid_t value)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&sdcardfs_super_list_lock);
|
||||||
|
err = insert_packagelist_appid_entry_locked(key, value);
|
||||||
|
if (!err)
|
||||||
|
fixup_all_perms_name(key);
|
||||||
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int insert_ext_gid_entry(const struct qstr *key, appid_t value)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&sdcardfs_super_list_lock);
|
||||||
|
err = insert_ext_gid_entry_locked(key, value);
|
||||||
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int insert_userid_exclude_entry(const struct qstr *key, userid_t value)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&sdcardfs_super_list_lock);
|
||||||
|
err = insert_userid_exclude_entry_locked(key, value);
|
||||||
|
if (!err)
|
||||||
|
fixup_all_perms_name_userid(key, value);
|
||||||
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_hashtable_entry(struct hashtable_entry *entry)
|
||||||
|
{
|
||||||
|
kfree(entry->key.name);
|
||||||
|
kmem_cache_free(hashtable_entry_cachep, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_packagelist_entry_locked(const struct qstr *key)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
unsigned int hash = key->hash;
|
||||||
|
struct hlist_node *h_t;
|
||||||
|
HLIST_HEAD(free_list);
|
||||||
|
|
||||||
|
hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
|
||||||
|
if (qstr_case_eq(key, &hash_cur->key)) {
|
||||||
|
hash_del_rcu(&hash_cur->hlist);
|
||||||
|
hlist_add_head(&hash_cur->dlist, &free_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
|
||||||
|
if (qstr_case_eq(key, &hash_cur->key)) {
|
||||||
|
hash_del_rcu(&hash_cur->hlist);
|
||||||
|
hlist_add_head(&hash_cur->dlist, &free_list);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronize_rcu();
|
||||||
|
hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
|
||||||
|
free_hashtable_entry(hash_cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_packagelist_entry(const struct qstr *key)
|
||||||
|
{
|
||||||
|
mutex_lock(&sdcardfs_super_list_lock);
|
||||||
|
remove_packagelist_entry_locked(key);
|
||||||
|
fixup_all_perms_name(key);
|
||||||
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
unsigned int hash = key->hash;
|
||||||
|
|
||||||
|
hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
|
||||||
|
if (qstr_case_eq(key, &hash_cur->key) && atomic_read(&hash_cur->value) == group) {
|
||||||
|
hash_del_rcu(&hash_cur->hlist);
|
||||||
|
synchronize_rcu();
|
||||||
|
free_hashtable_entry(hash_cur);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_ext_gid_entry(const struct qstr *key, gid_t group)
|
||||||
|
{
|
||||||
|
mutex_lock(&sdcardfs_super_list_lock);
|
||||||
|
remove_ext_gid_entry_locked(key, group);
|
||||||
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_userid_all_entry_locked(userid_t userid)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
struct hlist_node *h_t;
|
||||||
|
HLIST_HEAD(free_list);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
|
||||||
|
if (atomic_read(&hash_cur->value) == userid) {
|
||||||
|
hash_del_rcu(&hash_cur->hlist);
|
||||||
|
hlist_add_head(&hash_cur->dlist, &free_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronize_rcu();
|
||||||
|
hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) {
|
||||||
|
free_hashtable_entry(hash_cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_userid_all_entry(userid_t userid)
|
||||||
|
{
|
||||||
|
mutex_lock(&sdcardfs_super_list_lock);
|
||||||
|
remove_userid_all_entry_locked(userid);
|
||||||
|
fixup_all_perms_userid(userid);
|
||||||
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
unsigned int hash = key->hash;
|
||||||
|
|
||||||
|
hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
|
||||||
|
if (qstr_case_eq(key, &hash_cur->key) &&
|
||||||
|
atomic_read(&hash_cur->value) == userid) {
|
||||||
|
hash_del_rcu(&hash_cur->hlist);
|
||||||
|
synchronize_rcu();
|
||||||
|
free_hashtable_entry(hash_cur);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid)
|
||||||
|
{
|
||||||
|
mutex_lock(&sdcardfs_super_list_lock);
|
||||||
|
remove_userid_exclude_entry_locked(key, userid);
|
||||||
|
fixup_all_perms_name_userid(key, userid);
|
||||||
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void packagelist_destroy(void)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
struct hlist_node *h_t;
|
||||||
|
HLIST_HEAD(free_list);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mutex_lock(&sdcardfs_super_list_lock);
|
||||||
|
hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) {
|
||||||
|
hash_del_rcu(&hash_cur->hlist);
|
||||||
|
hlist_add_head(&hash_cur->dlist, &free_list);
|
||||||
|
}
|
||||||
|
hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
|
||||||
|
hash_del_rcu(&hash_cur->hlist);
|
||||||
|
hlist_add_head(&hash_cur->dlist, &free_list);
|
||||||
|
}
|
||||||
|
synchronize_rcu();
|
||||||
|
hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
|
||||||
|
free_hashtable_entry(hash_cur);
|
||||||
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
pr_info("sdcardfs: destroyed packagelist pkgld\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \
|
||||||
|
static struct configfs_attribute _pfx##attr_##_name = { \
|
||||||
|
.ca_name = __stringify(_name), \
|
||||||
|
.ca_mode = S_IRUGO | S_IWUGO, \
|
||||||
|
.ca_owner = THIS_MODULE, \
|
||||||
|
.show = _pfx##_name##_show, \
|
||||||
|
.store = _pfx##_name##_store, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SDCARDFS_CONFIGFS_ATTR_RO(_pfx, _name) \
|
||||||
|
static struct configfs_attribute _pfx##attr_##_name = { \
|
||||||
|
.ca_name = __stringify(_name), \
|
||||||
|
.ca_mode = S_IRUGO, \
|
||||||
|
.ca_owner = THIS_MODULE, \
|
||||||
|
.show = _pfx##_name##_show, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SDCARDFS_CONFIGFS_ATTR_WO(_pfx, _name) \
|
||||||
|
static struct configfs_attribute _pfx##attr_##_name = { \
|
||||||
|
.ca_name = __stringify(_name), \
|
||||||
|
.ca_mode = S_IWUGO, \
|
||||||
|
.ca_owner = THIS_MODULE, \
|
||||||
|
.store = _pfx##_name##_store, \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct package_details {
|
||||||
|
struct config_item item;
|
||||||
|
struct qstr name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct package_details *to_package_details(struct config_item *item)
|
||||||
|
{
|
||||||
|
return item ? container_of(item, struct package_details, item) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t package_details_appid_show(struct config_item *item, char *page)
|
||||||
|
{
|
||||||
|
return scnprintf(page, PAGE_SIZE, "%u\n", __get_appid(&to_package_details(item)->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t package_details_appid_store(struct config_item *item,
|
||||||
|
const char *page, size_t count)
|
||||||
|
{
|
||||||
|
unsigned int tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtouint(page, 10, &tmp);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = insert_packagelist_entry(&to_package_details(item)->name, tmp);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t package_details_excluded_userids_show(struct config_item *item,
|
||||||
|
char *page)
|
||||||
|
{
|
||||||
|
struct package_details *package_details = to_package_details(item);
|
||||||
|
struct hashtable_entry *hash_cur;
|
||||||
|
unsigned int hash = package_details->name.hash;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
|
||||||
|
if (qstr_case_eq(&package_details->name, &hash_cur->key))
|
||||||
|
count += scnprintf(page + count, PAGE_SIZE - count,
|
||||||
|
"%d ", atomic_read(&hash_cur->value));
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
if (count)
|
||||||
|
count--;
|
||||||
|
count += scnprintf(page + count, PAGE_SIZE - count, "\n");
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t package_details_excluded_userids_store(struct config_item *item,
|
||||||
|
const char *page, size_t count)
|
||||||
|
{
|
||||||
|
unsigned int tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtouint(page, 10, &tmp);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = insert_userid_exclude_entry(&to_package_details(item)->name, tmp);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t package_details_clear_userid_store(struct config_item *item,
|
||||||
|
const char *page, size_t count)
|
||||||
|
{
|
||||||
|
unsigned int tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtouint(page, 10, &tmp);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
remove_userid_exclude_entry(&to_package_details(item)->name, tmp);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void package_details_release(struct config_item *item)
|
||||||
|
{
|
||||||
|
struct package_details *package_details = to_package_details(item);
|
||||||
|
|
||||||
|
pr_info("sdcardfs: removing %s\n", package_details->name.name);
|
||||||
|
remove_packagelist_entry(&package_details->name);
|
||||||
|
kfree(package_details->name.name);
|
||||||
|
kfree(package_details);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDCARDFS_CONFIGFS_ATTR(package_details_, appid);
|
||||||
|
SDCARDFS_CONFIGFS_ATTR(package_details_, excluded_userids);
|
||||||
|
SDCARDFS_CONFIGFS_ATTR_WO(package_details_, clear_userid);
|
||||||
|
|
||||||
|
static struct configfs_attribute *package_details_attrs[] = {
|
||||||
|
&package_details_attr_appid,
|
||||||
|
&package_details_attr_excluded_userids,
|
||||||
|
&package_details_attr_clear_userid,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct configfs_item_operations package_details_item_ops = {
|
||||||
|
.release = package_details_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct config_item_type package_appid_type = {
|
||||||
|
.ct_item_ops = &package_details_item_ops,
|
||||||
|
.ct_attrs = package_details_attrs,
|
||||||
|
.ct_owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct extensions_value {
|
||||||
|
struct config_group group;
|
||||||
|
unsigned int num;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct extension_details {
|
||||||
|
struct config_item item;
|
||||||
|
struct qstr name;
|
||||||
|
unsigned int num;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct extensions_value *to_extensions_value(struct config_item *item)
|
||||||
|
{
|
||||||
|
return item ? container_of(to_config_group(item), struct extensions_value, group) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct extension_details *to_extension_details(struct config_item *item)
|
||||||
|
{
|
||||||
|
return item ? container_of(item, struct extension_details, item) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void extension_details_release(struct config_item *item)
|
||||||
|
{
|
||||||
|
struct extension_details *extension_details = to_extension_details(item);
|
||||||
|
|
||||||
|
pr_info("sdcardfs: No longer mapping %s files to gid %d\n",
|
||||||
|
extension_details->name.name, extension_details->num);
|
||||||
|
remove_ext_gid_entry(&extension_details->name, extension_details->num);
|
||||||
|
kfree(extension_details->name.name);
|
||||||
|
kfree(extension_details);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct configfs_item_operations extension_details_item_ops = {
|
||||||
|
.release = extension_details_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct config_item_type extension_details_type = {
|
||||||
|
.ct_item_ops = &extension_details_item_ops,
|
||||||
|
.ct_owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct config_item *extension_details_make_item(struct config_group *group, const char *name)
|
||||||
|
{
|
||||||
|
struct extensions_value *extensions_value = to_extensions_value(&group->cg_item);
|
||||||
|
struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL);
|
||||||
|
const char *tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!extension_details)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
tmp = kstrdup(name, GFP_KERNEL);
|
||||||
|
if (!tmp) {
|
||||||
|
kfree(extension_details);
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
qstr_init(&extension_details->name, tmp);
|
||||||
|
extension_details->num = extensions_value->num;
|
||||||
|
ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
kfree(extension_details->name.name);
|
||||||
|
kfree(extension_details);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
config_item_init_type_name(&extension_details->item, name, &extension_details_type);
|
||||||
|
|
||||||
|
return &extension_details->item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct configfs_group_operations extensions_value_group_ops = {
|
||||||
|
.make_item = extension_details_make_item,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct config_item_type extensions_name_type = {
|
||||||
|
.ct_group_ops = &extensions_value_group_ops,
|
||||||
|
.ct_owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct config_group *extensions_make_group(struct config_group *group, const char *name)
|
||||||
|
{
|
||||||
|
struct extensions_value *extensions_value;
|
||||||
|
unsigned int tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
extensions_value = kzalloc(sizeof(struct extensions_value), GFP_KERNEL);
|
||||||
|
if (!extensions_value)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
ret = kstrtouint(name, 10, &tmp);
|
||||||
|
if (ret) {
|
||||||
|
kfree(extensions_value);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions_value->num = tmp;
|
||||||
|
config_group_init_type_name(&extensions_value->group, name,
|
||||||
|
&extensions_name_type);
|
||||||
|
return &extensions_value->group;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void extensions_drop_group(struct config_group *group, struct config_item *item)
|
||||||
|
{
|
||||||
|
struct extensions_value *value = to_extensions_value(item);
|
||||||
|
|
||||||
|
pr_info("sdcardfs: No longer mapping any files to gid %d\n", value->num);
|
||||||
|
kfree(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct configfs_group_operations extensions_group_ops = {
|
||||||
|
.make_group = extensions_make_group,
|
||||||
|
.drop_item = extensions_drop_group,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct config_item_type extensions_type = {
|
||||||
|
.ct_group_ops = &extensions_group_ops,
|
||||||
|
.ct_owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct config_group extension_group = {
|
||||||
|
.cg_item = {
|
||||||
|
.ci_namebuf = "extensions",
|
||||||
|
.ci_type = &extensions_type,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct config_item *packages_make_item(struct config_group *group, const char *name)
|
||||||
|
{
|
||||||
|
struct package_details *package_details;
|
||||||
|
const char *tmp;
|
||||||
|
|
||||||
|
package_details = kzalloc(sizeof(struct package_details), GFP_KERNEL);
|
||||||
|
if (!package_details)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
tmp = kstrdup(name, GFP_KERNEL);
|
||||||
|
if (!tmp) {
|
||||||
|
kfree(package_details);
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
qstr_init(&package_details->name, tmp);
|
||||||
|
config_item_init_type_name(&package_details->item, name,
|
||||||
|
&package_appid_type);
|
||||||
|
|
||||||
|
return &package_details->item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t packages_list_show(struct config_item *item, char *page)
|
||||||
|
{
|
||||||
|
struct hashtable_entry *hash_cur_app;
|
||||||
|
struct hashtable_entry *hash_cur_user;
|
||||||
|
int i;
|
||||||
|
int count = 0, written = 0;
|
||||||
|
const char errormsg[] = "<truncated>\n";
|
||||||
|
unsigned int hash;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) {
|
||||||
|
written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n",
|
||||||
|
hash_cur_app->key.name, atomic_read(&hash_cur_app->value));
|
||||||
|
hash = hash_cur_app->key.hash;
|
||||||
|
hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) {
|
||||||
|
if (qstr_case_eq(&hash_cur_app->key, &hash_cur_user->key)) {
|
||||||
|
written += scnprintf(page + count + written - 1,
|
||||||
|
PAGE_SIZE - sizeof(errormsg) - count - written + 1,
|
||||||
|
" %d\n", atomic_read(&hash_cur_user->value)) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) {
|
||||||
|
count += scnprintf(page + count, PAGE_SIZE - count, errormsg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
count += written;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t packages_remove_userid_store(struct config_item *item,
|
||||||
|
const char *page, size_t count)
|
||||||
|
{
|
||||||
|
unsigned int tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtouint(page, 10, &tmp);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
remove_userid_all_entry(tmp);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct configfs_attribute packages_attr_packages_gid_list = {
|
||||||
|
.ca_name = "packages_gid.list",
|
||||||
|
.ca_mode = S_IRUGO,
|
||||||
|
.ca_owner = THIS_MODULE,
|
||||||
|
.show = packages_list_show,
|
||||||
|
};
|
||||||
|
|
||||||
|
SDCARDFS_CONFIGFS_ATTR_WO(packages_, remove_userid);
|
||||||
|
|
||||||
|
static struct configfs_attribute *packages_attrs[] = {
|
||||||
|
&packages_attr_packages_gid_list,
|
||||||
|
&packages_attr_remove_userid,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that, since no extra work is required on ->drop_item(),
|
||||||
|
* no ->drop_item() is provided.
|
||||||
|
*/
|
||||||
|
static struct configfs_group_operations packages_group_ops = {
|
||||||
|
.make_item = packages_make_item,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct config_item_type packages_type = {
|
||||||
|
.ct_group_ops = &packages_group_ops,
|
||||||
|
.ct_attrs = packages_attrs,
|
||||||
|
.ct_owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct config_group *sd_default_groups[] = {
|
||||||
|
&extension_group,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct configfs_subsystem sdcardfs_packages = {
|
||||||
|
.su_group = {
|
||||||
|
.cg_item = {
|
||||||
|
.ci_namebuf = "sdcardfs",
|
||||||
|
.ci_type = &packages_type,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int configfs_sdcardfs_init(void)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
struct configfs_subsystem *subsys = &sdcardfs_packages;
|
||||||
|
|
||||||
|
config_group_init(&subsys->su_group);
|
||||||
|
for (i = 0; sd_default_groups[i]; i++) {
|
||||||
|
config_group_init(sd_default_groups[i]);
|
||||||
|
configfs_add_default_group(sd_default_groups[i], &subsys->su_group);
|
||||||
|
}
|
||||||
|
mutex_init(&subsys->su_mutex);
|
||||||
|
ret = configfs_register_subsystem(subsys);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("Error %d while registering subsystem %s\n",
|
||||||
|
ret,
|
||||||
|
subsys->su_group.cg_item.ci_namebuf);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void configfs_sdcardfs_exit(void)
|
||||||
|
{
|
||||||
|
configfs_unregister_subsystem(&sdcardfs_packages);
|
||||||
|
}
|
||||||
|
|
||||||
|
int packagelist_init(void)
|
||||||
|
{
|
||||||
|
hashtable_entry_cachep =
|
||||||
|
kmem_cache_create("packagelist_hashtable_entry",
|
||||||
|
sizeof(struct hashtable_entry), 0, 0, NULL);
|
||||||
|
if (!hashtable_entry_cachep) {
|
||||||
|
pr_err("sdcardfs: failed creating pkgl_hashtable entry slab cache\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
configfs_sdcardfs_init();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void packagelist_exit(void)
|
||||||
|
{
|
||||||
|
configfs_sdcardfs_exit();
|
||||||
|
packagelist_destroy();
|
||||||
|
kmem_cache_destroy(hashtable_entry_cachep);
|
||||||
|
}
|
||||||
658
fs/sdcardfs/sdcardfs.h
Normal file
658
fs/sdcardfs/sdcardfs.h
Normal file
@@ -0,0 +1,658 @@
|
|||||||
|
/*
|
||||||
|
* 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/aio.h>
|
||||||
|
#include <linux/kref.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/statfs.h>
|
||||||
|
#include <linux/fs_stack.h>
|
||||||
|
#include <linux/magic.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/security.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/iversion.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 pr_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_MEDIA_OBB 1059 /* obb files */
|
||||||
|
|
||||||
|
#define AID_SDCARD_IMAGE 1057
|
||||||
|
|
||||||
|
#define AID_PACKAGE_INFO 1027
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Permissions are handled by our permission function.
|
||||||
|
* We don't want anyone who happens to look at our inode value to prematurely
|
||||||
|
* block access, so store more permissive values. These are probably never
|
||||||
|
* used.
|
||||||
|
*/
|
||||||
|
#define fixup_tmp_permissions(x) \
|
||||||
|
do { \
|
||||||
|
(x)->i_uid = make_kuid(&init_user_ns, \
|
||||||
|
SDCARDFS_I(x)->data->d_uid); \
|
||||||
|
(x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \
|
||||||
|
(x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Android 5.0 support */
|
||||||
|
|
||||||
|
/* Permission mode for a specific node. Controls how file permissions
|
||||||
|
* are derived for children nodes.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
/* Nothing special; this node should just inherit from its parent. */
|
||||||
|
PERM_INHERIT,
|
||||||
|
/* This node is one level above a normal root; used for legacy layouts
|
||||||
|
* which use the first level to represent user_id.
|
||||||
|
*/
|
||||||
|
PERM_PRE_ROOT,
|
||||||
|
/* This node is "/" */
|
||||||
|
PERM_ROOT,
|
||||||
|
/* This node is "/Android" */
|
||||||
|
PERM_ANDROID,
|
||||||
|
/* This node is "/Android/data" */
|
||||||
|
PERM_ANDROID_DATA,
|
||||||
|
/* This node is "/Android/obb" */
|
||||||
|
PERM_ANDROID_OBB,
|
||||||
|
/* This node is "/Android/media" */
|
||||||
|
PERM_ANDROID_MEDIA,
|
||||||
|
/* This node is "/Android/[data|media|obb]/[package]" */
|
||||||
|
PERM_ANDROID_PACKAGE,
|
||||||
|
/* This node is "/Android/[data|media|obb]/[package]/cache" */
|
||||||
|
PERM_ANDROID_PACKAGE_CACHE,
|
||||||
|
} perm_t;
|
||||||
|
|
||||||
|
struct sdcardfs_sb_info;
|
||||||
|
struct sdcardfs_mount_options;
|
||||||
|
struct sdcardfs_inode_info;
|
||||||
|
struct sdcardfs_inode_data;
|
||||||
|
|
||||||
|
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
|
||||||
|
const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
|
||||||
|
struct sdcardfs_inode_data *data);
|
||||||
|
/* Do not directly use this function, use REVERT_CRED() instead. */
|
||||||
|
void revert_fsids(const struct cred *old_cred);
|
||||||
|
|
||||||
|
/* operations vectors defined in specific files */
|
||||||
|
extern const struct file_operations sdcardfs_main_fops;
|
||||||
|
extern const struct file_operations sdcardfs_dir_fops;
|
||||||
|
extern const struct inode_operations sdcardfs_main_iops;
|
||||||
|
extern const struct inode_operations sdcardfs_dir_iops;
|
||||||
|
extern const struct inode_operations sdcardfs_symlink_iops;
|
||||||
|
extern const struct super_operations sdcardfs_sops;
|
||||||
|
extern const struct dentry_operations sdcardfs_ci_dops;
|
||||||
|
extern const struct address_space_operations sdcardfs_aops, sdcardfs_dummy_aops;
|
||||||
|
extern const struct vm_operations_struct sdcardfs_vm_ops;
|
||||||
|
|
||||||
|
extern int sdcardfs_init_inode_cache(void);
|
||||||
|
extern void sdcardfs_destroy_inode_cache(void);
|
||||||
|
extern int sdcardfs_init_dentry_cache(void);
|
||||||
|
extern void sdcardfs_destroy_dentry_cache(void);
|
||||||
|
extern int new_dentry_private_data(struct dentry *dentry);
|
||||||
|
extern void free_dentry_private_data(struct dentry *dentry);
|
||||||
|
extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
|
unsigned int flags);
|
||||||
|
extern struct inode *sdcardfs_iget(struct super_block *sb,
|
||||||
|
struct inode *lower_inode, userid_t id);
|
||||||
|
extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
|
||||||
|
struct path *lower_path, userid_t id);
|
||||||
|
extern int sdcardfs_on_fscrypt_key_removed(struct notifier_block *nb,
|
||||||
|
unsigned long action, void *data);
|
||||||
|
|
||||||
|
/* file private data */
|
||||||
|
struct sdcardfs_file_info {
|
||||||
|
struct file *lower_file;
|
||||||
|
const struct vm_operations_struct *lower_vm_ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdcardfs_inode_data {
|
||||||
|
struct kref refcount;
|
||||||
|
bool abandoned;
|
||||||
|
|
||||||
|
perm_t perm;
|
||||||
|
userid_t userid;
|
||||||
|
uid_t d_uid;
|
||||||
|
bool under_android;
|
||||||
|
bool under_cache;
|
||||||
|
bool under_obb;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* sdcardfs inode data in memory */
|
||||||
|
struct sdcardfs_inode_info {
|
||||||
|
struct inode *lower_inode;
|
||||||
|
/* state derived based on current position in hierarchy */
|
||||||
|
struct sdcardfs_inode_data *data;
|
||||||
|
|
||||||
|
/* top folder for ownership */
|
||||||
|
spinlock_t top_lock;
|
||||||
|
struct sdcardfs_inode_data *top_data;
|
||||||
|
|
||||||
|
struct inode vfs_inode;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* sdcardfs dentry data in memory */
|
||||||
|
struct sdcardfs_dentry_info {
|
||||||
|
spinlock_t lock; /* protects lower_path */
|
||||||
|
struct path lower_path;
|
||||||
|
struct path orig_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdcardfs_mount_options {
|
||||||
|
uid_t fs_low_uid;
|
||||||
|
gid_t fs_low_gid;
|
||||||
|
userid_t fs_user_id;
|
||||||
|
bool multiuser;
|
||||||
|
bool gid_derivation;
|
||||||
|
bool default_normal;
|
||||||
|
bool unshared_obb;
|
||||||
|
unsigned int reserved_mb;
|
||||||
|
bool nocache;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdcardfs_vfsmount_options {
|
||||||
|
gid_t gid;
|
||||||
|
mode_t mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int parse_options_remount(struct super_block *sb, char *options, int silent,
|
||||||
|
struct sdcardfs_vfsmount_options *vfsopts);
|
||||||
|
|
||||||
|
/* sdcardfs super-block data in memory */
|
||||||
|
struct sdcardfs_sb_info {
|
||||||
|
struct super_block *sb;
|
||||||
|
struct super_block *lower_sb;
|
||||||
|
/* derived perm policy : some of options have been added
|
||||||
|
* to sdcardfs_mount_options (Android 4.4 support)
|
||||||
|
*/
|
||||||
|
struct sdcardfs_mount_options options;
|
||||||
|
spinlock_t lock; /* protects obbpath */
|
||||||
|
char *obbpath_s;
|
||||||
|
struct path obbpath;
|
||||||
|
void *pkgl_id;
|
||||||
|
struct list_head list;
|
||||||
|
struct notifier_block fscrypt_nb;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo)
|
||||||
|
{
|
||||||
|
return sbinfo && sbinfo->sb
|
||||||
|
&& sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct sdcardfs_inode_data *data_get(
|
||||||
|
struct sdcardfs_inode_data *data)
|
||||||
|
{
|
||||||
|
if (data)
|
||||||
|
kref_get(&data->refcount);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct sdcardfs_inode_data *top_data_get(
|
||||||
|
struct sdcardfs_inode_info *info)
|
||||||
|
{
|
||||||
|
struct sdcardfs_inode_data *top_data;
|
||||||
|
|
||||||
|
spin_lock(&info->top_lock);
|
||||||
|
top_data = data_get(info->top_data);
|
||||||
|
spin_unlock(&info->top_lock);
|
||||||
|
return top_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void data_release(struct kref *ref);
|
||||||
|
|
||||||
|
static inline void data_put(struct sdcardfs_inode_data *data)
|
||||||
|
{
|
||||||
|
kref_put(&data->refcount, data_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void release_own_data(struct sdcardfs_inode_info *info)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This happens exactly once per inode. At this point, the inode that
|
||||||
|
* originally held this data is about to be freed, and all references
|
||||||
|
* to it are held as a top value, and will likely be released soon.
|
||||||
|
*/
|
||||||
|
info->data->abandoned = true;
|
||||||
|
data_put(info->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_top(struct sdcardfs_inode_info *info,
|
||||||
|
struct sdcardfs_inode_info *top_owner)
|
||||||
|
{
|
||||||
|
struct sdcardfs_inode_data *old_top;
|
||||||
|
struct sdcardfs_inode_data *new_top = NULL;
|
||||||
|
|
||||||
|
if (top_owner)
|
||||||
|
new_top = top_data_get(top_owner);
|
||||||
|
|
||||||
|
spin_lock(&info->top_lock);
|
||||||
|
old_top = info->top_data;
|
||||||
|
info->top_data = new_top;
|
||||||
|
if (old_top)
|
||||||
|
data_put(old_top);
|
||||||
|
spin_unlock(&info->top_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int get_gid(struct vfsmount *mnt,
|
||||||
|
struct super_block *sb,
|
||||||
|
struct sdcardfs_inode_data *data)
|
||||||
|
{
|
||||||
|
struct sdcardfs_vfsmount_options *vfsopts = mnt->data;
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(sb);
|
||||||
|
|
||||||
|
if (vfsopts->gid == AID_SDCARD_RW && !sbi->options.default_normal)
|
||||||
|
/* As an optimization, certain trusted system components only run
|
||||||
|
* as owner but operate across all users. Since we're now handing
|
||||||
|
* out the sdcard_rw GID only to trusted apps, we're okay relaxing
|
||||||
|
* the user boundary enforcement for the default view. The UIDs
|
||||||
|
* assigned to app directories are still multiuser aware.
|
||||||
|
*/
|
||||||
|
return AID_SDCARD_RW;
|
||||||
|
else
|
||||||
|
return multiuser_get_uid(data->userid, vfsopts->gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int get_mode(struct vfsmount *mnt,
|
||||||
|
struct sdcardfs_inode_info *info,
|
||||||
|
struct sdcardfs_inode_data *data)
|
||||||
|
{
|
||||||
|
int owner_mode;
|
||||||
|
int filtered_mode;
|
||||||
|
struct sdcardfs_vfsmount_options *opts = mnt->data;
|
||||||
|
int visible_mode = 0775 & ~opts->mask;
|
||||||
|
|
||||||
|
|
||||||
|
if (data->perm == PERM_PRE_ROOT) {
|
||||||
|
/* Top of multi-user view should always be visible to ensure
|
||||||
|
* secondary users can traverse inside.
|
||||||
|
*/
|
||||||
|
visible_mode = 0711;
|
||||||
|
} else if (data->under_android) {
|
||||||
|
/* Block "other" access to Android directories, since only apps
|
||||||
|
* belonging to a specific user should be in there; we still
|
||||||
|
* leave +x open for the default view.
|
||||||
|
*/
|
||||||
|
if (opts->gid == AID_SDCARD_RW)
|
||||||
|
visible_mode = visible_mode & ~0006;
|
||||||
|
else
|
||||||
|
visible_mode = visible_mode & ~0007;
|
||||||
|
}
|
||||||
|
owner_mode = info->lower_inode->i_mode & 0700;
|
||||||
|
filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
|
||||||
|
return filtered_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int has_graft_path(const struct dentry *dent)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
spin_lock(&SDCARDFS_D(dent)->lock);
|
||||||
|
if (SDCARDFS_D(dent)->orig_path.dentry != NULL)
|
||||||
|
ret = 1;
|
||||||
|
spin_unlock(&SDCARDFS_D(dent)->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sdcardfs_get_real_lower(const struct dentry *dent,
|
||||||
|
struct path *real_lower)
|
||||||
|
{
|
||||||
|
/* in case of a local obb dentry
|
||||||
|
* the orig_path should be returned
|
||||||
|
*/
|
||||||
|
if (has_graft_path(dent))
|
||||||
|
sdcardfs_get_orig_path(dent, real_lower);
|
||||||
|
else
|
||||||
|
sdcardfs_get_lower_path(dent, real_lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sdcardfs_put_real_lower(const struct dentry *dent,
|
||||||
|
struct path *real_lower)
|
||||||
|
{
|
||||||
|
if (has_graft_path(dent))
|
||||||
|
sdcardfs_put_orig_path(dent, real_lower);
|
||||||
|
else
|
||||||
|
sdcardfs_put_lower_path(dent, real_lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern struct mutex sdcardfs_super_list_lock;
|
||||||
|
extern struct list_head sdcardfs_super_list;
|
||||||
|
|
||||||
|
/* for packagelist.c */
|
||||||
|
extern appid_t get_appid(const char *app_name);
|
||||||
|
extern appid_t get_ext_gid(const char *app_name);
|
||||||
|
extern appid_t is_excluded(const char *app_name, userid_t userid);
|
||||||
|
extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name);
|
||||||
|
extern int packagelist_init(void);
|
||||||
|
extern void packagelist_exit(void);
|
||||||
|
|
||||||
|
/* for derived_perm.c */
|
||||||
|
#define BY_NAME (1 << 0)
|
||||||
|
#define BY_USERID (1 << 1)
|
||||||
|
struct limit_search {
|
||||||
|
unsigned int flags;
|
||||||
|
struct qstr name;
|
||||||
|
userid_t userid;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void setup_derived_state(struct inode *inode, perm_t perm,
|
||||||
|
userid_t userid, uid_t uid);
|
||||||
|
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
|
||||||
|
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name);
|
||||||
|
extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
|
||||||
|
|
||||||
|
extern void update_derived_permission_lock(struct dentry *dentry);
|
||||||
|
void fixup_lower_ownership(struct dentry *dentry, const char *name);
|
||||||
|
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);
|
||||||
|
|
||||||
|
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void unlock_dir(struct dentry *dir)
|
||||||
|
{
|
||||||
|
inode_unlock(d_inode(dir));
|
||||||
|
dput(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t mode)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct dentry *dent;
|
||||||
|
struct iattr attrs;
|
||||||
|
struct path parent;
|
||||||
|
|
||||||
|
dent = kern_path_locked(path_s, &parent);
|
||||||
|
if (IS_ERR(dent)) {
|
||||||
|
err = PTR_ERR(dent);
|
||||||
|
if (err == -EEXIST)
|
||||||
|
err = 0;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = vfs_mkdir2(parent.mnt, d_inode(parent.dentry), dent, mode);
|
||||||
|
if (err) {
|
||||||
|
if (err == -EEXIST)
|
||||||
|
err = 0;
|
||||||
|
goto out_dput;
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs.ia_uid = make_kuid(&init_user_ns, uid);
|
||||||
|
attrs.ia_gid = make_kgid(&init_user_ns, gid);
|
||||||
|
attrs.ia_valid = ATTR_UID | ATTR_GID;
|
||||||
|
inode_lock(d_inode(dent));
|
||||||
|
notify_change2(parent.mnt, dent, &attrs, NULL);
|
||||||
|
inode_unlock(d_inode(dent));
|
||||||
|
|
||||||
|
out_dput:
|
||||||
|
dput(dent);
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
/* parent dentry locked by lookup_create */
|
||||||
|
inode_unlock(d_inode(parent.dentry));
|
||||||
|
path_put(&parent);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 1, if a disk has enough free space, otherwise 0.
|
||||||
|
* We assume that any files can not be overwritten.
|
||||||
|
*/
|
||||||
|
static inline int check_min_free_space(struct dentry *dentry, size_t size, int dir)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct path lower_path;
|
||||||
|
struct kstatfs statfs;
|
||||||
|
u64 avail;
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
|
||||||
|
if (sbi->options.reserved_mb) {
|
||||||
|
/* Get fs stat of lower filesystem. */
|
||||||
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
|
err = vfs_statfs(&lower_path, &statfs);
|
||||||
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
|
||||||
|
if (unlikely(err))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Invalid statfs informations. */
|
||||||
|
if (unlikely(statfs.f_bsize == 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* if you are checking directory, set size to f_bsize. */
|
||||||
|
if (unlikely(dir))
|
||||||
|
size = statfs.f_bsize;
|
||||||
|
|
||||||
|
/* available size */
|
||||||
|
avail = statfs.f_bavail * statfs.f_bsize;
|
||||||
|
|
||||||
|
/* not enough space */
|
||||||
|
if ((u64)size > avail)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* enough space */
|
||||||
|
if ((avail - size) > (sbi->options.reserved_mb * 1024 * 1024))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copies attrs and maintains sdcardfs managed attrs
|
||||||
|
* Since our permission check handles all special permissions, set those to be open
|
||||||
|
*/
|
||||||
|
static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
|
||||||
|
{
|
||||||
|
dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG |
|
||||||
|
S_IROTH | S_IXOTH; /* 0775 */
|
||||||
|
dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->data->d_uid);
|
||||||
|
dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW);
|
||||||
|
dest->i_rdev = src->i_rdev;
|
||||||
|
dest->i_atime = src->i_atime;
|
||||||
|
dest->i_mtime = src->i_mtime;
|
||||||
|
dest->i_ctime = src->i_ctime;
|
||||||
|
dest->i_blkbits = src->i_blkbits;
|
||||||
|
dest->i_flags = src->i_flags;
|
||||||
|
set_nlink(dest, src->i_nlink);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool str_case_eq(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
return !strcasecmp(s1, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len)
|
||||||
|
{
|
||||||
|
return !strncasecmp(s1, s2, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2)
|
||||||
|
{
|
||||||
|
return q1->len == q2->len && str_n_case_eq(q1->name, q2->name, q2->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1)
|
||||||
|
|
||||||
|
#endif /* not _SDCARDFS_H_ */
|
||||||
352
fs/sdcardfs/super.c
Normal file
352
fs/sdcardfs/super.c
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To support the top references, we must track some data separately.
|
||||||
|
* An sdcardfs_inode_info always has a reference to its data, and once set up,
|
||||||
|
* also has a reference to its top. The top may be itself, in which case it
|
||||||
|
* holds two references to its data. When top is changed, it takes a ref to the
|
||||||
|
* new data and then drops the ref to the old data.
|
||||||
|
*/
|
||||||
|
static struct kmem_cache *sdcardfs_inode_data_cachep;
|
||||||
|
|
||||||
|
void data_release(struct kref *ref)
|
||||||
|
{
|
||||||
|
struct sdcardfs_inode_data *data =
|
||||||
|
container_of(ref, struct sdcardfs_inode_data, refcount);
|
||||||
|
|
||||||
|
kmem_cache_free(sdcardfs_inode_data_cachep, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* final actions when unmounting a file system */
|
||||||
|
static void sdcardfs_put_super(struct super_block *sb)
|
||||||
|
{
|
||||||
|
struct sdcardfs_sb_info *spd;
|
||||||
|
struct super_block *s;
|
||||||
|
|
||||||
|
spd = SDCARDFS_SB(sb);
|
||||||
|
if (!spd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (spd->obbpath_s) {
|
||||||
|
kfree(spd->obbpath_s);
|
||||||
|
path_put(&spd->obbpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* decrement lower super references */
|
||||||
|
s = sdcardfs_lower_super(sb);
|
||||||
|
sdcardfs_set_lower_super(sb, NULL);
|
||||||
|
atomic_dec(&s->s_active);
|
||||||
|
|
||||||
|
kfree(spd);
|
||||||
|
sb->s_fs_info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct path lower_path;
|
||||||
|
u32 min_blocks;
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
|
||||||
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
|
err = vfs_statfs(&lower_path, buf);
|
||||||
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
|
||||||
|
if (sbi->options.reserved_mb) {
|
||||||
|
/* Invalid statfs informations. */
|
||||||
|
if (buf->f_bsize == 0) {
|
||||||
|
pr_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) {
|
||||||
|
pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags);
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @mnt: mount point we are remounting
|
||||||
|
* @sb: superblock we are remounting
|
||||||
|
* @flags: numeric mount options
|
||||||
|
* @options: mount options string
|
||||||
|
*/
|
||||||
|
static int sdcardfs_remount_fs2(struct vfsmount *mnt, 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 | MS_REMOUNT)) != 0) {
|
||||||
|
pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags);
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
|
pr_info("Remount options were %s for vfsmnt %p.\n", options, mnt);
|
||||||
|
err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data);
|
||||||
|
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *sdcardfs_clone_mnt_data(void *data)
|
||||||
|
{
|
||||||
|
struct sdcardfs_vfsmount_options *opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
|
||||||
|
struct sdcardfs_vfsmount_options *old = data;
|
||||||
|
|
||||||
|
if (!opt)
|
||||||
|
return NULL;
|
||||||
|
opt->gid = old->gid;
|
||||||
|
opt->mask = old->mask;
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdcardfs_copy_mnt_data(void *data, void *newdata)
|
||||||
|
{
|
||||||
|
struct sdcardfs_vfsmount_options *old = data;
|
||||||
|
struct sdcardfs_vfsmount_options *new = newdata;
|
||||||
|
|
||||||
|
old->gid = new->gid;
|
||||||
|
old->mask = new->mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
set_top(SDCARDFS_I(inode), NULL);
|
||||||
|
clear_inode(inode);
|
||||||
|
/*
|
||||||
|
* Decrement a reference to a lower_inode, which was incremented
|
||||||
|
* by our read_inode when it was created initially.
|
||||||
|
*/
|
||||||
|
lower_inode = sdcardfs_lower_inode(inode);
|
||||||
|
sdcardfs_set_lower_inode(inode, NULL);
|
||||||
|
iput(lower_inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
|
||||||
|
{
|
||||||
|
struct sdcardfs_inode_info *i;
|
||||||
|
struct sdcardfs_inode_data *d;
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
d = kmem_cache_alloc(sdcardfs_inode_data_cachep,
|
||||||
|
GFP_KERNEL | __GFP_ZERO);
|
||||||
|
if (!d) {
|
||||||
|
kmem_cache_free(sdcardfs_inode_cachep, i);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
i->data = d;
|
||||||
|
kref_init(&d->refcount);
|
||||||
|
i->top_data = d;
|
||||||
|
spin_lock_init(&i->top_lock);
|
||||||
|
kref_get(&d->refcount);
|
||||||
|
|
||||||
|
inode_set_iversion(&i->vfs_inode, 1);
|
||||||
|
return &i->vfs_inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i_callback(struct rcu_head *head)
|
||||||
|
{
|
||||||
|
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||||
|
|
||||||
|
release_own_data(SDCARDFS_I(inode));
|
||||||
|
kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdcardfs_destroy_inode(struct inode *inode)
|
||||||
|
{
|
||||||
|
call_rcu(&inode->i_rcu, i_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
sdcardfs_inode_cachep =
|
||||||
|
kmem_cache_create("sdcardfs_inode_cache",
|
||||||
|
sizeof(struct sdcardfs_inode_info), 0,
|
||||||
|
SLAB_RECLAIM_ACCOUNT, init_once);
|
||||||
|
|
||||||
|
if (!sdcardfs_inode_cachep)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sdcardfs_inode_data_cachep =
|
||||||
|
kmem_cache_create("sdcardfs_inode_data_cache",
|
||||||
|
sizeof(struct sdcardfs_inode_data), 0,
|
||||||
|
SLAB_RECLAIM_ACCOUNT, NULL);
|
||||||
|
if (!sdcardfs_inode_data_cachep) {
|
||||||
|
kmem_cache_destroy(sdcardfs_inode_cachep);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sdcardfs inode cache destructor */
|
||||||
|
void sdcardfs_destroy_inode_cache(void)
|
||||||
|
{
|
||||||
|
kmem_cache_destroy(sdcardfs_inode_data_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 vfsmount *mnt, struct seq_file *m,
|
||||||
|
struct dentry *root)
|
||||||
|
{
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
|
||||||
|
struct sdcardfs_mount_options *opts = &sbi->options;
|
||||||
|
struct sdcardfs_vfsmount_options *vfsopts = mnt->data;
|
||||||
|
|
||||||
|
if (opts->fs_low_uid != 0)
|
||||||
|
seq_printf(m, ",fsuid=%u", opts->fs_low_uid);
|
||||||
|
if (opts->fs_low_gid != 0)
|
||||||
|
seq_printf(m, ",fsgid=%u", opts->fs_low_gid);
|
||||||
|
if (vfsopts->gid != 0)
|
||||||
|
seq_printf(m, ",gid=%u", vfsopts->gid);
|
||||||
|
if (opts->multiuser)
|
||||||
|
seq_puts(m, ",multiuser");
|
||||||
|
if (vfsopts->mask)
|
||||||
|
seq_printf(m, ",mask=%u", vfsopts->mask);
|
||||||
|
if (opts->fs_user_id)
|
||||||
|
seq_printf(m, ",userid=%u", opts->fs_user_id);
|
||||||
|
if (opts->gid_derivation)
|
||||||
|
seq_puts(m, ",derive_gid");
|
||||||
|
if (opts->default_normal)
|
||||||
|
seq_puts(m, ",default_normal");
|
||||||
|
if (opts->reserved_mb != 0)
|
||||||
|
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
|
||||||
|
if (opts->nocache)
|
||||||
|
seq_printf(m, ",nocache");
|
||||||
|
if (opts->unshared_obb)
|
||||||
|
seq_printf(m, ",unshared_obb");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
int sdcardfs_on_fscrypt_key_removed(struct notifier_block *nb,
|
||||||
|
unsigned long action, void *data)
|
||||||
|
{
|
||||||
|
struct sdcardfs_sb_info *sbi = container_of(nb, struct sdcardfs_sb_info,
|
||||||
|
fscrypt_nb);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evict any unused sdcardfs dentries (and hence any unused sdcardfs
|
||||||
|
* inodes, since sdcardfs doesn't cache unpinned inodes by themselves)
|
||||||
|
* so that the lower filesystem's encrypted inodes can be evicted.
|
||||||
|
* This is needed to make the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl
|
||||||
|
* properly "lock" the files underneath the sdcardfs mount.
|
||||||
|
*/
|
||||||
|
shrink_dcache_sb(sbi->sb);
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct super_operations sdcardfs_sops = {
|
||||||
|
.put_super = sdcardfs_put_super,
|
||||||
|
.statfs = sdcardfs_statfs,
|
||||||
|
.remount_fs = sdcardfs_remount_fs,
|
||||||
|
.remount_fs2 = sdcardfs_remount_fs2,
|
||||||
|
.clone_mnt_data = sdcardfs_clone_mnt_data,
|
||||||
|
.copy_mnt_data = sdcardfs_copy_mnt_data,
|
||||||
|
.evict_inode = sdcardfs_evict_inode,
|
||||||
|
.umount_begin = sdcardfs_umount_begin,
|
||||||
|
.show_options2 = sdcardfs_show_options,
|
||||||
|
.alloc_inode = sdcardfs_alloc_inode,
|
||||||
|
.destroy_inode = sdcardfs_destroy_inode,
|
||||||
|
.drop_inode = generic_delete_inode,
|
||||||
|
};
|
||||||
@@ -88,7 +88,7 @@ static int utimes_common(const struct path *path, struct timespec64 *times)
|
|||||||
}
|
}
|
||||||
retry_deleg:
|
retry_deleg:
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
error = notify_change(path->dentry, &newattrs, &delegated_inode);
|
error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
error = break_deleg_wait(&delegated_inode);
|
error = break_deleg_wait(&delegated_inode);
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ xattr_permission(struct inode *inode, const char *name, int mask)
|
|||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
return inode_permission(inode, mask);
|
return inode_permission2(ERR_PTR(-EOPNOTSUPP), inode, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ struct dentry_operations {
|
|||||||
struct vfsmount *(*d_automount)(struct path *);
|
struct vfsmount *(*d_automount)(struct path *);
|
||||||
int (*d_manage)(const struct path *, bool);
|
int (*d_manage)(const struct path *, bool);
|
||||||
struct dentry *(*d_real)(struct dentry *, const struct inode *);
|
struct dentry *(*d_real)(struct dentry *, const struct inode *);
|
||||||
|
void (*d_canonical_path)(const struct path *, struct path *);
|
||||||
ANDROID_KABI_RESERVE(1);
|
ANDROID_KABI_RESERVE(1);
|
||||||
ANDROID_KABI_RESERVE(2);
|
ANDROID_KABI_RESERVE(2);
|
||||||
ANDROID_KABI_RESERVE(3);
|
ANDROID_KABI_RESERVE(3);
|
||||||
|
|||||||
@@ -1692,13 +1692,21 @@ extern bool inode_owner_or_capable(const struct inode *inode);
|
|||||||
* VFS helper functions..
|
* VFS helper functions..
|
||||||
*/
|
*/
|
||||||
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
|
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
|
||||||
|
extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool);
|
||||||
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
|
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
|
||||||
|
extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t);
|
||||||
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
|
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
|
||||||
|
extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t);
|
||||||
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
|
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
|
||||||
|
extern int vfs_symlink2(struct vfsmount *, struct inode *, struct dentry *, const char *);
|
||||||
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
|
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
|
||||||
|
extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *, struct inode **);
|
||||||
extern int vfs_rmdir(struct inode *, struct dentry *);
|
extern int vfs_rmdir(struct inode *, struct dentry *);
|
||||||
|
extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *);
|
||||||
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
|
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
|
||||||
|
extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *, struct inode **);
|
||||||
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
|
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
|
||||||
|
extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
|
||||||
extern int vfs_whiteout(struct inode *, struct dentry *);
|
extern int vfs_whiteout(struct inode *, struct dentry *);
|
||||||
|
|
||||||
extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode,
|
extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode,
|
||||||
@@ -1707,6 +1715,9 @@ extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode,
|
|||||||
int vfs_mkobj(struct dentry *, umode_t,
|
int vfs_mkobj(struct dentry *, umode_t,
|
||||||
int (*f)(struct dentry *, umode_t, void *),
|
int (*f)(struct dentry *, umode_t, void *),
|
||||||
void *);
|
void *);
|
||||||
|
int vfs_mkobj2(struct vfsmount *, struct dentry *, umode_t,
|
||||||
|
int (*f)(struct dentry *, umode_t, void *),
|
||||||
|
void *);
|
||||||
|
|
||||||
extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||||
|
|
||||||
@@ -1842,6 +1853,7 @@ struct inode_operations {
|
|||||||
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
|
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
|
||||||
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
|
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
|
||||||
int (*permission) (struct inode *, int);
|
int (*permission) (struct inode *, int);
|
||||||
|
int (*permission2) (struct vfsmount *, struct inode *, int);
|
||||||
struct posix_acl * (*get_acl)(struct inode *, int);
|
struct posix_acl * (*get_acl)(struct inode *, int);
|
||||||
|
|
||||||
int (*readlink) (struct dentry *, char __user *,int);
|
int (*readlink) (struct dentry *, char __user *,int);
|
||||||
@@ -1856,7 +1868,8 @@ struct inode_operations {
|
|||||||
int (*rename) (struct inode *, struct dentry *,
|
int (*rename) (struct inode *, struct dentry *,
|
||||||
struct inode *, struct dentry *, unsigned int);
|
struct inode *, struct dentry *, unsigned int);
|
||||||
int (*setattr) (struct dentry *, struct iattr *);
|
int (*setattr) (struct dentry *, struct iattr *);
|
||||||
int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
|
int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *);
|
||||||
|
int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
|
||||||
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
||||||
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
|
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
|
||||||
u64 len);
|
u64 len);
|
||||||
@@ -2538,6 +2551,8 @@ struct filename {
|
|||||||
extern long vfs_truncate(const struct path *, loff_t);
|
extern long vfs_truncate(const struct path *, loff_t);
|
||||||
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
|
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
|
||||||
struct file *filp);
|
struct file *filp);
|
||||||
|
extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start,
|
||||||
|
unsigned int time_attrs, struct file *filp);
|
||||||
extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
|
extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
|
||||||
loff_t len);
|
loff_t len);
|
||||||
extern long do_sys_open(int dfd, const char __user *filename, int flags,
|
extern long do_sys_open(int dfd, const char __user *filename, int flags,
|
||||||
@@ -2849,7 +2864,9 @@ extern void emergency_remount(void);
|
|||||||
extern sector_t bmap(struct inode *, sector_t);
|
extern sector_t bmap(struct inode *, sector_t);
|
||||||
#endif
|
#endif
|
||||||
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
|
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
|
||||||
|
extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **);
|
||||||
extern int inode_permission(struct inode *, int);
|
extern int inode_permission(struct inode *, int);
|
||||||
|
extern int inode_permission2(struct vfsmount *, struct inode *, int);
|
||||||
extern int generic_permission(struct inode *, int);
|
extern int generic_permission(struct inode *, int);
|
||||||
extern int __check_sticky(struct inode *dir, struct inode *inode);
|
extern int __check_sticky(struct inode *dir, struct inode *inode);
|
||||||
|
|
||||||
|
|||||||
@@ -178,6 +178,8 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
|
|||||||
int fscrypt_ioctl_remove_key(struct file *filp, void __user *arg);
|
int fscrypt_ioctl_remove_key(struct file *filp, void __user *arg);
|
||||||
int fscrypt_ioctl_remove_key_all_users(struct file *filp, void __user *arg);
|
int fscrypt_ioctl_remove_key_all_users(struct file *filp, void __user *arg);
|
||||||
int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg);
|
int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg);
|
||||||
|
int fscrypt_register_key_removal_notifier(struct notifier_block *nb);
|
||||||
|
int fscrypt_unregister_key_removal_notifier(struct notifier_block *nb);
|
||||||
|
|
||||||
/* keysetup.c */
|
/* keysetup.c */
|
||||||
int fscrypt_get_encryption_info(struct inode *inode);
|
int fscrypt_get_encryption_info(struct inode *inode);
|
||||||
@@ -380,6 +382,18 @@ static inline int fscrypt_ioctl_get_key_status(struct file *filp,
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int fscrypt_register_key_removal_notifier(
|
||||||
|
struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int fscrypt_unregister_key_removal_notifier(
|
||||||
|
struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* keysetup.c */
|
/* keysetup.c */
|
||||||
static inline int fscrypt_get_encryption_info(struct inode *inode)
|
static inline int fscrypt_get_encryption_info(struct inode *inode)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -210,12 +210,19 @@ static inline void fsnotify_modify(struct file *file)
|
|||||||
static inline void fsnotify_open(struct file *file)
|
static inline void fsnotify_open(struct file *file)
|
||||||
{
|
{
|
||||||
const struct path *path = &file->f_path;
|
const struct path *path = &file->f_path;
|
||||||
|
struct path lower_path;
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
__u32 mask = FS_OPEN;
|
__u32 mask = FS_OPEN;
|
||||||
|
|
||||||
if (S_ISDIR(inode->i_mode))
|
if (S_ISDIR(inode->i_mode))
|
||||||
mask |= FS_ISDIR;
|
mask |= FS_ISDIR;
|
||||||
|
|
||||||
|
if (path->dentry->d_op && path->dentry->d_op->d_canonical_path) {
|
||||||
|
path->dentry->d_op->d_canonical_path(path, &lower_path);
|
||||||
|
fsnotify_parent(&lower_path, NULL, mask);
|
||||||
|
fsnotify(lower_path.dentry->d_inode, mask, &lower_path, FSNOTIFY_EVENT_PATH, NULL, 0);
|
||||||
|
path_put(&lower_path);
|
||||||
|
}
|
||||||
fsnotify_parent(path, NULL, mask);
|
fsnotify_parent(path, NULL, mask);
|
||||||
fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
|
fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,10 +79,13 @@ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigne
|
|||||||
extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
|
extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
|
||||||
extern void done_path_create(struct path *, struct dentry *);
|
extern void done_path_create(struct path *, struct dentry *);
|
||||||
extern struct dentry *kern_path_locked(const char *, struct path *);
|
extern struct dentry *kern_path_locked(const char *, struct path *);
|
||||||
|
extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
|
||||||
|
const char *, unsigned int, struct path *);
|
||||||
extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
|
extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
|
||||||
|
|
||||||
extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
|
extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
|
||||||
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
|
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
|
||||||
|
extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int);
|
||||||
extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
|
extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
|
||||||
|
|
||||||
extern int follow_down_one(struct path *);
|
extern int follow_down_one(struct path *);
|
||||||
|
|||||||
14
ipc/mqueue.c
14
ipc/mqueue.c
@@ -738,7 +738,7 @@ static void remove_notification(struct mqueue_inode_info *info)
|
|||||||
info->notify_user_ns = NULL;
|
info->notify_user_ns = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int prepare_open(struct dentry *dentry, int oflag, int ro,
|
static int prepare_open(struct vfsmount *mnt, struct dentry *dentry, int oflag, int ro,
|
||||||
umode_t mode, struct filename *name,
|
umode_t mode, struct filename *name,
|
||||||
struct mq_attr *attr)
|
struct mq_attr *attr)
|
||||||
{
|
{
|
||||||
@@ -752,7 +752,7 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro,
|
|||||||
if (ro)
|
if (ro)
|
||||||
return ro;
|
return ro;
|
||||||
audit_inode_parent_hidden(name, dentry->d_parent);
|
audit_inode_parent_hidden(name, dentry->d_parent);
|
||||||
return vfs_mkobj(dentry, mode & ~current_umask(),
|
return vfs_mkobj2(mnt, dentry, mode & ~current_umask(),
|
||||||
mqueue_create_attr, attr);
|
mqueue_create_attr, attr);
|
||||||
}
|
}
|
||||||
/* it already existed */
|
/* it already existed */
|
||||||
@@ -762,7 +762,7 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro,
|
|||||||
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
|
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
acc = oflag2acc[oflag & O_ACCMODE];
|
acc = oflag2acc[oflag & O_ACCMODE];
|
||||||
return inode_permission(d_inode(dentry), acc);
|
return inode_permission2(mnt, d_inode(dentry), acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
|
static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
|
||||||
@@ -786,13 +786,13 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
|
|||||||
|
|
||||||
ro = mnt_want_write(mnt); /* we'll drop it in any case */
|
ro = mnt_want_write(mnt); /* we'll drop it in any case */
|
||||||
inode_lock(d_inode(root));
|
inode_lock(d_inode(root));
|
||||||
path.dentry = lookup_one_len(name->name, root, strlen(name->name));
|
path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name));
|
||||||
if (IS_ERR(path.dentry)) {
|
if (IS_ERR(path.dentry)) {
|
||||||
error = PTR_ERR(path.dentry);
|
error = PTR_ERR(path.dentry);
|
||||||
goto out_putfd;
|
goto out_putfd;
|
||||||
}
|
}
|
||||||
path.mnt = mntget(mnt);
|
path.mnt = mntget(mnt);
|
||||||
error = prepare_open(path.dentry, oflag, ro, mode, name, attr);
|
error = prepare_open(path.mnt, path.dentry, oflag, ro, mode, name, attr);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
struct file *file = dentry_open(&path, oflag, current_cred());
|
struct file *file = dentry_open(&path, oflag, current_cred());
|
||||||
if (!IS_ERR(file))
|
if (!IS_ERR(file))
|
||||||
@@ -842,7 +842,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
|
|||||||
if (err)
|
if (err)
|
||||||
goto out_name;
|
goto out_name;
|
||||||
inode_lock_nested(d_inode(mnt->mnt_root), I_MUTEX_PARENT);
|
inode_lock_nested(d_inode(mnt->mnt_root), I_MUTEX_PARENT);
|
||||||
dentry = lookup_one_len(name->name, mnt->mnt_root,
|
dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root,
|
||||||
strlen(name->name));
|
strlen(name->name));
|
||||||
if (IS_ERR(dentry)) {
|
if (IS_ERR(dentry)) {
|
||||||
err = PTR_ERR(dentry);
|
err = PTR_ERR(dentry);
|
||||||
@@ -854,7 +854,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
|
|||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
} else {
|
} else {
|
||||||
ihold(inode);
|
ihold(inode);
|
||||||
err = vfs_unlink(d_inode(dentry->d_parent), dentry, NULL);
|
err = vfs_unlink2(mnt, d_inode(dentry->d_parent), dentry, NULL);
|
||||||
}
|
}
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
|
|||||||
dir = d_inode(parent);
|
dir = d_inode(parent);
|
||||||
|
|
||||||
inode_lock(dir);
|
inode_lock(dir);
|
||||||
dentry = lookup_one_len(name, parent, strlen(name));
|
dentry = lookup_one_len2(name, mount, parent, strlen(name));
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|||||||
@@ -603,6 +603,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
|
|||||||
return 0;
|
return 0;
|
||||||
return call_int_hook(path_chown, 0, path, uid, gid);
|
return call_int_hook(path_chown, 0, path, uid, gid);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(security_path_chown);
|
||||||
|
|
||||||
int security_path_chroot(const struct path *path)
|
int security_path_chroot(const struct path *path)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user