vp = NULL;
error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
+ nd.nl_flags |= NLC_EXEC;
if (error == 0)
error = nlookup(&nd);
if (error == 0)
goto cleanup;
}
- /* Can we access it? */
- error = VOP_ACCESS(vp, VEXEC, p->p_ucred);
- if (error)
- goto cleanup;
-
error = VOP_OPEN(vp, FREAD, p->p_ucred, NULL);
if (error)
goto cleanup;
struct vnode *vp;
u_int newmin;
u_int oflags;
+ u_int nflags;
int tmp, error, flg = F_POSIX;
KKASSERT(p);
break;
case F_SETFL:
- oflags = fp->f_flag & FCNTLFLAGS;
- fp->f_flag &= ~FCNTLFLAGS;
- fp->f_flag |= FFLAGS(dat->fc_flags & ~O_ACCMODE) & FCNTLFLAGS;
+ oflags = fp->f_flag;
+ nflags = FFLAGS(dat->fc_flags & ~O_ACCMODE) & FCNTLFLAGS;
+ nflags |= oflags & ~FCNTLFLAGS;
+
error = 0;
- if ((fp->f_flag ^ oflags) & FASYNC) {
+ if (((nflags ^ oflags) & O_APPEND) && (oflags & FAPPENDONLY))
+ error = EINVAL;
+ if (error == 0 && ((nflags ^ oflags) & FASYNC)) {
tmp = fp->f_flag & FASYNC;
error = fo_ioctl(fp, FIOASYNC, (caddr_t)&tmp, cred);
}
- if (error)
- fp->f_flag = (fp->f_flag & ~FCNTLFLAGS) | oflags;
+ if (error == 0)
+ fp->f_flag = nflags;
break;
case F_GETOWN:
/* Otherwise, check the owner. */
if (cred->cr_uid == ino_uid) {
- if (mode & VOWN)
- return (0);
if (mode & VEXEC)
mask |= S_IXUSR;
if (mode & VREAD)
#include <sys/ktrace.h>
#endif
-static int naccess_va(struct vattr *va, int vmode, struct ucred *cred);
-
/*
* Initialize a nlookup() structure, early error return for copyin faults
* or a degenerate empty string (which is not allowed).
* and an error code of 0 will be returned for a non-existant
* target (not ENOENT).
*
- * If NLC_RENAME is set the last directory mut allow node deletion,
+ * If NLC_RENAME_DST is set the last directory mut allow node deletion,
* plus the sticky check is made, and an error code of 0 will be returned
- * for a non-existant target (not ENOENT). NLC_RENAME is set used for
- * the rename target.
+ * for a non-existant target (not ENOENT).
*
* If NLC_DELETE is set the last directory mut allow node deletion,
* plus the sticky check is made.
{
struct nlcomponent nlc;
struct nchandle nch;
+ struct nchandle par;
struct mount *mp;
int wasdotordotdot;
char *ptr;
char *xptr;
int error;
int len;
+ int dflags;
#ifdef KTRACE
if (KTRPOINT(nd->nl_td, KTR_NAMEI))
/*
* Check directory search permissions.
*/
- if ((error = naccess(&nd->nl_nch, VEXEC, nd->nl_cred, NULL)) != 0)
+ dflags = 0;
+ if ((error = naccess(&nd->nl_nch, NLC_EXEC, nd->nl_cred, &dflags)) != 0)
break;
/*
* When handling ".." we have to detect a traversal back through a
* mount point. If we are at the root, ".." just returns the root.
*
+ * When handling "." or ".." we also have to recalculate dflags
+ * since our dflags will be for some sub-directory instead of the
+ * parent dir.
+ *
* This subsection returns a locked, refd 'nch' unless it errors out.
* The namecache topology is not allowed to be disconnected, so
* encountering a NULL parent will generate EINVAL. This typically
}
wasdotordotdot = 0;
}
+
+ /*
+ * If the last component was "." or ".." our dflags no longer
+ * represents the parent directory and we have to explicitly
+ * look it up.
+ */
+ if (wasdotordotdot && error == 0) {
+ dflags = 0;
+ if ((par.ncp = nch.ncp->nc_parent) != NULL) {
+ par.mount = nch.mount;
+ cache_hold(&par);
+ dflags = 0;
+ error = naccess(&par, 0, nd->nl_cred, &dflags);
+ cache_drop(&par);
+ }
+ }
+
/*
* [end of subsection] ncp is locked and ref'd. nd->nl_nch is ref'd
*/
* requested. Note that ncp->nc_error is left as ENOENT in that
* case, which we check later on.
*
- * NOTE: For NLC_RENAME in the ENOENT case we do a VCREATE test,
- * same as for NLC_CREATE.
- *
* Also handle invalid '.' or '..' components terminating a path
* for a create/rename/delete. The standard requires this and pax
* pretty stupidly depends on it.
for (xptr = ptr; *xptr == '/'; ++xptr)
;
if (*xptr == 0) {
- if (error == ENOENT && (nd->nl_flags & (NLC_CREATE | NLC_RENAME))) {
- if (nd->nl_flags & NLC_NFS_RDONLY)
+ if (error == ENOENT &&
+ (nd->nl_flags & (NLC_CREATE | NLC_RENAME_DST))
+ ) {
+ if (nd->nl_flags & NLC_NFS_RDONLY) {
error = EROFS;
- else
- error = naccess(&nch, VCREATE, nd->nl_cred, NULL);
+ } else {
+ error = naccess(&nch, nd->nl_flags | dflags,
+ nd->nl_cred, NULL);
+ }
}
if (error == 0 && wasdotordotdot &&
- (nd->nl_flags & (NLC_CREATE | NLC_RENAME | NLC_DELETE))) {
+ (nd->nl_flags & (NLC_CREATE | NLC_DELETE |
+ NLC_RENAME_SRC | NLC_RENAME_DST))) {
error = EINVAL;
}
}
/*
* Successful lookup of last element.
*
- * Check directory permissions if a deletion or rename (target)
- * is specified. This also handles the sticky test.
+ * Check permissions if the target exists. If the target does not
+ * exist directory permissions were already tested in the early
+ * completion code above.
*
- * We already checked permissions for creates in the early
- * termination code above.
+ * nd->nl_flags will be adjusted on return with NLC_APPENDONLY
+ * if the file is marked append-only, and NLC_STICKY if the directory
+ * containing the file is sticky.
*/
- if (*ptr == 0 && (nd->nl_flags & (NLC_DELETE | NLC_RENAME))) {
- if (nd->nl_flags & NLC_DELETE)
- error = naccess(&nch, VDELETE, nd->nl_cred, NULL);
- else
- error = naccess(&nch, VRENAME, nd->nl_cred, NULL);
+ if (nch.ncp->nc_vp && (nd->nl_flags & NLC_ALLCHKS)) {
+ error = naccess(&nch, nd->nl_flags | dflags,
+ nd->nl_cred, NULL);
if (error) {
cache_put(&nch);
break;
}
/*
- * Termination: no more elements. If NLC_CREATE was set the
- * ncp may represent a negative hit (ncp->nc_error will be ENOENT),
- * but we still return an error code of 0.
+ * Termination: no more elements.
*
* If NLC_REFDVP is set acquire a referenced parent dvp.
*/
error = 0;
break;
}
+
+ /*
+ * NOTE: If NLC_CREATE was set the ncp may represent a negative hit
+ * (ncp->nc_error will be ENOENT), but we will still return an error
+ * code of 0.
+ */
return(error);
}
/*
* Check access [XXX cache vattr!] [XXX quota]
*
- * Generally check the V* access bits from sys/vnode.h. All specified bits
- * must pass for this function to return 0.
+ * Generally check the NLC_* access bits. All specified bits must pass
+ * for this function to return 0.
*
- * The file does not have to exist when checking VCREATE or VRENAME access.
+ * The file does not have to exist when checking NLC_CREATE or NLC_RENAME_DST
+ * access, otherwise it must exist. No error is returned in this case.
*
- * The file must not exist if VEXCL is specified.
+ * The file must not exist if NLC_EXCL is specified.
*
- * Directory permissions in general are tested for VCREATE if the file
- * does not exist, VDELETE if the file does exist, and VRENAME whether
- * the file exists or not.
+ * Directory permissions in general are tested for NLC_CREATE if the file
+ * does not exist, NLC_DELETE if the file does exist, and NLC_RENAME_DST
+ * whether the file exists or not.
*
- * The directory sticky bit is tested for VDELETE and VRENAME. NOTE: For
- * VRENAME we only care if the target exists.
+ * The directory sticky bit is tested for NLC_DELETE and NLC_RENAME_DST,
+ * the latter is only tested if the target exists.
*
* The passed ncp may or may not be locked. The caller should use a
- * locked ncp on leaf lookups, especially for VCREATE, VRENAME, VDELETE,
- * and VEXCL checks.
+ * locked ncp on leaf lookups, especially for NLC_CREATE, NLC_RENAME_DST,
+ * NLC_DELETE, and NLC_EXCL checks.
*/
int
-naccess(struct nchandle *nch, int vmode, struct ucred *cred, int *stickyp)
+naccess(struct nchandle *nch, int nflags, struct ucred *cred, int *nflagsp)
{
struct nchandle par;
struct vnode *vp;
cache_unlock(nch);
}
error = nch->ncp->nc_error;
- sticky = 0;
/*
- * Directory permissions and VEXCL checks. Do a precursor conditional
- * to reduce overhead since most access checks are for read-only.
+ * Directory permissions checks. Silently ignore ENOENT if these
+ * tests pass. It isn't an error.
*/
- if (vmode & (VDELETE|VRENAME|VCREATE|VEXCL)) {
- if (((vmode & VCREATE) && nch->ncp->nc_vp == NULL) ||
- ((vmode & VDELETE) && nch->ncp->nc_vp != NULL) ||
- (vmode & VRENAME)
+ if (nflags & (NLC_CREATE | NLC_DELETE | NLC_RENAME_SRC | NLC_RENAME_DST)) {
+ if (((nflags & NLC_CREATE) && nch->ncp->nc_vp == NULL) ||
+ ((nflags & NLC_DELETE) && nch->ncp->nc_vp != NULL) ||
+ ((nflags & NLC_RENAME_SRC) && nch->ncp->nc_vp != NULL) ||
+ (nflags & NLC_RENAME_DST)
) {
if ((par.ncp = nch->ncp->nc_parent) == NULL) {
if (error != EAGAIN)
error = EINVAL;
- } else {
+ } else if (error == 0 || error == ENOENT) {
par.mount = nch->mount;
cache_hold(&par);
- error = naccess(&par, VWRITE, cred, &sticky);
- if ((vmode & (VDELETE | VRENAME)) && sticky)
- vmode |= VSVTX;
+ sticky = 0;
+ error = naccess(&par, NLC_WRITE, cred, NULL);
cache_drop(&par);
}
}
- if ((vmode & VEXCL) && nch->ncp->nc_vp != NULL)
- error = EEXIST;
}
+
+ /*
+ * NLC_EXCL check. Target file must not exist.
+ */
+ if (error == 0 && (nflags & NLC_EXCL) && nch->ncp->nc_vp != NULL)
+ error = EEXIST;
+
+ /*
+ * Get the vnode attributes so we can do the rest of our checks.
+ *
+ * NOTE: We only call naccess_va() if the target exists.
+ */
if (error == 0) {
error = cache_vget(nch, cred, LK_SHARED, &vp);
if (error == ENOENT) {
- if (vmode & (VCREATE | VRENAME))
+ /*
+ * Silently zero-out ENOENT if creating or renaming
+ * (rename target). It isn't an error.
+ */
+ if (nflags & (NLC_CREATE | NLC_RENAME_DST))
error = 0;
} else if (error == 0) {
- /* XXX cache the va in the namecache or in the vnode */
- if ((error = VOP_GETATTR(vp, &va)) == 0) {
- if ((vmode & VWRITE) && vp->v_mount) {
- if (vp->v_mount->mnt_flag & MNT_RDONLY)
- error = EROFS;
+ /*
+ * Get the vnode attributes and check for illegal O_TRUNC
+ * requests and read-only mounts.
+ *
+ * NOTE: You can still open devices on read-only mounts for
+ * writing.
+ *
+ * NOTE: creates/deletes/renames are handled by the NLC_WRITE
+ * check on the parent directory above.
+ *
+ * XXX cache the va in the namecache or in the vnode
+ */
+ error = VOP_GETATTR(vp, &va);
+ if (error == 0 && (nflags & NLC_TRUNCATE)) {
+ switch(va.va_type) {
+ case VREG:
+ case VDATABASE:
+ case VCHR:
+ case VBLK:
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ }
+ if (error == 0 && (nflags & NLC_WRITE) && vp->v_mount &&
+ (vp->v_mount->mnt_flag & MNT_RDONLY)
+ ) {
+ switch(va.va_type) {
+ case VDIR:
+ case VLNK:
+ case VREG:
+ case VDATABASE:
+ error = EROFS;
+ break;
+ default:
+ break;
}
}
vput(vp);
+ /*
+ * Check permissions based on file attributes. The passed
+ * flags (*nflagsp) are modified with feedback based on
+ * special attributes and requirements.
+ */
if (error == 0) {
/*
- * Set the returned (*stickyp) if VSVTX is set and the uid
- * is not the owner of the directory. The caller uses this
- * disallow deletions of files not owned by the user if the
- * user also does not own the directory and the sticky bit
- * is set on the directory. Weird, I know.
+ * Adjust the returned (*nflagsp) if non-NULL.
*/
- if (stickyp && va.va_uid != cred->cr_uid)
- *stickyp = (va.va_mode & VSVTX);
+ if (nflagsp) {
+ if ((va.va_mode & VSVTX) && va.va_uid != cred->cr_uid)
+ *nflagsp |= NLC_STICKY;
+ if (va.va_flags & APPEND)
+ *nflagsp |= NLC_APPENDONLY;
+ if (va.va_flags & IMMUTABLE)
+ *nflagsp |= NLC_IMMUTABLE;
+ }
/*
* Process general access.
*/
- error = naccess_va(&va, vmode, cred);
+ error = naccess_va(&va, nflags, cred);
}
}
}
/*
* Check the requested access against the given vattr using cred.
*/
-static
int
-naccess_va(struct vattr *va, int vmode, struct ucred *cred)
+naccess_va(struct vattr *va, int nflags, struct ucred *cred)
{
int i;
+ int vmode;
/*
- * Test the immutable bit for files, directories, and softlinks.
+ * Test the immutable bit. Creations, deletions, renames (source
+ * or destination) are not allowed. chown/chmod/other is also not
+ * allowed but is handled by SETATTR. Hardlinks to the immutable
+ * file are allowed.
+ *
+ * If the directory is set to immutable then creations, deletions,
+ * renames (source or dest) and hardlinks to files within the directory
+ * are not allowed, and regular files opened through the directory may
+ * not be written to or truncated (unless a special device).
*
- * NOTE: Only called for VRENAME if the target exists.
+ * NOTE! New hardlinks to immutable files work but new hardlinks to
+ * files, immutable or not, sitting inside an immutable directory are
+ * not allowed. As always if the file is hardlinked via some other
+ * path additional hardlinks may be possible even if the file is marked
+ * immutable. The sysop needs to create a closure by checking the hard
+ * link count. Once closure is achieved you are good, and security
+ * scripts should check link counts anyway.
+ *
+ * Writes and truncations are only allowed on special devices.
*/
- if (vmode & (VWRITE|VDELETE|VRENAME)) {
- if (va->va_type == VDIR || va->va_type == VLNK || va->va_type == VREG) {
- if (va->va_flags & IMMUTABLE)
+ if ((va->va_flags & IMMUTABLE) || (nflags & NLC_IMMUTABLE)) {
+ if ((nflags & NLC_IMMUTABLE) && (nflags & NLC_HLINK))
+ return (EPERM);
+ if (nflags & (NLC_CREATE | NLC_DELETE |
+ NLC_RENAME_SRC | NLC_RENAME_DST)) {
+ return (EPERM);
+ }
+ if (nflags & (NLC_WRITE | NLC_TRUNCATE)) {
+ switch(va->va_type) {
+ case VDIR:
+ case VLNK:
+ case VREG:
+ case VDATABASE:
+ return (EPERM);
+ default:
+ break;
+ }
+ }
+ }
+
+ /*
+ * Test the no-unlink and append-only bits for opens, rename targets,
+ * and deletions. These bits are not tested for creations or
+ * rename sources.
+ *
+ * Unlike FreeBSD we allow a file with APPEND set to be renamed.
+ * If you do not wish this you must also set NOUNLINK.
+ *
+ * If the governing directory is marked APPEND-only it implies
+ * NOUNLINK for all entries in the directory.
+ */
+ if (((va->va_flags & NOUNLINK) || (nflags & NLC_APPENDONLY)) &&
+ (nflags & (NLC_DELETE | NLC_RENAME_SRC | NLC_RENAME_DST))
+ ) {
+ return (EPERM);
+ }
+
+ /*
+ * A file marked append-only may not be deleted but can be renamed.
+ */
+ if ((va->va_flags & APPEND) &&
+ (nflags & (NLC_DELETE | NLC_RENAME_DST))
+ ) {
+ return (EPERM);
+ }
+
+ /*
+ * A file marked append-only which is opened for writing must also
+ * be opened O_APPEND.
+ */
+ if ((va->va_flags & APPEND) && (nflags & (NLC_OPEN | NLC_TRUNCATE))) {
+ if (nflags & NLC_TRUNCATE)
+ return (EPERM);
+ if ((nflags & (NLC_OPEN | NLC_WRITE)) == (NLC_OPEN | NLC_WRITE)) {
+ if ((nflags & NLC_APPEND) == 0)
return (EPERM);
}
}
/*
* Check owner perms.
*
- * If VOWN is set the owner of the file is allowed no matter when
+ * If NLC_OWN is set the owner of the file is allowed no matter when
* the owner-mode bits say (utimes).
*/
+ vmode = 0;
+ if (nflags & NLC_READ)
+ vmode |= S_IRUSR;
+ if (nflags & NLC_WRITE)
+ vmode |= S_IWUSR;
+ if (nflags & NLC_EXEC)
+ vmode |= S_IXUSR;
+
if (cred->cr_uid == va->va_uid) {
- if ((vmode & VOWN) == 0) {
- vmode &= S_IRWXU;
+ if ((nflags & NLC_OWN) == 0) {
if ((vmode & va->va_mode) != vmode)
return(EACCES);
}
}
/*
- * If VSVTX is set only the owner may create or delete the file.
- * This bit is typically set for VDELETE checks from unlink or
- * the source file in a rename, and for VCREATE checks from the
- * target file in a rename.
+ * If NLC_STICKY is set only the owner may delete or rename a file.
+ * This bit is typically set on /tmp.
*
- * Note that other V bits are not typically set in the VSVTX case.
- * For creations and deletions we usually just care about directory
- * permissions, not file permissions. So if this test passes the
- * return value winds up being 0.
+ * Note that the NLC_READ/WRITE/EXEC bits are not typically set in
+ * the specific delete or rename case. For deletions and renames we
+ * usually just care about directory permissions, not file permissions.
*/
- if (vmode & VSVTX)
+ if ((nflags & NLC_STICKY) &&
+ (nflags & (NLC_RENAME_SRC | NLC_RENAME_DST | NLC_DELETE))) {
return(EACCES);
+ }
/*
* Check group perms
*/
- vmode &= S_IRWXU;
vmode >>= 3;
for (i = 0; i < cred->cr_ngroups; ++i) {
if (va->va_gid == cred->cr_groups[i]) {
static int setfown (struct vnode *, uid_t, gid_t);
static int setfmode (struct vnode *, int);
static int setfflags (struct vnode *, int);
-static int setutimes (struct vnode *, const struct timespec *, int);
+static int setutimes (struct vnode *, struct vattr *,
+ const struct timespec *, int);
static int usermount = 0; /* if 1, non-root can mount fs. */
int (*union_dircheckp) (struct thread *, struct vnode **, struct file *);
nlookup_done(&nd);
return(error);
}
+ nd.nl_flags |= NLC_EXEC;
error = nlookup(&nd);
if (error == 0)
error = kern_chroot(&nd.nl_nch);
if (error)
return (error);
fp = nfp;
- cmode = ((mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT;
+ cmode = ((mode &~ fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT;
/*
* XXX p_dupfd is a real mess. It allows a device to return a
/*
* Lookup the source and obtained a locked vnode.
*
+ * You may only hardlink a file which you have write permission
+ * on or which you own.
+ *
* XXX relookup on vget failure / race ?
*/
bwillinode(1);
+ nd->nl_flags |= NLC_WRITE | NLC_OWN | NLC_HLINK;
if ((error = nlookup(nd)) != 0)
return (error);
vp = nd->nl_nch.ncp->nc_vp;
vp = NULL;
error = nlookup_init(&nd, uap->path, UIO_USERSPACE, NLC_FOLLOW);
- /* XXX Add NLC flag indicating modifying operation? */
if (error == 0)
error = nlookup(&nd);
if (error == 0)
vp = NULL;
error = nlookup_init(&nd, uap->path, UIO_USERSPACE, 0);
- /* XXX Add NLC flag indicating modifying operation? */
if (error == 0)
error = nlookup(&nd);
if (error == 0)
struct vnode *vp;
int error;
- /* XXX Add NLC flag indicating modifying operation? */
if ((error = nlookup(nd)) != 0)
return (error);
if ((error = cache_vref(&nd->nl_nch, nd->nl_cred, &vp)) != 0)
struct vnode *vp;
int error;
- /* XXX Add NLC flag indicating modifying operation? */
if ((error = nlookup(nd)) != 0)
return (error);
if ((error = cache_vref(&nd->nl_nch, nd->nl_cred, &vp)) != 0)
}
static int
-setutimes(struct vnode *vp, const struct timespec *ts, int nullflag)
+setutimes(struct vnode *vp, struct vattr *vattr,
+ const struct timespec *ts, int nullflag)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
int error;
- struct vattr vattr;
- /*
- * note: vget is required for any operation that might mod the vnode
- * so VINACTIVE is properly cleared.
- */
- if ((error = vget(vp, LK_EXCLUSIVE)) == 0) {
- VATTR_NULL(&vattr);
- vattr.va_atime = ts[0];
- vattr.va_mtime = ts[1];
- if (nullflag)
- vattr.va_vaflags |= VA_UTIMES_NULL;
- error = VOP_SETATTR(vp, &vattr, p->p_ucred);
- vput(vp);
- }
+ VATTR_NULL(vattr);
+ vattr->va_atime = ts[0];
+ vattr->va_mtime = ts[1];
+ if (nullflag)
+ vattr->va_vaflags |= VA_UTIMES_NULL;
+ error = VOP_SETATTR(vp, vattr, p->p_ucred);
+
return error;
}
{
struct timespec ts[2];
struct vnode *vp;
+ struct vattr vattr;
int error;
if ((error = getutimes(tptr, ts)) != 0)
return (error);
- /* XXX Add NLC flag indicating modifying operation? */
+
+ /*
+ * NOTE: utimes() succeeds for the owner even if the file
+ * is not user-writable.
+ */
+ nd->nl_flags |= NLC_OWN | NLC_WRITE;
+
if ((error = nlookup(nd)) != 0)
return (error);
if ((error = ncp_writechk(&nd->nl_nch)) != 0)
return (error);
/*
- * NOTE: utimes() succeeds for the owner even if the file
- * is not user-writable.
+ * note: vget is required for any operation that might mod the vnode
+ * so VINACTIVE is properly cleared.
*/
- if ((error = vn_writechk(vp, &nd->nl_nch)) == 0 &&
- (error = VOP_ACCESS(vp, VWRITE | VOWN, nd->nl_cred)) == 0) {
- error = setutimes(vp, ts, tptr == NULL);
+ if ((error = vn_writechk(vp, &nd->nl_nch)) == 0) {
+ error = vget(vp, LK_EXCLUSIVE);
+ if (error == 0) {
+ error = setutimes(vp, &vattr, ts, (tptr == NULL));
+ vput(vp);
+ }
}
vrele(vp);
return (error);
return (error);
}
+/*
+ * Set utimes on a file descriptor. The creds used to open the
+ * file are used to determine whether the operation is allowed
+ * or not.
+ */
int
kern_futimes(int fd, struct timeval *tptr)
{
struct proc *p = td->td_proc;
struct timespec ts[2];
struct file *fp;
+ struct vnode *vp;
+ struct vattr vattr;
int error;
error = getutimes(tptr, ts);
return (error);
if (fp->f_nchandle.ncp)
error = ncp_writechk(&fp->f_nchandle);
- if (error == 0)
- error = setutimes((struct vnode *)fp->f_data, ts, tptr == NULL);
+ if (error == 0) {
+ vp = fp->f_data;
+ error = vget(vp, LK_EXCLUSIVE);
+ if (error == 0) {
+ error = VOP_GETATTR(vp, &vattr);
+ if (error == 0) {
+ error = naccess_va(&vattr, NLC_OWN | NLC_WRITE,
+ fp->f_cred);
+ }
+ if (error == 0) {
+ error = setutimes(vp, &vattr, ts,
+ (tptr == NULL));
+ }
+ vput(vp);
+ }
+ }
fdrop(fp);
return (error);
}
if (length < 0)
return(EINVAL);
- /* XXX Add NLC flag indicating modifying operation? */
+ nd->nl_flags |= NLC_WRITE | NLC_TRUNCATE;
if ((error = nlookup(nd)) != 0)
return (error);
if ((error = ncp_writechk(&nd->nl_nch)) != 0)
}
if (vp->v_type == VDIR) {
error = EISDIR;
- } else if ((error = vn_writechk(vp, &nd->nl_nch)) == 0 &&
- (error = VOP_ACCESS(vp, VWRITE, nd->nl_cred)) == 0) {
+ } else if ((error = vn_writechk(vp, &nd->nl_nch)) == 0) {
VATTR_NULL(&vattr);
vattr.va_size = length;
error = VOP_SETATTR(vp, &vattr, nd->nl_cred);
error = EINVAL;
goto done;
}
+ if (fp->f_flag & FAPPENDONLY) { /* inode was set s/uapnd */
+ error = EINVAL;
+ goto done;
+ }
vp = (struct vnode *)fp->f_data;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_type == VDIR) {
int error;
bwillinode(1);
- fromnd->nl_flags |= NLC_REFDVP | NLC_DELETE;
+ fromnd->nl_flags |= NLC_REFDVP | NLC_RENAME_SRC;
if ((error = nlookup(fromnd)) != 0)
return (error);
if ((fnchd.ncp = fromnd->nl_nch.ncp->nc_parent) == NULL)
fromnd->nl_flags &= ~NLC_NCPISLOCKED;
cache_unlock(&fromnd->nl_nch);
- tond->nl_flags |= NLC_RENAME | NLC_REFDVP;
+ tond->nl_flags |= NLC_RENAME_DST | NLC_REFDVP;
if ((error = nlookup(tond)) != 0) {
cache_drop(&fnchd);
return (error);
struct ucred *cred = nd->nl_cred;
struct vattr vat;
struct vattr *vap = &vat;
- int mode, error;
+ int error;
/*
* Lookup the path and create or obtain the vnode. After a
* XXX with only a little work we should be able to avoid locking
* the vnode if FWRITE, O_CREAT, and O_TRUNC are *not* set.
*/
+ nd->nl_flags |= NLC_OPEN;
+ if (fmode & O_APPEND)
+ nd->nl_flags |= NLC_APPEND;
+ if (fmode & O_TRUNC)
+ nd->nl_flags |= NLC_TRUNCATE;
+ if (fmode & FREAD)
+ nd->nl_flags |= NLC_READ;
+ if (fmode & FWRITE)
+ nd->nl_flags |= NLC_WRITE;
+
if (fmode & O_CREAT) {
/*
* CONDITIONAL CREATE FILE CASE
goto bad;
}
if ((fmode & O_CREAT) == 0) {
- mode = 0;
if (fmode & (FWRITE | O_TRUNC)) {
if (vp->v_type == VDIR) {
error = EISDIR;
}
goto bad;
}
- mode |= VWRITE;
- }
- if (fmode & FREAD)
- mode |= VREAD;
- if (mode) {
- error = VOP_ACCESS(vp, mode, cred);
- if (error) {
- /*
- * Special stale handling, re-resolve the
- * vnode.
- */
- if (error == ESTALE) {
- vput(vp);
- vp = NULL;
- cache_setunresolved(&nd->nl_nch);
- error = cache_resolve(&nd->nl_nch, cred);
- if (error == 0)
- goto again;
- }
- goto bad;
- }
}
}
if (fmode & O_TRUNC) {
* used to open the file.
*/
if (fp) {
+ if (nd->nl_flags & NLC_APPENDONLY)
+ fmode |= FAPPENDONLY;
fp->f_nchandle = nd->nl_nch;
cache_zero(&nd->nl_nch);
cache_unlock(&fp->f_nchandle);
#if defined(_KERNEL) || defined(_KERNEL_STRUCTURES)
#define FREVOKED 0x10000000 /* revoked by fdrevoke() */
+#define FAPPENDONLY 0x20000000 /* O_APPEND cannot be changed */
#endif
#define O_FMASK (O_FBLOCKING|O_FNONBLOCKING|O_FAPPEND|O_FOFFSET|\
/* bits to save after open */
#define FMASK (FREAD|FWRITE|FAPPEND|FASYNC|FFSYNC|FNONBLOCK|\
- O_DIRECT|O_MAPONREAD)
+ FAPPENDONLY|FREVOKED|O_DIRECT|O_MAPONREAD)
/* bits settable by fcntl(F_SETFL, ...) */
#define FCNTLFLAGS (FAPPEND|FASYNC|FFSYNC|FNONBLOCK|FPOSIXSHM|\
O_DIRECT|O_MAPONREAD)
int nl_vp_fmode;
};
+/*
+ * NOTE: nlookup() flags related to open checks do not actually perform
+ * any modifying operation. e.g. the file isn't created, truncated,
+ * etc. vn_open() handles that.
+ */
#define NLC_FOLLOW 0x00000001 /* follow leaf symlink */
#define NLC_NOCROSSMOUNT 0x00000002 /* do not cross mount points */
#define NLC_HASBUF 0x00000004 /* nl_path is allocated */
#define NLC_WILLBEDIR 0x00000010
#define NLC_NCPISLOCKED 0x00000020
#define NLC_LOCKVP 0x00000040 /* nl_open_vp from vn_open */
-#define NLC_CREATE 0x00000080
-#define NLC_DELETE 0x00000100
-#define NLC_RENAME 0x00000200 /* rename (target) */
+#define NLC_CREATE 0x00000080 /* do create checks */
+#define NLC_DELETE 0x00000100 /* do delete checks */
+#define NLC_RENAME_DST 0x00000200 /* do rename checks (target) */
+#define NLC_OPEN 0x00000400 /* do open checks */
+#define NLC_TRUNCATE 0x00000800 /* do truncation checks */
+#define NLC_HLINK 0x00001000 /* do hardlink checks */
+#define NLC_RENAME_SRC 0x00002000 /* do rename checks (source) */
+#define NLC_UNUSED00004000 0x00004000
+#define NLC_UNUSED00008000 0x00008000
#define NLC_NFS_RDONLY 0x00010000 /* set by nfs_namei() only */
#define NLC_NFS_NOSOFTLINKTRAV 0x00020000 /* do not traverse softlnks */
#define NLC_REFDVP 0x00040000 /* set ref'd/unlocked nl_dvp */
+#define NLC_APPEND 0x00100000 /* open check: append */
+#define NLC_UNUSED00200000 0x00200000
+
+#define NLC_READ 0x00400000 /* require read access */
+#define NLC_WRITE 0x00800000 /* require write access */
+#define NLC_EXEC 0x01000000 /* require execute access */
+#define NLC_EXCL 0x02000000 /* open check: exclusive */
+#define NLC_OWN 0x04000000 /* open check: owner override */
+#define NLC_UNUSED08000000 0x08000000
+#define NLC_STICKY 0x10000000 /* indicate sticky case */
+#define NLC_APPENDONLY 0x20000000 /* indicate append-only */
+#define NLC_IMMUTABLE 0x40000000 /* indicate immutable set */
+#define NLC_WRITABLE 0x80000000 /* indicate writeable */
+
+/*
+ * All checks. If any of these bits are set general user/group/world
+ * permission checks will be done by nlookup().
+ */
+#define NLC_ALLCHKS (NLC_CREATE | NLC_DELETE | NLC_RENAME_DST | \
+ NLC_OPEN | NLC_TRUNCATE | NLC_RENAME_SRC | \
+ NLC_READ | NLC_WRITE | NLC_EXEC | NLC_OWN)
+
#ifdef _KERNEL
int nlookup_init(struct nlookupdata *, const char *, enum uio_seg, int);
int nreadsymlink(struct nlookupdata *nd, struct nchandle *nch,
struct nlcomponent *nlc);
int naccess(struct nchandle *nch, int vmode, struct ucred *cred, int *stickyp);
+int naccess_va(struct vattr *va, int nflags, struct ucred *cred);
#endif
/*
* Modes. Note that these V-modes must match file S_I*USR, SUID, SGID,
* and SVTX flag bits.
- *
- * VOWN, VCREATE, VDELETE, VRENAME, and VEXCL may only be used in
- * naccess() calls.
*/
-#define VRENAME 0200000 /* set with VCREATE or VDELETE if rename */
-#define VOWN 0100000 /* succeed if file owner or (other flags) */
-#define VDELETE 040000 /* delete if the file/dir exists */
-#define VCREATE 020000 /* create if the file/dir does not exist */
-#define VEXCL 010000 /* error if the file/dir already exists */
-
#define VSUID 04000 /* set user id on execution */
#define VSGID 02000 /* set group id on execution */
#define VSVTX 01000 /* save swapped text even after use */
/* Otherwise, check the owner. */
if (cred->cr_uid == ip->i_uid) {
- if (mode & VOWN)
- return (0);
if (mode & VEXEC)
mask |= S_IXUSR;
if (mode & VREAD)
/* Otherwise, check the owner. */
if (cred->cr_uid == hp->h_uid) {
- if (mode & VOWN)
- return (0);
if (mode & VEXEC)
mask |= S_IXUSR;
if (mode & VREAD)
/* Otherwise, check the owner. */
if (cred->cr_uid == pmp->pm_uid) {
- if (mode & VOWN)
- return (0);
if (mode & VEXEC)
mask |= S_IXUSR;
if (mode & VREAD)
pubflag = nfs_ispublicfh(fhp);
- error = nfs_namei(&nd, cred, NAMEI_LOOKUP, NULL, &vp,
+ error = nfs_namei(&nd, cred, 0, NULL, &vp,
fhp, len, slp, nam, &md, &dpos,
&dirp, td, (nfsd->nd_flag & ND_KERBAUTH), pubflag);
* be valid at all if an error occurs so we have to invalidate it
* prior to calling nfsm_reply ( which might goto nfsmout ).
*/
- error = nfs_namei(&nd, cred, NAMEI_CREATE, &dvp, &vp,
+ error = nfs_namei(&nd, cred, NLC_CREATE, &dvp, &vp,
fhp, len, slp, nam, &md, &dpos, &dirp,
td, (nfsd->nd_flag & ND_KERBAUTH), FALSE);
mp = vfs_getvfs(&fhp->fh_fsid);
* nfsmout.
*/
- error = nfs_namei(&nd, cred, NAMEI_CREATE, &dvp, &vp,
+ error = nfs_namei(&nd, cred, NLC_CREATE, &dvp, &vp,
fhp, len, slp, nam, &md, &dpos, &dirp,
td, (nfsd->nd_flag & ND_KERBAUTH), FALSE);
if (dirp)
nfsm_srvmtofh(fhp);
nfsm_srvnamesiz(len);
- error = nfs_namei(&nd, cred, NAMEI_DELETE, &dvp, &vp,
+ error = nfs_namei(&nd, cred, NLC_DELETE, &dvp, &vp,
fhp, len, slp, nam, &md, &dpos, &dirp,
td, (nfsd->nd_flag & ND_KERBAUTH), FALSE);
if (dirp) {
* the second nfs_namei() call, in case it is remapped.
*/
saved_uid = cred->cr_uid;
- error = nfs_namei(&fromnd, cred, NAMEI_DELETE, NULL, NULL,
+ error = nfs_namei(&fromnd, cred, NLC_RENAME_SRC,
+ NULL, NULL,
ffhp, len, slp, nam, &md, &dpos, &fdirp,
td, (nfsd->nd_flag & ND_KERBAUTH), FALSE);
if (fdirp) {
nfsm_strsiz(len2, NFS_MAXNAMLEN);
cred->cr_uid = saved_uid;
- error = nfs_namei(&tond, cred, NAMEI_RENAME, NULL, NULL,
+ error = nfs_namei(&tond, cred, NLC_RENAME_DST, NULL, NULL,
tfhp, len2, slp, nam, &md, &dpos, &tdirp,
td, (nfsd->nd_flag & ND_KERBAUTH), FALSE);
if (tdirp) {
goto out1;
}
- error = nfs_namei(&nd, cred, NAMEI_CREATE, &dvp, &vp,
+ error = nfs_namei(&nd, cred, NLC_CREATE, &dvp, &vp,
dfhp, len, slp, nam, &md, &dpos, &dirp,
td, (nfsd->nd_flag & ND_KERBAUTH), FALSE);
if (dirp) {
nfsm_srvmtofh(fhp);
nfsm_srvnamesiz(len);
- error = nfs_namei(&nd, cred, NAMEI_CREATE, &dvp, &vp,
+ error = nfs_namei(&nd, cred, NLC_CREATE, &dvp, &vp,
fhp, len, slp, nam, &md, &dpos, &dirp,
td, (nfsd->nd_flag & ND_KERBAUTH), FALSE);
if (dirp) {
nfsm_srvmtofh(fhp);
nfsm_srvnamesiz(len);
- error = nfs_namei(&nd, cred, NAMEI_CREATE, &dvp, &vp,
+ error = nfs_namei(&nd, cred, NLC_CREATE, &dvp, &vp,
fhp, len, slp, nam, &md, &dpos, &dirp,
td, (nfsd->nd_flag & ND_KERBAUTH), FALSE);
if (dirp) {
nfsm_srvmtofh(fhp);
nfsm_srvnamesiz(len);
- error = nfs_namei(&nd, cred, NAMEI_DELETE, &dvp, &vp,
+ error = nfs_namei(&nd, cred, NLC_DELETE, &dvp, &vp,
fhp, len, slp, nam, &md, &dpos, &dirp,
td, (nfsd->nd_flag & ND_KERBAUTH), FALSE);
if (dirp) {
* to cleanup.
*/
int
-nfs_namei(struct nlookupdata *nd, struct ucred *cred, int nameiop,
+nfs_namei(struct nlookupdata *nd, struct ucred *cred, int nflags,
struct vnode **dvpp, struct vnode **vpp,
fhandle_t *fhp, int len,
struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
int kerbflag, int pubflag)
{
int i, rem;
- int flags;
struct mbuf *md;
char *fromcp, *tocp, *cp;
char *namebuf;
int error, rdonly;
namebuf = objcache_get(namei_oc, M_WAITOK);
- flags = 0;
*dirpp = NULL;
/*
* that dp is ref'd, but we no longer 'own' the ref (*dirpp owns it).
*/
if (pubflag == 0) {
- flags |= NLC_NFS_NOSOFTLINKTRAV;
- flags |= NLC_NOCROSSMOUNT;
+ nflags |= NLC_NFS_NOSOFTLINKTRAV;
+ nflags |= NLC_NOCROSSMOUNT;
}
if (rdonly)
- flags |= NLC_NFS_RDONLY;
- if (nameiop == NAMEI_CREATE)
- flags |= NLC_CREATE;
- if (nameiop == NAMEI_RENAME)
- flags |= NLC_RENAME;
+ nflags |= NLC_NFS_RDONLY;
/*
* We need a starting ncp from the directory vnode dp. dp must not
*/
if ((error = cache_fromdvp(dp, cred, 1, &nch)) != 0)
goto out;
- nlookup_init_raw(nd, namebuf, UIO_SYSSPACE, flags, cred, &nch);
+ nlookup_init_raw(nd, namebuf, UIO_SYSSPACE, nflags, cred, &nch);
cache_drop(&nch);
/*
/* Otherwise, check the owner. */
if (cred->cr_uid == ip->i_mp->ntm_uid) {
- if (mode & VOWN)
- return (0);
if (mode & VEXEC)
mask |= S_IXUSR;
if (mode & VREAD)
mode >>= 3;
if (!groupmember(nmp->m.gid, cred))
mode >>= 3;
- } else if (mode & VOWN) {
- return (0);
}
error = (((vp->v_type == VREG) ? nmp->m.file_mode : nmp->m.dir_mode) & mode) == mode ? 0 : EACCES;
return error;
mode >>= 3;
if (!groupmember(smp->sm_args.gid, cred))
mode >>= 3;
- } else if (mode & VOWN) {
- return (0);
}
error = (((vp->v_type == VREG) ? smp->sm_args.file_mode : smp->sm_args.dir_mode) & mode) == mode ? 0 : EACCES;
return error;
/* Otherwise, check the owner. */
if (cred->cr_uid == node->fentry->uid) {
- if (a_mode & VOWN)
- return (0);
if (a_mode & VEXEC)
mask |= S_IXUSR;
if (a_mode & VREAD)
static int ufs_mkdir (struct vop_old_mkdir_args *);
static int ufs_mknod (struct vop_old_mknod_args *);
static int ufs_mmap (struct vop_mmap_args *);
-static int ufs_open (struct vop_open_args *);
static int ufs_print (struct vop_print_args *);
static int ufs_readdir (struct vop_readdir_args *);
static int ufs_readlink (struct vop_readlink_args *);
}
/*
- * Open called.
- *
- * Nothing to do.
- *
- * ufs_open(struct vnode *a_vp, int a_mode, struct ucred *a_cred,
- * struct file *a_fp)
- */
-/* ARGSUSED */
-static
-int
-ufs_open(struct vop_open_args *ap)
-{
- struct vnode *vp = ap->a_vp;
-
- /*
- * Files marked append-only must be opened for appending.
- */
- if ((VTOI(vp)->i_flags & APPEND) &&
- (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) {
- return (EPERM);
- }
-
- return (vop_stdopen(ap));
-}
-
-/*
* Close called.
*
* Update the times on the inode.
}
}
+#if 0 /* handled by kernel now */
/* If immutable bit set, nobody gets to write it. */
if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE))
return (EPERM);
+#endif
/* Otherwise, user id 0 always gets access. */
if (cred->cr_uid == 0)
/* Otherwise, check the owner. */
if (cred->cr_uid == ip->i_uid) {
- if (mode & VOWN)
- return (0);
if (mode & VEXEC)
mask |= S_IXUSR;
if (mode & VREAD)
int error;
ip = VTOI(vp);
+#if 0 /* handled by kernel now */
if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
(VTOI(dvp)->i_flags & APPEND)) {
error = EPERM;
goto out;
}
+#endif
error = ufs_dirremove(dvp, ip, ap->a_cnp->cn_flags, 0);
VN_KNOTE(vp, NOTE_DELETE);
VN_KNOTE(dvp, NOTE_WRITE);
+#if 0
out:
+#endif
return (error);
}
error = EMLINK;
goto out1;
}
+#if 0 /* handled by kernel now, also DragonFly allows this */
if (ip->i_flags & (IMMUTABLE | APPEND)) {
error = EPERM;
goto out1;
}
+#endif
ip->i_effnlink++;
ip->i_nlink++;
ip->i_flag |= IN_CHANGE;
return (error);
}
+#if 0 /* handled by kernel now */
if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
(VTOI(tdvp)->i_flags & APPEND))) {
error = EPERM;
goto abortit;
}
+#endif
/*
* Renaming a file to itself has no effect. The upper layers should
error = EMLINK;
goto abortit;
}
+#if 0 /* handled by kernel now */
if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))
|| (dp->i_flags & APPEND)) {
vn_unlock(fvp);
error = EPERM;
goto abortit;
}
+#endif
if ((ip->i_mode & IFMT) == IFDIR) {
/*
* Avoid ".", "..", and aliases of "." for obvious reasons.
error = ENOTEMPTY;
goto out;
}
+#if 0 /* handled by kernel now */
if ((dp->i_flags & APPEND)
|| (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
error = EPERM;
goto out;
}
+#endif
/*
* Delete reference to directory before purging
* inode. If we crash in between, the directory
.vop_old_mkdir = ufs_mkdir,
.vop_old_mknod = ufs_mknod,
.vop_mmap = ufs_mmap,
- .vop_open = ufs_open,
+ .vop_open = vop_stdopen,
.vop_pathconf = vop_stdpathconf,
.vop_poll = vop_stdpoll,
.vop_kqfilter = ufs_kqfilter,