From owner-svn-src-all@FreeBSD.ORG Mon Jul 1 04:06:41 2013 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id 8068D8DE; Mon, 1 Jul 2013 04:06:41 +0000 (UTC) (envelope-from gleb@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 62E641BC6; Mon, 1 Jul 2013 04:06:41 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r6146eVV056488; Mon, 1 Jul 2013 04:06:40 GMT (envelope-from gleb@svn.freebsd.org) Received: (from gleb@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r6146eUB056486; Mon, 1 Jul 2013 04:06:40 GMT (envelope-from gleb@svn.freebsd.org) Message-Id: <201307010406.r6146eUB056486@svn.freebsd.org> From: Gleb Kurtsou Date: Mon, 1 Jul 2013 04:06:40 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r252438 - head/sys/ufs/ufs X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 01 Jul 2013 04:06:41 -0000 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 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 . 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 . */ 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); }