1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * This file contains vfs directory ops for the 9P2000 protocol. 4 * 5 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> 6 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> 7 */ 8 9 #include <linux/module.h> 10 #include <linux/errno.h> 11 #include <linux/fs.h> 12 #include <linux/file.h> 13 #include <linux/stat.h> 14 #include <linux/string.h> 15 #include <linux/sched.h> 16 #include <linux/slab.h> 17 #include <linux/uio.h> 18 #include <linux/fscache.h> 19 #include <net/9p/9p.h> 20 #include <net/9p/client.h> 21 22 #include "v9fs.h" 23 #include "v9fs_vfs.h" 24 #include "fid.h" 25 26 /** 27 * struct p9_rdir - readdir accounting 28 * @head: start offset of current dirread buffer 29 * @tail: end offset of current dirread buffer 30 * @buf: dirread buffer 31 * 32 * private structure for keeping track of readdir 33 * allocated on demand 34 */ 35 36 struct p9_rdir { 37 int head; 38 int tail; 39 uint8_t buf[]; 40 }; 41 42 /** 43 * dt_type - return file type 44 * @mistat: mistat structure 45 * 46 */ 47 48 static inline int dt_type(struct p9_wstat *mistat) 49 { 50 unsigned long perm = mistat->mode; 51 int rettype = DT_REG; 52 53 if (perm & P9_DMDIR) 54 rettype = DT_DIR; 55 if (perm & P9_DMSYMLINK) 56 rettype = DT_LNK; 57 58 return rettype; 59 } 60 61 /** 62 * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir 63 * @filp: opened file structure 64 * @buflen: Length in bytes of buffer to allocate 65 * 66 */ 67 68 static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen) 69 { 70 struct p9_fid *fid = filp->private_data; 71 72 if (!fid->rdir) 73 fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); 74 return fid->rdir; 75 } 76 77 /** 78 * v9fs_dir_readdir - iterate through a directory 79 * @file: opened file structure 80 * @ctx: actor we feed the entries to 81 * 82 */ 83 84 static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) 85 { 86 bool over; 87 struct p9_wstat st; 88 int err = 0; 89 struct p9_fid *fid; 90 int buflen; 91 struct p9_rdir *rdir; 92 struct kvec kvec; 93 94 p9_debug(P9_DEBUG_VFS, "name %pD\n", file); 95 fid = file->private_data; 96 97 buflen = fid->clnt->msize - P9_IOHDRSZ; 98 99 rdir = v9fs_alloc_rdir_buf(file, buflen); 100 if (!rdir) 101 return -ENOMEM; 102 kvec.iov_base = rdir->buf; 103 kvec.iov_len = buflen; 104 105 while (1) { 106 if (rdir->tail == rdir->head) { 107 struct iov_iter to; 108 int n; 109 110 iov_iter_kvec(&to, ITER_DEST, &kvec, 1, buflen); 111 n = p9_client_read(file->private_data, ctx->pos, &to, 112 &err); 113 if (err) 114 return err; 115 if (n == 0) 116 return 0; 117 118 rdir->head = 0; 119 rdir->tail = n; 120 } 121 while (rdir->head < rdir->tail) { 122 err = p9stat_read(fid->clnt, rdir->buf + rdir->head, 123 rdir->tail - rdir->head, &st); 124 if (err <= 0) { 125 p9_debug(P9_DEBUG_VFS, "returned %d\n", err); 126 return -EIO; 127 } 128 129 over = !dir_emit(ctx, st.name, strlen(st.name), 130 QID2INO(&st.qid), dt_type(&st)); 131 p9stat_free(&st); 132 if (over) 133 return 0; 134 135 rdir->head += err; 136 ctx->pos += err; 137 } 138 } 139 } 140 141 /** 142 * v9fs_dir_readdir_dotl - iterate through a directory 143 * @file: opened file structure 144 * @ctx: actor we feed the entries to 145 * 146 */ 147 static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx) 148 { 149 int err = 0; 150 struct p9_fid *fid; 151 int buflen; 152 struct p9_rdir *rdir; 153 struct p9_dirent curdirent; 154 155 p9_debug(P9_DEBUG_VFS, "name %pD\n", file); 156 fid = file->private_data; 157 158 buflen = fid->clnt->msize - P9_READDIRHDRSZ; 159 160 rdir = v9fs_alloc_rdir_buf(file, buflen); 161 if (!rdir) 162 return -ENOMEM; 163 164 while (1) { 165 if (rdir->tail == rdir->head) { 166 err = p9_client_readdir(fid, rdir->buf, buflen, 167 ctx->pos); 168 if (err <= 0) 169 return err; 170 171 rdir->head = 0; 172 rdir->tail = err; 173 } 174 175 while (rdir->head < rdir->tail) { 176 177 err = p9dirent_read(fid->clnt, rdir->buf + rdir->head, 178 rdir->tail - rdir->head, 179 &curdirent); 180 if (err < 0) { 181 p9_debug(P9_DEBUG_VFS, "returned %d\n", err); 182 return -EIO; 183 } 184 185 if (!dir_emit(ctx, curdirent.d_name, 186 strlen(curdirent.d_name), 187 QID2INO(&curdirent.qid), 188 curdirent.d_type)) 189 return 0; 190 191 ctx->pos = curdirent.d_off; 192 rdir->head += err; 193 } 194 } 195 } 196 197 198 /** 199 * v9fs_dir_release - close a directory or a file 200 * @inode: inode of the directory or file 201 * @filp: file pointer to a directory or file 202 * 203 */ 204 205 int v9fs_dir_release(struct inode *inode, struct file *filp) 206 { 207 struct v9fs_inode *v9inode = V9FS_I(inode); 208 struct p9_fid *fid; 209 __le32 version; 210 loff_t i_size; 211 int retval = 0, put_err; 212 213 fid = filp->private_data; 214 p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n", 215 inode, filp, fid ? fid->fid : -1); 216 217 if (fid) { 218 if ((S_ISREG(inode->i_mode)) && (filp->f_mode & FMODE_WRITE)) 219 retval = filemap_fdatawrite(inode->i_mapping); 220 221 spin_lock(&inode->i_lock); 222 hlist_del(&fid->ilist); 223 spin_unlock(&inode->i_lock); 224 put_err = p9_fid_put(fid); 225 retval = retval < 0 ? retval : put_err; 226 } 227 228 if ((filp->f_mode & FMODE_WRITE)) { 229 version = cpu_to_le32(v9inode->qid.version); 230 i_size = i_size_read(inode); 231 fscache_unuse_cookie(v9fs_inode_cookie(v9inode), 232 &version, &i_size); 233 } else { 234 fscache_unuse_cookie(v9fs_inode_cookie(v9inode), NULL, NULL); 235 } 236 return retval; 237 } 238 239 const struct file_operations v9fs_dir_operations = { 240 .read = generic_read_dir, 241 .llseek = generic_file_llseek, 242 .iterate_shared = v9fs_dir_readdir, 243 .open = v9fs_file_open, 244 .release = v9fs_dir_release, 245 }; 246 247 const struct file_operations v9fs_dir_operations_dotl = { 248 .read = generic_read_dir, 249 .llseek = generic_file_llseek, 250 .iterate_shared = v9fs_dir_readdir_dotl, 251 .open = v9fs_file_open, 252 .release = v9fs_dir_release, 253 .fsync = v9fs_file_fsync_dotl, 254 }; 255
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.