linux-old/fs/read_write.c
<<
>>
Prefs
   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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.