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