linux-bk/fs/read_write.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/read_write.c
   3 *
   4 *  Copyright (C) 1991, 1992  Linus Torvalds
   5 */
   6
   7#include <linux/slab.h> 
   8#include <linux/stat.h>
   9#include <linux/fcntl.h>
  10#include <linux/file.h>
  11#include <linux/uio.h>
  12#include <linux/smp_lock.h>
  13#include <linux/dnotify.h>
  14#include <linux/security.h>
  15
  16#include <asm/uaccess.h>
  17
  18struct file_operations generic_ro_fops = {
  19        .llseek         = generic_file_llseek,
  20        .read           = generic_file_read,
  21        .mmap           = generic_file_mmap,
  22        .sendfile       = generic_file_sendfile,
  23};
  24
  25loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
  26{
  27        long long retval;
  28        struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
  29
  30        down(&inode->i_sem);
  31        switch (origin) {
  32                case 2:
  33                        offset += inode->i_size;
  34                        break;
  35                case 1:
  36                        offset += file->f_pos;
  37        }
  38        retval = -EINVAL;
  39        if (offset>=0 && offset<=inode->i_sb->s_maxbytes) {
  40                if (offset != file->f_pos) {
  41                        file->f_pos = offset;
  42                        file->f_version = ++event;
  43                }
  44                retval = offset;
  45        }
  46        up(&inode->i_sem);
  47        return retval;
  48}
  49
  50loff_t remote_llseek(struct file *file, loff_t offset, int origin)
  51{
  52        long long retval;
  53
  54        lock_kernel();
  55        switch (origin) {
  56                case 2:
  57                        offset += file->f_dentry->d_inode->i_size;
  58                        break;
  59                case 1:
  60                        offset += file->f_pos;
  61        }
  62        retval = -EINVAL;
  63        if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) {
  64                if (offset != file->f_pos) {
  65                        file->f_pos = offset;
  66                        file->f_version = ++event;
  67                }
  68                retval = offset;
  69        }
  70        unlock_kernel();
  71        return retval;
  72}
  73
  74loff_t no_llseek(struct file *file, loff_t offset, int origin)
  75{
  76        return -ESPIPE;
  77}
  78
  79loff_t default_llseek(struct file *file, loff_t offset, int origin)
  80{
  81        long long retval;
  82
  83        lock_kernel();
  84        switch (origin) {
  85                case 2:
  86                        offset += file->f_dentry->d_inode->i_size;
  87                        break;
  88                case 1:
  89                        offset += file->f_pos;
  90        }
  91        retval = -EINVAL;
  92        if (offset >= 0) {
  93                if (offset != file->f_pos) {
  94                        file->f_pos = offset;
  95                        file->f_version = ++event;
  96                }
  97                retval = offset;
  98        }
  99        unlock_kernel();
 100        return retval;
 101}
 102
 103static inline loff_t llseek(struct file *file, loff_t offset, int origin)
 104{
 105        loff_t (*fn)(struct file *, loff_t, int);
 106
 107        fn = default_llseek;
 108        if (file->f_op && file->f_op->llseek)
 109                fn = file->f_op->llseek;
 110        return fn(file, offset, origin);
 111}
 112
 113asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
 114{
 115        off_t retval;
 116        struct file * file;
 117
 118        retval = -EBADF;
 119        file = fget(fd);
 120        if (!file)
 121                goto bad;
 122
 123        retval = security_ops->file_llseek(file);
 124        if (retval) {
 125                fput(file);
 126                goto bad;
 127        }
 128
 129        retval = -EINVAL;
 130        if (origin <= 2) {
 131                loff_t res = llseek(file, offset, origin);
 132                retval = res;
 133                if (res != (loff_t)retval)
 134                        retval = -EOVERFLOW;    /* LFS: should only happen on 32 bit platforms */
 135        }
 136        fput(file);
 137bad:
 138        return retval;
 139}
 140
 141#if !defined(__alpha__)
 142asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high,
 143                           unsigned long offset_low, loff_t * result,
 144                           unsigned int origin)
 145{
 146        int retval;
 147        struct file * file;
 148        loff_t offset;
 149
 150        retval = -EBADF;
 151        file = fget(fd);
 152        if (!file)
 153                goto bad;
 154
 155        retval = security_ops->file_llseek(file);
 156        if (retval)
 157                goto out_putf;
 158
 159        retval = -EINVAL;
 160        if (origin > 2)
 161                goto out_putf;
 162
 163        offset = llseek(file, ((loff_t) offset_high << 32) | offset_low,
 164                        origin);
 165
 166        retval = (int)offset;
 167        if (offset >= 0) {
 168                retval = -EFAULT;
 169                if (!copy_to_user(result, &offset, sizeof(offset)))
 170                        retval = 0;
 171        }
 172out_putf:
 173        fput(file);
 174bad:
 175        return retval;
 176}
 177#endif
 178
 179ssize_t vfs_read(struct file *file, char *buf, size_t count, loff_t *pos)
 180{
 181        struct inode *inode = file->f_dentry->d_inode;
 182        ssize_t ret;
 183
 184        if (!(file->f_mode & FMODE_READ))
 185                return -EBADF;
 186        if (!file->f_op || !file->f_op->read)
 187                return -EINVAL;
 188
 189        ret = locks_verify_area(FLOCK_VERIFY_READ, inode, file, *pos, count);
 190        if (!ret) {
 191                ret = security_ops->file_permission (file, MAY_READ);
 192                if (!ret) {
 193                        ret = file->f_op->read(file, buf, count, pos);
 194                        if (ret > 0)
 195                                dnotify_parent(file->f_dentry, DN_ACCESS);
 196                }
 197        }
 198
 199        return ret;
 200}
 201
 202ssize_t vfs_write(struct file *file, const char *buf, size_t count, loff_t *pos)
 203{
 204        struct inode *inode = file->f_dentry->d_inode;
 205        ssize_t ret;
 206
 207        if (!(file->f_mode & FMODE_WRITE))
 208                return -EBADF;
 209        if (!file->f_op || !file->f_op->write)
 210                return -EINVAL;
 211
 212        ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, *pos, count);
 213        if (!ret) {
 214                ret = security_ops->file_permission (file, MAY_WRITE);
 215                if (!ret) {
 216                        ret = file->f_op->write(file, buf, count, pos);
 217                        if (ret > 0)
 218                                dnotify_parent(file->f_dentry, DN_MODIFY);
 219                }
 220        }
 221
 222        return ret;
 223}
 224
 225asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count)
 226{
 227        struct file *file;
 228        ssize_t ret = -EBADF;
 229
 230        file = fget(fd);
 231        if (file) {
 232                ret = vfs_read(file, buf, count, &file->f_pos);
 233                fput(file);
 234        }
 235
 236        return ret;
 237}
 238
 239asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
 240{
 241        struct file *file;
 242        ssize_t ret = -EBADF;
 243
 244        file = fget(fd);
 245        if (file) {
 246                ret = vfs_write(file, buf, count, &file->f_pos);
 247                fput(file);
 248        }
 249
 250        return ret;
 251}
 252
 253asmlinkage ssize_t sys_pread64(unsigned int fd, char *buf,
 254                             size_t count, loff_t pos)
 255{
 256        struct file *file;
 257        ssize_t ret = -EBADF;
 258
 259        if (pos < 0)
 260                return -EINVAL;
 261
 262        file = fget(fd);
 263        if (file) {
 264                ret = vfs_read(file, buf, count, &pos);
 265                fput(file);
 266        }
 267
 268        return ret;
 269}
 270
 271asmlinkage ssize_t sys_pwrite64(unsigned int fd, const char *buf,
 272                              size_t count, loff_t pos)
 273{
 274        struct file *file;
 275        ssize_t ret = -EBADF;
 276
 277        if (pos < 0)
 278                return -EINVAL;
 279
 280        file = fget(fd);
 281        if (file) {
 282                ret = vfs_write(file, buf, count, &pos);
 283                fput(file);
 284        }
 285
 286        return ret;
 287}
 288
 289/*
 290 * Reduce an iovec's length in-place.  Return the resulting number of segments
 291 */
 292unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to)
 293{
 294        unsigned long seg = 0;
 295        size_t len = 0;
 296
 297        while (seg < nr_segs) {
 298                seg++;
 299                if (len + iov->iov_len >= to) {
 300                        iov->iov_len = to - len;
 301                        break;
 302                }
 303                len += iov->iov_len;
 304                iov++;
 305        }
 306        return seg;
 307}
 308
 309static ssize_t do_readv_writev(int type, struct file *file,
 310                               const struct iovec * vector,
 311                               unsigned long nr_segs)
 312{
 313        typedef ssize_t (*io_fn_t)(struct file *, char *, size_t, loff_t *);
 314        typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *);
 315
 316        size_t tot_len;
 317        struct iovec iovstack[UIO_FASTIOV];
 318        struct iovec *iov=iovstack;
 319        ssize_t ret = -EINVAL;
 320        int seg;
 321        io_fn_t fn;
 322        iov_fn_t fnv;
 323        struct inode *inode;
 324
 325        /*
 326         * SuS says "The readv() function *may* fail if the iovcnt argument
 327         * was less than or equal to 0, or greater than {IOV_MAX}.  Linux has
 328         * traditionally returned -EINVAL for zero segments, so...
 329         */
 330        if (nr_segs == 0)
 331                goto out;
 332
 333        /*
 334         * First get the "struct iovec" from user memory and
 335         * verify all the pointers
 336         */
 337        if ((nr_segs > UIO_MAXIOV) || (nr_segs <= 0))
 338                goto out;
 339        if (!file->f_op)
 340                goto out;
 341        if (nr_segs > UIO_FASTIOV) {
 342                ret = -ENOMEM;
 343                iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
 344                if (!iov)
 345                        goto out;
 346        }
 347        ret = -EFAULT;
 348        if (copy_from_user(iov, vector, nr_segs*sizeof(*vector)))
 349                goto out;
 350
 351        /*
 352         * Single unix specification:
 353         * We should -EINVAL if an element length is not >= 0 and fitting an
 354         * ssize_t.  The total length is fitting an ssize_t
 355         *
 356         * Be careful here because iov_len is a size_t not an ssize_t
 357         */
 358        tot_len = 0;
 359        ret = -EINVAL;
 360        for (seg = 0 ; seg < nr_segs; seg++) {
 361                ssize_t tmp = tot_len;
 362                ssize_t len = (ssize_t)iov[seg].iov_len;
 363                if (len < 0)    /* size_t not fitting an ssize_t .. */
 364                        goto out;
 365                tot_len += len;
 366                if (tot_len < tmp) /* maths overflow on the ssize_t */
 367                        goto out;
 368        }
 369        if (tot_len == 0) {
 370                ret = 0;
 371                goto out;
 372        }
 373
 374        inode = file->f_dentry->d_inode;
 375        /* VERIFY_WRITE actually means a read, as we write to user space */
 376        ret = locks_verify_area((type == READ 
 377                                 ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
 378                                inode, file, file->f_pos, tot_len);
 379        if (ret)
 380                goto out;
 381
 382        fnv = NULL;
 383        if (type == READ) {
 384                fn = file->f_op->read;
 385                fnv = file->f_op->readv;
 386        } else {
 387                fn = (io_fn_t)file->f_op->write;
 388                fnv = file->f_op->writev;
 389        }
 390        if (fnv) {
 391                ret = fnv(file, iov, nr_segs, &file->f_pos);
 392                goto out;
 393        }
 394
 395        /* Do it by hand, with file-ops */
 396        ret = 0;
 397        vector = iov;
 398        while (nr_segs > 0) {
 399                void * base;
 400                size_t len;
 401                ssize_t nr;
 402
 403                base = vector->iov_base;
 404                len = vector->iov_len;
 405                vector++;
 406                nr_segs--;
 407
 408                nr = fn(file, base, len, &file->f_pos);
 409
 410                if (nr < 0) {
 411                        if (!ret) ret = nr;
 412                        break;
 413                }
 414                ret += nr;
 415                if (nr != len)
 416                        break;
 417        }
 418out:
 419        if (iov != iovstack)
 420                kfree(iov);
 421        if ((ret + (type == READ)) > 0)
 422                dnotify_parent(file->f_dentry,
 423                                (type == READ) ? DN_MODIFY : DN_ACCESS);
 424        return ret;
 425}
 426
 427
 428asmlinkage ssize_t
 429sys_readv(unsigned long fd, const struct iovec *vector, unsigned long nr_segs)
 430{
 431        struct file * file;
 432        ssize_t ret;
 433
 434
 435        ret = -EBADF;
 436        file = fget(fd);
 437        if (!file)
 438                goto bad_file;
 439        if (file->f_op && (file->f_mode & FMODE_READ) &&
 440            (file->f_op->readv || file->f_op->read)) {
 441                ret = security_ops->file_permission (file, MAY_READ);
 442                if (!ret)
 443                        ret = do_readv_writev(READ, file, vector, nr_segs);
 444        }
 445        fput(file);
 446
 447bad_file:
 448        return ret;
 449}
 450
 451asmlinkage ssize_t
 452sys_writev(unsigned long fd, const struct iovec * vector, unsigned long nr_segs)
 453{
 454        struct file * file;
 455        ssize_t ret;
 456
 457
 458        ret = -EBADF;
 459        file = fget(fd);
 460        if (!file)
 461                goto bad_file;
 462        if (file->f_op && (file->f_mode & FMODE_WRITE) &&
 463            (file->f_op->writev || file->f_op->write)) {
 464                ret = security_ops->file_permission (file, MAY_WRITE);
 465                if (!ret)
 466                        ret = do_readv_writev(WRITE, file, vector, nr_segs);
 467        }
 468        fput(file);
 469
 470bad_file:
 471        return ret;
 472}
 473
 474static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
 475                           size_t count, loff_t max)
 476{
 477        struct file * in_file, * out_file;
 478        struct inode * in_inode, * out_inode;
 479        loff_t pos;
 480        ssize_t retval;
 481
 482        /*
 483         * Get input file, and verify that it is ok..
 484         */
 485        retval = -EBADF;
 486        in_file = fget(in_fd);
 487        if (!in_file)
 488                goto out;
 489        if (!(in_file->f_mode & FMODE_READ))
 490                goto fput_in;
 491        retval = -EINVAL;
 492        in_inode = in_file->f_dentry->d_inode;
 493        if (!in_inode)
 494                goto fput_in;
 495        if (!in_file->f_op || !in_file->f_op->sendfile)
 496                goto fput_in;
 497        retval = locks_verify_area(FLOCK_VERIFY_READ, in_inode, in_file, in_file->f_pos, count);
 498        if (retval)
 499                goto fput_in;
 500
 501        /*
 502         * Get output file, and verify that it is ok..
 503         */
 504        retval = -EBADF;
 505        out_file = fget(out_fd);
 506        if (!out_file)
 507                goto fput_in;
 508        if (!(out_file->f_mode & FMODE_WRITE))
 509                goto fput_out;
 510        retval = -EINVAL;
 511        if (!out_file->f_op || !out_file->f_op->sendpage)
 512                goto fput_out;
 513        out_inode = out_file->f_dentry->d_inode;
 514        retval = locks_verify_area(FLOCK_VERIFY_WRITE, out_inode, out_file, out_file->f_pos, count);
 515        if (retval)
 516                goto fput_out;
 517
 518        if (!ppos)
 519                ppos = &in_file->f_pos;
 520
 521        if (!max)
 522                max = min(in_inode->i_sb->s_maxbytes, out_inode->i_sb->s_maxbytes);
 523
 524        pos = *ppos;
 525        retval = -EINVAL;
 526        if (unlikely(pos < 0))
 527                goto fput_out;
 528        if (unlikely(pos + count > max)) {
 529                retval = -EOVERFLOW;
 530                if (pos >= max)
 531                        goto fput_out;
 532                count = max - pos;
 533        }
 534
 535        retval = in_file->f_op->sendfile(out_file, in_file, ppos, count);
 536
 537        if (*ppos > max)
 538                retval = -EOVERFLOW;
 539
 540fput_out:
 541        fput(out_file);
 542fput_in:
 543        fput(in_file);
 544out:
 545        return retval;
 546}
 547
 548asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
 549{
 550        loff_t pos;
 551        off_t off;
 552        ssize_t ret;
 553
 554        if (offset) {
 555                if (unlikely(get_user(off, offset)))
 556                        return -EFAULT;
 557                pos = off;
 558                ret = do_sendfile(out_fd, in_fd, &pos, count, MAX_NON_LFS);
 559                if (unlikely(put_user(pos, offset)))
 560                        return -EFAULT;
 561                return ret;
 562        }
 563
 564        return do_sendfile(out_fd, in_fd, NULL, count, MAX_NON_LFS);
 565}
 566
 567asmlinkage ssize_t sys_sendfile64(int out_fd, int in_fd, loff_t *offset, size_t count)
 568{
 569        loff_t pos;
 570        ssize_t ret;
 571
 572        if (offset) {
 573                if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t))))
 574                        return -EFAULT;
 575                ret = do_sendfile(out_fd, in_fd, &pos, count, 0);
 576                if (unlikely(put_user(pos, offset)))
 577                        return -EFAULT;
 578                return ret;
 579        }
 580
 581        return do_sendfile(out_fd, in_fd, NULL, count, 0);
 582}
 583
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.