1/* 2 * linux/fs/read_write.c 3 * 4 * Copyright (C) 1991, 1992 Linus Torvalds 5 * Minor pieces Copyright (C) 2002 Red Hat Inc, All Rights Reserved 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22#include <linux/slab.h> 23#include <linux/stat.h> 24#include <linux/fcntl.h> 25#include <linux/file.h> 26#include <linux/uio.h> 27#include <linux/smp_lock.h> 28#include <linux/dnotify.h> 29 30#include <asm/uaccess.h> 31 32struct file_operations generic_ro_fops = { 33 llseek: generic_file_llseek, 34 read: generic_file_read, 35 mmap: generic_file_mmap, 36}; 37 38ssize_t generic_read_dir(struct file *filp, char *buf, size_t siz, loff_t *ppos) 39{ 40 return -EISDIR; 41} 42 43loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) 44{ 45 long long retval; 46 47 switch (origin) { 48 case 2: 49 offset += file->f_dentry->d_inode->i_size; 50 break; 51 case 1: 52 offset += file->f_pos; 53 } 54 retval = -EINVAL; 55 if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) { 56 if (offset != file->f_pos) { 57 file->f_pos = offset; 58 file->f_reada = 0; 59 file->f_version = ++event; 60 } 61 retval = offset; 62 } 63 return retval; 64} 65 66loff_t no_llseek(struct file *file, loff_t offset, int origin) 67{ 68 return -ESPIPE; 69} 70 71loff_t default_llseek(struct file *file, loff_t offset, int origin) 72{ 73 long long retval; 74 75 switch (origin) { 76 case 2: 77 offset += file->f_dentry->d_inode->i_size; 78 break; 79 case 1: 80 offset += file->f_pos; 81 } 82 retval = -EINVAL; 83 if (offset >= 0) { 84 if (offset != file->f_pos) { 85 file->f_pos = offset; 86 file->f_reada = 0; 87 file->f_version = ++event; 88 } 89 retval = offset; 90 } 91 return retval; 92} 93 94static inline loff_t llseek(struct file *file, loff_t offset, int origin) 95{ 96 loff_t (*fn)(struct file *, loff_t, int); 97 loff_t retval; 98 99 fn = default_llseek; 100 if (file->f_op && file->f_op->llseek) 101 fn = file->f_op->llseek; 102 lock_kernel(); 103 retval = fn(file, offset, origin); 104 unlock_kernel(); 105 return retval; 106} 107 108asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin) 109{ 110 off_t retval; 111 struct file * file; 112 113 retval = -EBADF; 114 file = fget(fd); 115 if (!file) 116 goto bad; 117 retval = -EINVAL; 118 if (origin <= 2) { 119 loff_t res = llseek(file, offset, origin); 120 retval = res; 121 if (res != (loff_t)retval) 122 retval = -EOVERFLOW; /* LFS: should only happen on 32 bit platforms */ 123 } 124 fput(file); 125bad: 126 return retval; 127} 128 129#if !defined(__alpha__) 130asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high, 131 unsigned long offset_low, loff_t * result, 132 unsigned int origin) 133{ 134 int retval; 135 struct file * file; 136 loff_t offset; 137 138 retval = -EBADF; 139 file = fget(fd); 140 if (!file) 141 goto bad; 142 retval = -EINVAL; 143 if (origin > 2) 144 goto out_putf; 145 146 offset = llseek(file, ((loff_t) offset_high << 32) | offset_low, 147 origin); 148 149 retval = (int)offset; 150 if (offset >= 0) { 151 retval = -EFAULT; 152 if (!copy_to_user(result, &offset, sizeof(offset))) 153 retval = 0; 154 } 155out_putf: 156 fput(file); 157bad: 158 return retval; 159} 160#endif 161 162asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count) 163{ 164 ssize_t ret; 165 struct file * file; 166 167 ret = -EBADF; 168 file = fget(fd); 169 if (file) { 170 if (file->f_mode & FMODE_READ) { 171 ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode, 172 file, file->f_pos, count); 173 if (!ret) { 174 ssize_t (*read)(struct file *, char *, size_t, loff_t *); 175 ret = -EINVAL; 176 if (file->f_op && (read = file->f_op->read) != NULL) 177 ret = read(file, buf, count, &file->f_pos); 178 } 179 } 180 if (ret > 0) 181 dnotify_parent(file->f_dentry, DN_ACCESS); 182 fput(file); 183 } 184 return ret; 185} 186 187asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count) 188{ 189 ssize_t ret; 190 struct file * file; 191 192 ret = -EBADF; 193 file = fget(fd); 194 if (file) { 195 if (file->f_mode & FMODE_WRITE) { 196 struct inode *inode = file->f_dentry->d_inode; 197 ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, 198 file->f_pos, count); 199 if (!ret) { 200 ssize_t (*write)(struct file *, const char *, size_t, loff_t *); 201 ret = -EINVAL; 202 if (file->f_op && (write = file->f_op->write) != NULL) 203 ret = write(file, buf, count, &file->f_pos); 204 } 205 } 206 if (ret > 0) 207 dnotify_parent(file->f_dentry, DN_MODIFY); 208 fput(file); 209 } 210 return ret; 211} 212 213 214static ssize_t do_readv_writev(int type, struct file *file, 215 const struct iovec * vector, 216 unsigned long count) 217{ 218 typedef ssize_t (*io_fn_t)(struct file *, char *, size_t, loff_t *); 219 typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *); 220 221 ssize_t tot_len; 222 struct iovec iovstack[UIO_FASTIOV]; 223 struct iovec *iov=iovstack; 224 ssize_t ret, i; 225 io_fn_t fn; 226 iov_fn_t fnv; 227 struct inode *inode; 228 229 /* 230 * First get the "struct iovec" from user memory and 231 * verify all the pointers 232 */ 233 ret = 0; 234 if (!count) 235 goto out_nofree; 236 ret = -EINVAL; 237 if (count > UIO_MAXIOV) 238 goto out_nofree; 239 if (!file->f_op) 240 goto out_nofree; 241 if (count > UIO_FASTIOV) { 242 ret = -ENOMEM; 243 iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); 244 if (!iov) 245 goto out_nofree; 246 } 247 ret = -EFAULT; 248 if (copy_from_user(iov, vector, count*sizeof(*vector))) 249 goto out; 250 251 /* 252 * Single unix specification: 253 * We should -EINVAL if an element length is not >= 0 and fitting an ssize_t 254 * The total length is fitting an ssize_t 255 * 256 * Be careful here because iov_len is a size_t not an ssize_t 257 */ 258 259 tot_len = 0; 260 ret = -EINVAL; 261 for (i = 0 ; i < count ; i++) { 262 ssize_t tmp = tot_len; 263 ssize_t len = (ssize_t) iov[i].iov_len; 264 if (len < 0) /* size_t not fitting an ssize_t .. */ 265 goto out; 266 tot_len += len; 267 if (tot_len < tmp) /* maths overflow on the ssize_t */ 268 goto out; 269 } 270 271 inode = file->f_dentry->d_inode; 272 /* VERIFY_WRITE actually means a read, as we write to user space */ 273 ret = locks_verify_area((type == VERIFY_WRITE 274 ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), 275 inode, file, file->f_pos, tot_len); 276 if (ret) goto out; 277 278 fnv = (type == VERIFY_WRITE ? file->f_op->readv : file->f_op->writev); 279 if (fnv) { 280 ret = fnv(file, iov, count, &file->f_pos); 281 goto out; 282 } 283 284 /* VERIFY_WRITE actually means a read, as we write to user space */ 285 fn = (type == VERIFY_WRITE ? file->f_op->read : 286 (io_fn_t) file->f_op->write); 287 288 ret = 0; 289 vector = iov; 290 while (count > 0) { 291 void * base; 292 size_t len; 293 ssize_t nr; 294 295 base = vector->iov_base; 296 len = vector->iov_len; 297 vector++; 298 count--; 299 300 nr = fn(file, base, len, &file->f_pos); 301 302 if (nr < 0) { 303 if (!ret) ret = nr; 304 break; 305 } 306 ret += nr; 307 if (nr != len) 308 break; 309 } 310 311out: 312 if (iov != iovstack) 313 kfree(iov); 314out_nofree: 315 /* VERIFY_WRITE actually means a read, as we write to user space */ 316 if ((ret + (type == VERIFY_WRITE)) > 0) 317 dnotify_parent(file->f_dentry, 318 (type == VERIFY_WRITE) ? DN_MODIFY : DN_ACCESS); 319 return ret; 320} 321 322asmlinkage ssize_t sys_readv(unsigned long fd, const struct iovec * vector, 323 unsigned long count) 324{ 325 struct file * file; 326 ssize_t ret; 327 328 329 ret = -EBADF; 330 file = fget(fd); 331 if (!file) 332 goto bad_file; 333 if (file->f_op && (file->f_mode & FMODE_READ) && 334 (file->f_op->readv || file->f_op->read)) 335 ret = do_readv_writev(VERIFY_WRITE, file, vector, count); 336 fput(file); 337 338bad_file: 339 return ret; 340} 341 342asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector, 343 unsigned long count) 344{ 345 struct file * file; 346 ssize_t ret; 347 348 349 ret = -EBADF; 350 file = fget(fd); 351 if (!file) 352 goto bad_file; 353 if (file->f_op && (file->f_mode & FMODE_WRITE) && 354 (file->f_op->writev || file->f_op->write)) 355 ret = do_readv_writev(VERIFY_READ, file, vector, count); 356 fput(file); 357 358bad_file: 359 return ret; 360} 361 362/* From the Single Unix Spec: pread & pwrite act like lseek to pos + op + 363 lseek back to original location. They fail just like lseek does on 364 non-seekable files. */ 365 366asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, 367 size_t count, loff_t pos) 368{ 369 ssize_t ret; 370 struct file * file; 371 ssize_t (*read)(struct file *, char *, size_t, loff_t *); 372 373 ret = -EBADF; 374 file = fget(fd); 375 if (!file) 376 goto bad_file; 377 if (!(file->f_mode & FMODE_READ)) 378 goto out; 379 ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode, 380 file, pos, count); 381 if (ret) 382 goto out; 383 ret = -EINVAL; 384 if (!file->f_op || !(read = file->f_op->read)) 385 goto out; 386 if (pos < 0) 387 goto out; 388 ret = read(file, buf, count, &pos); 389 if (ret > 0) 390 dnotify_parent(file->f_dentry, DN_ACCESS); 391out: 392 fput(file); 393bad_file: 394 return ret; 395} 396 397asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, 398 size_t count, loff_t pos) 399{ 400 ssize_t ret; 401 struct file * file; 402 ssize_t (*write)(struct file *, const char *, size_t, loff_t *); 403 404 ret = -EBADF; 405 file = fget(fd); 406 if (!file) 407 goto bad_file; 408 if (!(file->f_mode & FMODE_WRITE)) 409 goto out; 410 ret = locks_verify_area(FLOCK_VERIFY_WRITE, file->f_dentry->d_inode, 411 file, pos, count); 412 if (ret) 413 goto out; 414 ret = -EINVAL; 415 if (!file->f_op || !(write = file->f_op->write)) 416 goto out; 417 if (pos < 0) 418 goto out; 419 420 ret = write(file, buf, count, &pos); 421 if (ret > 0) 422 dnotify_parent(file->f_dentry, DN_MODIFY); 423out: 424 fput(file); 425bad_file: 426 return ret; 427} 428