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 * @mutex: mutex protecting readdir 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 struct mutex mutex; 56 int head; 57 int tail; 58 uint8_t *buf; 59 }; 60 61 /** 62 * dt_type - return file type 63 * @mistat: mistat structure 64 * 65 */ 66 67 static inline int dt_type(struct p9_wstat *mistat) 68 { 69 unsigned long perm = mistat->mode; 70 int rettype = DT_REG; 71 72 if (perm & P9_DMDIR) 73 rettype = DT_DIR; 74 if (perm & P9_DMSYMLINK) 75 rettype = DT_LNK; 76 77 return rettype; 78 } 79 80 static void p9stat_init(struct p9_wstat *stbuf) 81 { 82 stbuf->name = NULL; 83 stbuf->uid = NULL; 84 stbuf->gid = NULL; 85 stbuf->muid = NULL; 86 stbuf->extension = NULL; 87 } 88 89 /** 90 * v9fs_dir_readdir - read a directory 91 * @filp: opened file structure 92 * @dirent: directory structure ??? 93 * @filldir: function to populate directory structure ??? 94 * 95 */ 96 97 static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) 98 { 99 int over; 100 struct p9_wstat st; 101 int err = 0; 102 struct p9_fid *fid; 103 int buflen; 104 int reclen = 0; 105 struct p9_rdir *rdir; 106 107 P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); 108 fid = filp->private_data; 109 110 buflen = fid->clnt->msize - P9_IOHDRSZ; 111 112 /* allocate rdir on demand */ 113 if (!fid->rdir) { 114 rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); 115 116 if (rdir == NULL) { 117 err = -ENOMEM; 118 goto exit; 119 } 120 spin_lock(&filp->f_dentry->d_lock); 121 if (!fid->rdir) { 122 rdir->buf = (uint8_t *)rdir + sizeof(struct p9_rdir); 123 mutex_init(&rdir->mutex); 124 rdir->head = rdir->tail = 0; 125 fid->rdir = (void *) rdir; 126 rdir = NULL; 127 } 128 spin_unlock(&filp->f_dentry->d_lock); 129 kfree(rdir); 130 } 131 rdir = (struct p9_rdir *) fid->rdir; 132 133 err = mutex_lock_interruptible(&rdir->mutex); 134 if (err) 135 return err; 136 while (err == 0) { 137 if (rdir->tail == rdir->head) { 138 err = v9fs_file_readn(filp, rdir->buf, NULL, 139 buflen, filp->f_pos); 140 if (err <= 0) 141 goto unlock_and_exit; 142 143 rdir->head = 0; 144 rdir->tail = err; 145 } 146 while (rdir->head < rdir->tail) { 147 p9stat_init(&st); 148 err = p9stat_read(rdir->buf + rdir->head, 149 buflen - rdir->head, &st, 150 fid->clnt->proto_version); 151 if (err) { 152 P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); 153 err = -EIO; 154 p9stat_free(&st); 155 goto unlock_and_exit; 156 } 157 reclen = st.size+2; 158 159 over = filldir(dirent, st.name, strlen(st.name), 160 filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st)); 161 162 p9stat_free(&st); 163 164 if (over) { 165 err = 0; 166 goto unlock_and_exit; 167 } 168 rdir->head += reclen; 169 filp->f_pos += reclen; 170 } 171 } 172 173 unlock_and_exit: 174 mutex_unlock(&rdir->mutex); 175 exit: 176 return err; 177 } 178 179 180 /** 181 * v9fs_dir_release - close a directory 182 * @inode: inode of the directory 183 * @filp: file pointer to a directory 184 * 185 */ 186 187 int v9fs_dir_release(struct inode *inode, struct file *filp) 188 { 189 struct p9_fid *fid; 190 191 fid = filp->private_data; 192 P9_DPRINTK(P9_DEBUG_VFS, 193 "inode: %p filp: %p fid: %d\n", inode, filp, fid->fid); 194 filemap_write_and_wait(inode->i_mapping); 195 p9_client_clunk(fid); 196 return 0; 197 } 198 199 const struct file_operations v9fs_dir_operations = { 200 .read = generic_read_dir, 201 .llseek = generic_file_llseek, 202 .readdir = v9fs_dir_readdir, 203 .open = v9fs_file_open, 204 .release = v9fs_dir_release, 205 }; 206 207 const struct file_operations v9fs_dir_operations_dotl = { 208 .read = generic_read_dir, 209 .llseek = generic_file_llseek, 210 .readdir = v9fs_dir_readdir, 211 .open = v9fs_file_open, 212 .release = v9fs_dir_release, 213 }; 214