Unverified Commit eae9e789 authored by David Howells's avatar David Howells Committed by Christian Brauner
Browse files

afs: Use netfslib for symlinks, allowing them to be cached



Use netfslib to read symlinks, thereby allowing them to be cached by
fscache and cachefiles.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20241216204124.3752367-23-dhowells@redhat.com


cc: Marc Dionne <marc.dionne@auristor.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: linux-afs@lists.infradead.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 6dd80936
Loading
Loading
Loading
Loading
+0 −32
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@
#include "internal.h"

static int afs_file_mmap(struct file *file, struct vm_area_struct *vma);
static int afs_symlink_read_folio(struct file *file, struct folio *folio);

static ssize_t afs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter);
static ssize_t afs_file_splice_read(struct file *in, loff_t *ppos,
@@ -61,13 +60,6 @@ const struct address_space_operations afs_file_aops = {
	.writepages	= afs_writepages,
};

const struct address_space_operations afs_symlink_aops = {
	.read_folio	= afs_symlink_read_folio,
	.release_folio	= netfs_release_folio,
	.invalidate_folio = netfs_invalidate_folio,
	.migrate_folio	= filemap_migrate_folio,
};

static const struct vm_operations_struct afs_vm_ops = {
	.open		= afs_vm_open,
	.close		= afs_vm_close,
@@ -346,30 +338,6 @@ static void afs_issue_read(struct netfs_io_subrequest *subreq)
	queue_work(system_long_wq, &subreq->work);
}

static int afs_symlink_read_folio(struct file *file, struct folio *folio)
{
	struct afs_vnode *vnode = AFS_FS_I(folio->mapping->host);
	struct afs_read *fsreq;
	int ret;

	fsreq = afs_alloc_read(GFP_NOFS);
	if (!fsreq)
		return -ENOMEM;

	fsreq->pos	= folio_pos(folio);
	fsreq->len	= folio_size(folio);
	fsreq->vnode	= vnode;
	fsreq->iter	= &fsreq->def_iter;
	iov_iter_xarray(&fsreq->def_iter, ITER_DEST, &folio->mapping->i_pages,
			fsreq->pos, fsreq->len);

	ret = afs_fetch_data(fsreq->vnode, fsreq);
	if (ret == 0)
		folio_mark_uptodate(folio);
	folio_unlock(folio);
	return ret;
}

static int afs_init_request(struct netfs_io_request *rreq, struct file *file)
{
	struct afs_vnode *vnode = AFS_FS_I(rreq->inode);
+59 −5
Original line number Diff line number Diff line
@@ -25,8 +25,60 @@
#include "internal.h"
#include "afs_fs.h"

static void afs_put_link(void *arg)
{
	struct folio *folio = virt_to_folio(arg);

	kunmap_local(arg);
	folio_put(folio);
}

const char *afs_get_link(struct dentry *dentry, struct inode *inode,
			 struct delayed_call *callback)
{
	struct afs_vnode *vnode = AFS_FS_I(inode);
	struct folio *folio;
	char *content;
	ssize_t ret;

	if (atomic64_read(&vnode->cb_expires_at) == AFS_NO_CB_PROMISE ||
	    !test_bit(AFS_VNODE_DIR_READ, &vnode->flags)) {
		if (!dentry)
			return ERR_PTR(-ECHILD);
		ret = afs_read_single(vnode, NULL);
		if (ret < 0)
			return ERR_PTR(ret);
	}

	folio = folioq_folio(vnode->directory, 0);
	folio_get(folio);
	content = kmap_local_folio(folio, 0);
	set_delayed_call(callback, afs_put_link, content);
	return content;
}

int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
	DEFINE_DELAYED_CALL(done);
	const char *content;
	int len;

	content = afs_get_link(dentry, d_inode(dentry), &done);
	if (IS_ERR(content)) {
		do_delayed_call(&done);
		return PTR_ERR(content);
	}

	len = umin(strlen(content), buflen);
	if (copy_to_user(buffer, content, len))
		len = -EFAULT;
	do_delayed_call(&done);
	return len;
}

static const struct inode_operations afs_symlink_inode_operations = {
	.get_link	= page_get_link,
	.get_link	= afs_get_link,
	.readlink	= afs_readlink,
};

static noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode *parent_vnode)
@@ -124,13 +176,13 @@ static int afs_inode_init_from_status(struct afs_operation *op,
			inode->i_mode	= S_IFDIR | 0555;
			inode->i_op	= &afs_mntpt_inode_operations;
			inode->i_fop	= &afs_mntpt_file_operations;
			inode->i_mapping->a_ops	= &afs_symlink_aops;
		} else {
			inode->i_mode	= S_IFLNK | status->mode;
			inode->i_op	= &afs_symlink_inode_operations;
			inode->i_mapping->a_ops	= &afs_symlink_aops;
		}
		inode->i_mapping->a_ops	= &afs_dir_aops;
		inode_nohighmem(inode);
		mapping_set_release_always(inode->i_mapping);
		break;
	default:
		dump_vnode(vnode, op->file[0].vnode != vnode ? op->file[0].vnode : NULL);
