Author: gleb
Date: Mon Jul  1 04:06:40 2013
New Revision: 252438
URL: http://svnweb.freebsd.org/changeset/base/252438

Log:
  Don't assume that UFS on-disk format of a directory is the same as
  defined by <sys/dirent.h>
  
  Always start parsing at DIRBLKSIZ aligned offset, skip first entries if
  uio_offset is not DIRBLKSIZ aligned. Return EINVAL if buffer is too
  small for single entry.
  
  Preallocate buffer for cookies. Cookies will be replaced with d_off
  field in struct dirent at later point.
  
  Skip entries with zero inode number.
  
  Stop mangling dirent in ufs_extattr_iterate_directory().
  
  Reviewed by:  kib
  Sponsored by: Google Summer Of Code 2011

Modified:
  head/sys/ufs/ufs/ufs_extattr.c
  head/sys/ufs/ufs/ufs_vnops.c

Modified: head/sys/ufs/ufs/ufs_extattr.c
==============================================================================
--- head/sys/ufs/ufs/ufs_extattr.c      Mon Jul  1 03:31:19 2013        
(r252437)
+++ head/sys/ufs/ufs/ufs_extattr.c      Mon Jul  1 04:06:40 2013        
(r252438)
@@ -399,20 +399,8 @@ ufs_extattr_iterate_directory(struct ufs
                        return (error);
                }
 
