Commit b05430fc authored by Linus Torvalds's avatar Linus Torvalds

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

Pull vfs pile 3 (of many) from Al Viro:
 "Waiman's conversion of d_path() and bits related to it,
  kern_path_mountpoint(), several cleanups and fixes (exportfs
  one is -stable fodder, IMO).

  There definitely will be more...  ;-/"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  split read_seqretry_or_unlock(), convert d_walk() to resulting primitives
  dcache: Translating dentry into pathname without taking rename_lock
  autofs4 - fix device ioctl mount lookup
  introduce kern_path_mountpoint()
  rename user_path_umountat() to user_path_mountpoint_at()
  take unlazy_walk() into umount_lookup_last()
  Kill indirect include of file.h from eventfd.h, use fdget() in cgroup.c
  prune_super(): sb->s_op is never NULL
  exportfs: don't assume that ->iterate() won't feed us too long entries
  afs: get rid of redundant ->d_name.len checks
parents d0d27277 48f5ec21
...@@ -751,10 +751,6 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -751,10 +751,6 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
_enter("{%x:%u},{%s},%ho", _enter("{%x:%u},{%s},%ho",
dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode);
ret = -ENAMETOOLONG;
if (dentry->d_name.len >= AFSNAMEMAX)
goto error;
key = afs_request_key(dvnode->volume->cell); key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) { if (IS_ERR(key)) {
ret = PTR_ERR(key); ret = PTR_ERR(key);
...@@ -816,10 +812,6 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -816,10 +812,6 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
_enter("{%x:%u},{%s}", _enter("{%x:%u},{%s}",
dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name);
ret = -ENAMETOOLONG;
if (dentry->d_name.len >= AFSNAMEMAX)
goto error;
key = afs_request_key(dvnode->volume->cell); key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) { if (IS_ERR(key)) {
ret = PTR_ERR(key); ret = PTR_ERR(key);
...@@ -936,10 +928,6 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -936,10 +928,6 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
_enter("{%x:%u},{%s},%ho,", _enter("{%x:%u},{%s},%ho,",
dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode);
ret = -ENAMETOOLONG;
if (dentry->d_name.len >= AFSNAMEMAX)
goto error;
key = afs_request_key(dvnode->volume->cell); key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) { if (IS_ERR(key)) {
ret = PTR_ERR(key); ret = PTR_ERR(key);
...@@ -1005,10 +993,6 @@ static int afs_link(struct dentry *from, struct inode *dir, ...@@ -1005,10 +993,6 @@ static int afs_link(struct dentry *from, struct inode *dir,
dvnode->fid.vid, dvnode->fid.vnode, dvnode->fid.vid, dvnode->fid.vnode,
dentry->d_name.name); dentry->d_name.name);
ret = -ENAMETOOLONG;
if (dentry->d_name.len >= AFSNAMEMAX)
goto error;
key = afs_request_key(dvnode->volume->cell); key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) { if (IS_ERR(key)) {
ret = PTR_ERR(key); ret = PTR_ERR(key);
...@@ -1053,10 +1037,6 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -1053,10 +1037,6 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name,
content); content);
ret = -ENAMETOOLONG;
if (dentry->d_name.len >= AFSNAMEMAX)
goto error;
ret = -EINVAL; ret = -EINVAL;
if (strlen(content) >= AFSPATHMAX) if (strlen(content) >= AFSPATHMAX)
goto error; goto error;
...@@ -1127,10 +1107,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1127,10 +1107,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dvnode->fid.vid, new_dvnode->fid.vnode, new_dvnode->fid.vid, new_dvnode->fid.vnode,
new_dentry->d_name.name); new_dentry->d_name.name);
ret = -ENAMETOOLONG;
if (new_dentry->d_name.len >= AFSNAMEMAX)
goto error;
key = afs_request_key(orig_dvnode->volume->cell); key = afs_request_key(orig_dvnode->volume->cell);
if (IS_ERR(key)) { if (IS_ERR(key)) {
ret = PTR_ERR(key); ret = PTR_ERR(key);
......
...@@ -183,13 +183,14 @@ static int autofs_dev_ioctl_protosubver(struct file *fp, ...@@ -183,13 +183,14 @@ static int autofs_dev_ioctl_protosubver(struct file *fp,
return 0; return 0;
} }
/* Find the topmost mount satisfying test() */
static int find_autofs_mount(const char *pathname, static int find_autofs_mount(const char *pathname,
struct path *res, struct path *res,
int test(struct path *path, void *data), int test(struct path *path, void *data),
void *data) void *data)
{ {
struct path path; struct path path;
int err = kern_path(pathname, 0, &path); int err = kern_path_mountpoint(AT_FDCWD, pathname, &path, 0);
if (err) if (err)
return err; return err;
err = -ENOENT; err = -ENOENT;
...@@ -197,10 +198,9 @@ static int find_autofs_mount(const char *pathname, ...@@ -197,10 +198,9 @@ static int find_autofs_mount(const char *pathname,
if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) { if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) {
if (test(&path, data)) { if (test(&path, data)) {
path_get(&path); path_get(&path);
if (!err) /* already found some */
path_put(res);
*res = path; *res = path;
err = 0; err = 0;
break;
} }
} }
if (!follow_up(&path)) if (!follow_up(&path))
...@@ -486,12 +486,11 @@ static int autofs_dev_ioctl_askumount(struct file *fp, ...@@ -486,12 +486,11 @@ static int autofs_dev_ioctl_askumount(struct file *fp,
* mount if there is one or 0 if it isn't a mountpoint. * mount if there is one or 0 if it isn't a mountpoint.
* *
* If we aren't supplied with a file descriptor then we * If we aren't supplied with a file descriptor then we
* lookup the nameidata of the path and check if it is the * lookup the path and check if it is the root of a mount.
* root of a mount. If a type is given we are looking for * If a type is given we are looking for a particular autofs
* a particular autofs mount and if we don't find a match * mount and if we don't find a match we return fail. If the
* we return fail. If the located nameidata path is the * located path is the root of a mount we return 1 along with
* root of a mount we return 1 along with the super magic * the super magic of the mount or 0 otherwise.
* of the mount or 0 otherwise.
* *
* In both cases the the device number (as returned by * In both cases the the device number (as returned by
* new_encode_dev()) is also returned. * new_encode_dev()) is also returned.
...@@ -519,9 +518,11 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, ...@@ -519,9 +518,11 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
if (!fp || param->ioctlfd == -1) { if (!fp || param->ioctlfd == -1) {
if (autofs_type_any(type)) if (autofs_type_any(type))
err = kern_path(name, LOOKUP_FOLLOW, &path); err = kern_path_mountpoint(AT_FDCWD,
name, &path, LOOKUP_FOLLOW);
else else
err = find_autofs_mount(name, &path, test_by_type, &type); err = find_autofs_mount(name, &path,
test_by_type, &type);
if (err) if (err)
goto out; goto out;
devid = new_encode_dev(path.dentry->d_sb->s_dev); devid = new_encode_dev(path.dentry->d_sb->s_dev);
......
...@@ -88,6 +88,35 @@ EXPORT_SYMBOL(rename_lock); ...@@ -88,6 +88,35 @@ EXPORT_SYMBOL(rename_lock);
static struct kmem_cache *dentry_cache __read_mostly; static struct kmem_cache *dentry_cache __read_mostly;
/**
* read_seqbegin_or_lock - begin a sequence number check or locking block
* lock: sequence lock
* seq : sequence number to be checked
*
* First try it once optimistically without taking the lock. If that fails,
* take the lock. The sequence number is also used as a marker for deciding
* whether to be a reader (even) or writer (odd).
* N.B. seq must be initialized to an even number to begin with.
*/
static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq)
{
if (!(*seq & 1)) /* Even */
*seq = read_seqbegin(lock);
else /* Odd */
write_seqlock(lock);
}
static inline int need_seqretry(seqlock_t *lock, int seq)
{
return !(seq & 1) && read_seqretry(lock, seq);
}
static inline void done_seqretry(seqlock_t *lock, int seq)
{
if (seq & 1)
write_sequnlock(lock);
}
/* /*
* This is the single most critical data structure when it comes * This is the single most critical data structure when it comes
* to the dcache: the hashtable for lookups. Somebody should try * to the dcache: the hashtable for lookups. Somebody should try
...@@ -1012,7 +1041,7 @@ void shrink_dcache_for_umount(struct super_block *sb) ...@@ -1012,7 +1041,7 @@ void shrink_dcache_for_umount(struct super_block *sb)
* the parenthood after dropping the lock and check * the parenthood after dropping the lock and check
* that the sequence number still matches. * that the sequence number still matches.
*/ */
static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq) static struct dentry *try_to_ascend(struct dentry *old, unsigned seq)
{ {
struct dentry *new = old->d_parent; struct dentry *new = old->d_parent;
...@@ -1026,7 +1055,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq ...@@ -1026,7 +1055,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq
*/ */
if (new != old->d_parent || if (new != old->d_parent ||
(old->d_flags & DCACHE_DENTRY_KILLED) || (old->d_flags & DCACHE_DENTRY_KILLED) ||
(!locked && read_seqretry(&rename_lock, seq))) { need_seqretry(&rename_lock, seq)) {
spin_unlock(&new->d_lock); spin_unlock(&new->d_lock);
new = NULL; new = NULL;
} }
...@@ -1063,13 +1092,12 @@ static void d_walk(struct dentry *parent, void *data, ...@@ -1063,13 +1092,12 @@ static void d_walk(struct dentry *parent, void *data,
{ {
struct dentry *this_parent; struct dentry *this_parent;
struct list_head *next; struct list_head *next;
unsigned seq; unsigned seq = 0;
int locked = 0;
enum d_walk_ret ret; enum d_walk_ret ret;
bool retry = true; bool retry = true;
seq = read_seqbegin(&rename_lock);
again: again:
read_seqbegin_or_lock(&rename_lock, &seq);
this_parent = parent; this_parent = parent;
spin_lock(&this_parent->d_lock); spin_lock(&this_parent->d_lock);
...@@ -1123,13 +1151,13 @@ static void d_walk(struct dentry *parent, void *data, ...@@ -1123,13 +1151,13 @@ static void d_walk(struct dentry *parent, void *data,
*/ */
if (this_parent != parent) { if (this_parent != parent) {
struct dentry *child = this_parent; struct dentry *child = this_parent;
this_parent = try_to_ascend(this_parent, locked, seq); this_parent = try_to_ascend(this_parent, seq);
if (!this_parent) if (!this_parent)
goto rename_retry; goto rename_retry;
next = child->d_u.d_child.next; next = child->d_u.d_child.next;
goto resume; goto resume;
} }
if (!locked && read_seqretry(&rename_lock, seq)) { if (need_seqretry(&rename_lock, seq)) {
spin_unlock(&this_parent->d_lock); spin_unlock(&this_parent->d_lock);
goto rename_retry; goto rename_retry;
} }
...@@ -1138,17 +1166,13 @@ static void d_walk(struct dentry *parent, void *data, ...@@ -1138,17 +1166,13 @@ static void d_walk(struct dentry *parent, void *data,
out_unlock: out_unlock:
spin_unlock(&this_parent->d_lock); spin_unlock(&this_parent->d_lock);
if (locked) done_seqretry(&rename_lock, seq);
write_sequnlock(&rename_lock);
return; return;
rename_retry: rename_retry:
if (!retry) if (!retry)
return; return;
if (locked) seq = 1;
goto again;
locked = 1;
write_seqlock(&rename_lock);
goto again; goto again;
} }
...@@ -2647,9 +2671,39 @@ static int prepend(char **buffer, int *buflen, const char *str, int namelen) ...@@ -2647,9 +2671,39 @@ static int prepend(char **buffer, int *buflen, const char *str, int namelen)
return 0; return 0;
} }
/**
* prepend_name - prepend a pathname in front of current buffer pointer
* buffer: buffer pointer
* buflen: allocated length of the buffer
* name: name string and length qstr structure
*
* With RCU path tracing, it may race with d_move(). Use ACCESS_ONCE() to
* make sure that either the old or the new name pointer and length are
* fetched. However, there may be mismatch between length and pointer.
* The length cannot be trusted, we need to copy it byte-by-byte until
* the length is reached or a null byte is found. It also prepends "/" at
* the beginning of the name. The sequence number check at the caller will
* retry it again when a d_move() does happen. So any garbage in the buffer
* due to mismatched pointer and length will be discarded.
*/
static int prepend_name(char **buffer, int *buflen, struct qstr *name) static int prepend_name(char **buffer, int *buflen, struct qstr *name)
{ {
return prepend(buffer, buflen, name->name, name->len); const char *dname = ACCESS_ONCE(name->name);
u32 dlen = ACCESS_ONCE(name->len);
char *p;
if (*buflen < dlen + 1)
return -ENAMETOOLONG;
*buflen -= dlen + 1;
p = *buffer -= dlen + 1;
*p++ = '/';
while (dlen--) {
char c = *dname++;
if (!c)
break;
*p++ = c;
}
return 0;
} }
/** /**
...@@ -2659,7 +2713,14 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) ...@@ -2659,7 +2713,14 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name)
* @buffer: pointer to the end of the buffer * @buffer: pointer to the end of the buffer
* @buflen: pointer to buffer length * @buflen: pointer to buffer length
* *
* Caller holds the rename_lock. * The function tries to write out the pathname without taking any lock other
* than the RCU read lock to make sure that dentries won't go away. It only
* checks the sequence number of the global rename_lock as any change in the
* dentry's d_seq will be preceded by changes in the rename_lock sequence
* number. If the sequence number had been change, it will restart the whole
* pathname back-tracing sequence again. It performs a total of 3 trials of
* lockless back-tracing sequences before falling back to take the
* rename_lock.
*/ */
static int prepend_path(const struct path *path, static int prepend_path(const struct path *path,
const struct path *root, const struct path *root,
...@@ -2668,54 +2729,66 @@ static int prepend_path(const struct path *path, ...@@ -2668,54 +2729,66 @@ static int prepend_path(const struct path *path,
struct dentry *dentry = path->dentry; struct dentry *dentry = path->dentry;
struct vfsmount *vfsmnt = path->mnt; struct vfsmount *vfsmnt = path->mnt;
struct mount *mnt = real_mount(vfsmnt); struct mount *mnt = real_mount(vfsmnt);
bool slash = false;
int error = 0; int error = 0;
unsigned seq = 0;
char *bptr;
int blen;
rcu_read_lock();
restart:
bptr = *buffer;
blen = *buflen;
read_seqbegin_or_lock(&rename_lock, &seq);
while (dentry != root->dentry || vfsmnt != root->mnt) { while (dentry != root->dentry || vfsmnt != root->mnt) {
struct dentry * parent; struct dentry * parent;
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
/* Global root? */ /* Global root? */
if (!mnt_has_parent(mnt)) if (mnt_has_parent(mnt)) {
goto global_root; dentry = mnt->mnt_mountpoint;
dentry = mnt->mnt_mountpoint; mnt = mnt->mnt_parent;
mnt = mnt->mnt_parent; vfsmnt = &mnt->mnt;
vfsmnt = &mnt->mnt; continue;
continue; }
/*
* Filesystems needing to implement special "root names"
* should do so with ->d_dname()
*/
if (IS_ROOT(dentry) &&
(dentry->d_name.len != 1 ||
dentry->d_name.name[0] != '/')) {
WARN(1, "Root dentry has weird name <%.*s>\n",
(int) dentry->d_name.len,
dentry->d_name.name);
}
if (!error)
error = is_mounted(vfsmnt) ? 1 : 2;
break;
} }
parent = dentry->d_parent; parent = dentry->d_parent;
prefetch(parent); prefetch(parent);
spin_lock(&dentry->d_lock); error = prepend_name(&bptr, &blen, &dentry->d_name);
error = prepend_name(buffer, buflen, &dentry->d_name);
spin_unlock(&dentry->d_lock);
if (!error)
error = prepend(buffer, buflen, "/", 1);
if (error) if (error)
break; break;
slash = true;
dentry = parent; dentry = parent;
} }
if (!(seq & 1))
rcu_read_unlock();
if (need_seqretry(&rename_lock, seq)) {
seq = 1;
goto restart;
}
done_seqretry(&rename_lock, seq);
if (!error && !slash) if (error >= 0 && bptr == *buffer) {
error = prepend(buffer, buflen, "/", 1); if (--blen < 0)
error = -ENAMETOOLONG;
return error; else
*--bptr = '/';
global_root: }
/* *buffer = bptr;
* Filesystems needing to implement special "root names" *buflen = blen;
* should do so with ->d_dname()
*/
if (IS_ROOT(dentry) &&
(dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) {
WARN(1, "Root dentry has weird name <%.*s>\n",
(int) dentry->d_name.len, dentry->d_name.name);
}
if (!slash)
error = prepend(buffer, buflen, "/", 1);
if (!error)
error = is_mounted(vfsmnt) ? 1 : 2;
return error; return error;
} }
...@@ -2744,9 +2817,7 @@ char *__d_path(const struct path *path, ...@@ -2744,9 +2817,7 @@ char *__d_path(const struct path *path,
prepend(&res, &buflen, "\0", 1); prepend(&res, &buflen, "\0", 1);
br_read_lock(&vfsmount_lock); br_read_lock(&vfsmount_lock);
write_seqlock(&rename_lock);
error = prepend_path(path, root, &res, &buflen); error = prepend_path(path, root, &res, &buflen);
write_sequnlock(&rename_lock);
br_read_unlock(&vfsmount_lock); br_read_unlock(&vfsmount_lock);
if (error < 0) if (error < 0)
...@@ -2765,9 +2836,7 @@ char *d_absolute_path(const struct path *path, ...@@ -2765,9 +2836,7 @@ char *d_absolute_path(const struct path *path,
prepend(&res, &buflen, "\0", 1); prepend(&res, &buflen, "\0", 1);
br_read_lock(&vfsmount_lock); br_read_lock(&vfsmount_lock);
write_seqlock(&rename_lock);
error = prepend_path(path, &root, &res, &buflen); error = prepend_path(path, &root, &res, &buflen);
write_sequnlock(&rename_lock);
br_read_unlock(&vfsmount_lock); br_read_unlock(&vfsmount_lock);
if (error > 1) if (error > 1)
...@@ -2833,9 +2902,7 @@ char *d_path(const struct path *path, char *buf, int buflen) ...@@ -2833,9 +2902,7 @@ char *d_path(const struct path *path, char *buf, int buflen)
get_fs_root(current->fs, &root); get_fs_root(current->fs, &root);
br_read_lock(&vfsmount_lock); br_read_lock(&vfsmount_lock);
write_seqlock(&rename_lock);
error = path_with_deleted(path, &root, &res, &buflen); error = path_with_deleted(path, &root, &res, &buflen);
write_sequnlock(&rename_lock);
br_read_unlock(&vfsmount_lock); br_read_unlock(&vfsmount_lock);
if (error < 0) if (error < 0)
res = ERR_PTR(error); res = ERR_PTR(error);
...@@ -2870,10 +2937,10 @@ char *simple_dname(struct dentry *dentry, char *buffer, int buflen) ...@@ -2870,10 +2937,10 @@ char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
char *end = buffer + buflen; char *end = buffer + buflen;
/* these dentries are never renamed, so d_lock is not needed */ /* these dentries are never renamed, so d_lock is not needed */
if (prepend(&end, &buflen, " (deleted)", 11) || if (prepend(&end, &buflen, " (deleted)", 11) ||
prepend_name(&end, &buflen, &dentry->d_name) || prepend(&end, &buflen, dentry->d_name.name, dentry->d_name.len) ||
prepend(&end, &buflen, "/", 1)) prepend(&end, &buflen, "/", 1))
end = ERR_PTR(-ENAMETOOLONG); end = ERR_PTR(-ENAMETOOLONG);
return end; return end;
} }
/* /*
...@@ -2881,30 +2948,42 @@ char *simple_dname(struct dentry *dentry, char *buffer, int buflen) ...@@ -2881,30 +2948,42 @@ char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
*/ */
static char *__dentry_path(struct dentry *dentry, char *buf, int buflen) static char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
{ {
char *end = buf + buflen; char *end, *retval;
char *retval; int len, seq = 0;
int error = 0;
prepend(&end, &buflen, "\0", 1); rcu_read_lock();
restart:
end = buf + buflen;
len = buflen;
prepend(&end, &len, "\0", 1);
if (buflen < 1) if (buflen < 1)
goto Elong; goto Elong;
/* Get '/' right */ /* Get '/' right */
retval = end-1; retval = end-1;
*retval = '/'; *retval = '/';
read_seqbegin_or_lock(&rename_lock, &seq);
while (!IS_ROOT(dentry)) { while (!IS_ROOT(dentry)) {
struct dentry *parent = dentry->d_parent; struct dentry *parent = dentry->d_parent;
int error; int error;
prefetch(parent); prefetch(parent);
spin_lock(&dentry->d_lock); error = prepend_name(&end, &len, &dentry->d_name);
error = prepend_name(&end, &buflen, &dentry->d_name); if (error)
spin_unlock(&dentry->d_lock); break;
if (error != 0 || prepend(&end, &buflen, "/", 1) != 0)
goto Elong;
retval = end; retval = end;
dentry = parent; dentry = parent;
} }
if (!(seq & 1))
rcu_read_unlock();
if (need_seqretry(&rename_lock, seq)) {
seq = 1;
goto restart;
}
done_seqretry(&rename_lock, seq);
if (error)
goto Elong;
return retval; return retval;
Elong: Elong:
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
...@@ -2912,13 +2991,7 @@ static char *__dentry_path(struct dentry *dentry, char *buf, int buflen) ...@@ -2912,13 +2991,7 @@ static char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
char *dentry_path_raw(struct dentry *dentry, char *buf, int buflen) char *dentry_path_raw(struct dentry *dentry, char *buf, int buflen)
{ {
char *retval; return __dentry_path(dentry, buf, buflen);
write_seqlock(&rename_lock);
retval = __dentry_path(dentry, buf, buflen);
write_sequnlock(&rename_lock);
return retval;
} }
EXPORT_SYMBOL(dentry_path_raw); EXPORT_SYMBOL(dentry_path_raw);
...@@ -2927,7 +3000,6 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen) ...@@ -2927,7 +3000,6 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen)
char *p = NULL; char *p = NULL;
char *retval; char *retval;
write_seqlock(&rename_lock);
if (d_unlinked(dentry)) { if (d_unlinked(dentry)) {
p = buf + buflen; p = buf + buflen;
if (prepend(&p, &buflen, "//deleted", 10) != 0) if (prepend(&p, &buflen, "//deleted", 10) != 0)
...@@ -2935,7 +3007,6 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen) ...@@ -2935,7 +3007,6 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen)
buflen++; buflen++;
} }
retval = __dentry_path(dentry, buf, buflen); retval = __dentry_path(dentry, buf, buflen);
write_sequnlock(&rename_lock);
if (!IS_ERR(retval) && p) if (!IS_ERR(retval) && p)
*p = '/'; /* restore '/' overriden with '\0' */ *p = '/'; /* restore '/' overriden with '\0' */
return retval; return retval;
...@@ -2974,7 +3045,6 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) ...@@ -2974,7 +3045,6 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
error = -ENOENT; error = -ENOENT;
br_read_lock(&vfsmount_lock); br_read_lock(&vfsmount_lock);
write_seqlock(&rename_lock);
if (!d_unlinked(pwd.dentry)) { if (!d_unlinked(pwd.dentry)) {
unsigned long len; unsigned long len;
char *cwd = page + PAGE_SIZE; char *cwd = page + PAGE_SIZE;
...@@ -2982,7 +3052,6 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) ...@@ -2982,7 +3052,6 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
prepend(&cwd, &buflen, "\0", 1); prepend(&cwd, &buflen, "\0", 1);
error = prepend_path(&pwd, &root, &cwd, &buflen); error = prepend_path(&pwd, &root, &cwd, &buflen);
write_sequnlock(&rename_lock);
br_read_unlock(&vfsmount_lock); br_read_unlock(&vfsmount_lock);
if (error < 0) if (error < 0)
...@@ -3003,7 +3072,6 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) ...@@ -3003,7 +3072,6 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
error = -EFAULT; error = -EFAULT;
} }
} else { } else {
write_sequnlock(&rename_lock);
br_read_unlock(&vfsmount_lock); br_read_unlock(&vfsmount_lock);
} }
......
...@@ -231,7 +231,7 @@ static int filldir_one(void * __buf, const char * name, int len, ...@@ -231,7 +231,7 @@ static int filldir_one(void * __buf, const char * name, int len,
int result = 0; int result = 0;
buf->sequence++; buf->sequence++;
if (buf->ino == ino) { if (buf->ino == ino && len <= NAME_MAX) {
memcpy(buf->name, name, len); memcpy(buf->name, name, len);
buf->name[len] = '\0'; buf->name[len] = '\0';
buf->found = 1; buf->found = 1;
......
...@@ -45,6 +45,9 @@ extern void __init chrdev_init(void); ...@@ -45,6 +45,9 @@ extern void __init chrdev_init(void);
* namei.c * namei.c
*/ */
extern int __inode_permission(struct inode *, int); extern int __inode_permission(struct inode *, int);
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 *);
/* /*
* namespace.c * namespace.c
......
...@@ -2209,7 +2209,7 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd, ...@@ -2209,7 +2209,7 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd,
} }
/** /**
* umount_lookup_last - look up last component for umount * mountpoint_last - look up last component for umount
* @nd: pathwalk nameidata - currently pointing at parent directory of "last" * @nd: pathwalk nameidata - currently pointing at parent directory of "last"
* @path: pointer to container for result * @path: pointer to container for result
* *
...@@ -2236,25 +2236,28 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd, ...@@ -2236,25 +2236,28 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd,
* to the link, and nd->path will *not* be put. * to the link, and nd->path will *not* be put.
*/ */
static int static int
umount_lookup_last(struct nameidata *nd, struct path *path) mountpoint_last(struct nameidata *nd, struct path *path)
{ {
int error = 0; int error = 0;
struct dentry *dentry; struct dentry *dentry;
struct dentry *dir = nd->path.dentry; struct dentry *dir = nd->path.dentry;
if (unlikely(nd->flags & LOOKUP_RCU)) { /* If we're in rcuwalk, drop out of it to handle last component */
WARN_ON_ONCE(1); if (nd->flags & LOOKUP_RCU) {
error = -ECHILD; if (unlazy_walk(nd, NULL)) {
goto error_check; error = -ECHILD;
goto out;
}
} }
nd->flags &= ~LOOKUP_PARENT; nd->flags &= ~LOOKUP_PARENT;
if (unlikely(nd->last_type != LAST_NORM)) { if (unlikely(nd->last_type != LAST_NORM)) {
error = handle_dots(nd, nd->last_type); error = handle_dots(nd, nd->last_type);
if (!error) if (error)
dentry = dget(nd->path.dentry); goto out;
goto error_check; dentry = dget(nd->path.dentry);
goto done;
} }
mutex_lock(&dir->d_inode->i_mutex); mutex_lock(&dir->d_inode->i_mutex);
...@@ -2268,44 +2271,43 @@ umount_lookup_last(struct nameidata *nd, struct path *path) ...@@ -2268,44 +2271,43 @@ umount_lookup_last(struct nameidata *nd, struct path *path)
dentry = d_alloc(dir, &nd->last); dentry = d_alloc(dir, &nd->last);
if (!dentry) { if (!dentry) {
error = -ENOMEM; error = -ENOMEM;
} else { goto out;
dentry = lookup_real(dir->d_inode, dentry, nd->flags);
if (IS_ERR(dentry))
error = PTR_ERR(dentry);
} }
dentry = lookup_real(dir->d_inode, dentry, nd->flags);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out;
} }
mutex_unlock(&dir->d_inode->i_mutex); mutex_unlock(&dir->d_inode->i_mutex);
error_check: done:
if (!error) { if (!dentry->d_inode) {
if (!dentry->d_inode) { error = -ENOENT;
error = -ENOENT; dput(dentry);
dput(dentry); goto out;
} else {
path->dentry = dentry;
path->mnt = mntget(nd->path.mnt);
if (should_follow_link(dentry->d_inode,
nd->flags & LOOKUP_FOLLOW))
return 1;
follow_mount(path);
}
} }
path->dentry = dentry;
path->mnt = mntget(nd->path.mnt);
if (should_follow_link(dentry->d_inode, nd->flags & LOOKUP_FOLLOW))
return 1;
follow_mount(path);
error = 0;
out:
terminate_walk(nd); terminate_walk(nd);
return error; return error;
} }
/** /**
* path_umountat - look up a path to be umounted * path_mountpoint - look up a path to be umounted
* @dfd: directory file descriptor to start walk from * @dfd: directory file descriptor to start walk from
* @name: full pathname to walk * @name: full pathname to walk
* @flags: lookup flags * @flags: lookup flags
* @nd: pathwalk nameidata
* *
* Look up the given name, but don't attempt to revalidate the last component. * Look up the given name, but don't attempt to revalidate the last component.
* Returns 0 and "path" will be valid on success; Retuns error otherwise. * Returns 0 and "path" will be valid on success; Retuns error otherwise.
*/ */
static int static int
path_umountat(int dfd, const char *name, struct path *path, unsigned int flags) path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags)
{ {
struct file *base = NULL; struct file *base = NULL;
struct nameidata nd; struct nameidata nd;
...@@ -2320,16 +2322,7 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags) ...@@ -2320,16 +2322,7 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags)
if (err) if (err)
goto out; goto out;
/* If we're in rcuwalk, drop out of it to handle last component */ err = mountpoint_last(&nd, path);
if (nd.flags & LOOKUP_RCU) {
err = unlazy_walk(&nd, NULL);
if (err) {
terminate_walk(&nd);
goto out;
}
}
err = umount_lookup_last(&nd, path);
while (err > 0) { while (err > 0) {
void *cookie; void *cookie;
struct path link = *path; struct path link = *path;
...@@ -2340,7 +2333,7 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags) ...@@ -2340,7 +2333,7 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags)
err = follow_link(&link, &nd, &cookie); err = follow_link(&link, &nd, &cookie);
if (err) if (err)
break; break;
err = umount_lookup_last(&nd, path); err = mountpoint_last(&nd, path);
put_link(&nd, &link, cookie); put_link(&nd, &link, cookie);
} }
out: out:
...@@ -2353,8 +2346,22 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags) ...@@ -2353,8 +2346,22 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags)
return err; return err;
} }
static int
filename_mountpoint(int dfd, struct filename *s, struct path *path,
unsigned int flags)
{
int error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU);
if (unlikely(error == -ECHILD))
error = path_mountpoint(dfd, s->name, path, flags);
if (unlikely(error == -ESTALE))
error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_REVAL);
if (likely(!error))
audit_inode(s, path->dentry, 0);
return error;
}
/** /**
* user_path_umountat - lookup a path from userland in order to umount it * user_path_mountpoint_at - lookup a path from userland in order to umount it
* @dfd: directory file descriptor * @dfd: directory file descriptor
* @name: pathname from userland * @name: pathname from userland
* @flags: lookup flags * @flags: lookup flags
...@@ -2368,28 +2375,27 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags) ...@@ -2368,28 +2375,27 @@ path_umountat(int dfd, const char *name, struct path *path, unsigned int flags)
* Returns 0 and populates "path" on success. * Returns 0 and populates "path" on success.
*/ */
int int
user_path_umountat(int dfd, const char __user *name, unsigned int flags, user_path_mountpoint_at(int dfd, const char __user *name, unsigned int flags,
struct path *path) struct path *path)
{ {
struct filename *s = getname(name); struct filename *s = getname(name);
int error; int error;
if (IS_ERR(s)) if (IS_ERR(s))
return PTR_ERR(s); return PTR_ERR(s);
error = filename_mountpoint(dfd, s, path, flags);
error = path_umountat(dfd, s->name, path, flags | LOOKUP_RCU);
if (unlikely(error == -ECHILD))
error = path_umountat(dfd, s->name, path, flags);
if (unlikely(error == -ESTALE))
error = path_umountat(dfd, s->name, path, flags | LOOKUP_REVAL);
if (likely(!error))
audit_inode(s, path->dentry, 0);
putname(s); putname(s);
return error; return error;
} }
int
kern_path_mountpoint(int dfd, const char *name, struct path *path,
unsigned int flags)
{
struct filename s = {.name = name};
return filename_mountpoint(dfd, &s, path, flags);
}
EXPORT_SYMBOL(kern_path_mountpoint);
/* /*
* It's inline, so penalty for filesystems that don't use sticky bit is * It's inline, so penalty for filesystems that don't use sticky bit is
* minimal. * minimal.
......
...@@ -1321,7 +1321,7 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags) ...@@ -1321,7 +1321,7 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
if (!(flags & UMOUNT_NOFOLLOW)) if (!(flags & UMOUNT_NOFOLLOW))
lookup_flags |= LOOKUP_FOLLOW; lookup_flags |= LOOKUP_FOLLOW;
retval = user_path_umountat(AT_FDCWD, name, lookup_flags, &path); retval = user_path_mountpoint_at(AT_FDCWD, name, lookup_flags, &path);
if (retval) if (retval)
goto out; goto out;
mnt = real_mount(path.mnt); mnt = real_mount(path.mnt);
......
...@@ -71,7 +71,7 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc) ...@@ -71,7 +71,7 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc)
if (!grab_super_passive(sb)) if (!grab_super_passive(sb))
return -1; return -1;
if (sb->s_op && sb->s_op->nr_cached_objects) if (sb->s_op->nr_cached_objects)
fs_objects = sb->s_op->nr_cached_objects(sb); fs_objects = sb->s_op->nr_cached_objects(sb);
total_objects = sb->s_nr_dentry_unused + total_objects = sb->s_nr_dentry_unused +
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#define _LINUX_EVENTFD_H #define _LINUX_EVENTFD_H
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/wait.h> #include <linux/wait.h>
/* /*
...@@ -26,6 +25,8 @@ ...@@ -26,6 +25,8 @@
#define EFD_SHARED_FCNTL_FLAGS (O_CLOEXEC | O_NONBLOCK) #define EFD_SHARED_FCNTL_FLAGS (O_CLOEXEC | O_NONBLOCK)
#define EFD_FLAGS_SET (EFD_SHARED_FCNTL_FLAGS | EFD_SEMAPHORE) #define EFD_FLAGS_SET (EFD_SHARED_FCNTL_FLAGS | EFD_SEMAPHORE)
struct file;
#ifdef CONFIG_EVENTFD #ifdef CONFIG_EVENTFD
struct file *eventfd_file_create(unsigned int count, int flags); struct file *eventfd_file_create(unsigned int count, int flags);
......
...@@ -58,7 +58,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; ...@@ -58,7 +58,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
extern int user_path_at(int, const char __user *, unsigned, struct path *); extern int user_path_at(int, const char __user *, unsigned, struct path *);
extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty); extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
extern int user_path_umountat(int, const char __user *, unsigned int, struct path *);
#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path) #define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)
#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path) #define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path)
...@@ -71,8 +70,7 @@ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigne ...@@ -71,8 +70,7 @@ 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 *, extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
const char *, unsigned int, struct path *);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
......
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/flex_array.h> /* used in cgroup_attach_task */ #include <linux/flex_array.h> /* used in cgroup_attach_task */
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/file.h>
#include <linux/atomic.h> #include <linux/atomic.h>
...@@ -4034,8 +4035,8 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css, ...@@ -4034,8 +4035,8 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css,
struct cgroup_event *event; struct cgroup_event *event;
struct cgroup_subsys_state *cfile_css; struct cgroup_subsys_state *cfile_css;
unsigned int efd, cfd; unsigned int efd, cfd;
struct file *efile; struct fd efile;
struct file *cfile; struct fd cfile;
char *endp; char *endp;
int ret; int ret;
...@@ -4058,31 +4059,31 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css, ...@@ -4058,31 +4059,31 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css,
init_waitqueue_func_entry(&event->wait, cgroup_event_wake); init_waitqueue_func_entry(&event->wait, cgroup_event_wake);
INIT_WORK(&event->remove, cgroup_event_remove); INIT_WORK(&event->remove, cgroup_event_remove);
efile = eventfd_fget(efd); efile = fdget(efd);
if (IS_ERR(efile)) { if (!efile.file) {
ret = PTR_ERR(efile); ret = -EBADF;
goto out_kfree; goto out_kfree;
} }
event->eventfd = eventfd_ctx_fileget(efile); event->eventfd = eventfd_ctx_fileget(efile.file);
if (IS_ERR(event->eventfd)) { if (IS_ERR(event->eventfd)) {
ret = PTR_ERR(event->eventfd); ret = PTR_ERR(event->eventfd);
goto out_put_efile; goto out_put_efile;
} }
cfile = fget(cfd); cfile = fdget(cfd);
if (!cfile) { if (!cfile.file) {
ret = -EBADF; ret = -EBADF;
goto out_put_eventfd; goto out_put_eventfd;
} }
/* the process need read permission on control file */ /* the process need read permission on control file */
/* AV: shouldn't we check that it's been opened for read instead? */ /* AV: shouldn't we check that it's been opened for read instead? */
ret = inode_permission(file_inode(cfile), MAY_READ); ret = inode_permission(file_inode(cfile.file), MAY_READ);
if (ret < 0) if (ret < 0)
goto out_put_cfile; goto out_put_cfile;
event->cft = __file_cft(cfile); event->cft = __file_cft(cfile.file);
if (IS_ERR(event->cft)) { if (IS_ERR(event->cft)) {
ret = PTR_ERR(event->cft); ret = PTR_ERR(event->cft);
goto out_put_cfile; goto out_put_cfile;
...@@ -4103,7 +4104,7 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css, ...@@ -4103,7 +4104,7 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css,
ret = -EINVAL; ret = -EINVAL;
event->css = cgroup_css(cgrp, event->cft->ss); event->css = cgroup_css(cgrp, event->cft->ss);
cfile_css = css_from_dir(cfile->f_dentry->d_parent, event->cft->ss); cfile_css = css_from_dir(cfile.file->f_dentry->d_parent, event->cft->ss);
if (event->css && event->css == cfile_css && css_tryget(event->css)) if (event->css && event->css == cfile_css && css_tryget(event->css))
ret = 0; ret = 0;
...@@ -4121,25 +4122,25 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css, ...@@ -4121,25 +4122,25 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *dummy_css,
if (ret) if (ret)
goto out_put_css; goto out_put_css;
efile->f_op->poll(efile, &event->pt); efile.file->f_op->poll(efile.file, &event->pt);
spin_lock(&cgrp->event_list_lock); spin_lock(&cgrp->event_list_lock);
list_add(&event->list, &cgrp->event_list); list_add(&event->list, &cgrp->event_list);
spin_unlock(&cgrp->event_list_lock); spin_unlock(&cgrp->event_list_lock);
fput(cfile); fdput(cfile);
fput(efile); fdput(efile);
return 0; return 0;
out_put_css: out_put_css:
css_put(event->css); css_put(event->css);
out_put_cfile: out_put_cfile:
fput(cfile); fdput(cfile);
out_put_eventfd: out_put_eventfd:
eventfd_ctx_put(event->eventfd); eventfd_ctx_put(event->eventfd);
out_put_efile: out_put_efile:
fput(efile); fdput(efile);
out_kfree: out_kfree:
kfree(event); kfree(event);
......
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