Commit be5e6616 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull more vfs updates from Al Viro:
 "Assorted stuff from this cycle.  The big ones here are multilayer
  overlayfs from Miklos and beginning of sorting ->d_inode accesses out
  from David"

* 'for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (51 commits)
  autofs4 copy_dev_ioctl(): keep the value of ->size we'd used for allocation
  procfs: fix race between symlink removals and traversals
  debugfs: leave freeing a symlink body until inode eviction
  Documentation/filesystems/Locking: ->get_sb() is long gone
  trylock_super(): replacement for grab_super_passive()
  fanotify: Fix up scripted S_ISDIR/S_ISREG/S_ISLNK conversions
  Cachefiles: Fix up scripted S_ISDIR/S_ISREG/S_ISLNK conversions
  VFS: (Scripted) Convert S_ISLNK/DIR/REG(dentry->d_inode) to d_is_*(dentry)
  SELinux: Use d_is_positive() rather than testing dentry->d_inode
  Smack: Use d_is_positive() rather than testing dentry->d_inode
  TOMOYO: Use d_is_dir() rather than d_inode and S_ISDIR()
  Apparmor: Use d_is_positive/negative() rather than testing dentry->d_inode
  Apparmor: mediated_filesystem() should use dentry->d_sb not inode->i_sb
  VFS: Split DCACHE_FILE_TYPE into regular and special types
  VFS: Add a fallthrough flag for marking virtual dentries
  VFS: Add a whiteout dentry type
  VFS: Introduce inode-getting helpers for layered/unioned fs environments
  Infiniband: Fix potential NULL d_inode dereference
  posix_acl: fix reference leaks in posix_acl_create
  autofs4: Wrong format for printing dentry
  ...
