Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * This file contains vfs directory ops for the 9P2000 protocol.
0004  *
0005  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
0006  *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
0007  */
0008 
0009 #include <linux/module.h>
0010 #include <linux/errno.h>
0011 #include <linux/fs.h>
0012 #include <linux/file.h>
0013 #include <linux/stat.h>
0014 #include <linux/string.h>
0015 #include <linux/sched.h>
0016 #include <linux/inet.h>
0017 #include <linux/idr.h>
0018 #include <linux/slab.h>
0019 #include <linux/uio.h>
0020 #include <linux/fscache.h>
0021 #include <net/9p/9p.h>
0022 #include <net/9p/client.h>
0023 
0024 #include "v9fs.h"
0025 #include "v9fs_vfs.h"
0026 #include "fid.h"
0027 
0028 /**
0029  * struct p9_rdir - readdir accounting
0030  * @head: start offset of current dirread buffer
0031  * @tail: end offset of current dirread buffer
0032  * @buf: dirread buffer
0033  *
0034  * private structure for keeping track of readdir
0035  * allocated on demand
0036  */
0037 
0038 struct p9_rdir {
0039     int head;
0040     int tail;
0041     uint8_t buf[];
0042 };
0043 
0044 /**
0045  * dt_type - return file type
0046  * @mistat: mistat structure
0047  *
0048  */
0049 
0050 static inline int dt_type(struct p9_wstat *mistat)
0051 {
0052     unsigned long perm = mistat->mode;
0053     int rettype = DT_REG;
0054 
0055     if (perm & P9_DMDIR)
0056         rettype = DT_DIR;
0057     if (perm & P9_DMSYMLINK)
0058         rettype = DT_LNK;
0059 
0060     return rettype;
0061 }
0062 
0063 /**
0064  * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
0065  * @filp: opened file structure
0066  * @buflen: Length in bytes of buffer to allocate
0067  *
0068  */
0069 
0070 static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
0071 {
0072     struct p9_fid *fid = filp->private_data;
0073 
0074     if (!fid->rdir)
0075         fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
0076     return fid->rdir;
0077 }
0078 
0079 /**
0080  * v9fs_dir_readdir - iterate through a directory
0081  * @file: opened file structure
0082  * @ctx: actor we feed the entries to
0083  *
0084  */
0085 
0086 static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
0087 {
0088     bool over;
0089     struct p9_wstat st;
0090     int err = 0;
0091     struct p9_fid *fid;
0092     int buflen;
0093     struct p9_rdir *rdir;
0094     struct kvec kvec;
0095 
0096     p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
0097     fid = file->private_data;
0098 
0099     buflen = fid->clnt->msize - P9_IOHDRSZ;
0100 
0101     rdir = v9fs_alloc_rdir_buf(file, buflen);
0102     if (!rdir)
0103         return -ENOMEM;
0104     kvec.iov_base = rdir->buf;
0105     kvec.iov_len = buflen;
0106 
0107     while (1) {
0108         if (rdir->tail == rdir->head) {
0109             struct iov_iter to;
0110             int n;
0111 
0112             iov_iter_kvec(&to, READ, &kvec, 1, buflen);
0113             n = p9_client_read(file->private_data, ctx->pos, &to,
0114                        &err);
0115             if (err)
0116                 return err;
0117             if (n == 0)
0118                 return 0;
0119 
0120             rdir->head = 0;
0121             rdir->tail = n;
0122         }
0123         while (rdir->head < rdir->tail) {
0124             err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
0125                       rdir->tail - rdir->head, &st);
0126             if (err <= 0) {
0127                 p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
0128                 return -EIO;
0129             }
0130 
0131             over = !dir_emit(ctx, st.name, strlen(st.name),
0132                      v9fs_qid2ino(&st.qid), dt_type(&st));
0133             p9stat_free(&st);
0134             if (over)
0135                 return 0;
0136 
0137             rdir->head += err;
0138             ctx->pos += err;
0139         }
0140     }
0141 }
0142 
0143 /**
0144  * v9fs_dir_readdir_dotl - iterate through a directory
0145  * @file: opened file structure
0146  * @ctx: actor we feed the entries to
0147  *
0148  */
0149 static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
0150 {
0151     int err = 0;
0152     struct p9_fid *fid;
0153     int buflen;
0154     struct p9_rdir *rdir;
0155     struct p9_dirent curdirent;
0156 
0157     p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
0158     fid = file->private_data;
0159 
0160     buflen = fid->clnt->msize - P9_READDIRHDRSZ;
0161 
0162     rdir = v9fs_alloc_rdir_buf(file, buflen);
0163     if (!rdir)
0164         return -ENOMEM;
0165 
0166     while (1) {
0167         if (rdir->tail == rdir->head) {
0168             err = p9_client_readdir(fid, rdir->buf, buflen,
0169                         ctx->pos);
0170             if (err <= 0)
0171                 return err;
0172 
0173             rdir->head = 0;
0174             rdir->tail = err;
0175         }
0176 
0177         while (rdir->head < rdir->tail) {
0178 
0179             err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
0180                         rdir->tail - rdir->head,
0181                         &curdirent);
0182             if (err < 0) {
0183                 p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
0184                 return -EIO;
0185             }
0186 
0187             if (!dir_emit(ctx, curdirent.d_name,
0188                       strlen(curdirent.d_name),
0189                       v9fs_qid2ino(&curdirent.qid),
0190                       curdirent.d_type))
0191                 return 0;
0192 
0193             ctx->pos = curdirent.d_off;
0194             rdir->head += err;
0195         }
0196     }
0197 }
0198 
0199 
0200 /**
0201  * v9fs_dir_release - close a directory
0202  * @inode: inode of the directory
0203  * @filp: file pointer to a directory
0204  *
0205  */
0206 
0207 int v9fs_dir_release(struct inode *inode, struct file *filp)
0208 {
0209     struct v9fs_inode *v9inode = V9FS_I(inode);
0210     struct p9_fid *fid;
0211     __le32 version;
0212     loff_t i_size;
0213 
0214     fid = filp->private_data;
0215     p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
0216          inode, filp, fid ? fid->fid : -1);
0217     if (fid) {
0218         spin_lock(&inode->i_lock);
0219         hlist_del(&fid->ilist);
0220         spin_unlock(&inode->i_lock);
0221         p9_fid_put(fid);
0222     }
0223 
0224     if ((filp->f_mode & FMODE_WRITE)) {
0225         version = cpu_to_le32(v9inode->qid.version);
0226         i_size = i_size_read(inode);
0227         fscache_unuse_cookie(v9fs_inode_cookie(v9inode),
0228                      &version, &i_size);
0229     } else {
0230         fscache_unuse_cookie(v9fs_inode_cookie(v9inode), NULL, NULL);
0231     }
0232     return 0;
0233 }
0234 
0235 const struct file_operations v9fs_dir_operations = {
0236     .read = generic_read_dir,
0237     .llseek = generic_file_llseek,
0238     .iterate_shared = v9fs_dir_readdir,
0239     .open = v9fs_file_open,
0240     .release = v9fs_dir_release,
0241 };
0242 
0243 const struct file_operations v9fs_dir_operations_dotl = {
0244     .read = generic_read_dir,
0245     .llseek = generic_file_llseek,
0246     .iterate_shared = v9fs_dir_readdir_dotl,
0247     .open = v9fs_file_open,
0248     .release = v9fs_dir_release,
0249     .fsync = v9fs_file_fsync_dotl,
0250 };