1 /* 2 * linux/fs/9p/vfs_dir.c 3 * 4 * This file contains vfs directory ops for the 9P2000 protocol. 5 * 6 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> 7 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 11 * as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to: 20 * Free Software Foundation 21 * 51 Franklin Street, Fifth Floor 22 * Boston, MA 02111-1301 USA 23 * 24 */ 25 26 #include <linux/module.h> 27 #include <linux/errno.h> 28 #include <linux/fs.h> 29 #include <linux/file.h> 30 #include <linux/stat.h> 31 #include <linux/string.h> 32 #include <linux/sched.h> 33 #include <linux/inet.h> 34 #include <linux/idr.h> 35 #include <linux/slab.h> 36 #include <linux/uio.h> 37 #include <net/9p/9p.h> 38 #include <net/9p/client.h> 39 40 #include "v9fs.h" 41 #include "v9fs_vfs.h" 42 #include "fid.h" 43 44 /** 45 * struct p9_rdir - readdir accounting 46 * @head: start offset of current dirread buffer 47 * @tail: end offset of current dirread buffer 48 * @buf: dirread buffer 49 * 50 * private structure for keeping track of readdir 51 * allocated on demand 52 */ 53 54 struct p9_rdir { 55 int head; 56 int tail; 57 uint8_t buf[]; 58 }; 59 60 /** 61 * dt_type - return file type 62 * @mistat: mistat structure 63 * 64 */ 65 66 static inline int dt_type(struct p9_wstat *mistat) 67 { 68 unsigned long perm = mistat->mode; 69 int rettype = DT_REG; 70 71 if (perm & P9_DMDIR) 72 rettype = DT_DIR; 73 if (perm & P9_DMSYMLINK) 74 rettype = DT_LNK; 75 76 return rettype; 77 } 78 79 /** 80 * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir 81 * @filp: opened file structure 82 * @buflen: Length in bytes of buffer to allocate 83 * 84 */ 85 86 static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen) 87 { 88 struct p9_fid *fid = filp->private_data; 89 if (!fid->rdir) 90 fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); 91 return fid->rdir; 92 } 93 94 /** 95 * v9fs_dir_readdir - iterate through a directory 96 * @file: opened file structure 97 * @ctx: actor we feed the entries to 98 * 99 */ 100 101 static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) 102 { 103 bool over; 104 struct p9_wstat st; 105 int err = 0; 106 struct p9_fid *fid; 107 int buflen; 108 struct p9_rdir *rdir; 109 struct kvec kvec; 110 111 p9_debug(P9_DEBUG_VFS, "name %pD\n", file); 112 fid = file->private_data; 113 114 buflen = fid->clnt->msize - P9_IOHDRSZ; 115 116 rdir = v9fs_alloc_rdir_buf(file, buflen); 117 if (!rdir) 118 return -ENOMEM; 119 kvec.iov_base = rdir->buf; 120 kvec.iov_len = buflen; 121 122 while (1) { 123 if (rdir->tail == rdir->head) { 124 struct iov_iter to; 125 int n; 126 iov_iter_kvec(&to, READ, &kvec, 1, buflen); 127 n = p9_client_read(file->private_data, ctx->pos, &to, 128 &err); 129 if (err) 130 return err; 131 if (n == 0) 132 return 0; 133 134 rdir->head = 0; 135 rdir->tail = n; 136 } 137 while (rdir->head < rdir->tail) { 138 err = p9stat_read(fid->clnt, rdir->buf + rdir->head, 139 rdir->tail - rdir->head, &st); 140 if (err <= 0) { 141 p9_debug(P9_DEBUG_VFS, "returned %d\n", err); 142 return -EIO; 143 } 144 145 over = !dir_emit(ctx, st.name, strlen(st.name), 146 v9fs_qid2ino(&st.qid), dt_type(&st)); 147 p9stat_free(&st); 148 if (over) 149 return 0; 150 151 rdir->head += err; 152 ctx->pos += err; 153 } 154 } 155 } 156 157 /** 158 * v9fs_dir_readdir_dotl - iterate through a directory 159 * @file: opened file structure 160 * @ctx: actor we feed the entries to 161 * 162 */ 163 static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx) 164 { 165 int err = 0; 166 struct p9_fid *fid; 167 int buflen; 168 struct p9_rdir *rdir; 169 struct p9_dirent curdirent; 170 171 p9_debug(P9_DEBUG_VFS, "name %pD\n", file); 172 fid = file->private_data; 173 174 buflen = fid->clnt->msize - P9_READDIRHDRSZ; 175 176 rdir = v9fs_alloc_rdir_buf(file, buflen); 177 if (!rdir) 178 return -ENOMEM; 179 180 while (1) { 181 if (rdir->tail == rdir->head) { 182 err = p9_client_readdir(fid, rdir->buf, buflen, 183 ctx->pos); 184 if (err <= 0) 185 return err; 186 187 rdir->head = 0; 188 rdir->tail = err; 189 } 190 191 while (rdir->head < rdir->tail) { 192 193 err = p9dirent_read(fid->clnt, rdir->buf + rdir->head, 194 rdir->tail - rdir->head, 195 &curdirent); 196 if (err < 0) { 197 p9_debug(P9_DEBUG_VFS, "returned %d\n", err); 198 return -EIO; 199 } 200 201 if (!dir_emit(ctx, curdirent.d_name, 202 strlen(curdirent.d_name), 203 v9fs_qid2ino(&curdirent.qid), 204 curdirent.d_type)) 205 return 0; 206 207 ctx->pos = curdirent.d_off; 208 rdir->head += err; 209 } 210 } 211 } 212 213 214 /** 215 * v9fs_dir_release - close a directory 216 * @inode: inode of the directory 217 * @filp: file pointer to a directory 218 * 219 */ 220 221 int v9fs_dir_release(struct inode *inode, struct file *filp) 222 { 223 struct p9_fid *fid; 224 225 fid = filp->private_data; 226 p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n", 227 inode, filp, fid ? fid->fid : -1); 228 if (fid) 229 p9_client_clunk(fid); 230 return 0; 231 } 232 233 const struct file_operations v9fs_dir_operations = { 234 .read = generic_read_dir, 235 .llseek = generic_file_llseek, 236 .iterate_shared = v9fs_dir_readdir, 237 .open = v9fs_file_open, 238 .release = v9fs_dir_release, 239 }; 240 241 const struct file_operations v9fs_dir_operations_dotl = { 242 .read = generic_read_dir, 243 .llseek = generic_file_llseek, 244 .iterate_shared = v9fs_dir_readdir_dotl, 245 .open = v9fs_file_open, 246 .release = v9fs_dir_release, 247 .fsync = v9fs_file_fsync_dotl, 248 }; 249