title: need help understanding the read part of read_write.c
body: <p>Hi I was hoping someone might help me understand the read section of the <strong>read_write.c</strong> kernel file when I look at it I don't really understand a thing.</p>
  <p>I can't really tell which part is actually reading the file considering there are several instances where read functions are called. I ask because I have to know where to modify it and how for an assignment I have where I have to modify the output of the read without actually modifying the file.</p>
  <p>By the way I am using the latest version of the Linux kernel from kernel.org version 4.9 any and all help is appreciated thank you. Below is where I believe the read is happening.</p>
  <pre><code>typedef ssize_t (*iter_fn_t)(struct kiocb *, struct iov_iter *);
  
  const struct file_operations generic_ro_fops = {
  .llseek     = generic_file_llseek,
  .read_iter  = generic_file_read_iter,
  .mmap       = generic_file_readonly_mmap,
  .splice_read    = generic_file_splice_read,
  };
  
  static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter   *iter,
      loff_t *ppos, iter_fn_t fn, int flags)
  {
      struct kiocb kiocb;
      ssize_t ret;
  
      if (flags &amp; ~(RWF_HIPRI | RWF_DSYNC | RWF_SYNC))
          return -EOPNOTSUPP;
  
          init_sync_kiocb(&amp;kiocb, filp);
      if (flags &amp; RWF_HIPRI)
          kiocb.ki_flags |= IOCB_HIPRI;
      if (flags &amp; RWF_DSYNC)
          kiocb.ki_flags |= IOCB_DSYNC;
      if (flags &amp; RWF_SYNC)
          kiocb.ki_flags |= (IOCB_DSYNC | IOCB_SYNC);
          kiocb.ki_pos = *ppos;
  
      ret = fn(&amp;kiocb, iter);
      BUG_ON(ret == -EIOCBQUEUED);
      *ppos = kiocb.ki_pos;
      return ret;
  }
  
  ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
                unsigned long nr_segs, unsigned long fast_segs,
                struct iovec *fast_pointer,
                struct iovec **ret_pointer)
  {
      unsigned long seg;
      ssize_t ret;
      struct iovec *iov = fast_pointer;
  
      /*
       * SuS says &quot;The readv() function *may* fail if the iovcnt argument
       * was less than or equal to 0, or greater than {IOV_MAX}.  Linux has
       * traditionally returned zero for zero segments, so...
       */
      if (nr_segs == 0) {
          ret = 0;
          goto out;
      }
  
      /*
       * First get the &quot;struct iovec&quot; from user memory and
       * verify all the pointers
       */
      if (nr_segs &gt; UIO_MAXIOV) {
          ret = -EINVAL;
          goto out;
      }
      if (nr_segs &gt; fast_segs) {
          iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
          printk(KERN_DEBUG &quot;Hello from read_write.c\n&quot;);
          printk(KERN_DEBUG &quot;Inside the copy check uvector method\n&quot;);
          if (iov == NULL) {
              ret = -ENOMEM;
              goto out;
      }
      }
      if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
          ret = -EFAULT;
          goto out;
      }
  
      /*
       * According to the Single Unix Specification we should return EINVAL
       * if an element length is &lt; 0 when cast to ssize_t or if the
       * total length would overflow the ssize_t return value of the
       * system call.
       *
       * Linux caps all read/write calls to MAX_RW_COUNT, and avoids the
       * overflow case.
       */
       ret = 0;
       for (seg = 0; seg &lt; nr_segs; seg++) {
          void __user *buf = iov[seg].iov_base;
          ssize_t len = (ssize_t)iov[seg].iov_len;
  
          /* see if we we're about to use an invalid len or if
           * it's about to overflow ssize_t */
          if (len &lt; 0) {
              ret = -EINVAL;
              goto out;
          }
          if (type &gt;= 0
              &amp;&amp; unlikely(!access_ok(vrfy_dir(type), buf, len))) {
              ret = -EFAULT;
              goto out;
          }
          if (len &gt; MAX_RW_COUNT - ret) {
              len = MAX_RW_COUNT - ret;
              iov[seg].iov_len = len;
          }
          ret += len;
      }
  out:
      *ret_pointer = iov;
      return ret;
  }
  
  
  /* Do it by hand, with file-ops */
  static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,
      loff_t *ppos, io_fn_t fn, int flags)
  {
      ssize_t ret = 0;
  
      if (flags &amp; ~RWF_HIPRI)
          return -EOPNOTSUPP;
  
      while (iov_iter_count(iter)) {
              struct iovec iovec = iov_iter_iovec(iter);
              ssize_t nr;
  
              nr = fn(filp, iovec.iov_base, iovec.iov_len, ppos);
  
              if (nr &lt; 0) {
                  if (!ret)
                      ret = nr;
                  break;
              }
              ret += nr;
              if (nr != iovec.iov_len)
                  break;
              iov_iter_advance(iter, nr);
          }
  
          return ret;
  }
  
  static ssize_t do_readv_writev(int type, struct file *file,
                                 const struct iovec __user * uvector,
                                 unsigned long nr_segs, loff_t *pos,
                                 int flags)
  {
      size_t tot_len;
      struct iovec iovstack[UIO_FASTIOV];
      struct iovec *iov = iovstack;
      struct iov_iter iter;
      ssize_t ret;
      io_fn_t fn;
      iter_fn_t iter_fn;
  
      ret = import_iovec(type, uvector, nr_segs,
                         ARRAY_SIZE(iovstack), &amp;iov, &amp;iter);
      if (ret &lt; 0)
          return ret;
  
      tot_len = iov_iter_count(&amp;iter);
      if (!tot_len)
          goto out;
      ret = rw_verify_area(type, file, pos, tot_len);
      if (ret &lt; 0)
          goto out;
  
      if (type == READ) {
          fn = file-&gt;f_op-&gt;read;
          iter_fn = file-&gt;f_op-&gt;read_iter;
      } else {
          fn = (io_fn_t)file-&gt;f_op-&gt;write;
          iter_fn = file-&gt;f_op-&gt;write_iter;
          file_start_write(file);
      }
  
      if (iter_fn)
          ret = do_iter_readv_writev(file, &amp;iter, pos, iter_fn, flags);
      else
          ret = do_loop_readv_writev(file, &amp;iter, pos, fn, flags);
  
      if (type != READ)
          file_end_write(file);
  
      out:
          kfree(iov);
          if ((ret + (type == READ)) &gt; 0) {
              if (type == READ)
                  fsnotify_access(file);
              else
                  fsnotify_modify(file);
          }
          return ret;
  }
  
  ssize_t vfs_readv(struct file *file, const struct iovec __user *vec,
                    unsigned long vlen, loff_t *pos, int flags)
  {
      if (!(file-&gt;f_mode &amp; FMODE_READ))
          return -EBADF;
      if (!(file-&gt;f_mode &amp; FMODE_CAN_READ))
          return -EINVAL;
  
      return do_readv_writev(READ, file, vec, vlen, pos, flags);
  }
  
  EXPORT_SYMBOL(vfs_readv);
  static ssize_t do_readv(unsigned long fd, const struct iovec __user *vec,
                          unsigned long vlen, int flags)
  {
      struct fd f = fdget_pos(fd);
      ssize_t ret = -EBADF;
  
      if (f.file) {
          loff_t pos = file_pos_read(f.file);
          ret = vfs_readv(f.file, vec, vlen, &amp;pos, flags);
          if (ret &gt;= 0)
              file_pos_write(f.file, pos);
          fdput_pos(f);
      }
  
      if (ret &gt; 0)
          add_rchar(current, ret);
      inc_syscr(current);
      return ret;
  }
  
  static ssize_t do_preadv(unsigned long fd, const struct iovec __user *vec,
                           unsigned long vlen, loff_t pos, int flags)
  {
      struct fd f;
      ssize_t ret = -EBADF;
  
      if (pos &lt; 0)
          return -EINVAL;
  
      f = fdget(fd);
      if (f.file) {
          ret = -ESPIPE;
          if (f.file-&gt;f_mode &amp; FMODE_PREAD)
              ret = vfs_readv(f.file, vec, vlen, &amp;pos, flags);
          fdput(f);
      }
  
      if (ret &gt; 0)
          add_rchar(current, ret);
      inc_syscr(current);
      return ret;
  }
  
  
  static ssize_t compat_do_readv_writev(int type, struct file *file,
                 const struct compat_iovec __user *uvector,
                 unsigned long nr_segs, loff_t *pos,
                 int flags)
  {
      compat_ssize_t tot_len;
      struct iovec iovstack[UIO_FASTIOV];
      struct iovec *iov = iovstack;
      struct iov_iter iter;
      ssize_t ret;
      io_fn_t fn;
      iter_fn_t iter_fn;
  
      ret = compat_import_iovec(type, uvector, nr_segs,
                UIO_FASTIOV, &amp;iov, &amp;iter);
      if (ret &lt; 0)
          return ret;
  
      tot_len = iov_iter_count(&amp;iter);
      if (!tot_len)
          goto out;
          ret = rw_verify_area(type, file, pos, tot_len);
      if (ret &lt; 0)
          goto out;
  
      if (type == READ) {
          fn = file-&gt;f_op-&gt;read;
          iter_fn = file-&gt;f_op-&gt;read_iter;
      } else {
              fn = (io_fn_t)file-&gt;f_op-&gt;write;
              iter_fn = file-&gt;f_op-&gt;write_iter;
              file_start_write(file);
      }
  
      if (iter_fn)
          ret = do_iter_readv_writev(file, &amp;iter, pos, iter_fn, flags);
      else
          ret = do_loop_readv_writev(file, &amp;iter, pos, fn, flags);
  
      if (type != READ)
          file_end_write(file);
  
      out:
          kfree(iov);
          if ((ret + (type == READ)) &gt; 0) {
              if (type == READ)
                  fsnotify_access(file);
              else
                  fsnotify_modify(file);
          }
          return ret;
  }
  
  static size_t compat_readv(struct file *file,
             const struct compat_iovec __user *vec,
             unsigned long vlen, loff_t *pos, int flags)
  {
      ssize_t ret = -EBADF;
  
      if (!(file-&gt;f_mode &amp; FMODE_READ))
         goto out;
  
      ret = -EINVAL;
      if (!(file-&gt;f_mode &amp; FMODE_CAN_READ))
          goto out;
  
      ret = compat_do_readv_writev(READ, file, vec, vlen, pos, flags);
  
       out:
          if (ret &gt; 0)
              add_rchar(current, ret);
          inc_syscr(current);
          return ret;
  }
  
  static size_t do_compat_readv(compat_ulong_t fd,
               const struct compat_iovec __user *vec,
               compat_ulong_t vlen, int flags)
  {
      struct fd f = fdget_pos(fd);
      ssize_t ret;
      loff_t pos;
  
      if (!f.file)
          return -EBADF;
      pos = f.file-&gt;f_pos;
      ret = compat_readv(f.file, vec, vlen, &amp;pos, flags);
      if (ret &gt;= 0)
          f.file-&gt;f_pos = pos;
      fdput_pos(f);
      return ret;
  
  }
  
  COMPAT_SYSCALL_DEFINE3(readv, compat_ulong_t, fd,
          const struct compat_iovec __user *,vec,
          compat_ulong_t, vlen)
  {
      return do_compat_readv(fd, vec, vlen, 0);
  }
  
  static long do_compat_preadv64(unsigned long fd,
                const struct compat_iovec __user *vec,
                unsigned long vlen, loff_t pos, int flags)
  {
      struct fd f;
      ssize_t ret;
  
      if (pos &lt; 0)
          return -EINVAL;
      f = fdget(fd);
      if (!f.file)
          return -EBADF;
      ret = -ESPIPE;
          if (f.file-&gt;f_mode &amp; FMODE_PREAD)
              ret = compat_readv(f.file, vec, vlen, &amp;pos, flags);
          fdput(f);
          return ret;
  }
  </code></pre>