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 <net/9p/9p.h> 37 #include <net/9p/client.h> 38 39 #include "v9fs.h" 40 #include "v9fs_vfs.h" 41 #include "fid.h" 42 43 /** 44 * struct p9_rdir - readdir accounting 45 * @head: start offset of current dirread buffer 46 * @tail: end offset of current dirread buffer 47 * @buf: dirread buffer 48 * 49 * private structure for keeping track of readdir 50 * allocated on demand 51 */ 52 53 struct p9_rdir { 54 int head; 55 int tail; 56 uint8_t buf[]; 57 }; 58 59 /** 60 * dt_type - return file type 61 * @mistat: mistat structure 62 * 63 */ 64 65 static inline int dt_type(struct p9_wstat *mistat) 66 { 67 unsigned long perm = mistat->mode; 68 int rettype = DT_REG; 69 70 if (perm & P9_DMDIR) 71 rettype = DT_DIR; 72 if (perm & P9_DMSYMLINK) 73 rettype = DT_LNK; 74 75 return rettype; 76 } 77 78 static void p9stat_init(struct p9_wstat *stbuf) 79 { 80 stbuf->name = NULL; 81 stbuf->uid = NULL; 82 stbuf->gid = NULL; 83 stbuf->muid = NULL; 84 stbuf->extension = NULL; 85 } 86 87 /** 88 * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir 89 * @filp: opened file structure 90 * @buflen: Length in bytes of buffer to allocate 91 * 92 */ 93 94 static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen) 95 { 96 struct p9_fid *fid = filp->private_data; 97 if (!fid->rdir) 98 fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); 99 return fid->rdir; 100 } 101 102 /** 103 * v9fs_dir_readdir - iterate through a directory 104 * @file: opened file structure 105 * @ctx: actor we feed the entries to 106 * 107 */ 108 109 static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) 110 { 111 bool over; 112 struct p9_wstat st; 113 int err = 0; 114 struct p9_fid *fid; 115 int buflen; 116 int reclen = 0; 117 struct p9_rdir *rdir; 118 119 p9_debug(P9_DEBUG_VFS, "name %pD\n", file); 120 fid = file->private_data; 121 122 buflen = fid->clnt->msize - P9_IOHDRSZ; 123 124 rdir = v9fs_alloc_rdir_buf(file, buflen); 125 if (!rdir) 126 return -ENOMEM; 127 128 while (1) { 129 if (rdir->tail == rdir->head) { 130 err = v9fs_file_readn(file, rdir->buf, NULL, 131 buflen, ctx->pos); 132 if (err <= 0) 133 return err; 134 135 rdir->head = 0; 136 rdir->tail = err; 137 } 138 while (rdir->head < rdir->tail) { 139 p9stat_init(&st); 140 err = p9stat_read(fid->clnt, rdir->buf + rdir->head, 141 rdir->tail - rdir->head, &st); 142 if (err) { 143 p9_debug(P9_DEBUG_VFS, "returned %d\n", err); 144 p9stat_free(&st); 145 return -EIO; 146 } 147 reclen = st.size+2; 148 149 over = !dir_emit(ctx, st.name, strlen(st.name), 150 v9fs_qid2ino(&st.qid), dt_type(&st)); 151 p9stat_free(&st); 152 if (over) 153 return 0; 154 155 rdir->head += reclen; 156 ctx->pos += reclen; 157 } 158 } 159 } 160 161 /** 162 * v9fs_dir_readdir_dotl - iterate through a directory 163 * @file: opened file structure 164 * @ctx: actor we feed the entries to 165 * 166 */ 167 static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx) 168 { 169 int err = 0; 170 struct p9_fid *fid; 171 int buflen; 172 struct p9_rdir *rdir; 173 struct p9_dirent curdirent; 174 175 p9_debug(P9_DEBUG_VFS, "name %pD\n", file); 176 fid = file->private_data; 177 178 buflen = fid->clnt->msize - P9_READDIRHDRSZ; 179 180 rdir = v9fs_alloc_rdir_buf(file, buflen); 181 if (!rdir) 182 return -ENOMEM; 183 184 while (1) { 185 if (rdir->tail == rdir->head) { 186 err = p9_client_readdir(fid, rdir->buf, buflen, 187 ctx->pos); 188 if (err <= 0) 189 return err; 190 191 rdir->head = 0; 192 rdir->tail = err; 193 } 194 195 while (rdir->head < rdir->tail) { 196 197 err = p9dirent_read(fid->clnt, rdir->buf + rdir->head, 198 rdir->tail - rdir->head, 199 &curdirent); 200 if (err < 0) { 201 p9_debug(P9_DEBUG_VFS, "returned %d\n", err); 202 return -EIO; 203 } 204 205 if (!dir_emit(ctx, curdirent.d_name, 206 strlen(curdirent.d_name), 207 v9fs_qid2ino(&curdirent.qid), 208 curdirent.d_type)) 209 return 0; 210 211 ctx->pos = curdirent.d_off; 212 rdir->head += err; 213 } 214 } 215 } 216 217 218 /** 219 * v9fs_dir_release - close a directory 220 * @inode: inode of the directory 221 * @filp: file pointer to a directory 222 * 223 */ 224 225 int v9fs_dir_release(struct inode *inode, struct file *filp) 226 { 227 struct p9_fid *fid; 228 229 fid = filp->private_data; 230 p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n", 231 inode, filp, fid ? fid->fid : -1); 232 if (fid) 233 p9_client_clunk(fid); 234 return 0; 235 } 236 237 const struct file_operations v9fs_dir_operations = { 238 .read = generic_read_dir, 239 .llseek = generic_file_llseek, 240 .iterate = v9fs_dir_readdir, 241 .open = v9fs_file_open, 242 .release = v9fs_dir_release, 243 }; 244 245 const struct file_operations v9fs_dir_operations_dotl = { 246 .read = generic_read_dir, 247 .llseek = generic_file_llseek, 248 .iterate = v9fs_dir_readdir_dotl, 249 .open = v9fs_file_open, 250 .release = v9fs_dir_release, 251 .fsync = v9fs_file_fsync_dotl, 252 }; 253