{
struct netfs_io_request *rreq = subreq->rreq;
struct p9_fid *fid = rreq->netfs_priv;
+ char *target;
unsigned long long pos = subreq->start + subreq->transferred;
- int total, err;
-
- total = p9_client_read(fid, pos, &subreq->io_iter, &err);
+ int total = 0, err, len, n;
+
+ if (S_ISLNK(rreq->inode->i_mode)) {
+ /* p9_client_readlink() must not be called for legacy protocols
+ * 9p2000 or 9p2000.u.
+ */
+ BUG_ON(!p9_is_proto_dotl(fid->clnt));
+ if (WARN_ON_ONCE(pos)) {
+ /* reading a link at a non null offset should
+ * not happen
+ */
+ err = -EIO;
+ goto fill_subreq;
+ }
+ err = p9_client_readlink(fid, &target);
+ if (err != 0)
+ goto fill_subreq;
+ len = strlen(target);
+ n = copy_to_iter(target, len, &subreq->io_iter);
+ kfree(target);
+ total = n;
+ } else {
+ total = p9_client_read(fid, pos, &subreq->io_iter, &err);
+ }
+fill_subreq:
/* if we just extended the file size, any portion not in
* cache won't be on server and is zeroes */
if (subreq->rreq->origin != NETFS_UNBUFFERED_READ &&
static int v9fs_init_request(struct netfs_io_request *rreq, struct file *file)
{
struct p9_fid *fid;
+ struct dentry *dentry;
bool writing = (rreq->origin == NETFS_READ_FOR_WRITE ||
rreq->origin == NETFS_WRITETHROUGH ||
rreq->origin == NETFS_UNBUFFERED_WRITE ||
if (!fid)
goto no_fid;
p9_fid_get(fid);
+ } else if (S_ISLNK(rreq->inode->i_mode)) {
+ dentry = d_find_any_alias(rreq->inode);
+ if (!dentry)
+ goto no_fid;
+ fid = v9fs_fid_lookup(dentry);
+ dput(dentry);
+ if (IS_ERR(fid))
+ goto no_fid;
} else {
fid = v9fs_fid_find_inode(rreq->inode, writing, INVALID_UID, true);
if (!fid)
int err;
kgid_t gid;
const unsigned char *name;
+ struct v9fs_session_info *v9ses;
struct p9_qid qid;
struct p9_fid *dfid;
struct p9_fid *fid = NULL;
+ struct inode *inode;
name = dentry->d_name.name;
p9_debug(P9_DEBUG_VFS, "%lu,%s,%s\n", dir->i_ino, name, symname);
v9fs_invalidate_inode_attr(dir);
+ /* instantiate inode and assign the unopened fid to the dentry */
+ fid = p9_client_walk(dfid, 1, &name, 1);
+ if (IS_ERR(fid)) {
+ err = PTR_ERR(fid);
+ p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
+ err);
+ goto error;
+ }
+
+ v9ses = v9fs_inode2v9ses(dir);
+ inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
+ err);
+ goto error;
+ }
+ v9fs_fid_add(dentry, &fid);
+ d_instantiate(dentry, inode);
+ err = 0;
error:
p9_fid_put(fid);
p9_fid_put(dfid);
}
/**
- * v9fs_vfs_get_link_dotl - follow a symlink path
+ * v9fs_vfs_get_link_nocache_dotl - Resolve a symlink directly.
+ *
+ * To be used when symlink caching is not enabled.
+ *
* @dentry: dentry for symlink
* @inode: inode for symlink
* @done: destructor for return value
*/
-
static const char *
-v9fs_vfs_get_link_dotl(struct dentry *dentry,
- struct inode *inode,
- struct delayed_call *done)
+v9fs_vfs_get_link_nocache_dotl(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
struct p9_fid *fid;
char *target;
return target;
}
+/**
+ * v9fs_vfs_get_link_dotl - follow a symlink path
+ * @dentry: dentry for symlink
+ * @inode: inode for symlink
+ * @done: destructor for return value
+ */
+static const char *
+v9fs_vfs_get_link_dotl(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
+{
+ struct v9fs_session_info *v9ses;
+
+ v9ses = v9fs_inode2v9ses(inode);
+ if (v9ses->cache & (CACHE_META|CACHE_LOOSE))
+ return page_get_link(dentry, inode, done);
+
+ return v9fs_vfs_get_link_nocache_dotl(dentry, inode, done);
+}
+
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
{
struct p9_stat_dotl *st;