-               /*
-                * XXXRW: While in UFS, we always get DIRBLKSIZ returns from
-                * the directory code on success, on other file systems this
-                * may not be the case.  For portability, we should check the
-                * read length on return from ufs_readdir().
-                */
-               edp = (struct dirent *)&dirbuf[DIRBLKSIZ];
+               edp = (struct dirent *)&dirbuf[DIRBLKSIZ - auio.uio_resid];
                for (dp = (struct dirent *)dirbuf; dp < edp; ) {
-#if (BYTE_ORDER == LITTLE_ENDIAN)
-                       dp->d_type = dp->d_namlen;
-                       dp->d_namlen = 0;
-#else
-                       dp->d_type = 0;
-#endif
                        if (dp->d_reclen == 0)
                                break;
                        error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT,

Modified: head/sys/ufs/ufs/ufs_vnops.c
==============================================================================
--- head/sys/ufs/ufs/ufs_vnops.c        Mon Jul  1 03:31:19 2013        
(r252437)
+++ head/sys/ufs/ufs/ufs_vnops.c        Mon Jul  1 04:06:40 2013        
(r252438)
@@ -2161,12 +2161,6 @@ ufs_symlink(ap)
 
 /*
  * Vnode op for reading directories.
- *
- * The routine below assumes that the on-disk format of a directory
- * is the same as that defined by <sys/dirent.h>. If the on-disk
- * format changes, then it will be necessary to do a conversion
- * from the on-disk format that read returns to the format defined
- * by <sys/dirent.h>.
  */
 int
 ufs_readdir(ap)
@@ -2179,103 +2173,123 @@ ufs_readdir(ap)
                u_long **a_cookies;
        } */ *ap;
 {
+       struct vnode *vp = ap->a_vp;
        struct uio *uio = ap->a_uio;
+       struct buf *bp;
        struct inode *ip;
+       struct direct *dp, *edp;
+       u_long *cookies;
+       struct dirent dstdp;
+       off_t offset, startoffset;
+       size_t readcnt, skipcnt;
+       ssize_t startresid;
+       int ncookies;
        int error;
-       size_t count, lost;
-       off_t off;
 
-       if (ap->a_ncookies != NULL)
-               /*
-                * Ensure that the block is aligned.  The caller can use
-                * the cookies to determine where in the block to start.
-                */
-               uio->uio_offset &= ~(DIRBLKSIZ - 1);
-       ip = VTOI(ap->a_vp);
+       if (uio->uio_offset < 0)
+               return (EINVAL);
+       ip = VTOI(vp);
        if (ip->i_effnlink == 0)
                return (0);
-       off = uio->uio_offset;
-       count = uio->uio_resid;
-       /* Make sure we don't return partial entries. */
-       if (count <= ((uio->uio_offset + count) & (DIRBLKSIZ -1)))
-               return (EINVAL);
-       count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
-       lost = uio->uio_resid - count;
-       uio->uio_resid = count;
-       uio->uio_iov->iov_len = count;
-#      if (BYTE_ORDER == LITTLE_ENDIAN)
-               if (ap->a_vp->v_mount->mnt_maxsymlinklen > 0) {
-                       error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred);
-               } else {
-                       struct dirent *dp, *edp;
-                       struct uio auio;
-                       struct iovec aiov;
-                       caddr_t dirbuf;
-                       int readcnt;
-                       u_char tmp;
-
-                       auio = *uio;
-                       auio.uio_iov = &aiov;
-                       auio.uio_iovcnt = 1;
-                       auio.uio_segflg = UIO_SYSSPACE;
-                       aiov.iov_len = count;
-                       dirbuf = malloc(count, M_TEMP, M_WAITOK);
-                       aiov.iov_base = dirbuf;
-                       error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
-                       if (error == 0) {
-                               readcnt = count - auio.uio_resid;
-                               edp = (struct dirent *)&dirbuf[readcnt];
-                               for (dp = (struct dirent *)dirbuf; dp < edp; ) {
-                                       tmp = dp->d_namlen;
-                                       dp->d_namlen = dp->d_type;
-                                       dp->d_type = tmp;
-                                       if (dp->d_reclen > 0) {
-                                               dp = (struct dirent *)
-                                                   ((char *)dp + dp->d_reclen);
-                                       } else {
-                                               error = EIO;
-                                               break;
-                                       }
-                               }
-                               if (dp >= edp)
-                                       error = uiomove(dirbuf, readcnt, uio);
+       if (ap->a_ncookies != NULL) {
+               ncookies = uio->uio_resid;
+               if (uio->uio_offset >= ip->i_size)
+                       ncookies = 0;
+               else if (ip->i_size - uio->uio_offset < ncookies)
+                       ncookies = ip->i_size - uio->uio_offset;
+               ncookies = ncookies / (offsetof(struct direct, d_name) + 4) + 1;
+               cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
+               *ap->a_ncookies = ncookies;
+               *ap->a_cookies = cookies;
+       } else {
+               ncookies = 0;
+               cookies = NULL;
+       }
+       offset = startoffset = uio->uio_offset;
+       startresid = uio->uio_resid;
+       error = 0;
+       while (error == 0 && uio->uio_resid > 0 &&
+           uio->uio_offset < ip->i_size) {
+               error = ffs_blkatoff(vp, uio->uio_offset, NULL, &bp);
+               if (error)
+                       break;
+               if (bp->b_offset + bp->b_bcount > ip->i_size)
+                       readcnt = ip->i_size - bp->b_offset;
+               else
+                       readcnt = bp->b_bcount;
+               skipcnt = (size_t)(uio->uio_offset - bp->b_offset) &
+                   ~(size_t)(DIRBLKSIZ - 1);
+               offset = bp->b_offset + skipcnt;
+               dp = (struct direct *)&bp->b_data[skipcnt];
+               edp = (struct direct *)&bp->b_data[readcnt];
+               while (error == 0 && uio->uio_resid > 0 && dp < edp) {
+                       if (dp->d_reclen <= offsetof(struct direct, d_name) ||
+                           (caddr_t)dp + dp->d_reclen > (caddr_t)edp) {
+                               error = EIO;
+                               break;
+                       }
+#if BYTE_ORDER == LITTLE_ENDIAN
+                       /* Old filesystem format. */
+                       if (vp->v_mount->mnt_maxsymlinklen <= 0) {
+                               dstdp.d_namlen = dp->d_type;
+                               dstdp.d_type = dp->d_namlen;
+                       } else
+#endif
+                       {
+                               dstdp.d_namlen = dp->d_namlen;
+                               dstdp.d_type = dp->d_type;
+                       }
+                       if (offsetof(struct direct, d_name) + dstdp.d_namlen >
+                           dp->d_reclen) {
+                               error = EIO;
+                               break;
+                       }
+                       if (offset < startoffset || dp->d_ino == 0)
+                               goto nextentry;
+                       dstdp.d_fileno = dp->d_ino;
+                       dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
+                       bcopy(dp->d_name, dstdp.d_name, dstdp.d_namlen);
+                       dstdp.d_name[dstdp.d_namlen] = '\0';
+                       if (dstdp.d_reclen > uio->uio_resid) {
+                               if (uio->uio_resid == startresid)
+                                       error = EINVAL;
+                               else
+                                       error = EJUSTRETURN;
+                               break;
                        }
-                       free(dirbuf, M_TEMP);
+                       /* Advance dp. */
+                       error = uiomove((caddr_t)&dstdp, dstdp.d_reclen, uio);
+                       if (error)
+                               break;
+                       if (cookies != NULL) {
+                               KASSERT(ncookies > 0,
+                                   ("ufs_readdir: cookies buffer too small"));
+                               *cookies = offset + dp->d_reclen;
+                               cookies++;
+                               ncookies--;
+                       }
+nextentry:
+                       offset += dp->d_reclen;
+                       dp = (struct direct *)((caddr_t)dp + dp->d_reclen);
                }
-#      else
-               error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred);
-#      endif
-       if (!error && ap->a_ncookies != NULL) {
-               struct dirent* dpStart;
-               struct dirent* dpEnd;
-               struct dirent* dp;
-               int ncookies;
-               u_long *cookies;
-               u_long *cookiep;
-
-               if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
-                       panic("ufs_readdir: unexpected uio from NFS server");
-               dpStart = (struct dirent *)
-                   ((char *)uio->uio_iov->iov_base - (uio->uio_offset - off));
-               dpEnd = (struct dirent *) uio->uio_iov->iov_base;
-               for (dp = dpStart, ncookies = 0;
-                    dp < dpEnd;
-                    dp = (struct dirent *)((caddr_t) dp + dp->d_reclen))
-                       ncookies++;
-               cookies = malloc(ncookies * sizeof(u_long), M_TEMP,
-                   M_WAITOK);
-               for (dp = dpStart, cookiep = cookies;
-                    dp < dpEnd;
-                    dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
-                       off += dp->d_reclen;
-                       *cookiep++ = (u_long) off;
+               bqrelse(bp);
+               uio->uio_offset = offset;
+       }
+       /* We need to correct uio_offset. */
+       uio->uio_offset = offset;
+       if (error == EJUSTRETURN)
+               error = 0;
+       if (ap->a_ncookies != NULL) {
+               if (error == 0) {
+                       ap->a_ncookies -= ncookies;
+               } else {
+                       free(*ap->a_cookies, M_TEMP);
+                       *ap->a_ncookies = 0;
+                       *ap->a_cookies = NULL;
                }
-               *ap->a_ncookies = ncookies;
-               *ap->a_cookies = cookies;
        }
-       uio->uio_resid += lost;
-       if (ap->a_eofflag)
-           *ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset;
+       if (error == 0 && ap->a_eofflag)
+               *ap->a_eofflag = ip->i_size <= uio->uio_offset;
        return (error);
 }
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to