The diff below adds the remaining necessary functionality for the
*at() functions.  Changes:

  - Functions each check that they're given the right flags and return
    EINVAL otherwise.

  - AT_SYMLINK_NOFOLLOW and AT_SYMLINK_FOLLOW are pretty trivial; just
    control whether the FOLLOW or NOFOLLOW bit is set when calling
    NDINITAT().

  - Make sys_lstat() use dofstatat() and AT_SYMLINK_NOFOLLOW.

  - Our behavior for sys_link() is to follow symbolic links (this is
    implementation defined), so sys_link should pass AT_SYMLINK_FOLLOW
    to dolinkat().

  - In faccessat(), fix a bug where VOP_ACCESS() might sleep and a
    concurrent call to setresuid() or setresgid() could change the
    value of cr_uid/cr_gid in the middle.  This is done by holding a
    reference to the ucred for the duration of the VOP_ACCESS() call.
    (Tip for reviewers: crcopy() does an implicit crfree() on the
    argument, so there's no reference leak.)

ok?


Index: sys/fcntl.h
===================================================================
RCS file: /home/matthew/anoncvs/cvs/src/sys/sys/fcntl.h,v
retrieving revision 1.13
diff -u -p -r1.13 fcntl.h
--- sys/fcntl.h 7 Jul 2011 23:45:00 -0000       1.13
+++ sys/fcntl.h 8 Jul 2011 01:28:04 -0000
@@ -190,6 +190,10 @@ struct flock {
 
 #if __BSD_VISIBLE || __POSIX_VISIBLE >= 200809
 #define        AT_FDCWD        -100
+
+#define        AT_EACCESS              0x01
+#define        AT_SYMLINK_NOFOLLOW     0x02
+#define        AT_SYMLINK_FOLLOW       0x04
 #endif
 
 #ifndef _KERNEL
Index: kern/vfs_syscalls.c
===================================================================
RCS file: /home/matthew/anoncvs/cvs/src/sys/kern/vfs_syscalls.c,v
retrieving revision 1.169
diff -u -p -r1.169 vfs_syscalls.c
--- kern/vfs_syscalls.c 7 Jul 2011 23:45:00 -0000       1.169
+++ kern/vfs_syscalls.c 8 Jul 2011 01:25:58 -0000
@@ -1326,7 +1326,7 @@ sys_link(struct proc *p, void *v, regist
        } */ *uap = v;
 
        return (dolinkat(p, AT_FDCWD, SCARG(uap, path), AT_FDCWD,
-           SCARG(uap, link), 0, retval));
+           SCARG(uap, link), AT_SYMLINK_FOLLOW, retval));
 }
 
 int
@@ -1350,14 +1350,14 @@ dolinkat(struct proc *p, int fd1, const 
 {
        struct vnode *vp;
        struct nameidata nd;
-       int error;
+       int error, follow;
        int flags;
 
-       /* XXX: Support AT_SYMLINK_FOLLOW. */
-       if (flag != 0)
-               return (ENOTSUP);
+       if (flag & ~AT_SYMLINK_FOLLOW)
+               return (EINVAL);
 
-       NDINITAT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fd1, path1, p);
+       follow = (flag & AT_SYMLINK_FOLLOW) ? FOLLOW : NOFOLLOW;
+       NDINITAT(&nd, LOOKUP, follow, UIO_USERSPACE, fd1, path1, p);
        if ((error = namei(&nd)) != 0)
                return (error);
        vp = nd.ni_vp;
@@ -1613,43 +1613,47 @@ int
 dofaccessat(struct proc *p, int fd, const char *path, int amode, int flag,
     register_t *retval)
 {
-       struct ucred *cred = p->p_ucred;
        struct vnode *vp;
-       int error, flags, t_gid, t_uid;
+       int error;
        struct nameidata nd;
 
        if (amode & ~(R_OK | W_OK | X_OK))
                return (EINVAL);
-       /* XXX: Support AT_EACCESS. */
-       if (flag != 0)
-               return (ENOTSUP);
-       t_uid = cred->cr_uid;
-       t_gid = cred->cr_gid;
-       cred->cr_uid = p->p_cred->p_ruid;
-       cred->cr_gid = p->p_cred->p_rgid;
+       if (flag & ~AT_EACCESS)
+               return (EINVAL);
+
        NDINITAT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, fd, path, p);
        if ((error = namei(&nd)) != 0)
-               goto out1;
+               return (error);
        vp = nd.ni_vp;
 
        /* Flags == 0 means only check for existence. */
        if (amode) {
-               flags = 0;
+               struct ucred *cred = p->p_ucred;
+               int vflags = 0;
+
+               crhold(cred);
+
+               if (!(flag & AT_EACCESS)) {
+                       cred = crcopy(cred);
+                       cred->cr_uid = p->p_cred->p_ruid;
+                       cred->cr_gid = p->p_cred->p_rgid;
+               }
+
                if (amode & R_OK)
-                       flags |= VREAD;
+                       vflags |= VREAD;
                if (amode & W_OK)
-                       flags |= VWRITE;
+                       vflags |= VWRITE;
                if (amode & X_OK)
-                       flags |= VEXEC;
+                       vflags |= VEXEC;
 
-               error = VOP_ACCESS(vp, flags, cred, p);
-               if (!error && (flags & VWRITE))
+               error = VOP_ACCESS(vp, vflags, cred, p);
+               if (!error && (vflags & VWRITE))
                        error = vn_writechk(vp);
+
+               crfree(cred);
        }
        vput(vp);
-out1:
-       cred->cr_uid = t_uid;
-       cred->cr_gid = t_gid;
        return (error);
 }
 
