Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_syscalls.c
Show First 20 Lines • Show All 1,437 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_linkat(struct thread *td, struct linkat_args *uap) | sys_linkat(struct thread *td, struct linkat_args *uap) | ||||
{ | { | ||||
int flag; | int flag; | ||||
flag = uap->flag; | flag = uap->flag; | ||||
if (flag & ~AT_SYMLINK_FOLLOW) | if ((flag & ~(AT_SYMLINK_FOLLOW | AT_BENEATH)) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2, | return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2, | ||||
UIO_USERSPACE, (flag & AT_SYMLINK_FOLLOW) ? FOLLOW : NOFOLLOW)); | UIO_USERSPACE, ((flag & AT_SYMLINK_FOLLOW) != 0 ? FOLLOW : | ||||
NOFOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0))); | |||||
} | } | ||||
int hardlink_check_uid = 0; | int hardlink_check_uid = 0; | ||||
SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, | SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, | ||||
&hardlink_check_uid, 0, | &hardlink_check_uid, 0, | ||||
"Unprivileged processes cannot create hard links to files owned by other " | "Unprivileged processes cannot create hard links to files owned by other " | ||||
"users"); | "users"); | ||||
static int hardlink_check_gid = 0; | static int hardlink_check_gid = 0; | ||||
▲ Show 20 Lines • Show All 267 Lines • ▼ Show 20 Lines | |||||
struct unlink_args { | struct unlink_args { | ||||
char *path; | char *path; | ||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_unlink(struct thread *td, struct unlink_args *uap) | sys_unlink(struct thread *td, struct unlink_args *uap) | ||||
{ | { | ||||
return (kern_unlinkat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0)); | return (kern_unlinkat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0, 0)); | ||||
} | } | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct unlinkat_args { | struct unlinkat_args { | ||||
int fd; | int fd; | ||||
char *path; | char *path; | ||||
int flag; | int flag; | ||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_unlinkat(struct thread *td, struct unlinkat_args *uap) | sys_unlinkat(struct thread *td, struct unlinkat_args *uap) | ||||
{ | { | ||||
int flag = uap->flag; | int fd, flag; | ||||
int fd = uap->fd; | char *path; | ||||
char *path = uap->path; | |||||
if (flag & ~AT_REMOVEDIR) | flag = uap->flag; | ||||
fd = uap->fd; | |||||
path = uap->path; | |||||
if ((flag & ~(AT_REMOVEDIR | AT_BENEATH)) != 0) | |||||
return (EINVAL); | return (EINVAL); | ||||
if (flag & AT_REMOVEDIR) | if ((uap->flag & AT_REMOVEDIR) != 0) | ||||
return (kern_rmdirat(td, fd, path, UIO_USERSPACE)); | return (kern_rmdirat(td, fd, path, UIO_USERSPACE, flag)); | ||||
else | else | ||||
return (kern_unlinkat(td, fd, path, UIO_USERSPACE, 0)); | return (kern_unlinkat(td, fd, path, UIO_USERSPACE, flag, 0)); | ||||
} | } | ||||
int | int | ||||
kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | ||||
ino_t oldinum) | int flag, ino_t oldinum) | ||||
{ | { | ||||
struct mount *mp; | struct mount *mp; | ||||
struct vnode *vp; | struct vnode *vp; | ||||
struct nameidata nd; | struct nameidata nd; | ||||
struct stat sb; | struct stat sb; | ||||
int error; | int error; | ||||
restart: | restart: | ||||
bwillwrite(); | bwillwrite(); | ||||
NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1, | NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | | ||||
((flag & AT_BENEATH) != 0 ? BENEATH : 0), | |||||
pathseg, path, fd, &cap_unlinkat_rights, td); | pathseg, path, fd, &cap_unlinkat_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error == EINVAL ? EPERM : error); | return (error == EINVAL ? EPERM : error); | ||||
vp = nd.ni_vp; | vp = nd.ni_vp; | ||||
if (vp->v_type == VDIR && oldinum == 0) { | if (vp->v_type == VDIR && oldinum == 0) { | ||||
error = EPERM; /* POSIX */ | error = EPERM; /* POSIX */ | ||||
} else if (oldinum != 0 && | } else if (oldinum != 0 && | ||||
((error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td)) == 0) && | ((error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td)) == 0) && | ||||
▲ Show 20 Lines • Show All 174 Lines • ▼ Show 20 Lines | |||||
kern_accessat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | kern_accessat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | ||||
int flag, int amode) | int flag, int amode) | ||||
{ | { | ||||
struct ucred *cred, *usecred; | struct ucred *cred, *usecred; | ||||
struct vnode *vp; | struct vnode *vp; | ||||
struct nameidata nd; | struct nameidata nd; | ||||
int error; | int error; | ||||
if (flag & ~AT_EACCESS) | if ((flag & ~(AT_EACCESS | AT_BENEATH)) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0) | if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
/* | /* | ||||
* Create and modify a temporary credential instead of one that | * Create and modify a temporary credential instead of one that | ||||
* is potentially shared (if we need one). | * is potentially shared (if we need one). | ||||
*/ | */ | ||||
cred = td->td_ucred; | cred = td->td_ucred; | ||||
if ((flag & AT_EACCESS) == 0 && | if ((flag & AT_EACCESS) == 0 && | ||||
((cred->cr_uid != cred->cr_ruid || | ((cred->cr_uid != cred->cr_ruid || | ||||
cred->cr_rgid != cred->cr_groups[0]))) { | cred->cr_rgid != cred->cr_groups[0]))) { | ||||
usecred = crdup(cred); | usecred = crdup(cred); | ||||
usecred->cr_uid = cred->cr_ruid; | usecred->cr_uid = cred->cr_ruid; | ||||
usecred->cr_groups[0] = cred->cr_rgid; | usecred->cr_groups[0] = cred->cr_rgid; | ||||
td->td_ucred = usecred; | td->td_ucred = usecred; | ||||
} else | } else | ||||
usecred = cred; | usecred = cred; | ||||
AUDIT_ARG_VALUE(amode); | AUDIT_ARG_VALUE(amode); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | | NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | | ||||
AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights, | AUDITVNODE1 | ((flag & AT_BENEATH) != 0 ? BENEATH : 0), | ||||
td); | pathseg, path, fd, &cap_fstat_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
goto out; | goto out; | ||||
vp = nd.ni_vp; | vp = nd.ni_vp; | ||||
error = vn_access(vp, amode, usecred, td); | error = vn_access(vp, amode, usecred, td); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
vput(vp); | vput(vp); | ||||
out: | out: | ||||
▲ Show 20 Lines • Show All 274 Lines • ▼ Show 20 Lines | |||||
kern_statat(struct thread *td, int flag, int fd, char *path, | kern_statat(struct thread *td, int flag, int fd, char *path, | ||||
enum uio_seg pathseg, struct stat *sbp, | enum uio_seg pathseg, struct stat *sbp, | ||||
void (*hook)(struct vnode *vp, struct stat *sbp)) | void (*hook)(struct vnode *vp, struct stat *sbp)) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
struct stat sb; | struct stat sb; | ||||
int error; | int error; | ||||
if (flag & ~AT_SYMLINK_NOFOLLOW) | if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : | NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) != 0 ? | ||||
FOLLOW) | LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, fd, | NOFOLLOW : FOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0) | | ||||
LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, fd, | |||||
&cap_fstat_rights, td); | &cap_fstat_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); | error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); | ||||
if (error == 0) { | if (error == 0) { | ||||
SDT_PROBE2(vfs, , stat, mode, path, sb.st_mode); | SDT_PROBE2(vfs, , stat, mode, path, sb.st_mode); | ||||
if (S_ISREG(sb.st_mode)) | if (S_ISREG(sb.st_mode)) | ||||
▲ Show 20 Lines • Show All 294 Lines • ▼ Show 20 Lines | struct chflagsat_args { | ||||
const char *path; | const char *path; | ||||
u_long flags; | u_long flags; | ||||
int atflag; | int atflag; | ||||
} | } | ||||
#endif | #endif | ||||
int | int | ||||
sys_chflagsat(struct thread *td, struct chflagsat_args *uap) | sys_chflagsat(struct thread *td, struct chflagsat_args *uap) | ||||
{ | { | ||||
int fd = uap->fd; | |||||
const char *path = uap->path; | |||||
u_long flags = uap->flags; | |||||
int atflag = uap->atflag; | |||||
if (atflag & ~AT_SYMLINK_NOFOLLOW) | if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (kern_chflagsat(td, fd, path, UIO_USERSPACE, flags, atflag)); | return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE, | ||||
uap->flags, uap->atflag)); | |||||
} | } | ||||
/* | /* | ||||
* Same as chflags() but doesn't follow symlinks. | * Same as chflags() but doesn't follow symlinks. | ||||
*/ | */ | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct lchflags_args { | struct lchflags_args { | ||||
const char *path; | const char *path; | ||||
Show All 12 Lines | |||||
kern_chflagsat(struct thread *td, int fd, const char *path, | kern_chflagsat(struct thread *td, int fd, const char *path, | ||||
enum uio_seg pathseg, u_long flags, int atflag) | enum uio_seg pathseg, u_long flags, int atflag) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
int error, follow; | int error, follow; | ||||
AUDIT_ARG_FFLAGS(flags); | AUDIT_ARG_FFLAGS(flags); | ||||
follow = (atflag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; | follow = (atflag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; | ||||
follow |= (atflag & AT_BENEATH) != 0 ? BENEATH : 0; | |||||
NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, | NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, | ||||
&cap_fchflags_rights, td); | &cap_fchflags_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
error = setfflags(td, nd.ni_vp, flags); | error = setfflags(td, nd.ni_vp, flags); | ||||
vrele(nd.ni_vp); | vrele(nd.ni_vp); | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | struct fchmodat_args { | ||||
char *path; | char *path; | ||||
mode_t mode; | mode_t mode; | ||||
int flag; | int flag; | ||||
} | } | ||||
#endif | #endif | ||||
int | int | ||||
sys_fchmodat(struct thread *td, struct fchmodat_args *uap) | sys_fchmodat(struct thread *td, struct fchmodat_args *uap) | ||||
{ | { | ||||
int flag = uap->flag; | |||||
int fd = uap->fd; | |||||
char *path = uap->path; | |||||
mode_t mode = uap->mode; | |||||
if (flag & ~AT_SYMLINK_NOFOLLOW) | if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (kern_fchmodat(td, fd, path, UIO_USERSPACE, mode, flag)); | return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE, | ||||
uap->mode, uap->flag)); | |||||
} | } | ||||
/* | /* | ||||
* Change mode of a file given path name (don't follow links.) | * Change mode of a file given path name (don't follow links.) | ||||
*/ | */ | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct lchmod_args { | struct lchmod_args { | ||||
char *path; | char *path; | ||||
Show All 11 Lines | |||||
int | int | ||||
kern_fchmodat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | kern_fchmodat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | ||||
mode_t mode, int flag) | mode_t mode, int flag) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
int error, follow; | int error, follow; | ||||
AUDIT_ARG_MODE(mode); | AUDIT_ARG_MODE(mode); | ||||
follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; | follow = (flag & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : FOLLOW; | ||||
follow |= (flag & AT_BENEATH) != 0 ? BENEATH : 0; | |||||
NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, | NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, | ||||
&cap_fchmod_rights, td); | &cap_fchmod_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
error = setfmode(td, td->td_ucred, nd.ni_vp, mode); | error = setfmode(td, td->td_ucred, nd.ni_vp, mode); | ||||
vrele(nd.ni_vp); | vrele(nd.ni_vp); | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | struct fchownat_args { | ||||
uid_t uid; | uid_t uid; | ||||
gid_t gid; | gid_t gid; | ||||
int flag; | int flag; | ||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_fchownat(struct thread *td, struct fchownat_args *uap) | sys_fchownat(struct thread *td, struct fchownat_args *uap) | ||||
{ | { | ||||
int flag; | |||||
flag = uap->flag; | if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) | ||||
if (flag & ~AT_SYMLINK_NOFOLLOW) | |||||
return (EINVAL); | return (EINVAL); | ||||
return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid, | return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid, | ||||
uap->gid, uap->flag)); | uap->gid, uap->flag)); | ||||
} | } | ||||
int | int | ||||
kern_fchownat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | kern_fchownat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | ||||
int uid, int gid, int flag) | int uid, int gid, int flag) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
int error, follow; | int error, follow; | ||||
AUDIT_ARG_OWNER(uid, gid); | AUDIT_ARG_OWNER(uid, gid); | ||||
follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; | follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; | ||||
follow |= (flag & AT_BENEATH) != 0 ? BENEATH : 0; | |||||
NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, | NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, | ||||
&cap_fchown_rights, td); | &cap_fchown_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
error = setfown(td, td->td_ucred, nd.ni_vp, uid, gid); | error = setfown(td, td->td_ucred, nd.ni_vp, uid, gid); | ||||
vrele(nd.ni_vp); | vrele(nd.ni_vp); | ||||
▲ Show 20 Lines • Show All 335 Lines • ▼ Show 20 Lines | |||||
int | int | ||||
kern_utimensat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | kern_utimensat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | ||||
struct timespec *tptr, enum uio_seg tptrseg, int flag) | struct timespec *tptr, enum uio_seg tptrseg, int flag) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
struct timespec ts[2]; | struct timespec ts[2]; | ||||
int error, flags; | int error, flags; | ||||
if (flag & ~AT_SYMLINK_NOFOLLOW) | if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0) | if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0) | ||||
return (error); | return (error); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : | NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : | ||||
FOLLOW) | AUDITVNODE1, pathseg, path, fd, | FOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0) | AUDITVNODE1, | ||||
&cap_futimes_rights, td); | pathseg, path, fd, &cap_futimes_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
/* | /* | ||||
* We are allowed to call namei() regardless of 2xUTIME_OMIT. | * We are allowed to call namei() regardless of 2xUTIME_OMIT. | ||||
* POSIX states: | * POSIX states: | ||||
* "If both tv_nsec fields are UTIME_OMIT... EACCESS may be detected." | * "If both tv_nsec fields are UTIME_OMIT... EACCESS may be detected." | ||||
* "Search permission is denied by a component of the path prefix." | * "Search permission is denied by a component of the path prefix." | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 427 Lines • ▼ Show 20 Lines | |||||
struct rmdir_args { | struct rmdir_args { | ||||
char *path; | char *path; | ||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_rmdir(struct thread *td, struct rmdir_args *uap) | sys_rmdir(struct thread *td, struct rmdir_args *uap) | ||||
{ | { | ||||
return (kern_rmdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE)); | return (kern_rmdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0)); | ||||
} | } | ||||
int | int | ||||
kern_rmdirat(struct thread *td, int fd, char *path, enum uio_seg pathseg) | kern_rmdirat(struct thread *td, int fd, char *path, enum uio_seg pathseg, | ||||
int flag) | |||||
{ | { | ||||
struct mount *mp; | struct mount *mp; | ||||
struct vnode *vp; | struct vnode *vp; | ||||
struct nameidata nd; | struct nameidata nd; | ||||
int error; | int error; | ||||
restart: | restart: | ||||
bwillwrite(); | bwillwrite(); | ||||
NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1, | NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | | ||||
((flag & AT_BENEATH) != 0 ? BENEATH : 0), | |||||
pathseg, path, fd, &cap_unlinkat_rights, td); | pathseg, path, fd, &cap_unlinkat_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
vp = nd.ni_vp; | vp = nd.ni_vp; | ||||
if (vp->v_type != VDIR) { | if (vp->v_type != VDIR) { | ||||
error = ENOTDIR; | error = ENOTDIR; | ||||
goto out; | goto out; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 918 Lines • Show Last 20 Lines |