18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/fs/9p/vfs_dir.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file contains vfs directory ops for the 9P2000 protocol.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
88c2ecf20Sopenharmony_ci *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/file.h>
158c2ecf20Sopenharmony_ci#include <linux/stat.h>
168c2ecf20Sopenharmony_ci#include <linux/string.h>
178c2ecf20Sopenharmony_ci#include <linux/sched.h>
188c2ecf20Sopenharmony_ci#include <linux/inet.h>
198c2ecf20Sopenharmony_ci#include <linux/idr.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/uio.h>
228c2ecf20Sopenharmony_ci#include <net/9p/9p.h>
238c2ecf20Sopenharmony_ci#include <net/9p/client.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "v9fs.h"
268c2ecf20Sopenharmony_ci#include "v9fs_vfs.h"
278c2ecf20Sopenharmony_ci#include "fid.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/**
308c2ecf20Sopenharmony_ci * struct p9_rdir - readdir accounting
318c2ecf20Sopenharmony_ci * @head: start offset of current dirread buffer
328c2ecf20Sopenharmony_ci * @tail: end offset of current dirread buffer
338c2ecf20Sopenharmony_ci * @buf: dirread buffer
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * private structure for keeping track of readdir
368c2ecf20Sopenharmony_ci * allocated on demand
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct p9_rdir {
408c2ecf20Sopenharmony_ci	int head;
418c2ecf20Sopenharmony_ci	int tail;
428c2ecf20Sopenharmony_ci	uint8_t buf[];
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/**
468c2ecf20Sopenharmony_ci * dt_type - return file type
478c2ecf20Sopenharmony_ci * @mistat: mistat structure
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic inline int dt_type(struct p9_wstat *mistat)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	unsigned long perm = mistat->mode;
548c2ecf20Sopenharmony_ci	int rettype = DT_REG;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (perm & P9_DMDIR)
578c2ecf20Sopenharmony_ci		rettype = DT_DIR;
588c2ecf20Sopenharmony_ci	if (perm & P9_DMSYMLINK)
598c2ecf20Sopenharmony_ci		rettype = DT_LNK;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return rettype;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/**
658c2ecf20Sopenharmony_ci * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
668c2ecf20Sopenharmony_ci * @filp: opened file structure
678c2ecf20Sopenharmony_ci * @buflen: Length in bytes of buffer to allocate
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct p9_fid *fid = filp->private_data;
748c2ecf20Sopenharmony_ci	if (!fid->rdir)
758c2ecf20Sopenharmony_ci		fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
768c2ecf20Sopenharmony_ci	return fid->rdir;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/**
808c2ecf20Sopenharmony_ci * v9fs_dir_readdir - iterate through a directory
818c2ecf20Sopenharmony_ci * @file: opened file structure
828c2ecf20Sopenharmony_ci * @ctx: actor we feed the entries to
838c2ecf20Sopenharmony_ci *
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	bool over;
898c2ecf20Sopenharmony_ci	struct p9_wstat st;
908c2ecf20Sopenharmony_ci	int err = 0;
918c2ecf20Sopenharmony_ci	struct p9_fid *fid;
928c2ecf20Sopenharmony_ci	int buflen;
938c2ecf20Sopenharmony_ci	struct p9_rdir *rdir;
948c2ecf20Sopenharmony_ci	struct kvec kvec;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
978c2ecf20Sopenharmony_ci	fid = file->private_data;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	buflen = fid->clnt->msize - P9_IOHDRSZ;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	rdir = v9fs_alloc_rdir_buf(file, buflen);
1028c2ecf20Sopenharmony_ci	if (!rdir)
1038c2ecf20Sopenharmony_ci		return -ENOMEM;
1048c2ecf20Sopenharmony_ci	kvec.iov_base = rdir->buf;
1058c2ecf20Sopenharmony_ci	kvec.iov_len = buflen;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	while (1) {
1088c2ecf20Sopenharmony_ci		if (rdir->tail == rdir->head) {
1098c2ecf20Sopenharmony_ci			struct iov_iter to;
1108c2ecf20Sopenharmony_ci			int n;
1118c2ecf20Sopenharmony_ci			iov_iter_kvec(&to, READ, &kvec, 1, buflen);
1128c2ecf20Sopenharmony_ci			n = p9_client_read(file->private_data, ctx->pos, &to,
1138c2ecf20Sopenharmony_ci					   &err);
1148c2ecf20Sopenharmony_ci			if (err)
1158c2ecf20Sopenharmony_ci				return err;
1168c2ecf20Sopenharmony_ci			if (n == 0)
1178c2ecf20Sopenharmony_ci				return 0;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci			rdir->head = 0;
1208c2ecf20Sopenharmony_ci			rdir->tail = n;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci		while (rdir->head < rdir->tail) {
1238c2ecf20Sopenharmony_ci			err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
1248c2ecf20Sopenharmony_ci					  rdir->tail - rdir->head, &st);
1258c2ecf20Sopenharmony_ci			if (err <= 0) {
1268c2ecf20Sopenharmony_ci				p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
1278c2ecf20Sopenharmony_ci				return -EIO;
1288c2ecf20Sopenharmony_ci			}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci			over = !dir_emit(ctx, st.name, strlen(st.name),
1318c2ecf20Sopenharmony_ci					 v9fs_qid2ino(&st.qid), dt_type(&st));
1328c2ecf20Sopenharmony_ci			p9stat_free(&st);
1338c2ecf20Sopenharmony_ci			if (over)
1348c2ecf20Sopenharmony_ci				return 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci			rdir->head += err;
1378c2ecf20Sopenharmony_ci			ctx->pos += err;
1388c2ecf20Sopenharmony_ci		}
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/**
1438c2ecf20Sopenharmony_ci * v9fs_dir_readdir_dotl - iterate through a directory
1448c2ecf20Sopenharmony_ci * @file: opened file structure
1458c2ecf20Sopenharmony_ci * @ctx: actor we feed the entries to
1468c2ecf20Sopenharmony_ci *
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_cistatic int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	int err = 0;
1518c2ecf20Sopenharmony_ci	struct p9_fid *fid;
1528c2ecf20Sopenharmony_ci	int buflen;
1538c2ecf20Sopenharmony_ci	struct p9_rdir *rdir;
1548c2ecf20Sopenharmony_ci	struct p9_dirent curdirent;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
1578c2ecf20Sopenharmony_ci	fid = file->private_data;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	buflen = fid->clnt->msize - P9_READDIRHDRSZ;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	rdir = v9fs_alloc_rdir_buf(file, buflen);
1628c2ecf20Sopenharmony_ci	if (!rdir)
1638c2ecf20Sopenharmony_ci		return -ENOMEM;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	while (1) {
1668c2ecf20Sopenharmony_ci		if (rdir->tail == rdir->head) {
1678c2ecf20Sopenharmony_ci			err = p9_client_readdir(fid, rdir->buf, buflen,
1688c2ecf20Sopenharmony_ci						ctx->pos);
1698c2ecf20Sopenharmony_ci			if (err <= 0)
1708c2ecf20Sopenharmony_ci				return err;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci			rdir->head = 0;
1738c2ecf20Sopenharmony_ci			rdir->tail = err;
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		while (rdir->head < rdir->tail) {
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci			err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
1798c2ecf20Sopenharmony_ci					    rdir->tail - rdir->head,
1808c2ecf20Sopenharmony_ci					    &curdirent);
1818c2ecf20Sopenharmony_ci			if (err < 0) {
1828c2ecf20Sopenharmony_ci				p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
1838c2ecf20Sopenharmony_ci				return -EIO;
1848c2ecf20Sopenharmony_ci			}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci			if (!dir_emit(ctx, curdirent.d_name,
1878c2ecf20Sopenharmony_ci				      strlen(curdirent.d_name),
1888c2ecf20Sopenharmony_ci				      v9fs_qid2ino(&curdirent.qid),
1898c2ecf20Sopenharmony_ci				      curdirent.d_type))
1908c2ecf20Sopenharmony_ci				return 0;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci			ctx->pos = curdirent.d_off;
1938c2ecf20Sopenharmony_ci			rdir->head += err;
1948c2ecf20Sopenharmony_ci		}
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/**
2008c2ecf20Sopenharmony_ci * v9fs_dir_release - close a directory
2018c2ecf20Sopenharmony_ci * @inode: inode of the directory
2028c2ecf20Sopenharmony_ci * @filp: file pointer to a directory
2038c2ecf20Sopenharmony_ci *
2048c2ecf20Sopenharmony_ci */
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ciint v9fs_dir_release(struct inode *inode, struct file *filp)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct p9_fid *fid;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	fid = filp->private_data;
2118c2ecf20Sopenharmony_ci	p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
2128c2ecf20Sopenharmony_ci		 inode, filp, fid ? fid->fid : -1);
2138c2ecf20Sopenharmony_ci	if (fid)
2148c2ecf20Sopenharmony_ci		p9_client_clunk(fid);
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciconst struct file_operations v9fs_dir_operations = {
2198c2ecf20Sopenharmony_ci	.read = generic_read_dir,
2208c2ecf20Sopenharmony_ci	.llseek = generic_file_llseek,
2218c2ecf20Sopenharmony_ci	.iterate_shared = v9fs_dir_readdir,
2228c2ecf20Sopenharmony_ci	.open = v9fs_file_open,
2238c2ecf20Sopenharmony_ci	.release = v9fs_dir_release,
2248c2ecf20Sopenharmony_ci};
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ciconst struct file_operations v9fs_dir_operations_dotl = {
2278c2ecf20Sopenharmony_ci	.read = generic_read_dir,
2288c2ecf20Sopenharmony_ci	.llseek = generic_file_llseek,
2298c2ecf20Sopenharmony_ci	.iterate_shared = v9fs_dir_readdir_dotl,
2308c2ecf20Sopenharmony_ci	.open = v9fs_file_open,
2318c2ecf20Sopenharmony_ci	.release = v9fs_dir_release,
2328c2ecf20Sopenharmony_ci        .fsync = v9fs_file_fsync_dotl,
2338c2ecf20Sopenharmony_ci};
234