1 /* 2 * linux/fs/9p/vfs_file.c 3 * 4 * This file contians vfs file ops for 9P2000. 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 as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to: 21 * Free Software Foundation 22 * 51 Franklin Street, Fifth Floor 23 * Boston, MA 02111-1301 USA 24 * 25 */ 26 27 #include <linux/module.h> 28 #include <linux/errno.h> 29 #include <linux/fs.h> 30 #include <linux/file.h> 31 #include <linux/stat.h> 32 #include <linux/string.h> 33 #include <linux/smp_lock.h> 34 #include <linux/inet.h> 35 #include <linux/list.h> 36 #include <asm/uaccess.h> 37 #include <linux/idr.h> 38 39 #include "debug.h" 40 #include "v9fs.h" 41 #include "9p.h" 42 #include "v9fs_vfs.h" 43 #include "fid.h" 44 45 /** 46 * v9fs_file_open - open a file (or directory) 47 * @inode: inode to be opened 48 * @file: file being opened 49 * 50 */ 51 52 int v9fs_file_open(struct inode *inode, struct file *file) 53 { 54 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); 55 struct v9fs_fid *v9fid, *fid; 56 struct v9fs_fcall *fcall = NULL; 57 int open_mode = 0; 58 unsigned int iounit = 0; 59 int newfid = -1; 60 long result = -1; 61 62 dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file); 63 64 v9fid = v9fs_fid_get_created(file->f_dentry); 65 if (!v9fid) 66 v9fid = v9fs_fid_lookup(file->f_dentry); 67 68 if (!v9fid) { 69 dprintk(DEBUG_ERROR, "Couldn't resolve fid from dentry\n"); 70 return -EBADF; 71 } 72 73 if (!v9fid->fidcreate) { 74 fid = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL); 75 if (fid == NULL) { 76 dprintk(DEBUG_ERROR, "Out of Memory\n"); 77 return -ENOMEM; 78 } 79 80 fid->fidopen = 0; 81 fid->fidcreate = 0; 82 fid->fidclunked = 0; 83 fid->iounit = 0; 84 fid->v9ses = v9ses; 85 86 newfid = v9fs_get_idpool(&v9ses->fidpool); 87 if (newfid < 0) { 88 eprintk(KERN_WARNING, "newfid fails!\n"); 89 return -ENOSPC; 90 } 91 92 result = 93 v9fs_t_walk(v9ses, v9fid->fid, newfid, NULL, NULL); 94 95 if (result < 0) { 96 v9fs_put_idpool(newfid, &v9ses->fidpool); 97 dprintk(DEBUG_ERROR, "rewalk didn't work\n"); 98 return -EBADF; 99 } 100 101 fid->fid = newfid; 102 v9fid = fid; 103 /* TODO: do special things for O_EXCL, O_NOFOLLOW, O_SYNC */ 104 /* translate open mode appropriately */ 105 open_mode = file->f_flags & 0x3; 106 107 if (file->f_flags & O_EXCL) 108 open_mode |= V9FS_OEXCL; 109 110 if (v9ses->extended) { 111 if (file->f_flags & O_TRUNC) 112 open_mode |= V9FS_OTRUNC; 113 114 if (file->f_flags & O_APPEND) 115 open_mode |= V9FS_OAPPEND; 116 } 117 118 result = v9fs_t_open(v9ses, newfid, open_mode, &fcall); 119 if (result < 0) { 120 dprintk(DEBUG_ERROR, 121 "open failed, open_mode 0x%x: %s\n", open_mode, 122 FCALL_ERROR(fcall)); 123 kfree(fcall); 124 return result; 125 } 126 127 iounit = fcall->params.ropen.iounit; 128 kfree(fcall); 129 } else { 130 /* create case */ 131 newfid = v9fid->fid; 132 iounit = v9fid->iounit; 133 v9fid->fidcreate = 0; 134 } 135 136 file->private_data = v9fid; 137 138 v9fid->rdir_pos = 0; 139 v9fid->rdir_fcall = NULL; 140 v9fid->fidopen = 1; 141 v9fid->filp = file; 142 v9fid->iounit = iounit; 143 144 return 0; 145 } 146 147 /** 148 * v9fs_file_lock - lock a file (or directory) 149 * @inode: inode to be opened 150 * @file: file being opened 151 * 152 * XXX - this looks like a local only lock, we should extend into 9P 153 * by using open exclusive 154 */ 155 156 static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl) 157 { 158 int res = 0; 159 struct inode *inode = filp->f_dentry->d_inode; 160 161 dprintk(DEBUG_VFS, "filp: %p lock: %p\n", filp, fl); 162 163 /* No mandatory locks */ 164 if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) 165 return -ENOLCK; 166 167 if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { 168 filemap_fdatawrite(inode->i_mapping); 169 filemap_fdatawait(inode->i_mapping); 170 invalidate_inode_pages(&inode->i_data); 171 } 172 173 return res; 174 } 175 176 /** 177 * v9fs_file_read - read from a file 178 * @filep: file pointer to read 179 * @data: data buffer to read data into 180 * @count: size of buffer 181 * @offset: offset at which to read data 182 * 183 */ 184 static ssize_t 185 v9fs_file_read(struct file *filp, char __user * data, size_t count, 186 loff_t * offset) 187 { 188 struct inode *inode = filp->f_dentry->d_inode; 189 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); 190 struct v9fs_fid *v9f = filp->private_data; 191 struct v9fs_fcall *fcall = NULL; 192 int fid = v9f->fid; 193 int rsize = 0; 194 int result = 0; 195 int total = 0; 196 int n; 197 198 dprintk(DEBUG_VFS, "\n"); 199 200 rsize = v9ses->maxdata - V9FS_IOHDRSZ; 201 if (v9f->iounit != 0 && rsize > v9f->iounit) 202 rsize = v9f->iounit; 203 204 do { 205 if (count < rsize) 206 rsize = count; 207 208 result = v9fs_t_read(v9ses, fid, *offset, rsize, &fcall); 209 210 if (result < 0) { 211 printk(KERN_ERR "9P2000: v9fs_t_read returned %d\n", 212 result); 213 214 kfree(fcall); 215 return total; 216 } else 217 *offset += result; 218 219 n = copy_to_user(data, fcall->params.rread.data, result); 220 if (n) { 221 dprintk(DEBUG_ERROR, "Problem copying to user %d\n", n); 222 kfree(fcall); 223 return -EFAULT; 224 } 225 226 count -= result; 227 data += result; 228 total += result; 229 230 kfree(fcall); 231 232 if (result < rsize) 233 break; 234 } while (count); 235 236 return total; 237 } 238 239 /** 240 * v9fs_file_write - write to a file 241 * @filep: file pointer to write 242 * @data: data buffer to write data from 243 * @count: size of buffer 244 * @offset: offset at which to write data 245 * 246 */ 247 248 static ssize_t 249 v9fs_file_write(struct file *filp, const char __user * data, 250 size_t count, loff_t * offset) 251 { 252 struct inode *inode = filp->f_dentry->d_inode; 253 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); 254 struct v9fs_fid *v9fid = filp->private_data; 255 struct v9fs_fcall *fcall; 256 int fid = v9fid->fid; 257 int result = -EIO; 258 int rsize = 0; 259 int total = 0; 260 char *buf; 261 262 dprintk(DEBUG_VFS, "data %p count %d offset %x\n", data, (int)count, 263 (int)*offset); 264 rsize = v9ses->maxdata - V9FS_IOHDRSZ; 265 if (v9fid->iounit != 0 && rsize > v9fid->iounit) 266 rsize = v9fid->iounit; 267 268 buf = kmalloc(v9ses->maxdata - V9FS_IOHDRSZ, GFP_KERNEL); 269 if (!buf) 270 return -ENOMEM; 271 272 do { 273 if (count < rsize) 274 rsize = count; 275 276 result = copy_from_user(buf, data, rsize); 277 if (result) { 278 dprintk(DEBUG_ERROR, "Problem copying from user\n"); 279 kfree(buf); 280 return -EFAULT; 281 } 282 283 dump_data(buf, rsize); 284 result = v9fs_t_write(v9ses, fid, *offset, rsize, buf, &fcall); 285 if (result < 0) { 286 eprintk(KERN_ERR, "error while writing: %s(%d)\n", 287 FCALL_ERROR(fcall), result); 288 kfree(fcall); 289 kfree(buf); 290 return result; 291 } else 292 *offset += result; 293 294 kfree(fcall); 295 fcall = NULL; 296 297 if (result != rsize) { 298 eprintk(KERN_ERR, 299 "short write: v9fs_t_write returned %d\n", 300 result); 301 break; 302 } 303 304 count -= result; 305 data += result; 306 total += result; 307 } while (count); 308 309 kfree(buf); 310 return total; 311 } 312 313 struct file_operations v9fs_file_operations = { 314 .llseek = generic_file_llseek, 315 .read = v9fs_file_read, 316 .write = v9fs_file_write, 317 .open = v9fs_file_open, 318 .release = v9fs_dir_release, 319 .lock = v9fs_file_lock, 320 }; 321