parents 90c453ca 0a280962
...@@ -138,7 +138,7 @@ static struct dentry *jffs2_get_parent(struct dentry *child) ...@@ -138,7 +138,7 @@ static struct dentry *jffs2_get_parent(struct dentry *child)
struct jffs2_inode_info *f; struct jffs2_inode_info *f;
uint32_t pino; uint32_t pino;
BUG_ON(!S_ISDIR(child->d_inode->i_mode)); BUG_ON(!d_is_dir(child));
f = JFFS2_INODE_INFO(child->d_inode); f = JFFS2_INODE_INFO(child->d_inode);
......
...@@ -329,7 +329,7 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -329,7 +329,7 @@ int simple_rename(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 *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
int they_are_dirs = S_ISDIR(old_dentry->d_inode->i_mode); int they_are_dirs = d_is_dir(old_dentry);
if (!simple_empty(new_dentry)) if (!simple_empty(new_dentry))
return -ENOTEMPTY; return -ENOTEMPTY;
......
...@@ -2814,7 +2814,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, ...@@ -2814,7 +2814,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
} else if (!dentry->d_inode) { } else if (!dentry->d_inode) {
goto out; goto out;
} else if ((open_flag & O_TRUNC) && } else if ((open_flag & O_TRUNC) &&
S_ISREG(dentry->d_inode->i_mode)) { d_is_reg(dentry)) {
goto out; goto out;
} }
/* will fail later, go on to get the right error */ /* will fail later, go on to get the right error */
......
...@@ -1907,8 +1907,8 @@ static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp) ...@@ -1907,8 +1907,8 @@ static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp)
if (mnt->mnt.mnt_sb->s_flags & MS_NOUSER) if (mnt->mnt.mnt_sb->s_flags & MS_NOUSER)
return -EINVAL; return -EINVAL;
if (S_ISDIR(mp->m_dentry->d_inode->i_mode) != if (d_is_dir(mp->m_dentry) !=
S_ISDIR(mnt->mnt.mnt_root->d_inode->i_mode)) d_is_dir(mnt->mnt.mnt_root))
return -ENOTDIR; return -ENOTDIR;
return attach_recursive_mnt(mnt, p, mp, NULL); return attach_recursive_mnt(mnt, p, mp, NULL);
...@@ -2180,8 +2180,8 @@ static int do_move_mount(struct path *path, const char *old_name) ...@@ -2180,8 +2180,8 @@ static int do_move_mount(struct path *path, const char *old_name)
if (!mnt_has_parent(old)) if (!mnt_has_parent(old))
goto out1; goto out1;
if (S_ISDIR(path->dentry->d_inode->i_mode) != if (d_is_dir(path->dentry) !=
S_ISDIR(old_path.dentry->d_inode->i_mode)) d_is_dir(old_path.dentry))
goto out1; goto out1;
/* /*
* Don't move a mount residing in a shared parent. * Don't move a mount residing in a shared parent.
...@@ -2271,7 +2271,7 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags) ...@@ -2271,7 +2271,7 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
goto unlock; goto unlock;
err = -EINVAL; err = -EINVAL;
if (S_ISLNK(newmnt->mnt.mnt_root->d_inode->i_mode)) if (d_is_symlink(newmnt->mnt.mnt_root))
goto unlock; goto unlock;
newmnt->mnt.mnt_flags = mnt_flags; newmnt->mnt.mnt_flags = mnt_flags;
......
...@@ -583,7 +583,7 @@ nfs4_reset_recoverydir(char *recdir) ...@@ -583,7 +583,7 @@ nfs4_reset_recoverydir(char *recdir)
if (status) if (status)
return status; return status;
status = -ENOTDIR; status = -ENOTDIR;
if (S_ISDIR(path.dentry->d_inode->i_mode)) { if (d_is_dir(path.dentry)) {
strcpy(user_recovery_dirname, recdir); strcpy(user_recovery_dirname, recdir);
status = 0; status = 0;
} }
...@@ -1426,7 +1426,7 @@ nfsd4_client_tracking_init(struct net *net) ...@@ -1426,7 +1426,7 @@ nfsd4_client_tracking_init(struct net *net)
nn->client_tracking_ops = &nfsd4_legacy_tracking_ops; nn->client_tracking_ops = &nfsd4_legacy_tracking_ops;
status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
if (!status) { if (!status) {
status = S_ISDIR(path.dentry->d_inode->i_mode); status = d_is_dir(path.dentry);
path_put(&path); path_put(&path);
if (status) if (status)
goto do_init; goto do_init;
......
...@@ -114,8 +114,8 @@ static inline __be32 check_pseudo_root(struct svc_rqst *rqstp, ...@@ -114,8 +114,8 @@ static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
* We're exposing only the directories and symlinks that have to be * We're exposing only the directories and symlinks that have to be
* traversed on the way to real exports: * traversed on the way to real exports:
*/ */
if (unlikely(!S_ISDIR(dentry->d_inode->i_mode) && if (unlikely(!d_is_dir(dentry) &&
!S_ISLNK(dentry->d_inode->i_mode))) !d_is_symlink(dentry)))
return nfserr_stale; return nfserr_stale;
/* /*
* A pseudoroot export gives permission to access only one * A pseudoroot export gives permission to access only one
...@@ -259,7 +259,7 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp) ...@@ -259,7 +259,7 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
goto out; goto out;
} }
if (S_ISDIR(dentry->d_inode->i_mode) && if (d_is_dir(dentry) &&
(dentry->d_flags & DCACHE_DISCONNECTED)) { (dentry->d_flags & DCACHE_DISCONNECTED)) {
printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %pd2\n", printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %pd2\n",
dentry); dentry);
...@@ -414,7 +414,7 @@ static inline void _fh_update_old(struct dentry *dentry, ...@@ -414,7 +414,7 @@ static inline void _fh_update_old(struct dentry *dentry,
{ {
fh->ofh_ino = ino_t_to_u32(dentry->d_inode->i_ino); fh->ofh_ino = ino_t_to_u32(dentry->d_inode->i_ino);
fh->ofh_generation = dentry->d_inode->i_generation; fh->ofh_generation = dentry->d_inode->i_generation;
if (S_ISDIR(dentry->d_inode->i_mode) || if (d_is_dir(dentry) ||
(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) (exp->ex_flags & NFSEXP_NOSUBTREECHECK))
fh->ofh_dirino = 0; fh->ofh_dirino = 0;
} }
......
...@@ -615,9 +615,9 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor ...@@ -615,9 +615,9 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor
export = fhp->fh_export; export = fhp->fh_export;
dentry = fhp->fh_dentry; dentry = fhp->fh_dentry;
if (S_ISREG(dentry->d_inode->i_mode)) if (d_is_reg(dentry))
map = nfs3_regaccess; map = nfs3_regaccess;
else if (S_ISDIR(dentry->d_inode->i_mode)) else if (d_is_dir(dentry))
map = nfs3_diraccess; map = nfs3_diraccess;
else else
map = nfs3_anyaccess; map = nfs3_anyaccess;
...@@ -1402,7 +1402,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -1402,7 +1402,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
switch (createmode) { switch (createmode) {
case NFS3_CREATE_UNCHECKED: case NFS3_CREATE_UNCHECKED:
if (! S_ISREG(dchild->d_inode->i_mode)) if (! d_is_reg(dchild))
goto out; goto out;
else if (truncp) { else if (truncp) {
/* in nfsv4, we need to treat this case a little /* in nfsv4, we need to treat this case a little
...@@ -1615,7 +1615,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, ...@@ -1615,7 +1615,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
if (err) if (err)
goto out; goto out;
err = nfserr_isdir; err = nfserr_isdir;
if (S_ISDIR(tfhp->fh_dentry->d_inode->i_mode)) if (d_is_dir(tfhp->fh_dentry))
goto out; goto out;
err = nfserr_perm; err = nfserr_perm;
if (!len) if (!len)
......
...@@ -115,8 +115,8 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark, ...@@ -115,8 +115,8 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
return false; return false;
/* sorry, fanotify only gives a damn about files and dirs */ /* sorry, fanotify only gives a damn about files and dirs */
if (!S_ISREG(path->dentry->d_inode->i_mode) && if (!d_is_reg(path->dentry) &&
!S_ISDIR(path->dentry->d_inode->i_mode)) !d_can_lookup(path->dentry))
return false; return false;
if (inode_mark && vfsmnt_mark) { if (inode_mark && vfsmnt_mark) {
...@@ -139,7 +139,7 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark, ...@@ -139,7 +139,7 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
BUG(); BUG();
} }
if (S_ISDIR(path->dentry->d_inode->i_mode) && if (d_is_dir(path->dentry) &&
!(marks_mask & FS_ISDIR & ~marks_ignored_mask)) !(marks_mask & FS_ISDIR & ~marks_ignored_mask))
return false; return false;
......
...@@ -191,7 +191,6 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) ...@@ -191,7 +191,6 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
ovl_set_timestamps(upperdentry, stat); ovl_set_timestamps(upperdentry, stat);
return err; return err;
} }
static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
...@@ -385,7 +384,7 @@ int ovl_copy_up(struct dentry *dentry) ...@@ -385,7 +384,7 @@ int ovl_copy_up(struct dentry *dentry)
struct kstat stat; struct kstat stat;
enum ovl_path_type type = ovl_path_type(dentry); enum ovl_path_type type = ovl_path_type(dentry);
if (type != OVL_PATH_LOWER) if (OVL_TYPE_UPPER(type))
break; break;
next = dget(dentry); next = dget(dentry);
...@@ -394,7 +393,7 @@ int ovl_copy_up(struct dentry *dentry) ...@@ -394,7 +393,7 @@ int ovl_copy_up(struct dentry *dentry)
parent = dget_parent(next); parent = dget_parent(next);
type = ovl_path_type(parent); type = ovl_path_type(parent);
if (type != OVL_PATH_LOWER) if (OVL_TYPE_UPPER(type))
break; break;
dput(next); dput(next);
......
...@@ -19,7 +19,7 @@ void ovl_cleanup(struct inode *wdir, struct dentry *wdentry) ...@@ -19,7 +19,7 @@ void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
int err; int err;
dget(wdentry); dget(wdentry);
if (S_ISDIR(wdentry->d_inode->i_mode)) if (d_is_dir(wdentry))
err = ovl_do_rmdir(wdir, wdentry); err = ovl_do_rmdir(wdir, wdentry);
else else
err = ovl_do_unlink(wdir, wdentry); err = ovl_do_unlink(wdir, wdentry);
...@@ -118,14 +118,14 @@ int ovl_create_real(struct inode *dir, struct dentry *newdentry, ...@@ -118,14 +118,14 @@ int ovl_create_real(struct inode *dir, struct dentry *newdentry,
static int ovl_set_opaque(struct dentry *upperdentry) static int ovl_set_opaque(struct dentry *upperdentry)
{ {
return ovl_do_setxattr(upperdentry, ovl_opaque_xattr, "y", 1, 0); return ovl_do_setxattr(upperdentry, OVL_XATTR_OPAQUE, "y", 1, 0);
} }
static void ovl_remove_opaque(struct dentry *upperdentry) static void ovl_remove_opaque(struct dentry *upperdentry)
{ {
int err; int err;
err = ovl_do_removexattr(upperdentry, ovl_opaque_xattr); err = ovl_do_removexattr(upperdentry, OVL_XATTR_OPAQUE);
if (err) { if (err) {
pr_warn("overlayfs: failed to remove opaque from '%s' (%i)\n", pr_warn("overlayfs: failed to remove opaque from '%s' (%i)\n",
upperdentry->d_name.name, err); upperdentry->d_name.name, err);
...@@ -152,7 +152,7 @@ static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -152,7 +152,7 @@ static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry,
* correct link count. nlink=1 seems to pacify 'find' and * correct link count. nlink=1 seems to pacify 'find' and
* other utilities. * other utilities.
*/ */
if (type == OVL_PATH_MERGE) if (OVL_TYPE_MERGE(type))
stat->nlink = 1; stat->nlink = 1;
return 0; return 0;
...@@ -506,7 +506,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) ...@@ -506,7 +506,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
struct dentry *opaquedir = NULL; struct dentry *opaquedir = NULL;
int err; int err;
if (is_dir) { if (is_dir && OVL_TYPE_MERGE_OR_LOWER(ovl_path_type(dentry))) {
opaquedir = ovl_check_empty_and_clear(dentry); opaquedir = ovl_check_empty_and_clear(dentry);
err = PTR_ERR(opaquedir); err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) if (IS_ERR(opaquedir))
...@@ -630,7 +630,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -630,7 +630,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
goto out_drop_write; goto out_drop_write;
type = ovl_path_type(dentry); type = ovl_path_type(dentry);
if (type == OVL_PATH_PURE_UPPER) { if (OVL_TYPE_PURE_UPPER(type)) {
err = ovl_remove_upper(dentry, is_dir); err = ovl_remove_upper(dentry, is_dir);
} else { } else {
const struct cred *old_cred; const struct cred *old_cred;
...@@ -693,7 +693,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -693,7 +693,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
bool new_create = false; bool new_create = false;
bool cleanup_whiteout = false; bool cleanup_whiteout = false;
bool overwrite = !(flags & RENAME_EXCHANGE); bool overwrite = !(flags & RENAME_EXCHANGE);
bool is_dir = S_ISDIR(old->d_inode->i_mode); bool is_dir = d_is_dir(old);
bool new_is_dir = false; bool new_is_dir = false;
struct dentry *opaquedir = NULL; struct dentry *opaquedir = NULL;
const struct cred *old_cred = NULL; const struct cred *old_cred = NULL;
...@@ -712,7 +712,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -712,7 +712,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
/* Don't copy up directory trees */ /* Don't copy up directory trees */
old_type = ovl_path_type(old); old_type = ovl_path_type(old);
err = -EXDEV; err = -EXDEV;
if ((old_type == OVL_PATH_LOWER || old_type == OVL_PATH_MERGE) && is_dir) if (OVL_TYPE_MERGE_OR_LOWER(old_type) && is_dir)
goto out; goto out;
if (new->d_inode) { if (new->d_inode) {
...@@ -720,30 +720,30 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -720,30 +720,30 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
if (err) if (err)
goto out; goto out;
if (S_ISDIR(new->d_inode->i_mode)) if (d_is_dir(new))
new_is_dir = true; new_is_dir = true;
new_type = ovl_path_type(new); new_type = ovl_path_type(new);
err = -EXDEV; err = -EXDEV;
if (!overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) if (!overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir)
goto out; goto out;
err = 0; err = 0;
if (new_type == OVL_PATH_LOWER && old_type == OVL_PATH_LOWER) { if (!OVL_TYPE_UPPER(new_type) && !OVL_TYPE_UPPER(old_type)) {
if (ovl_dentry_lower(old)->d_inode == if (ovl_dentry_lower(old)->d_inode ==
ovl_dentry_lower(new)->d_inode) ovl_dentry_lower(new)->d_inode)
goto out; goto out;
} }
if (new_type != OVL_PATH_LOWER && old_type != OVL_PATH_LOWER) { if (OVL_TYPE_UPPER(new_type) && OVL_TYPE_UPPER(old_type)) {
if (ovl_dentry_upper(old)->d_inode == if (ovl_dentry_upper(old)->d_inode ==
ovl_dentry_upper(new)->d_inode) ovl_dentry_upper(new)->d_inode)
goto out; goto out;
} }
} else { } else {
if (ovl_dentry_is_opaque(new)) if (ovl_dentry_is_opaque(new))
new_type = OVL_PATH_UPPER; new_type = __OVL_PATH_UPPER;
else else
new_type = OVL_PATH_PURE_UPPER; new_type = __OVL_PATH_UPPER | __OVL_PATH_PURE;
} }
err = ovl_want_write(old); err = ovl_want_write(old);
...@@ -763,8 +763,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -763,8 +763,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
goto out_drop_write; goto out_drop_write;
} }
old_opaque = old_type != OVL_PATH_PURE_UPPER; old_opaque = !OVL_TYPE_PURE_UPPER(old_type);
new_opaque = new_type != OVL_PATH_PURE_UPPER; new_opaque = !OVL_TYPE_PURE_UPPER(new_type);
if (old_opaque || new_opaque) { if (old_opaque || new_opaque) {
err = -ENOMEM; err = -ENOMEM;
...@@ -787,7 +787,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -787,7 +787,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
old_cred = override_creds(override_cred); old_cred = override_creds(override_cred);
} }
if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) { if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) {
opaquedir = ovl_check_empty_and_clear(new); opaquedir = ovl_check_empty_and_clear(new);
err = PTR_ERR(opaquedir); err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) { if (IS_ERR(opaquedir)) {
......
...@@ -205,7 +205,7 @@ static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ...@@ -205,7 +205,7 @@ static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
static bool ovl_is_private_xattr(const char *name) static bool ovl_is_private_xattr(const char *name)
{ {
return strncmp(name, "trusted.overlay.", 14) == 0; return strncmp(name, OVL_XATTR_PRE_NAME, OVL_XATTR_PRE_LEN) == 0;
} }
int ovl_setxattr(struct dentry *dentry, const char *name, int ovl_setxattr(struct dentry *dentry, const char *name,
...@@ -238,7 +238,10 @@ int ovl_setxattr(struct dentry *dentry, const char *name, ...@@ -238,7 +238,10 @@ int ovl_setxattr(struct dentry *dentry, const char *name,
static bool ovl_need_xattr_filter(struct dentry *dentry, static bool ovl_need_xattr_filter(struct dentry *dentry,
enum ovl_path_type type) enum ovl_path_type type)
{ {
return type == OVL_PATH_UPPER && S_ISDIR(dentry->d_inode->i_mode); if ((type & (__OVL_PATH_PURE | __OVL_PATH_UPPER)) == __OVL_PATH_UPPER)
return S_ISDIR(dentry->d_inode->i_mode);
else
return false;
} }
ssize_t ovl_getxattr(struct dentry *dentry, const char *name, ssize_t ovl_getxattr(struct dentry *dentry, const char *name,
...@@ -299,7 +302,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name) ...@@ -299,7 +302,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name)) if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
goto out_drop_write; goto out_drop_write;
if (type == OVL_PATH_LOWER) { if (!OVL_TYPE_UPPER(type)) {
err = vfs_getxattr(realpath.dentry, name, NULL, 0); err = vfs_getxattr(realpath.dentry, name, NULL, 0);
if (err < 0) if (err < 0)
goto out_drop_write; goto out_drop_write;
...@@ -321,7 +324,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name) ...@@ -321,7 +324,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
struct dentry *realdentry) struct dentry *realdentry)
{ {
if (type != OVL_PATH_LOWER) if (OVL_TYPE_UPPER(type))
return false; return false;
if (special_file(realdentry->d_inode->i_mode)) if (special_file(realdentry->d_inode->i_mode))
...@@ -430,5 +433,4 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, ...@@ -430,5 +433,4 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
} }
return inode; return inode;
} }
...@@ -12,13 +12,20 @@ ...@@ -12,13 +12,20 @@
struct ovl_entry; struct ovl_entry;
enum ovl_path_type { enum ovl_path_type {
OVL_PATH_PURE_UPPER, __OVL_PATH_PURE = (1 << 0),
OVL_PATH_UPPER, __OVL_PATH_UPPER = (1 << 1),
OVL_PATH_MERGE, __OVL_PATH_MERGE = (1 << 2),
OVL_PATH_LOWER,
}; };
extern const char *ovl_opaque_xattr; #define OVL_TYPE_UPPER(type) ((type) & __OVL_PATH_UPPER)
#define OVL_TYPE_MERGE(type) ((type) & __OVL_PATH_MERGE)
#define OVL_TYPE_PURE_UPPER(type) ((type) & __OVL_PATH_PURE)
#define OVL_TYPE_MERGE_OR_LOWER(type) \
(OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type))
#define OVL_XATTR_PRE_NAME "trusted.overlay."
#define OVL_XATTR_PRE_LEN 16
#define OVL_XATTR_OPAQUE OVL_XATTR_PRE_NAME"opaque"
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
{ {
...@@ -130,6 +137,7 @@ void ovl_dentry_version_inc(struct dentry *dentry); ...@@ -130,6 +137,7 @@ void ovl_dentry_version_inc(struct dentry *dentry);
void ovl_path_upper(struct dentry *dentry, struct path *path); void ovl_path_upper(struct dentry *dentry, struct path *path);
void ovl_path_lower(struct dentry *dentry, struct path *path); void ovl_path_lower(struct dentry *dentry, struct path *path);
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path); enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
struct dentry *ovl_dentry_upper(struct dentry *dentry); struct dentry *ovl_dentry_upper(struct dentry *dentry);
struct dentry *ovl_dentry_lower(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry);
......
...@@ -24,7 +24,6 @@ struct ovl_cache_entry { ...@@ -24,7 +24,6 @@ struct ovl_cache_entry {
struct list_head l_node; struct list_head l_node;
struct rb_node node; struct rb_node node;
bool is_whiteout; bool is_whiteout;
bool is_cursor;
char name[]; char name[];
}; };
...@@ -40,6 +39,7 @@ struct ovl_readdir_data { ...@@ -40,6 +39,7 @@ struct ovl_readdir_data {
struct rb_root root; struct rb_root root;
struct list_head *list; struct list_head *list;
struct list_head middle; struct list_head middle;
struct dentry *dir;
int count; int count;
int err; int err;
}; };
...@@ -48,7 +48,7 @@ struct ovl_dir_file { ...@@ -48,7 +48,7 @@ struct ovl_dir_file {
bool is_real; bool is_real;
bool is_upper; bool is_upper;
struct ovl_dir_cache *cache; struct ovl_dir_cache *cache;
struct ovl_cache_entry cursor; struct list_head *cursor;
struct file *realfile; struct file *realfile;
struct file *upperfile; struct file *upperfile;
}; };
...@@ -79,23 +79,49 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root, ...@@ -79,23 +79,49 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
return NULL; return NULL;
} }
static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len, static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
const char *name, int len,
u64 ino, unsigned int d_type) u64 ino, unsigned int d_type)
{ {
struct ovl_cache_entry *p; struct ovl_cache_entry *p;
size_t size = offsetof(struct ovl_cache_entry, name[len + 1]); size_t size = offsetof(struct ovl_cache_entry, name[len + 1]);
p = kmalloc(size, GFP_KERNEL); p = kmalloc(size, GFP_KERNEL);
if (p) { if (!p)
memcpy(p->name, name, len); return NULL;
p->name[len] = '\0';
p->len = len; memcpy(p->name, name, len);
p->type = d_type; p->name[len] = '\0';
p->ino = ino; p->len = len;
p->is_whiteout = false; p->type = d_type;
p->is_cursor = false; p->ino = ino;
} p->is_whiteout = false;
if (d_type == DT_CHR) {
struct dentry *dentry;
const struct cred *old_cred;
struct cred *override_cred;
override_cred = prepare_creds();
if (!override_cred) {
kfree(p);
return NULL;
}
/*
* CAP_DAC_OVERRIDE for lookup
*/
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
old_cred = override_creds(override_cred);
dentry = lookup_one_len(name, dir, len);
if (!IS_ERR(dentry)) {
p->is_whiteout = ovl_is_whiteout(dentry);
dput(dentry);
}
revert_creds(old_cred);
put_cred(override_cred);
}
return p; return p;
} }
...@@ -122,7 +148,7 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd, ...@@ -122,7 +148,7 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
return 0; return 0;
} }
p = ovl_cache_entry_new(name, len, ino, d_type); p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type);
if (p == NULL) if (p == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -143,7 +169,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd, ...@@ -143,7 +169,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd,
if (p) { if (p) {
list_move_tail(&p->l_node, &rdd->middle); list_move_tail(&p->l_node, &rdd->middle);
} else { } else {
p = ovl_cache_entry_new(name, namelen, ino, d_type); p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type);
if (p == NULL) if (p == NULL)
rdd->err = -ENOMEM; rdd->err = -ENOMEM;
else else
...@@ -168,7 +194,6 @@ static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry) ...@@ -168,7 +194,6 @@ static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry)
{ {
struct ovl_dir_cache *cache = od->cache; struct ovl_dir_cache *cache = od->cache;
list_del_init(&od->cursor.l_node);
WARN_ON(cache->refcount <= 0); WARN_ON(cache->refcount <= 0);
cache->refcount--; cache->refcount--;
if (!cache->refcount) { if (!cache->refcount) {
...@@ -204,6 +229,7 @@ static inline int ovl_dir_read(struct path *realpath, ...@@ -204,6 +229,7 @@ static inline int ovl_dir_read(struct path *realpath,
if (IS_ERR(realfile)) if (IS_ERR(realfile))
return PTR_ERR(realfile); return PTR_ERR(realfile);
rdd->dir = realpath->dentry;
rdd->ctx.pos = 0; rdd->ctx.pos = 0;
do { do {
rdd->count = 0; rdd->count = 0;
...@@ -227,108 +253,58 @@ static void ovl_dir_reset(struct file *file) ...@@ -227,108 +253,58 @@ static void ovl_dir_reset(struct file *file)
if (cache && ovl_dentry_version_get(dentry) != cache->version) { if (cache && ovl_dentry_version_get(dentry) != cache->version) {
ovl_cache_put(od, dentry); ovl_cache_put(od, dentry);
od->cache = NULL; od->cache = NULL;
od->cursor = NULL;
} }
WARN_ON(!od->is_real && type != OVL_PATH_MERGE); WARN_ON(!od->is_real && !OVL_TYPE_MERGE(type));
if (od->is_real && type == OVL_PATH_MERGE) if (od->is_real && OVL_TYPE_MERGE(type))
od->is_real = false; od->is_real = false;
} }
static int ovl_dir_mark_whiteouts(struct dentry *dir,
struct ovl_readdir_data *rdd)
{
struct ovl_cache_entry *p;
struct dentry *dentry;
const struct cred *old_cred;
struct cred *override_cred;
override_cred = prepare_creds();
if (!override_cred) {
ovl_cache_free(rdd->list);
return -ENOMEM;
}
/*
* CAP_DAC_OVERRIDE for lookup
*/
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
old_cred = override_creds(override_cred);
mutex_lock(&dir->d_inode->i_mutex);
list_for_each_entry(p, rdd->list, l_node) {
if (p->is_cursor)
continue;
if (p->type != DT_CHR)
continue;
dentry = lookup_one_len(p->name, dir, p->len);
if (IS_ERR(dentry))
continue;
p->is_whiteout = ovl_is_whiteout(dentry);
dput(dentry);
}
mutex_unlock(&dir->d_inode->i_mutex);
revert_creds(old_cred);
put_cred(override_cred);
return 0;
}
static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list) static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
{ {
int err; int err;
struct path lowerpath; struct path realpath;
struct path upperpath;
struct ovl_readdir_data rdd = { struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge, .ctx.actor = ovl_fill_merge,
.list = list, .list = list,
.root = RB_ROOT, .root = RB_ROOT,
.is_merge = false, .is_merge = false,
}; };
int idx, next;
ovl_path_lower(dentry, &lowerpath); for (idx = 0; idx != -1; idx = next) {
ovl_path_upper(dentry, &upperpath); next = ovl_path_next(idx, dentry, &realpath);
if (upperpath.dentry) { if (next != -1) {
err = ovl_dir_read(&upperpath, &rdd); err = ovl_dir_read(&realpath, &rdd);
if (err)
goto out;
if (lowerpath.dentry) {
err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
if (err) if (err)
goto out; break;
} else {
/*
* Insert lowest layer entries before upper ones, this
* allows offsets to be reasonably constant
*/
list_add(&rdd.middle, rdd.list);
rdd.is_merge = true;
err = ovl_dir_read(&realpath, &rdd);
list_del(&rdd.middle);
} }
} }
if (lowerpath.dentry) {
/*
* Insert lowerpath entries before upperpath ones, this allows
* offsets to be reasonably constant
*/
list_add(&rdd.middle, rdd.list);
rdd.is_merge = true;
err = ovl_dir_read(&lowerpath, &rdd);
list_del(&rdd.middle);
}
out:
return err; return err;
} }
static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos) static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos)
{ {
struct ovl_cache_entry *p; struct list_head *p;
loff_t off = 0; loff_t off = 0;
list_for_each_entry(p, &od->cache->entries, l_node) { list_for_each(p, &od->cache->entries) {
if (p->is_cursor)
continue;
if (off >= pos) if (off >= pos)
break; break;
off++; off++;
} }
list_move_tail(&od->cursor.l_node, &p->l_node); /* Cursor is safe since the cache is stable */
od->cursor = p;
} }
static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
...@@ -367,6 +343,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -367,6 +343,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
{ {
struct ovl_dir_file *od = file->private_data; struct ovl_dir_file *od = file->private_data;
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
struct ovl_cache_entry *p;
if (!ctx->pos) if (!ctx->pos)
ovl_dir_reset(file); ovl_dir_reset(file);
...@@ -385,19 +362,13 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -385,19 +362,13 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
ovl_seek_cursor(od, ctx->pos); ovl_seek_cursor(od, ctx->pos);
} }
while (od->cursor.l_node.next != &od->cache->entries) { while (od->cursor != &od->cache->entries) {
struct ovl_cache_entry *p; p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
if (!p->is_whiteout)
p = list_entry(od->cursor.l_node.next, struct ovl_cache_entry, l_node); if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
/* Skip cursors */ break;
if (!p->is_cursor) { od->cursor = p->l_node.next;
if (!p->is_whiteout) { ctx->pos++;
if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
break;
}
ctx->pos++;
}
list_move(&od->cursor.l_node, &p->l_node);
} }
return 0; return 0;
} }
...@@ -452,7 +423,7 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end, ...@@ -452,7 +423,7 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
/* /*
* Need to check if we started out being a lower dir, but got copied up * Need to check if we started out being a lower dir, but got copied up
*/ */
if (!od->is_upper && ovl_path_type(dentry) != OVL_PATH_LOWER) { if (!od->is_upper && OVL_TYPE_UPPER(ovl_path_type(dentry))) {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
realfile = lockless_dereference(od->upperfile); realfile = lockless_dereference(od->upperfile);
...@@ -516,11 +487,9 @@ static int ovl_dir_open(struct inode *inode, struct file *file) ...@@ -516,11 +487,9 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
kfree(od); kfree(od);
return PTR_ERR(realfile); return PTR_ERR(realfile);
} }
INIT_LIST_HEAD(&od->cursor.l_node);
od->realfile = realfile; od->realfile = realfile;
od->is_real = (type != OVL_PATH_MERGE); od->is_real = !OVL_TYPE_MERGE(type);
od->is_upper = (type != OVL_PATH_LOWER); od->is_upper = OVL_TYPE_UPPER(type);
od->cursor.is_cursor = true;
file->private_data = od; file->private_data = od;
return 0; return 0;
......
This diff is collapsed.
...@@ -564,13 +564,11 @@ posix_acl_create(struct inode *dir, umode_t *mode, ...@@ -564,13 +564,11 @@ posix_acl_create(struct inode *dir, umode_t *mode,
*acl = posix_acl_clone(p, GFP_NOFS); *acl = posix_acl_clone(p, GFP_NOFS);
if (!*acl) if (!*acl)
return -ENOMEM; goto no_mem;
ret = posix_acl_create_masq(*acl, mode); ret = posix_acl_create_masq(*acl, mode);
if (ret < 0) { if (ret < 0)
posix_acl_release(*acl); goto no_mem_clone;
return -ENOMEM;
}
if (ret == 0) { if (ret == 0) {
posix_acl_release(*acl); posix_acl_release(*acl);
...@@ -591,6 +589,12 @@ posix_acl_create(struct inode *dir, umode_t *mode, ...@@ -591,6 +589,12 @@ posix_acl_create(struct inode *dir, umode_t *mode,
*default_acl = NULL; *default_acl = NULL;
*acl = NULL; *acl = NULL;
return 0; return 0;
no_mem_clone:
posix_acl_release(*acl);
no_mem:
posix_acl_release(p);
return -ENOMEM;
} }
EXPORT_SYMBOL_GPL(posix_acl_create); EXPORT_SYMBOL_GPL(posix_acl_create);
...@@ -772,7 +776,7 @@ posix_acl_xattr_get(struct dentry *dentry, const char *name, ...@@ -772,7 +776,7 @@ posix_acl_xattr_get(struct dentry *dentry, const char *name,
if (!IS_POSIXACL(dentry->d_inode)) if (!IS_POSIXACL(dentry->d_inode))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (S_ISLNK(dentry->d_inode->i_mode)) if (d_is_symlink(dentry))
return -EOPNOTSUPP; return -EOPNOTSUPP;
acl = get_acl(dentry->d_inode, type); acl = get_acl(dentry->d_inode, type);
...@@ -832,7 +836,7 @@ posix_acl_xattr_list(struct dentry *dentry, char *list, size_t list_size, ...@@ -832,7 +836,7 @@ posix_acl_xattr_list(struct dentry *dentry, char *list, size_t list_size,
if (!IS_POSIXACL(dentry->d_inode)) if (!IS_POSIXACL(dentry->d_inode))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (S_ISLNK(dentry->d_inode->i_mode)) if (d_is_symlink(dentry))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (type == ACL_TYPE_ACCESS) if (type == ACL_TYPE_ACCESS)
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/namei.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/completion.h> #include <linux/completion.h>
...@@ -223,17 +222,6 @@ void proc_free_inum(unsigned int inum) ...@@ -223,17 +222,6 @@ void proc_free_inum(unsigned int inum)
spin_unlock_irqrestore(&proc_inum_lock, flags); spin_unlock_irqrestore(&proc_inum_lock, flags);
} }
static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd)
{
nd_set_link(nd, __PDE_DATA(dentry->d_inode));
return NULL;
}
static const struct inode_operations proc_link_inode_operations = {
.readlink = generic_readlink,
.follow_link = proc_follow_link,
};
/* /*
* Don't create negative dentries here, return -ENOENT by hand * Don't create negative dentries here, return -ENOENT by hand
* instead. * instead.
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/namei.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -393,6 +394,26 @@ static const struct file_operations proc_reg_file_ops_no_compat = { ...@@ -393,6 +394,26 @@ static const struct file_operations proc_reg_file_ops_no_compat = {
}; };
#endif #endif
static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct proc_dir_entry *pde = PDE(dentry->d_inode);
if (unlikely(!use_pde(pde)))
return ERR_PTR(-EINVAL);
nd_set_link(nd, pde->data);
return pde;
}
static void proc_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
{
unuse_pde(p);
}
const struct inode_operations proc_link_inode_operations = {
.readlink = generic_readlink,
.follow_link = proc_follow_link,
.put_link = proc_put_link,
};
struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
{ {
struct inode *inode = new_inode_pseudo(sb); struct inode *inode = new_inode_pseudo(sb);
......
...@@ -200,6 +200,7 @@ struct pde_opener { ...@@ -200,6 +200,7 @@ struct pde_opener {
int closing; int closing;
struct completion *c; struct completion *c;
}; };
extern const struct inode_operations proc_link_inode_operations;
extern const struct inode_operations proc_pid_link_inode_operations; extern const struct inode_operations proc_pid_link_inode_operations;
......
...@@ -266,7 +266,7 @@ static int reiserfs_for_each_xattr(struct inode *inode, ...@@ -266,7 +266,7 @@ static int reiserfs_for_each_xattr(struct inode *inode,
for (i = 0; !err && i < buf.count && buf.dentries[i]; i++) { for (i = 0; !err && i < buf.count && buf.dentries[i]; i++) {
struct dentry *dentry = buf.dentries[i]; struct dentry *dentry = buf.dentries[i];
if (!S_ISDIR(dentry->d_inode->i_mode)) if (!d_is_dir(dentry))
err = action(dentry, data); err = action(dentry, data);
dput(dentry); dput(dentry);
...@@ -322,7 +322,7 @@ static int delete_one_xattr(struct dentry *dentry, void *data) ...@@ -322,7 +322,7 @@ static int delete_one_xattr(struct dentry *dentry, void *data)
struct inode *dir = dentry->d_parent->d_inode; struct inode *dir = dentry->d_parent->d_inode;
/* This is the xattr dir, handle specially. */ /* This is the xattr dir, handle specially. */
if (S_ISDIR(dentry->d_inode->i_mode)) if (d_is_dir(dentry))
return xattr_rmdir(dir, dentry); return xattr_rmdir(dir, dentry);
return xattr_unlink(dir, dentry); return xattr_unlink(dir, dentry);
......
...@@ -71,7 +71,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink, ...@@ -71,7 +71,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,
if (!(sc->gfp_mask & __GFP_FS)) if (!(sc->gfp_mask & __GFP_FS))
return SHRINK_STOP; return SHRINK_STOP;
if (!grab_super_passive(sb)) if (!trylock_super(sb))
return SHRINK_STOP; return SHRINK_STOP;
if (sb->s_op->nr_cached_objects) if (sb->s_op->nr_cached_objects)
...@@ -105,7 +105,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink, ...@@ -105,7 +105,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,
freed += sb->s_op->free_cached_objects(sb, sc); freed += sb->s_op->free_cached_objects(sb, sc);
} }
drop_super(sb); up_read(&sb->s_umount);
return freed; return freed;
} }
...@@ -118,7 +118,7 @@ static unsigned long super_cache_count(struct shrinker *shrink, ...@@ -118,7 +118,7 @@ static unsigned long super_cache_count(struct shrinker *shrink,
sb = container_of(shrink, struct super_block, s_shrink); sb = container_of(shrink, struct super_block, s_shrink);
/* /*
* Don't call grab_super_passive as it is a potential * Don't call trylock_super as it is a potential
* scalability bottleneck. The counts could get updated * scalability bottleneck. The counts could get updated
* between super_cache_count and super_cache_scan anyway. * between super_cache_count and super_cache_scan anyway.
* Call to super_cache_count with shrinker_rwsem held * Call to super_cache_count with shrinker_rwsem held
...@@ -348,35 +348,31 @@ static int grab_super(struct super_block *s) __releases(sb_lock) ...@@ -348,35 +348,31 @@ static int grab_super(struct super_block *s) __releases(sb_lock)
} }
/* /*
* grab_super_passive - acquire a passive reference * trylock_super - try to grab ->s_umount shared
* @sb: reference we are trying to grab * @sb: reference we are trying to grab
* *
* Tries to acquire a passive reference. This is used in places where we * Try to prevent fs shutdown. This is used in places where we
* cannot take an active reference but we need to ensure that the * cannot take an active reference but we need to ensure that the
* superblock does not go away while we are working on it. It returns * filesystem is not shut down while we are working on it. It returns
* false if a reference was not gained, and returns true with the s_umount * false if we cannot acquire s_umount or if we lose the race and
* lock held in read mode if a reference is gained. On successful return, * filesystem already got into shutdown, and returns true with the s_umount
* the caller must drop the s_umount lock and the passive reference when * lock held in read mode in case of success. On successful return,
* done. * the caller must drop the s_umount lock when done.
*
* Note that unlike get_super() et.al. this one does *not* bump ->s_count.
* The reason why it's safe is that we are OK with doing trylock instead
* of down_read(). There's a couple of places that are OK with that, but
* it's very much not a general-purpose interface.
*/ */
bool grab_super_passive(struct super_block *sb) bool trylock_super(struct super_block *sb)
{ {
spin_lock(&sb_lock);
if (hlist_unhashed(&sb->s_instances)) {
spin_unlock(&sb_lock);
return false;
}
sb->s_count++;
spin_unlock(&sb_lock);
if (down_read_trylock(&sb->s_umount)) { if (down_read_trylock(&sb->s_umount)) {
if (sb->s_root && (sb->s_flags & MS_BORN)) if (!hlist_unhashed(&sb->s_instances) &&
sb->s_root && (sb->s_flags & MS_BORN))
return true; return true;
up_read(&sb->s_umount); up_read(&sb->s_umount);
} }
put_super(sb);
return false; return false;
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment