diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index a8b4dfdb6717..80ea2f05dc7c 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -707,7 +708,14 @@ __getcwd(td, uap) struct thread *td; struct __getcwd_args *uap; { - char *bp, *buf; + + return (kern___getcwd(td, uap->buf, UIO_USERSPACE, uap->buflen)); +} + +int +kern___getcwd(struct thread *td, u_char *buf, enum uio_seg bufseg, u_int buflen) +{ + char *bp, *tmpbuf; int error, i, slash_prefixed; struct filedesc *fdp; struct namecache *ncp; @@ -716,12 +724,13 @@ __getcwd(td, uap) numcwdcalls++; if (disablecwd) return (ENODEV); - if (uap->buflen < 2) + if (buflen < 2) return (EINVAL); - if (uap->buflen > MAXPATHLEN) - uap->buflen = MAXPATHLEN; - buf = bp = malloc(uap->buflen, M_TEMP, M_WAITOK); - bp += uap->buflen - 1; + if (buflen > MAXPATHLEN) + buflen = MAXPATHLEN; + error = 0; + tmpbuf = bp = malloc(buflen, M_TEMP, M_WAITOK); + bp += buflen - 1; *bp = '\0'; fdp = td->td_proc->p_fd; slash_prefixed = 0; @@ -731,7 +740,7 @@ __getcwd(td, uap) if (vp->v_vflag & VV_ROOT) { if (vp->v_mount == NULL) { /* forced unmount */ FILEDESC_UNLOCK(fdp); - free(buf, M_TEMP); + free(tmpbuf, M_TEMP); return (EBADF); } vp = vp->v_mount->mnt_vnodecovered; @@ -740,35 +749,35 @@ __getcwd(td, uap) if (vp->v_dd->v_id != vp->v_ddid) { FILEDESC_UNLOCK(fdp); numcwdfail1++; - free(buf, M_TEMP); + free(tmpbuf, M_TEMP); return (ENOTDIR); } ncp = TAILQ_FIRST(&vp->v_cache_dst); if (!ncp) { FILEDESC_UNLOCK(fdp); numcwdfail2++; - free(buf, M_TEMP); + free(tmpbuf, M_TEMP); return (ENOENT); } if (ncp->nc_dvp != vp->v_dd) { FILEDESC_UNLOCK(fdp); numcwdfail3++; - free(buf, M_TEMP); + free(tmpbuf, M_TEMP); return (EBADF); } for (i = ncp->nc_nlen - 1; i >= 0; i--) { - if (bp == buf) { + if (bp == tmpbuf) { FILEDESC_UNLOCK(fdp); numcwdfail4++; - free(buf, M_TEMP); + free(tmpbuf, M_TEMP); return (ENOMEM); } *--bp = ncp->nc_name[i]; } - if (bp == buf) { + if (bp == tmpbuf) { FILEDESC_UNLOCK(fdp); numcwdfail4++; - free(buf, M_TEMP); + free(tmpbuf, M_TEMP); return (ENOMEM); } *--bp = '/'; @@ -777,16 +786,19 @@ __getcwd(td, uap) } FILEDESC_UNLOCK(fdp); if (!slash_prefixed) { - if (bp == buf) { + if (bp == tmpbuf) { numcwdfail4++; - free(buf, M_TEMP); + free(tmpbuf, M_TEMP); return (ENOMEM); } *--bp = '/'; } numcwdfound++; - error = copyout(bp, uap->buf, strlen(bp) + 1); - free(buf, M_TEMP); + if (bufseg == UIO_SYSSPACE) + bcopy(bp, buf, strlen(bp) + 1); + else + error = copyout(bp, buf, strlen(bp) + 1); + free(tmpbuf, M_TEMP); return (error); } diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 9d43b401c5d0..49a3017f15d9 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -31,6 +31,8 @@ #include #include +int kern___getcwd(struct thread *td, u_char *buf, enum uio_seg bufseg, + u_int buflen); int kern_access(struct thread *td, char *path, enum uio_seg pathseg, int flags); int kern_chdir(struct thread *td, char *path, enum uio_seg pathseg);