@@ -443,7 +495,8 @@ static void afs_get_inode_cache(struct afs_vnode *vnode)
	struct afs_vnode_cache_aux aux;

	if (vnode->status.type != AFS_FTYPE_FILE &&
	    vnode->status.type != AFS_FTYPE_DIR) {
	    vnode->status.type != AFS_FTYPE_DIR &&
	    vnode->status.type != AFS_FTYPE_SYMLINK) {
		vnode->netfs.cache = NULL;
		return;
	}
@@ -657,7 +710,8 @@ void afs_evict_inode(struct inode *inode)

	ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode);

	if ((S_ISDIR(inode->i_mode)) &&
	if ((S_ISDIR(inode->i_mode) ||
	     S_ISLNK(inode->i_mode)) &&
	    (inode->i_state & I_DIRTY) &&
	    !sbi->dyn_root) {
		struct writeback_control wbc = {
+3 −1
Original line number Diff line number Diff line
@@ -1116,7 +1116,6 @@ extern void afs_dynroot_depopulate(struct super_block *);
 * file.c
 */
extern const struct address_space_operations afs_file_aops;
extern const struct address_space_operations afs_symlink_aops;
extern const struct inode_operations afs_file_inode_operations;
extern const struct file_operations afs_file_operations;
extern const struct netfs_request_ops afs_req_ops;
@@ -1222,6 +1221,9 @@ extern void afs_fs_probe_cleanup(struct afs_net *);
 */
extern const struct afs_operation_ops afs_fetch_status_operation;

const char *afs_get_link(struct dentry *dentry, struct inode *inode,
			 struct delayed_call *callback);
int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen);
extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vnode_param *);
extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_access_t *);
extern int afs_ilookup5_test_by_fid(struct inode *, void *);
+11 −11
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ const struct file_operations afs_mntpt_file_operations = {

const struct inode_operations afs_mntpt_inode_operations = {
	.lookup		= afs_mntpt_lookup,
	.readlink	= page_readlink,
	.readlink	= afs_readlink,
	.getattr	= afs_getattr,
};

@@ -118,9 +118,9 @@ static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt)
		ctx->volnamesz = sizeof(afs_root_volume) - 1;
	} else {
		/* read the contents of the AFS special symlink */
		struct page *page;
		DEFINE_DELAYED_CALL(cleanup);
		const char *content;
		loff_t size = i_size_read(d_inode(mntpt));
		char *buf;

		if (src_as->cell)
			ctx->cell = afs_use_cell(src_as->cell, afs_cell_trace_use_mntpt);
@@ -128,16 +128,16 @@ static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt)
		if (size < 2 || size > PAGE_SIZE - 1)
			return -EINVAL;

		page = read_mapping_page(d_inode(mntpt)->i_mapping, 0, NULL);
		if (IS_ERR(page))
			return PTR_ERR(page);
		content = afs_get_link(mntpt, d_inode(mntpt), &cleanup);
		if (IS_ERR(content)) {
			do_delayed_call(&cleanup);
			return PTR_ERR(content);
		}

		buf = kmap(page);
		ret = -EINVAL;
		if (buf[size - 1] == '.')
			ret = vfs_parse_fs_string(fc, "source", buf, size - 1);
		kunmap(page);
		put_page(page);
		if (content[size - 1] == '.')
			ret = vfs_parse_fs_string(fc, "source", content, size - 1);
		do_delayed_call(&cleanup);
		if (ret < 0)
			return ret;

+1 −0
Original line number Diff line number Diff line
@@ -422,6 +422,7 @@ enum yfs_cm_operation {
	EM(afs_file_error_dir_over_end,		"DIR_ENT_OVER_END")	\
	EM(afs_file_error_dir_small,		"DIR_SMALL")		\
	EM(afs_file_error_dir_unmarked_ext,	"DIR_UNMARKED_EXT")	\
	EM(afs_file_error_symlink_big,		"SYM_BIG")		\
	EM(afs_file_error_mntpt,		"MNTPT_READ_FAILED")	\
	E_(afs_file_error_writeback_fail,	"WRITEBACK_FAILED")