* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
* %sccs.include.redist.c%
* @(#)vfs_syscalls.c 8.38 (Berkeley) %G%
#include <sys/filedesc.h>
#include <sys/syscallargs.h>
static int change_dir
__P((struct nameidata
*ndp
, struct proc
*p
));
static void checkdirs
__P((struct vnode
*olddp
));
#define CURCOUNT (curproc ? curproc->p_spare[0] : 0)
#define CHECKPOINTREF int oldrefcount = CURCOUNT;
#define CHECKREFS(F) if (oldrefcount != CURCOUNT) \
printf("REFCOUNT: %s, old=%d, new=%d\n", (F), oldrefcount, CURCOUNT);
* Virtual File System System Calls
register struct mount_args
/* {
syscallarg(caddr_t) data;
char fstypename
[MFSNAMELEN
];
* Get vnode to be covered
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
if (SCARG(uap
, flags
) & MNT_UPDATE
) {
if ((vp
->v_flag
& VROOT
) == 0) {
* We only allow the filesystem to be reloaded if it
* is currently mounted read-only.
if ((SCARG(uap
, flags
) & MNT_RELOAD
) &&
((mp
->mnt_flag
& MNT_RDONLY
) == 0)) {
return (EOPNOTSUPP
); /* Needs translation */
SCARG(uap
, flags
) & (MNT_RELOAD
| MNT_FORCE
| MNT_UPDATE
);
* Only root, or the user that did the original mount is
* permitted to update it.
if (mp
->mnt_stat
.f_owner
!= p
->p_ucred
->cr_uid
&&
(error
= suser(p
->p_ucred
, &p
->p_acflag
))) {
* Do not allow NFS export by non-root users. Silently
* enforce MNT_NOSUID and MNT_NODEV for non-root users.
if (p
->p_ucred
->cr_uid
!= 0) {
if (SCARG(uap
, flags
) & MNT_EXPORTED
) {
SCARG(uap
, flags
) |= MNT_NOSUID
| MNT_NODEV
;
* If the user is not root, ensure that they own the directory
* onto which we are attempting to mount.
if ((error
= VOP_GETATTR(vp
, &va
, p
->p_ucred
, p
)) ||
(va
.va_uid
!= p
->p_ucred
->cr_uid
&&
(error
= suser(p
->p_ucred
, &p
->p_acflag
)))) {
* Do not allow NFS export by non-root users. Silently
* enforce MNT_NOSUID and MNT_NODEV for non-root users.
if (p
->p_ucred
->cr_uid
!= 0) {
if (SCARG(uap
, flags
) & MNT_EXPORTED
) {
SCARG(uap
, flags
) |= MNT_NOSUID
| MNT_NODEV
;
if (error
= vinvalbuf(vp
, V_SAVE
, p
->p_ucred
, p
, 0, 0))
if (vp
->v_type
!= VDIR
) {
* Historically filesystem types were identified by number. If we
* get an integer for the filesystem type instead of a string, we
* check to see if it matches one of the historic filesystem types.
fstypenum
= (u_long
)SCARG(uap
, type
);
if (fstypenum
< maxvfsconf
) {
for (vfsp
= vfsconf
; vfsp
; vfsp
= vfsp
->vfc_next
)
if (vfsp
->vfc_typenum
== fstypenum
)
strncpy(fstypename
, vfsp
->vfc_name
, MFSNAMELEN
);
if (error
= copyinstr(SCARG(uap
, type
), fstypename
, MFSNAMELEN
, NULL
)) {
for (vfsp
= vfsconf
; vfsp
; vfsp
= vfsp
->vfc_next
)
if (!strcmp(vfsp
->vfc_name
, fstypename
))
if (vp
->v_mountedhere
!= NULL
) {
* Allocate and initialize the filesystem.
mp
= (struct mount
*)malloc((u_long
)sizeof(struct mount
),
bzero((char *)mp
, (u_long
)sizeof(struct mount
));
mp
->mnt_op
= vfsp
->vfc_vfsops
;
if (error
= vfs_lock(mp
)) {
free((caddr_t
)mp
, M_MOUNT
);
mp
->mnt_stat
.f_type
= vfsp
->vfc_typenum
;
mp
->mnt_flag
|= vfsp
->vfc_flags
& MNT_VISFLAGMASK
;
strncpy(mp
->mnt_stat
.f_fstypename
, vfsp
->vfc_name
, MFSNAMELEN
);
mp
->mnt_vnodecovered
= vp
;
mp
->mnt_stat
.f_owner
= p
->p_ucred
->cr_uid
;
* Set the mount level flags.
if (SCARG(uap
, flags
) & MNT_RDONLY
)
mp
->mnt_flag
|= MNT_RDONLY
;
else if (mp
->mnt_flag
& MNT_RDONLY
)
mp
->mnt_flag
|= MNT_WANTRDWR
;
mp
->mnt_flag
&=~ (MNT_NOSUID
| MNT_NOEXEC
| MNT_NODEV
|
MNT_SYNCHRONOUS
| MNT_UNION
| MNT_ASYNC
);
mp
->mnt_flag
|= SCARG(uap
, flags
) & (MNT_NOSUID
| MNT_NOEXEC
|
MNT_NODEV
| MNT_SYNCHRONOUS
| MNT_UNION
| MNT_ASYNC
);
error
= VFS_MOUNT(mp
, SCARG(uap
, path
), SCARG(uap
, data
), &nd
, p
);
if (mp
->mnt_flag
& MNT_UPDATE
) {
if (mp
->mnt_flag
& MNT_WANTRDWR
)
mp
->mnt_flag
&= ~MNT_RDONLY
;
(MNT_UPDATE
| MNT_RELOAD
| MNT_FORCE
| MNT_WANTRDWR
);
* Put the new filesystem on the mount list after root.
CIRCLEQ_INSERT_TAIL(&mountlist
, mp
, mnt_list
);
error
= VFS_START(mp
, 0, p
);
mp
->mnt_vnodecovered
->v_mountedhere
= (struct mount
*)0;
mp
->mnt_vfc
->vfc_refcount
--;
free((caddr_t
)mp
, M_MOUNT
);
* Scan all active processes to see if any of them have a current
* or root directory onto which the new filesystem has just been
* mounted. If so, replace them with the new mount point.
if (olddp
->v_usecount
== 1)
if (VFS_ROOT(olddp
->v_mountedhere
, &newdp
))
panic("mount: lost mount");
for (p
= allproc
.lh_first
; p
!= 0; p
= p
->p_list
.le_next
) {
if (fdp
->fd_cdir
== olddp
) {
if (fdp
->fd_rdir
== olddp
) {
if (rootvnode
== olddp
) {
* Note: unmount takes a path to the vnode mounted on as argument,
* not special file (as before).
register struct unmount_args
/* {
register struct vnode
*vp
;
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
* Only root, or the user that did the original mount is
* permitted to unmount this filesystem.
if ((mp
->mnt_stat
.f_owner
!= p
->p_ucred
->cr_uid
) &&
(error
= suser(p
->p_ucred
, &p
->p_acflag
))) {
* Don't allow unmounting the root file system.
if (mp
->mnt_flag
& MNT_ROOTFS
) {
* Must be the root of the filesystem
if ((vp
->v_flag
& VROOT
) == 0) {
return (dounmount(mp
, SCARG(uap
, flags
), p
));
* Do the actual file system unmount.
register struct mount
*mp
;
coveredvp
= mp
->mnt_vnodecovered
;
mp
->mnt_flag
|= MNT_UNMOUNT
;
if (error
= vfs_lock(mp
))
mp
->mnt_flag
&=~ MNT_ASYNC
;
vnode_pager_umount(mp
); /* release cached vnodes */
cache_purgevfs(mp
); /* remove cache entries for this file sys */
if (((mp
->mnt_flag
& MNT_RDONLY
) ||
(error
= VFS_SYNC(mp
, MNT_WAIT
, p
->p_ucred
, p
)) == 0) ||
error
= VFS_UNMOUNT(mp
, flags
, p
);
mp
->mnt_flag
&= ~MNT_UNMOUNT
;
CIRCLEQ_REMOVE(&mountlist
, mp
, mnt_list
);
if (coveredvp
!= NULLVP
) {
mp
->mnt_vnodecovered
->v_mountedhere
= (struct mount
*)0;
mp
->mnt_vfc
->vfc_refcount
--;
if (mp
->mnt_vnodelist
.lh_first
!= NULL
)
panic("unmount: dangling vnode");
free((caddr_t
)mp
, M_MOUNT
);
* Sync each mounted filesystem.
struct ctldebug debug0
= { "syncprt", &syncprt
};
register struct mount
*mp
, *nmp
;
for (mp
= mountlist
.cqh_first
; mp
!= (void *)&mountlist
; mp
= nmp
) {
* Get the next pointer in case we hang on vfs_busy
* while we are being unmounted.
nmp
= mp
->mnt_list
.cqe_next
;
* The lock check below is to avoid races with mount
if ((mp
->mnt_flag
& (MNT_MLOCK
|MNT_RDONLY
|MNT_MPBUSY
)) == 0 &&
asyncflag
= mp
->mnt_flag
& MNT_ASYNC
;
mp
->mnt_flag
&= ~MNT_ASYNC
;
VFS_SYNC(mp
, MNT_NOWAIT
, p
->p_ucred
, p
);
mp
->mnt_flag
|= MNT_ASYNC
;
* Get the next pointer again, as the next filesystem
* might have been unmounted while we were sync'ing.
nmp
= mp
->mnt_list
.cqe_next
;
* Change filesystem quotas.
register struct quotactl_args
/* {
register struct mount
*mp
;
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
return (VFS_QUOTACTL(mp
, SCARG(uap
, cmd
), SCARG(uap
, uid
),
* Get filesystem statistics.
register struct statfs_args
/* {
syscallarg(struct statfs *) buf;
register struct mount
*mp
;
register struct statfs
*sp
;
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
if (error
= VFS_STATFS(mp
, sp
, p
))
sp
->f_flags
= mp
->mnt_flag
& MNT_VISFLAGMASK
;
return (copyout((caddr_t
)sp
, (caddr_t
)SCARG(uap
, buf
), sizeof(*sp
)));
* Get filesystem statistics.
register struct fstatfs_args
/* {
syscallarg(struct statfs *) buf;
register struct statfs
*sp
;
if (error
= getvnode(p
->p_fd
, SCARG(uap
, fd
), &fp
))
mp
= ((struct vnode
*)fp
->f_data
)->v_mount
;
if (error
= VFS_STATFS(mp
, sp
, p
))
sp
->f_flags
= mp
->mnt_flag
& MNT_VISFLAGMASK
;
return (copyout((caddr_t
)sp
, (caddr_t
)SCARG(uap
, buf
), sizeof(*sp
)));
* Get statistics on all filesystems.
getfsstat(p
, uap
, retval
)
register struct getfsstat_args
/* {
syscallarg(struct statfs *) buf;
syscallarg(long) bufsize;
register struct mount
*mp
, *nmp
;
register struct statfs
*sp
;
long count
, maxcount
, error
;
maxcount
= SCARG(uap
, bufsize
) / sizeof(struct statfs
);
sfsp
= (caddr_t
)SCARG(uap
, buf
);
for (mp
= mountlist
.cqh_first
; mp
!= (void *)&mountlist
; mp
= nmp
) {
nmp
= mp
->mnt_list
.cqe_next
;
if (sfsp
&& count
< maxcount
&&
((mp
->mnt_flag
& MNT_MLOCK
) == 0)) {
* If MNT_NOWAIT is specified, do not refresh the
* fsstat cache. MNT_WAIT overrides MNT_NOWAIT.
if (((SCARG(uap
, flags
) & MNT_NOWAIT
) == 0 ||
(SCARG(uap
, flags
) & MNT_WAIT
)) &&
(error
= VFS_STATFS(mp
, sp
, p
)))
sp
->f_flags
= mp
->mnt_flag
& MNT_VISFLAGMASK
;
if (error
= copyout((caddr_t
)sp
, sfsp
, sizeof(*sp
)))
if (sfsp
&& count
> maxcount
)
* Change current working directory to a given file descriptor.
register struct filedesc
*fdp
= p
->p_fd
;
if (error
= getvnode(fdp
, SCARG(uap
, fd
), &fp
))
vp
= (struct vnode
*)fp
->f_data
;
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
error
= VOP_ACCESS(vp
, VEXEC
, p
->p_ucred
, p
);
while (!error
&& (mp
= vp
->v_mountedhere
) != NULL
) {
if (mp
->mnt_flag
& MNT_MLOCK
) {
mp
->mnt_flag
|= MNT_MWAIT
;
sleep((caddr_t
)mp
, PVFS
);
if (error
= VFS_ROOT(mp
, &tdp
))
* Change current working directory (``.'').
register struct filedesc
*fdp
= p
->p_fd
;
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
if (error
= change_dir(&nd
, p
))
* Change notion of root (``/'') directory.
register struct filedesc
*fdp
= p
->p_fd
;
if (error
= suser(p
->p_ucred
, &p
->p_acflag
))
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
if (error
= change_dir(&nd
, p
))
if (fdp
->fd_rdir
!= NULL
)
* Common routine for chroot and chdir.
register struct nameidata
*ndp
;
error
= VOP_ACCESS(vp
, VEXEC
, p
->p_ucred
, p
);
* Check permissions, allocate an open file structure,
* and call the device open routine if any.
register struct open_args
/* {
register struct filedesc
*fdp
= p
->p_fd
;
register struct file
*fp
;
register struct vnode
*vp
;
extern struct fileops vnops
;
if (error
= falloc(p
, &nfp
, &indx
))
flags
= FFLAGS(SCARG(uap
, flags
));
cmode
= ((SCARG(uap
, mode
) &~ fdp
->fd_cmask
) & ALLPERMS
) &~ S_ISTXT
;
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
p
->p_dupfd
= -indx
- 1; /* XXX check for fdopen */
if (error
= vn_open(&nd
, flags
, cmode
)) {
if ((error
== ENODEV
|| error
== ENXIO
) &&
p
->p_dupfd
>= 0 && /* XXX from fdopen */
dupfdopen(fdp
, indx
, p
->p_dupfd
, flags
, error
)) == 0) {
fdp
->fd_ofiles
[indx
] = NULL
;
fp
->f_flag
= flags
& FMASK
;
fp
->f_type
= DTYPE_VNODE
;
fp
->f_data
= (caddr_t
)vp
;
if (flags
& (O_EXLOCK
| O_SHLOCK
)) {
if ((flags
& FNONBLOCK
) == 0)
if (error
= VOP_ADVLOCK(vp
, (caddr_t
)fp
, F_SETLK
, &lf
, type
)) {
(void) vn_close(vp
, fp
->f_flag
, fp
->f_cred
, p
);
fdp
->fd_ofiles
[indx
] = NULL
;
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
compat_43_creat(p
, uap
, retval
)
register struct compat_43_creat_args
/* {
SCARG(&nuap
, path
) = SCARG(uap
, path
);
SCARG(&nuap
, mode
) = SCARG(uap
, mode
);
SCARG(&nuap
, flags
) = O_WRONLY
| O_CREAT
| O_TRUNC
;
return (open(p
, &nuap
, retval
));
register struct mknod_args
/* {
register struct vnode
*vp
;
if (error
= suser(p
->p_ucred
, &p
->p_acflag
))
NDINIT(&nd
, CREATE
, LOCKPARENT
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
vattr
.va_mode
= (SCARG(uap
, mode
) & ALLPERMS
) &~ p
->p_fd
->fd_cmask
;
vattr
.va_rdev
= SCARG(uap
, dev
);
switch (SCARG(uap
, mode
) & S_IFMT
) {
case S_IFMT
: /* used by badsect to flag bad sectors */
VOP_LEASE(nd
.ni_dvp
, p
, p
->p_ucred
, LEASE_WRITE
);
error
= VOP_WHITEOUT(nd
.ni_dvp
, &nd
.ni_cnd
, CREATE
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
error
= VOP_MKNOD(nd
.ni_dvp
, &nd
.ni_vp
,
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
register struct mkfifo_args
/* {
NDINIT(&nd
, CREATE
, LOCKPARENT
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
vattr
.va_mode
= (SCARG(uap
, mode
) & ALLPERMS
) &~ p
->p_fd
->fd_cmask
;
VOP_LEASE(nd
.ni_dvp
, p
, p
->p_ucred
, LEASE_WRITE
);
return (VOP_MKNOD(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &vattr
));
register struct link_args
/* {
register struct vnode
*vp
;
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
if (vp
->v_type
!= VDIR
||
(error
= suser(p
->p_ucred
, &p
->p_acflag
)) == 0) {
nd
.ni_cnd
.cn_nameiop
= CREATE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
;
nd
.ni_dirp
= SCARG(uap
, link
);
if ((error
= namei(&nd
)) == 0) {
VOP_LEASE(nd
.ni_dvp
, p
, p
->p_ucred
,
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
error
= VOP_LINK(vp
, nd
.ni_dvp
, &nd
.ni_cnd
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
register struct symlink_args
/* {
MALLOC(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
if (error
= copyinstr(SCARG(uap
, path
), path
, MAXPATHLEN
, NULL
))
NDINIT(&nd
, CREATE
, LOCKPARENT
, UIO_USERSPACE
, SCARG(uap
, link
), p
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
vattr
.va_mode
= ACCESSPERMS
&~ p
->p_fd
->fd_cmask
;
VOP_LEASE(nd
.ni_dvp
, p
, p
->p_ucred
, LEASE_WRITE
);
error
= VOP_SYMLINK(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &vattr
, path
);
* Delete a whiteout from the filesystem.
register struct undelete_args
/* {
NDINIT(&nd
, DELETE
, LOCKPARENT
|DOWHITEOUT
, UIO_USERSPACE
,
if (nd
.ni_vp
!= NULLVP
|| !(nd
.ni_cnd
.cn_flags
& ISWHITEOUT
)) {
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
VOP_LEASE(nd
.ni_dvp
, p
, p
->p_ucred
, LEASE_WRITE
);
if (error
= VOP_WHITEOUT(nd
.ni_dvp
, &nd
.ni_cnd
, DELETE
))
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
* Delete a name from the filesystem.
register struct vnode
*vp
;
NDINIT(&nd
, DELETE
, LOCKPARENT
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
if (vp
->v_type
!= VDIR
||
(error
= suser(p
->p_ucred
, &p
->p_acflag
)) == 0) {
* The root of a mounted filesystem cannot be deleted.
(void)vnode_pager_uncache(vp
);
VOP_LEASE(nd
.ni_dvp
, p
, p
->p_ucred
, LEASE_WRITE
);
error
= VOP_REMOVE(nd
.ni_dvp
, nd
.ni_vp
, &nd
.ni_cnd
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
* Reposition read/write file offset.
register struct lseek_args
/* {
syscallarg(off_t) offset;
struct ucred
*cred
= p
->p_ucred
;
register struct filedesc
*fdp
= p
->p_fd
;
register struct file
*fp
;
if ((u_int
)SCARG(uap
, fd
) >= fdp
->fd_nfiles
||
(fp
= fdp
->fd_ofiles
[SCARG(uap
, fd
)]) == NULL
)
if (fp
->f_type
!= DTYPE_VNODE
)
switch (SCARG(uap
, whence
)) {
fp
->f_offset
+= SCARG(uap
, offset
);
VOP_GETATTR((struct vnode
*)fp
->f_data
, &vattr
, cred
, p
))
fp
->f_offset
= SCARG(uap
, offset
) + vattr
.va_size
;
fp
->f_offset
= SCARG(uap
, offset
);
*(off_t
*)retval
= fp
->f_offset
;
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
* Reposition read/write file offset.
compat_43_lseek(p
, uap
, retval
)
register struct compat_43_lseek_args
/* {
syscallarg(off_t) offset;
SCARG(&nuap
, fd
) = SCARG(uap
, fd
);
SCARG(&nuap
, offset
) = SCARG(uap
, offset
);
SCARG(&nuap
, whence
) = SCARG(uap
, whence
);
error
= lseek(p
, &nuap
, &qret
);
* Check access permissions.
register struct access_args
/* {
register struct ucred
*cred
= p
->p_ucred
;
register struct vnode
*vp
;
int error
, flags
, t_gid
, t_uid
;
t_gid
= cred
->cr_groups
[0];
cred
->cr_uid
= p
->p_cred
->p_ruid
;
cred
->cr_groups
[0] = p
->p_cred
->p_rgid
;
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
/* Flags == 0 means only check for existence. */
if (SCARG(uap
, flags
) & R_OK
)
if (SCARG(uap
, flags
) & W_OK
)
if (SCARG(uap
, flags
) & X_OK
)
if ((flags
& VWRITE
) == 0 || (error
= vn_writechk(vp
)) == 0)
error
= VOP_ACCESS(vp
, flags
, cred
, p
);
cred
->cr_groups
[0] = t_gid
;
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
* Get file status; this version follows links.
compat_43_stat(p
, uap
, retval
)
register struct compat_43_stat_args
/* {
syscallarg(struct ostat *) ub;
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
error
= vn_stat(nd
.ni_vp
, &sb
, p
);
error
= copyout((caddr_t
)&osb
, (caddr_t
)SCARG(uap
, ub
), sizeof (osb
));
* Get file status; this version does not follow links.
compat_43_lstat(p
, uap
, retval
)
register struct compat_43_lstat_args
/* {
syscallarg(struct ostat *) ub;
NDINIT(&nd
, LOOKUP
, NOFOLLOW
| LOCKLEAF
| LOCKPARENT
, UIO_USERSPACE
,
* For symbolic links, always return the attributes of its
* containing directory, except for mode, size, and links.
if (vp
->v_type
!= VLNK
) {
error
= vn_stat(vp
, &sb
, p
);
error
= vn_stat(dvp
, &sb
, p
);
error
= vn_stat(vp
, &sb1
, p
);
sb
.st_nlink
= sb1
.st_nlink
;
sb
.st_size
= sb1
.st_size
;
sb
.st_blocks
= sb1
.st_blocks
;
error
= copyout((caddr_t
)&osb
, (caddr_t
)SCARG(uap
, ub
), sizeof (osb
));
* Convert from an old to a new stat structure.
ost
->st_dev
= st
->st_dev
;
ost
->st_ino
= st
->st_ino
;
ost
->st_mode
= st
->st_mode
;
ost
->st_nlink
= st
->st_nlink
;
ost
->st_uid
= st
->st_uid
;
ost
->st_gid
= st
->st_gid
;
ost
->st_rdev
= st
->st_rdev
;
if (st
->st_size
< (quad_t
)1 << 32)
ost
->st_size
= st
->st_size
;
ost
->st_atime
= st
->st_atime
;
ost
->st_mtime
= st
->st_mtime
;
ost
->st_ctime
= st
->st_ctime
;
ost
->st_blksize
= st
->st_blksize
;
ost
->st_blocks
= st
->st_blocks
;
ost
->st_flags
= st
->st_flags
;
ost
->st_gen
= st
->st_gen
;
#endif /* COMPAT_43 || COMPAT_SUNOS */
* Get file status; this version follows links.
register struct stat_args
/* {
syscallarg(struct stat *) ub;
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
error
= vn_stat(nd
.ni_vp
, &sb
, p
);
error
= copyout((caddr_t
)&sb
, (caddr_t
)SCARG(uap
, ub
), sizeof (sb
));
* Get file status; this version does not follow links.
register struct lstat_args
/* {
syscallarg(struct stat *) ub;
NDINIT(&nd
, LOOKUP
, NOFOLLOW
| LOCKLEAF
| LOCKPARENT
, UIO_USERSPACE
,
* For symbolic links, always return the attributes of its containing
* directory, except for mode, size, inode number, and links.
if (vp
->v_type
!= VLNK
) {
error
= vn_stat(vp
, &sb
, p
);
error
= vn_stat(dvp
, &sb
, p
);
error
= vn_stat(vp
, &sb1
, p
);
sb
.st_nlink
= sb1
.st_nlink
;
sb
.st_size
= sb1
.st_size
;
sb
.st_blocks
= sb1
.st_blocks
;
error
= copyout((caddr_t
)&sb
, (caddr_t
)SCARG(uap
, ub
), sizeof (sb
));
* Get configurable pathname variables.
register struct pathconf_args
/* {
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
error
= VOP_PATHCONF(nd
.ni_vp
, SCARG(uap
, name
), retval
);
* Return target name of a symbolic link.
register struct readlink_args
/* {
register struct vnode
*vp
;
NDINIT(&nd
, LOOKUP
, NOFOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
aiov
.iov_base
= SCARG(uap
, buf
);
aiov
.iov_len
= SCARG(uap
, count
);
auio
.uio_segflg
= UIO_USERSPACE
;
auio
.uio_resid
= SCARG(uap
, count
);
error
= VOP_READLINK(vp
, &auio
, p
->p_ucred
);
*retval
= SCARG(uap
, count
) - auio
.uio_resid
;
* Change flags of a file given a path name.
register struct chflags_args
/* {
register struct vnode
*vp
;
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
vattr
.va_flags
= SCARG(uap
, flags
);
error
= VOP_SETATTR(vp
, &vattr
, p
->p_ucred
, p
);
* Change flags of a file given a file descriptor.
register struct fchflags_args
/* {
if (error
= getvnode(p
->p_fd
, SCARG(uap
, fd
), &fp
))
vp
= (struct vnode
*)fp
->f_data
;
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
vattr
.va_flags
= SCARG(uap
, flags
);
error
= VOP_SETATTR(vp
, &vattr
, p
->p_ucred
, p
);
* Change mode of a file given path name.
register struct chmod_args
/* {
register struct vnode
*vp
;
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
vattr
.va_mode
= SCARG(uap
, mode
) & ALLPERMS
;
error
= VOP_SETATTR(vp
, &vattr
, p
->p_ucred
, p
);
* Change mode of a file given a file descriptor.
register struct fchmod_args
/* {
if (error
= getvnode(p
->p_fd
, SCARG(uap
, fd
), &fp
))
vp
= (struct vnode
*)fp
->f_data
;
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
vattr
.va_mode
= SCARG(uap
, mode
) & ALLPERMS
;
error
= VOP_SETATTR(vp
, &vattr
, p
->p_ucred
, p
);
* Set ownership given a path name.
register struct chown_args
/* {
register struct vnode
*vp
;
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
vattr
.va_uid
= SCARG(uap
, uid
);
vattr
.va_gid
= SCARG(uap
, gid
);
error
= VOP_SETATTR(vp
, &vattr
, p
->p_ucred
, p
);
* Set ownership given a file descriptor.
register struct fchown_args
/* {
if (error
= getvnode(p
->p_fd
, SCARG(uap
, fd
), &fp
))
vp
= (struct vnode
*)fp
->f_data
;
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
vattr
.va_uid
= SCARG(uap
, uid
);
vattr
.va_gid
= SCARG(uap
, gid
);
error
= VOP_SETATTR(vp
, &vattr
, p
->p_ucred
, p
);
* Set the access and modification times of a file.
register struct utimes_args
/* {
syscallarg(struct timeval *) tptr;
register struct vnode
*vp
;
if (SCARG(uap
, tptr
) == NULL
) {
vattr
.va_vaflags
|= VA_UTIMES_NULL
;
} else if (error
= copyin((caddr_t
)SCARG(uap
, tptr
), (caddr_t
)tv
,
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
vattr
.va_atime
.ts_sec
= tv
[0].tv_sec
;
vattr
.va_atime
.ts_nsec
= tv
[0].tv_usec
* 1000;
vattr
.va_mtime
.ts_sec
= tv
[1].tv_sec
;
vattr
.va_mtime
.ts_nsec
= tv
[1].tv_usec
* 1000;
error
= VOP_SETATTR(vp
, &vattr
, p
->p_ucred
, p
);
* Truncate a file given its path name.
register struct truncate_args
/* {
syscallarg(off_t) length;
register struct vnode
*vp
;
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
else if ((error
= vn_writechk(vp
)) == 0 &&
(error
= VOP_ACCESS(vp
, VWRITE
, p
->p_ucred
, p
)) == 0) {
vattr
.va_size
= SCARG(uap
, length
);
error
= VOP_SETATTR(vp
, &vattr
, p
->p_ucred
, p
);
* Truncate a file given a file descriptor.
ftruncate(p
, uap
, retval
)
register struct ftruncate_args
/* {
syscallarg(off_t) length;
if (error
= getvnode(p
->p_fd
, SCARG(uap
, fd
), &fp
))
if ((fp
->f_flag
& FWRITE
) == 0)
vp
= (struct vnode
*)fp
->f_data
;
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
else if ((error
= vn_writechk(vp
)) == 0) {
vattr
.va_size
= SCARG(uap
, length
);
error
= VOP_SETATTR(vp
, &vattr
, fp
->f_cred
, p
);
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
* Truncate a file given its path name.
compat_43_truncate(p
, uap
, retval
)
register struct compat_43_truncate_args
/* {
struct truncate_args
/* {
syscallarg(off_t) length;
SCARG(&nuap
, path
) = SCARG(uap
, path
);
SCARG(&nuap
, length
) = SCARG(uap
, length
);
return (truncate(p
, &nuap
, retval
));
* Truncate a file given a file descriptor.
compat_43_ftruncate(p
, uap
, retval
)
register struct compat_43_ftruncate_args
/* {
struct ftruncate_args
/* {
syscallarg(off_t) length;
SCARG(&nuap
, fd
) = SCARG(uap
, fd
);
SCARG(&nuap
, length
) = SCARG(uap
, length
);
return (ftruncate(p
, &nuap
, retval
));
#endif /* COMPAT_43 || COMPAT_SUNOS */
register struct vnode
*vp
;
if (error
= getvnode(p
->p_fd
, SCARG(uap
, fd
), &fp
))
vp
= (struct vnode
*)fp
->f_data
;
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
error
= VOP_FSYNC(vp
, fp
->f_cred
, MNT_WAIT
, p
);
* Rename files. Source and destination must either both be directories,
* or both not be directories. If target is a directory, it must be empty.
register struct rename_args
/* {
register struct vnode
*tvp
, *fvp
, *tdvp
;
struct nameidata fromnd
, tond
;
NDINIT(&fromnd
, DELETE
, WANTPARENT
| SAVESTART
, UIO_USERSPACE
,
if (error
= namei(&fromnd
))
NDINIT(&tond
, RENAME
, LOCKPARENT
| LOCKLEAF
| NOCACHE
| SAVESTART
,
UIO_USERSPACE
, SCARG(uap
, to
), p
);
if (error
= namei(&tond
)) {
VOP_ABORTOP(fromnd
.ni_dvp
, &fromnd
.ni_cnd
);
if (fvp
->v_type
== VDIR
&& tvp
->v_type
!= VDIR
) {
} else if (fvp
->v_type
!= VDIR
&& tvp
->v_type
== VDIR
) {
* If source is the same as the destination (that is the
* same inode number with the same name in the same directory),
* then there is nothing to do.
if (fvp
== tvp
&& fromnd
.ni_dvp
== tdvp
&&
fromnd
.ni_cnd
.cn_namelen
== tond
.ni_cnd
.cn_namelen
&&
!bcmp(fromnd
.ni_cnd
.cn_nameptr
, tond
.ni_cnd
.cn_nameptr
,
fromnd
.ni_cnd
.cn_namelen
))
VOP_LEASE(tdvp
, p
, p
->p_ucred
, LEASE_WRITE
);
if (fromnd
.ni_dvp
!= tdvp
)
VOP_LEASE(fromnd
.ni_dvp
, p
, p
->p_ucred
, LEASE_WRITE
);
VOP_LEASE(tvp
, p
, p
->p_ucred
, LEASE_WRITE
);
error
= VOP_RENAME(fromnd
.ni_dvp
, fromnd
.ni_vp
, &fromnd
.ni_cnd
,
tond
.ni_dvp
, tond
.ni_vp
, &tond
.ni_cnd
);
VOP_ABORTOP(tond
.ni_dvp
, &tond
.ni_cnd
);
VOP_ABORTOP(fromnd
.ni_dvp
, &fromnd
.ni_cnd
);
FREE(tond
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
vrele(fromnd
.ni_startdir
);
FREE(fromnd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
register struct mkdir_args
/* {
register struct vnode
*vp
;
NDINIT(&nd
, CREATE
, LOCKPARENT
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
vattr
.va_mode
= (SCARG(uap
, mode
) & ACCESSPERMS
) &~ p
->p_fd
->fd_cmask
;
VOP_LEASE(nd
.ni_dvp
, p
, p
->p_ucred
, LEASE_WRITE
);
error
= VOP_MKDIR(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &vattr
);
* Remove a directory file.
register struct vnode
*vp
;
NDINIT(&nd
, DELETE
, LOCKPARENT
| LOCKLEAF
, UIO_USERSPACE
,
if (vp
->v_type
!= VDIR
) {
* The root of a mounted filesystem cannot be deleted.
VOP_LEASE(nd
.ni_dvp
, p
, p
->p_ucred
, LEASE_WRITE
);
VOP_LEASE(vp
, p
, p
->p_ucred
, LEASE_WRITE
);
error
= VOP_RMDIR(nd
.ni_dvp
, nd
.ni_vp
, &nd
.ni_cnd
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
* Read a block of directory entries in a file system independent format.
compat_43_getdirentries(p
, uap
, retval
)
register struct compat_43_getdirentries_args
/* {
syscallarg(long *) basep;
register struct vnode
*vp
;
int error
, eofflag
, readcnt
;
if (error
= getvnode(p
->p_fd
, SCARG(uap
, fd
), &fp
))
if ((fp
->f_flag
& FREAD
) == 0)
vp
= (struct vnode
*)fp
->f_data
;
aiov
.iov_base
= SCARG(uap
, buf
);
aiov
.iov_len
= SCARG(uap
, count
);
auio
.uio_segflg
= UIO_USERSPACE
;
auio
.uio_resid
= SCARG(uap
, count
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
loff
= auio
.uio_offset
= fp
->f_offset
;
# if (BYTE_ORDER != LITTLE_ENDIAN)
if (vp
->v_mount
->mnt_maxsymlinklen
<= 0) {
error
= VOP_READDIR(vp
, &auio
, fp
->f_cred
, &eofflag
,
fp
->f_offset
= auio
.uio_offset
;
kuio
.uio_segflg
= UIO_SYSSPACE
;
kiov
.iov_len
= SCARG(uap
, count
);
MALLOC(dirbuf
, caddr_t
, SCARG(uap
, count
), M_TEMP
, M_WAITOK
);
error
= VOP_READDIR(vp
, &kuio
, fp
->f_cred
, &eofflag
,
fp
->f_offset
= kuio
.uio_offset
;
readcnt
= SCARG(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
dp
->d_type
= dp
->d_namlen
;
* The dp->d_type is the high byte
* of the expected dp->d_namlen,
((char *)dp
+ dp
->d_reclen
);
error
= uiomove(dirbuf
, readcnt
, &auio
);
extern int (**union_vnodeop_p
)();
extern struct vnode
*union_dircache
__P((struct vnode
*, struct proc
*));
if ((SCARG(uap
, count
) == auio
.uio_resid
) &&
(vp
->v_op
== union_vnodeop_p
)) {
lvp
= union_dircache(vp
, p
);
* If the directory is opaque,
* then don't show lower entries
error
= VOP_GETATTR(vp
, &va
, fp
->f_cred
, p
);
if (va
.va_flags
& OPAQUE
) {
error
= VOP_OPEN(lvp
, FREAD
, fp
->f_cred
, p
);
fp
->f_data
= (caddr_t
) lvp
;
error
= vn_close(vp
, FREAD
, fp
->f_cred
, p
);
if ((SCARG(uap
, count
) == auio
.uio_resid
) &&
(vp
->v_mount
->mnt_flag
& MNT_UNION
)) {
vp
= vp
->v_mount
->mnt_vnodecovered
;
fp
->f_data
= (caddr_t
) vp
;
error
= copyout((caddr_t
)&loff
, (caddr_t
)SCARG(uap
, basep
),
*retval
= SCARG(uap
, count
) - auio
.uio_resid
;
* Read a block of directory entries in a file system independent format.
getdirentries(p
, uap
, retval
)
register struct getdirentries_args
/* {
syscallarg(long *) basep;
register struct vnode
*vp
;
if (error
= getvnode(p
->p_fd
, SCARG(uap
, fd
), &fp
))
if ((fp
->f_flag
& FREAD
) == 0)
vp
= (struct vnode
*)fp
->f_data
;
aiov
.iov_base
= SCARG(uap
, buf
);
aiov
.iov_len
= SCARG(uap
, count
);
auio
.uio_segflg
= UIO_USERSPACE
;
auio
.uio_resid
= SCARG(uap
, count
);
vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
loff
= auio
.uio_offset
= fp
->f_offset
;
error
= VOP_READDIR(vp
, &auio
, fp
->f_cred
, &eofflag
,
fp
->f_offset
= auio
.uio_offset
;
extern int (**union_vnodeop_p
)();
extern struct vnode
*union_dircache
__P((struct vnode
*, struct proc
*));
if ((SCARG(uap
, count
) == auio
.uio_resid
) &&
(vp
->v_op
== union_vnodeop_p
)) {
lvp
= union_dircache(vp
, p
);
* If the directory is opaque,
* then don't show lower entries
error
= VOP_GETATTR(vp
, &va
, fp
->f_cred
, p
);
if (va
.va_flags
& OPAQUE
) {
error
= VOP_OPEN(lvp
, FREAD
, fp
->f_cred
, p
);
fp
->f_data
= (caddr_t
) lvp
;
error
= vn_close(vp
, FREAD
, fp
->f_cred
, p
);
if ((SCARG(uap
, count
) == auio
.uio_resid
) &&
(vp
->v_mount
->mnt_flag
& MNT_UNION
)) {
vp
= vp
->v_mount
->mnt_vnodecovered
;
fp
->f_data
= (caddr_t
) vp
;
error
= copyout((caddr_t
)&loff
, (caddr_t
)SCARG(uap
, basep
),
*retval
= SCARG(uap
, count
) - auio
.uio_resid
;
* Set the mode mask for creation of filesystem nodes.
register struct filedesc
*fdp
;
fdp
->fd_cmask
= SCARG(uap
, newmask
) & ALLPERMS
;
* Void all references to file by ripping underlying filesystem
register struct revoke_args
/* {
register struct vnode
*vp
;
NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_USERSPACE
, SCARG(uap
, path
), p
);
if (error
= VOP_GETATTR(vp
, &vattr
, p
->p_ucred
, p
))
if (p
->p_ucred
->cr_uid
!= vattr
.va_uid
&&
(error
= suser(p
->p_ucred
, &p
->p_acflag
)))
if (vp
->v_usecount
> 1 || (vp
->v_flag
& VALIASED
))
VOP_REVOKE(vp
, REVOKEALL
);
* Convert a user file descriptor to a kernel file entry.
if ((u_int
)fd
>= fdp
->fd_nfiles
||
(fp
= fdp
->fd_ofiles
[fd
]) == NULL
)
if (fp
->f_type
!= DTYPE_VNODE
)