#include <linux/hash.h>
#include <linux/bitops.h>
#include <linux/init_task.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "internal.h"
#include "mount.h"
if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage);
- ret = path->dentry->d_op->d_manage(path->dentry, false);
+ ret = path->dentry->d_op->d_manage(path, false);
if (ret < 0)
break;
}
}
EXPORT_SYMBOL(follow_down_one);
-static inline int managed_dentry_rcu(struct dentry *dentry)
+static inline int managed_dentry_rcu(const struct path *path)
{
- return (dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
- dentry->d_op->d_manage(dentry, true) : 0;
+ return (path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
+ path->dentry->d_op->d_manage(path, true) : 0;
}
/*
* Don't forget we might have a non-mountpoint managed dentry
* that wants to block transit.
*/
- switch (managed_dentry_rcu(path->dentry)) {
+ switch (managed_dentry_rcu(path)) {
case -ECHILD:
default:
return false;
if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage);
- ret = path->dentry->d_op->d_manage(
- path->dentry, false);
+ ret = path->dentry->d_op->d_manage(path, false);
if (ret < 0)
return ret == -EISDIR ? 0 : ret;
}
return 1;
}
-enum {WALK_GET = 1, WALK_MORE = 2};
+enum {WALK_FOLLOW = 1, WALK_MORE = 2};
/*
* Do we need to follow links? We _really_ want to be able
* so we keep a cache of "no, this doesn't need follow_link"
* for the common case.
*/
-static inline int should_follow_link(struct nameidata *nd, struct path *link,
- int flags,
- struct inode *inode, unsigned seq)
+static inline int step_into(struct nameidata *nd, struct path *path,
+ int flags, struct inode *inode, unsigned seq)
{
if (!(flags & WALK_MORE) && nd->depth)
put_link(nd);
- if (likely(!d_is_symlink(link->dentry)))
- return 0;
- if (!(flags & WALK_GET) && !(nd->flags & LOOKUP_FOLLOW))
+ if (likely(!d_is_symlink(path->dentry)) ||
+ !(flags & WALK_FOLLOW || nd->flags & LOOKUP_FOLLOW)) {
+ /* not a symlink or should not follow */
+ path_to_nameidata(path, nd);
+ nd->inode = inode;
+ nd->seq = seq;
return 0;
+ }
/* make sure that d_is_symlink above matches inode */
if (nd->flags & LOOKUP_RCU) {
- if (read_seqcount_retry(&link->dentry->d_seq, seq))
+ if (read_seqcount_retry(&path->dentry->d_seq, seq))
return -ECHILD;
}
- return pick_link(nd, link, inode, seq);
+ return pick_link(nd, path, inode, seq);
}
static int walk_component(struct nameidata *nd, int flags)
inode = d_backing_inode(path.dentry);
}
- err = should_follow_link(nd, &path, flags, inode, seq);
- if (unlikely(err))
- return err;
- path_to_nameidata(&path, nd);
- nd->inode = inode;
- nd->seq = seq;
- return 0;
+ return step_into(nd, &path, flags, inode, seq);
}
/*
if (!name)
return 0;
/* last component of nested symlink */
- err = walk_component(nd, WALK_GET);
+ err = walk_component(nd, WALK_FOLLOW);
} else {
/* not the last component */
- err = walk_component(nd, WALK_GET | WALK_MORE);
+ err = walk_component(nd, WALK_FOLLOW | WALK_MORE);
}
if (err < 0)
return err;
return -ENOENT;
}
path.mnt = nd->path.mnt;
- error = should_follow_link(nd, &path, 0,
- d_backing_inode(path.dentry), 0);
- if (unlikely(error))
- return error;
- path_to_nameidata(&path, nd);
- return 0;
+ return step_into(nd, &path, 0, d_backing_inode(path.dentry), 0);
}
/**
!(path->mnt->mnt_sb->s_iflags & SB_I_NODEV);
}
-static int may_open(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 inode *inode = dentry->d_inode;
static int handle_truncate(struct file *filp)
{
- struct path *path = &filp->f_path;
+ const struct path *path = &filp->f_path;
struct inode *inode = path->dentry->d_inode;
int error = get_write_access(inode);
if (error)
seq = 0; /* out of RCU mode, so the value doesn't matter */
inode = d_backing_inode(path.dentry);
finish_lookup:
- error = should_follow_link(nd, &path, 0, inode, seq);
+ error = step_into(nd, &path, 0, inode, seq);
if (unlikely(error))
return error;
-
- path_to_nameidata(&path, nd);
- nd->inode = inode;
- nd->seq = seq;
- /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */
finish_open:
+ /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */
error = complete_walk(nd);
if (error)
return error;
bool new_is_dir = false;
unsigned max_links = new_dir->i_sb->s_max_links;
- /*
- * Check source == target.
- * On overlayfs need to look at underlying inodes.
- */
- if (d_real_inode(old_dentry) == d_real_inode(new_dentry))
+ if (source == target)
return 0;
error = may_delete(old_dir, old_dentry, is_dir);
* have ->get_link() not calling nd_jump_link(). Using (or not using) it
* for any given inode is up to filesystem.
*/
-int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+static int generic_readlink(struct dentry *dentry, char __user *buffer,
+ int buflen)
{
DEFINE_DELAYED_CALL(done);
struct inode *inode = d_inode(dentry);
do_delayed_call(&done);
return res;
}
-EXPORT_SYMBOL(generic_readlink);
+
+/**
+ * vfs_readlink - copy symlink body into userspace buffer
+ * @dentry: dentry on which to get symbolic link
+ * @buffer: user memory pointer
+ * @buflen: size of buffer
+ *
+ * Does not touch atime. That's up to the caller if necessary
+ *
+ * Does not call security hook.
+ */
+int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+{
+ struct inode *inode = d_inode(dentry);
+
+ if (unlikely(!(inode->i_opflags & IOP_DEFAULT_READLINK))) {
+ if (unlikely(inode->i_op->readlink))
+ return inode->i_op->readlink(dentry, buffer, buflen);
+
+ if (!d_is_symlink(dentry))
+ return -EINVAL;
+
+ spin_lock(&inode->i_lock);
+ inode->i_opflags |= IOP_DEFAULT_READLINK;
+ spin_unlock(&inode->i_lock);
+ }
+
+ return generic_readlink(dentry, buffer, buflen);
+}
+EXPORT_SYMBOL(vfs_readlink);
/**
* vfs_get_link - get symlink body
EXPORT_SYMBOL(page_symlink);
const struct inode_operations page_symlink_inode_operations = {
- .readlink = generic_readlink,
.get_link = page_get_link,
};
EXPORT_SYMBOL(page_symlink_inode_operations);