@@ -1688,14 +1692,14 @@ dofstatat(struct proc *p, int fd, const 
     int flag, register_t *retval)
 {
        struct stat sb;
-       int error;
+       int error, follow;
        struct nameidata nd;
 
-       /* XXX: Support AT_SYMLINK_NOFOLLOW (and make lstat use this??) */
-       if (flag != 0)
-               return (ENOTSUP);
+       if (flag & ~AT_SYMLINK_NOFOLLOW)
+               return (EINVAL);
 
-       NDINITAT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, fd, path, p);
+       follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
+       NDINITAT(&nd, LOOKUP, follow | LOCKLEAF, UIO_USERSPACE, fd, path, p);
        if ((error = namei(&nd)) != 0)
                return (error);
        error = vn_stat(nd.ni_vp, &sb, p);
@@ -1720,23 +1724,9 @@ sys_lstat(struct proc *p, void *v, regis
                syscallarg(const char *) path;
                syscallarg(struct stat *) ub;
        } */ *uap = v;
-       struct stat sb;
-       int error;
-       struct nameidata nd;
 
-       NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE,
-           SCARG(uap, path), p);
-       if ((error = namei(&nd)) != 0)
-               return (error);
-       error = vn_stat(nd.ni_vp, &sb, p);
-       vput(nd.ni_vp);
-       if (error)
-               return (error);
-       /* Don't let non-root see generation numbers (for NFS security) */
-       if (suser(p, 0))
-               sb.st_gen = 0;
-       error = copyout(&sb, SCARG(uap, ub), sizeof(sb));
-       return (error);
+       return (dofstatat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, ub),
+           AT_SYMLINK_NOFOLLOW, retval));
 }
 
 /*
@@ -1951,15 +1941,15 @@ dofchmodat(struct proc *p, int fd, const
 {
        struct vnode *vp;
        struct vattr vattr;
-       int error;
+       int error, follow;
        struct nameidata nd;
 
        if (mode & ~(S_IFMT | ALLPERMS))
                return (EINVAL);
-       /* XXX: Support AT_SYMLINK_NOFOLLOW. */
-       if (flag != 0)
-               return (ENOTSUP);
+       if (flag & ~AT_SYMLINK_NOFOLLOW)
+               return (EINVAL);
 
+       follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
        NDINITAT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fd, path, p);
        if ((error = namei(&nd)) != 0)
                return (error);
@@ -2049,14 +2039,14 @@ dofchownat(struct proc *p, int fd, const
 {
        struct vnode *vp;
        struct vattr vattr;
-       int error;
+       int error, follow;
        struct nameidata nd;
        mode_t mode;
 
-       /* XXX: Support AT_SYMLINK_NOFOLLOW. */
-       if (flag != 0)
-               return (ENOTSUP);
+       if (flag & ~AT_SYMLINK_NOFOLLOW)
+               return (EINVAL);
 
+       follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
        NDINITAT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fd, path, p);
        if ((error = namei(&nd)) != 0)
                return (error);
@@ -2252,12 +2242,11 @@ doutimensat(struct proc *p, int fd, cons
        struct vnode *vp;
        struct timeval tv[2];
        struct vattr vattr;
-       int error;
+       int error, follow;
        struct nameidata nd;
 
-       /* XXX: Support AT_SYMLINK_NOFOLLOW. */
-       if (flag != 0)
-               return (ENOTSUP);
+       if (flag & ~AT_SYMLINK_NOFOLLOW)
+               return (EINVAL);
 
        VATTR_NULL(&vattr);
        if (tvp == NULL) {
@@ -2272,7 +2261,8 @@ doutimensat(struct proc *p, int fd, cons
                if (tv[1].tv_sec == VNOVAL)
                        tv[1].tv_sec = VNOVAL - 1;
        }
-       NDINITAT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fd, path, p);
+       follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
+       NDINITAT(&nd, LOOKUP, follow, UIO_USERSPACE, fd, path, p);
        if ((error = namei(&nd)) != 0)
                return (error);
        vp = nd.ni_vp;

----- End forwarded message -----

Reply via email to