Date: Tue, 27 Jun 2006 20:55:53 GMT From: John Baldwin <jhb@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 100171 for review Message-ID: <200606272055.k5RKtrEL004718@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=100171 Change 100171 by jhb@jhb_mutex on 2006/06/27 20:55:43 Revert last. All the other places that do getdirentries() do things differently. It's going to be simpler to just add the VFS Giant locking I think. Affected files ... .. //depot/projects/smpng/sys/kern/vfs_syscalls.c#108 edit .. //depot/projects/smpng/sys/sys/syscallsubr.h#32 edit Differences ... ==== //depot/projects/smpng/sys/kern/vfs_syscalls.c#108 (text+ko) ==== @@ -3563,67 +3563,144 @@ long *basep; } */ *uap; { + struct vnode *vp; + struct file *fp; + struct uio auio, kuio; + struct iovec aiov, kiov; struct dirent *dp, *edp; - char *dirbuf; - int error, readcnt, ufs_43; - long base; + caddr_t dirbuf; + int error, eofflag, readcnt, vfslocked; + long loff; /* XXX arbitrary sanity limit on `count'. */ if (uap->count > 64 * 1024) return (EINVAL); - - dirbuf = malloc(uap->count, M_TEMP, M_WAITOK); - error = kern_getdirentries(td, uap->fd, dirbuf, uap->count, &base, - UIO_SYSSPACE, &ufs_43); + if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) + return (error); + if ((fp->f_flag & FREAD) == 0) { + fdrop(fp, td); + return (EBADF); + } + vp = fp->f_vnode; +unionread: + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + if (vp->v_type != VDIR) { + VFS_UNLOCK_GIANT(vfslocked); + fdrop(fp, td); + return (EINVAL); + } + aiov.iov_base = uap->buf; + aiov.iov_len = uap->count; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_td = td; + auio.uio_resid = uap->count; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); + loff = auio.uio_offset = fp->f_offset; +#ifdef MAC + error = mac_check_vnode_readdir(td->td_ucred, vp); if (error) { - free(dirbuf, M_TEMP); + VOP_UNLOCK(vp, 0, td); + VFS_UNLOCK_GIANT(vfslocked); + fdrop(fp, td); return (error); } - - readcnt = td->td_retval[0]; +#endif # if (BYTE_ORDER != LITTLE_ENDIAN) - /* - * On big endian systems, we can return the contents from a 4.3 - * UFS without performing a fixup. - */ - if (!ufs_43) { -# else + if (vp->v_mount->mnt_maxsymlinklen <= 0) { + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, + NULL, NULL); + fp->f_offset = auio.uio_offset; + } else +# endif { -# endif - edp = (struct dirent *)&dirbuf[readcnt]; - for (dp = (struct dirent *)dirbuf; dp < edp; ) { -# if (BYTE_ORDER == LITTLE_ENDIAN) - /* - * The expected low byte of - * dp->d_namlen is our dp->d_type. - * The high MBZ byte of dp->d_namlen - * is our dp->d_namlen. - */ - dp->d_type = dp->d_namlen; - dp->d_namlen = 0; -# else - /* - * The dp->d_type is the high byte - * of the expected dp->d_namlen, - * so must be zero'ed. - */ - dp->d_type = 0; -# endif - if (dp->d_reclen > 0) { - dp = (struct dirent *) - ((char *)dp + dp->d_reclen); - } else { - error = EIO; - break; + kuio = auio; + kuio.uio_iov = &kiov; + kuio.uio_segflg = UIO_SYSSPACE; + kiov.iov_len = uap->count; + MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK); + kiov.iov_base = dirbuf; + error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, + NULL, NULL); + fp->f_offset = kuio.uio_offset; + if (error == 0) { + readcnt = uap->count - kuio.uio_resid; + edp = (struct dirent *)&dirbuf[readcnt]; + for (dp = (struct dirent *)dirbuf; dp < edp; ) { +# if (BYTE_ORDER == LITTLE_ENDIAN) + /* + * The expected low byte of + * dp->d_namlen is our dp->d_type. + * The high MBZ byte of dp->d_namlen + * is our dp->d_namlen. + */ + dp->d_type = dp->d_namlen; + dp->d_namlen = 0; +# else + /* + * The dp->d_type is the high byte + * of the expected dp->d_namlen, + * so must be zero'ed. + */ + dp->d_type = 0; +# endif + if (dp->d_reclen > 0) { + dp = (struct dirent *) + ((char *)dp + dp->d_reclen); + } else { + error = EIO; + break; + } + } + if (dp >= edp) + error = uiomove(dirbuf, readcnt, &auio); + } + FREE(dirbuf, M_TEMP); + } + VOP_UNLOCK(vp, 0, td); + if (error) { + VFS_UNLOCK_GIANT(vfslocked); + fdrop(fp, td); + return (error); + } + if (uap->count == auio.uio_resid) { + if (union_dircheckp) { + error = union_dircheckp(td, &vp, fp); + if (error == -1) { + VFS_UNLOCK_GIANT(vfslocked); + goto unionread; + } + if (error) { + VFS_UNLOCK_GIANT(vfslocked); + fdrop(fp, td); + return (error); } } + /* + * XXX We could delay dropping the lock above but + * union_dircheckp complicates things. + */ + vn_lock(vp, LK_EXCLUSIVE|LK_RETRY, td); + if ((vp->v_vflag & VV_ROOT) && + (vp->v_mount->mnt_flag & MNT_UNION)) { + struct vnode *tvp = vp; + vp = vp->v_mount->mnt_vnodecovered; + VREF(vp); + fp->f_vnode = vp; + fp->f_data = vp; + fp->f_offset = 0; + vput(tvp); + VFS_UNLOCK_GIANT(vfslocked); + goto unionread; + } + VOP_UNLOCK(vp, 0, td); } - - if (error == 0) - error = copyout(dirbuf, uap->buf, readcnt); - free(dirbuf, M_TEMP); - if (error == 0) - error = copyout(&base, uap->basep, sizeof(long)); + VFS_UNLOCK_GIANT(vfslocked); + error = copyout(&loff, uap->basep, sizeof(long)); + fdrop(fp, td); + td->td_retval[0] = uap->count - auio.uio_resid; return (error); } #endif /* COMPAT_43 */ @@ -3649,20 +3726,6 @@ long *basep; } */ *uap; { - long base; - int error; - - error = kern_getdirentries(td, uap->fd, uap->buf, uap->count, &base, - UIO_USERSPACE, NULL); - if (error == 0 && uap->basep != NULL) - error = copyout(&base, uap->basep, sizeof(long)); - return (error); -} - -int -kern_getdirentries(struct thread *td, int fd, char *buf, u_int count, - long *basep, enum uio_seg bufseg, int *ufs_43) -{ struct vnode *vp; struct file *fp; struct uio auio; @@ -3671,38 +3734,28 @@ long loff; int error, eofflag; - AUDIT_ARG(fd, fd); - if ((error = getvnode(td->td_proc->p_fd, fd, &fp)) != 0) + AUDIT_ARG(fd, uap->fd); + if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { fdrop(fp, td); return (EBADF); } vp = fp->f_vnode; -#ifdef COMPAT_43 - /* - * This is for ogetdirentries() as it returns the contents of a - * 4.3 UFS directly but has to massage the entries of a 4.4 UFS - * to 4.3 binaries. Can one even run any 4.3 binaries on any - * version of FreeBSD? - */ - if (ufs_43 != NULL) - *ufs_43 = vp->v_mount->mnt_maxsymlinklen <= 0; -#endif unionread: vfslocked = VFS_LOCK_GIANT(vp->v_mount); if (vp->v_type != VDIR) { error = EINVAL; goto fail; } - aiov.iov_base = buf; - aiov.iov_len = count; + aiov.iov_base = uap->buf; + aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; - auio.uio_segflg = bufseg; + auio.uio_segflg = UIO_USERSPACE; auio.uio_td = td; - auio.uio_resid = count; + auio.uio_resid = uap->count; /* vn_lock(vp, LK_SHARED | LK_RETRY, td); */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); AUDIT_ARG(vnode, vp, ARG_VNODE1); @@ -3717,7 +3770,7 @@ VOP_UNLOCK(vp, 0, td); if (error) goto fail; - if (count == auio.uio_resid) { + if (uap->count == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(td, &vp, fp); if (error == -1) { @@ -3746,9 +3799,10 @@ } VOP_UNLOCK(vp, 0, td); } - if (basep != NULL) - *basep = loff; - td->td_retval[0] = count - auio.uio_resid; + if (uap->basep != NULL) { + error = copyout(&loff, uap->basep, sizeof(long)); + } + td->td_retval[0] = uap->count - auio.uio_resid; fail: VFS_UNLOCK_GIANT(vfslocked); fdrop(fp, td); @@ -3770,9 +3824,12 @@ u_int count; } */ *uap; { - - return (kern_getdirentries(td, uap->fd, uap->buf, uap->count, NULL, - UIO_USERSPACE, NULL)); + struct getdirentries_args ap; + ap.fd = uap->fd; + ap.buf = uap->buf; + ap.count = uap->count; + ap.basep = NULL; + return (getdirentries(td, &ap)); } /* ==== //depot/projects/smpng/sys/sys/syscallsubr.h#32 (text+ko) ==== @@ -79,8 +79,6 @@ int kern_fstatfs(struct thread *td, int fd, struct statfs *buf); int kern_futimes(struct thread *td, int fd, struct timeval *tptr, enum uio_seg tptrseg); -int kern_getdirentries(struct thread *td, int fd, char *buf, u_int count, - long *basep, enum uio_seg bufseg, int *ufs_43); int kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize, enum uio_seg bufseg, int flags); int kern_getitimer(struct thread *, u_int, struct itimerval *);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200606272055.k5RKtrEL004718>