Files
Arquivotheca.SunOS-4.1.3/sys/os/vfs_syscalls.c
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

1275 lines
26 KiB
C

#ident "@(#)vfs_syscalls.c 1.1 92/07/30 SMI"
#include <sys/param.h>
#include <sys/label.h>
#include <sys/user.h>
#include <sys/session.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/vfs.h>
#include <sys/pathname.h>
#include <sys/vnode.h>
#include <sys/dirent.h>
#include <sys/audit.h>
#include <sys/kmem_alloc.h>
#include <ufs/inode.h>
extern struct fileops vnodefops;
/*
* System call routines for operations on files other
* than read, write and ioctl. These calls manipulate
* the per-process file table which references the
* networkable version of normal UNIX inodes, called vnodes.
*
* Many operations take a pathname, which is read
* into a kernel buffer by pn_get (see vfs_pathname.c).
* After preparing arguments for an operation, a simple
* operation proceeds:
*
* error = lookupname(pname, seg, followlink, &dvp, &vp, &vattr)
*
* where pname is the pathname operated on, seg is the segment that the
* pathname is in (UIO_USERSPACE or UIO_SYSSPACE), followlink specifies
* whether to follow symbolic links, dvp is a pointer to the vnode that
* represents the parent directory of vp, the pointer to the vnode
* referenced by the pathname. vattr is a vattr structure which hold the
* attributes of the final component. The lookupname routine fetches the
* pathname string into an internal buffer using pn_get (vfs_pathname.c),
* and iteratively running down each component of the path until the
* the final vnode and/or it's parent are found. If either of the addresses
* for dvp or vp are NULL, then it assumes that the caller is not interested
* in that vnode. If the pointer to the vattr structure is NULL then attributes
* are not returned. Once the vnode or its parent is found, then a vnode
* operation (e.g. VOP_OPEN) may be applied to it.
*
* One important point is that the operations on vnode's are atomic, so that
* vnode's are never locked at this level. Vnode locking occurs
* at lower levels either on this or a remote machine. Also permission
* checking is generally done by the specific filesystem. The only
* checks done by the vnode layer is checks involving file types
* (e.g. VREG, VDIR etc.), since this is static over the life of the vnode.
*
*/
/*
* Change current working directory (".").
*/
chdir(uap)
register struct a {
char *dirnamep;
} *uap;
{
u.u_error = chdirec(uap->dirnamep, -1, &u.u_cdir, (au_path_t *)0);
}
/*
* Change current directory to open directory
*/
fchdir(uap)
register struct a {
int fd;
} *uap;
{
u.u_error = chdirec((char *)NULL, uap->fd, &u.u_cdir, (au_path_t *)0);
}
/*
* Change notion of root ("/") directory.
*/
chroot(uap)
register struct a {
char *dirnamep;
} *uap;
{
u.u_error = chdirec(uap->dirnamep, -1, &u.u_rdir, (au_path_t *)0);
}
/*
* Change notion of root to open directory
* This lets you get back out of a chroot!
*/
fchroot(uap)
register struct a {
int fd;
} *uap;
{
u.u_error = chdirec((char *)NULL, uap->fd, &u.u_rdir, (au_path_t *)0);
}
/*
* Common code for chdir and chroot.
* Translate the pathname or file descriptor and insist that it
* is a directory to which we have execute access.
* If it is replace u.u_[cr]dir with new vnode.
*/
int
chdirec(dirnamep, fd, vpp, au_path)
char *dirnamep;
int fd;
struct vnode **vpp;
au_path_t *au_path;
{
struct vnode *vp; /* new directory vnode */
register int error;
if (dirnamep) {
error = au_lookupname(dirnamep, UIO_USERSPACE, FOLLOW_LINK,
(struct vnode **)0, &vp, au_path);
if (error)
return (error);
} else {
struct file *fp;
error = getvnodefp(fd, &fp);
if (error)
return (error);
vp = (struct vnode *)fp->f_data;
VN_HOLD(vp);
}
if (vp->v_type != VDIR)
error = ENOTDIR;
else
error = VOP_ACCESS(vp, VEXEC, u.u_cred);
if (error) {
VN_RELE(vp);
return (error);
}
/*
* Special chroot semantics:
* chroot is allowed if root or if the target is really
* a loopback mount of the root as determined by comparing
* dev and inode numbers
*/
if (vpp == &u.u_rdir) {
struct vattr tattr;
struct vattr rattr;
if (error = VOP_GETATTR(vp, &tattr, u.u_cred)) {
VN_RELE(vp);
return (error);
}
if (error = VOP_GETATTR(rootdir, &rattr, u.u_cred)) {
VN_RELE(vp);
return (error);
}
if ((tattr.va_fsid != rattr.va_fsid ||
tattr.va_nodeid != rattr.va_nodeid) && !suser()) {
error = u.u_error;
VN_RELE(vp);
return (error);
}
}
if (*vpp != (struct vnode *)0)
VN_RELE(*vpp);
*vpp = vp;
return (0);
}
/*
* Open system call.
*/
open(uap)
register struct a {
char *fnamep;
int fmode;
int cmode;
} *uap;
{
u.u_error = copen(uap->fnamep, uap->fmode - FOPEN, uap->cmode);
}
/*
* Creat system call.
*/
creat(uap)
register struct a {
char *fnamep;
int cmode;
} *uap;
{
u.u_error = copen(uap->fnamep, FWRITE|FCREAT|FTRUNC, uap->cmode);
}
/*
* Common code for open, creat.
*/
static int
copen(pnamep, filemode, createmode)
char *pnamep;
int filemode;
int createmode;
{
register struct file *fp;
struct vnode *vp;
register int error;
register int indx;
/*
* allocate a user file descriptor and file table entry.
*/
fp = falloc();
if (fp == NULL)
return (u.u_error);
indx = u.u_r.r_val1; /* this is bullshit */
/*
* open the vnode.
*/
error = vn_open(pnamep, UIO_USERSPACE, filemode,
((createmode & 07777) & ~u.u_cmask), &vp);
/*
* If there was an error, deallocate the file descriptor.
* Otherwise fill in the file table entry to point to the vnode.
*/
if (error) {
u.u_ofile[indx] = NULL;
crfree(fp->f_cred);
fp->f_count = 0;
} else {
fp->f_flag = filemode & FMASK;
fp->f_type = DTYPE_VNODE;
fp->f_data = (caddr_t)vp;
fp->f_ops = &vnodefops;
/*
* For named pipes, the FNDELAY flag must propagate to the
* rdwr layer, for backward compatibility.
*/
if (vp->v_type == VFIFO)
fp->f_flag |= (filemode & FNDELAY);
}
return (error);
}
/*
* Create a special (or regular) file.
*/
mknod(uap)
register struct a {
char *pnamep;
int fmode;
int dev;
} *uap;
{
struct vnode *vp;
struct vattr vattr;
int error = 0;
/* map 0 type into regular file, as other versions of UNIX do */
if ((uap->fmode & IFMT) == 0)
uap->fmode |= IFREG;
/* Must be super-user unless making a FIFO node */
if (((uap->fmode & IFMT) != IFIFO) && !suser()) {
#ifdef SYSAUDIT
au_sysaudit(AUR_MKNOD, AU_MINPRIV, u.u_error, u.u_rval1, 4,
0, (caddr_t)u.u_cwd->cw_root,
0, (caddr_t)u.u_cwd->cw_dir,
AUP_USER, (caddr_t)uap->pnamep,
sizeof (uap->fmode), (caddr_t)&(uap->fmode));
#endif SYSAUDIT
return;
}
/*
* Setup desired attributes and vn_create the file.
*/
vattr_null(&vattr);
vattr.va_type = MFTOVT(uap->fmode);
vattr.va_mode = (uap->fmode & 07777) & ~u.u_cmask;
switch (vattr.va_type) {
case VDIR:
error = EISDIR; /* Can't mknod directories: use mkdir */
break;
case VBAD:
case VCHR:
case VBLK:
vattr.va_rdev = uap->dev;
break;
case VNON:
error = EINVAL;
break;
default:
break;
}
if (error == 0) {
error = vn_create(uap->pnamep, UIO_USERSPACE,
&vattr, EXCL, 0, &vp);
if (error == 0)
VN_RELE(vp);
}
u.u_error = error;
#ifdef SYSAUDIT
au_sysaudit(AUR_MKNOD, AU_DCREATE, error, u.u_rval1, 4,
0, (caddr_t)u.u_cwd->cw_root,
0, (caddr_t)u.u_cwd->cw_dir,
AUP_USER, (caddr_t)uap->pnamep,
sizeof (uap->fmode), (caddr_t)&(uap->fmode));
#endif SYSAUDIT
}
/*
* Make a directory.
*/
mkdir(uap)
struct a {
char *dirnamep;
int dmode;
} *uap;
{
struct vnode *vp;
struct vattr vattr;
vattr_null(&vattr);
vattr.va_type = VDIR;
vattr.va_mode = (uap->dmode & 0777) & ~u.u_cmask;
u.u_error =
vn_create(uap->dirnamep, UIO_USERSPACE, &vattr, EXCL, 0, &vp);
if (u.u_error == 0)
VN_RELE(vp);
}
/*
* make a hard link
*/
link(uap)
register struct a {
char *from;
char *to;
} *uap;
{
u.u_error = vn_link(uap->from, uap->to, UIO_USERSPACE);
}
/*
* rename or move an existing file
*/
rename(uap)
register struct a {
char *from;
char *to;
} *uap;
{
u.u_error = vn_rename(uap->from, uap->to, UIO_USERSPACE);
}
/*
* Create a symbolic link.
* Similar to link or rename except target
* name is passed as string argument, not
* converted to vnode reference.
*/
symlink(uap)
register struct a {
char *target;
char *linkname;
} *uap;
{
struct vnode *dvp;
struct vattr vattr;
struct pathname tpn;
struct pathname lpn;
u.u_error = pn_get(uap->linkname, UIO_USERSPACE, &lpn);
if (u.u_error)
return;
u.u_error = lookuppn(&lpn, NO_FOLLOW, &dvp, (struct vnode **)0);
if (u.u_error) {
pn_free(&lpn);
return;
}
if (dvp->v_vfsp->vfs_flag & VFS_RDONLY) {
u.u_error = EROFS;
goto out;
}
u.u_error = pn_get(uap->target, UIO_USERSPACE, &tpn);
vattr_null(&vattr);
vattr.va_mode = 0777;
if (u.u_error == 0) {
u.u_error = VOP_SYMLINK(dvp, lpn.pn_path, &vattr,
tpn.pn_path, u.u_cred);
pn_free(&tpn);
}
out:
pn_free(&lpn);
VN_RELE(dvp);
}
/*
* Unlink (i.e. delete) a file.
*/
unlink(uap)
struct a {
char *pnamep;
} *uap;
{
u.u_error = vn_remove(uap->pnamep, UIO_USERSPACE, FILE);
}
/*
* Remove a directory.
*/
rmdir(uap)
struct a {
char *dnamep;
} *uap;
{
u.u_error = vn_remove(uap->dnamep, UIO_USERSPACE, DIRECTORY);
}
/*
* get directory entries in a file system independent format
* This is the old system call. It remains in 4.0 for binary compatibility.
* It will disappear in 5-?.0. It returns directory entries in the
* old file-system independent directory entry format. This structure was
* formerly defined in /usr/include/sys/dir.h. It is now no longer available to
* user source files. It is defined only by struct direct below.
* This system call is superseded by the new getdents() call, which
* returns directory entries in the new filesystem-independent format
* given in /usr/include/sys/dirent.h and /usr/include/sys/dir.h. The
* vn_readdir interface has also been modified to return entries in this
* format.
*/
getdirentries(uap)
register struct a {
int fd;
char *buf;
unsigned count;
long *basep;
} *uap;
{
struct direct {
u_long d_fileno;
u_short d_reclen;
u_short d_namlen;
char d_name[MAXNAMLEN + 1];
};
#define DIRECTSIZ(dp) \
(((sizeof (*(dp)) - (MAXNAMLEN+1) + ((dp)->d_namlen+1)) + 3) & ~3)
struct file *fp;
struct uio auio;
struct iovec aiov;
register caddr_t ibuf, obuf, ibufend, obufend;
register struct dirent *idp;
register struct direct *odp;
int outcount;
off_t offset;
#ifdef UFS
extern struct vnodeops ufs_vnodeops;
#endif
extern char *strcpy();
u.u_error = getvnodefp(uap->fd, &fp);
if (u.u_error)
return;
if ((fp->f_flag & FREAD) == 0) {
u.u_error = EBADF;
return;
}
if (uap->count < sizeof (struct direct)) {
u.u_error = EINVAL;
return;
}
#ifdef UFS
/* Performance hack, use old dir. stuff for local ufs filesystems */
if (((struct vnode *)fp->f_data)->v_op == &ufs_vnodeops) {
old_getdirentries(uap, fp);
return;
}
#endif
/* Allocate temporary space for format conversion */
ibuf = new_kmem_alloc(uap->count, KMEM_SLEEP);
obuf = new_kmem_alloc(uap->count + sizeof (struct direct), KMEM_SLEEP);
aiov.iov_base = ibuf;
aiov.iov_len = uap->count;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = fp->f_offset;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_resid = uap->count;
u.u_error = VOP_READDIR((struct vnode *)fp->f_data, &auio, fp->f_cred);
if (u.u_error)
goto out;
offset = auio.uio_offset;
/* Convert entries from new back to old format */
for (idp = (struct dirent *)ibuf, odp = (struct direct *)obuf,
ibufend = ibuf + (uap->count - auio.uio_resid),
obufend = obuf + uap->count;
(caddr_t)idp < ibufend;
idp = (struct dirent *)((caddr_t)idp + idp->d_reclen),
odp = (struct direct *)((caddr_t)odp + odp->d_reclen)) {
odp->d_fileno = idp->d_fileno;
odp->d_namlen = idp->d_namlen;
(void) strcpy(odp->d_name, idp->d_name);
odp->d_reclen = DIRECTSIZ(odp);
if ((caddr_t)odp + odp->d_reclen > obufend)
break;
}
outcount = (caddr_t)odp - obuf;
aiov.iov_base = uap->buf;
aiov.iov_len = outcount;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = fp->f_offset;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_resid = outcount;
if (u.u_error = uiomove(obuf, outcount, UIO_READ, &auio))
goto out;
u.u_error =
copyout((caddr_t)&fp->f_offset, (caddr_t)uap->basep, sizeof (long));
u.u_r.r_val1 = outcount - auio.uio_resid;
fp->f_offset = offset;
out:
kmem_free(ibuf, uap->count);
kmem_free(obuf, uap->count + sizeof (struct direct));
}
#ifdef UFS
/*
* old form of the getdirentries system call. This is called by
* getdirentries() when it discovers it is dealing with a ufs filesystem.
* This routine in turn calls the old ufs_readdir routine directly. This
* ugly hack is to avoid a big performance penalty that occurs with local ufs
* filesystems because the directory entries have to be converted to new
* format by ufs_readdir and then back to the old format by getdirentries.
* By using this calling method no conversion takes place and no penalties
* are incurred. Unsightly, but it will go away soon, we hope!
*/
old_getdirentries(uap, fp)
register struct a {
int fd;
char *buf;
unsigned count;
long *basep;
} *uap;
struct file *fp;
{
struct uio auio;
struct iovec aiov;
extern int old_ufs_readdir();
aiov.iov_base = uap->buf;
aiov.iov_len = uap->count;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = fp->f_offset;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_resid = uap->count;
u.u_error =
old_ufs_readdir((struct vnode *)fp->f_data, &auio, fp->f_cred);
if (u.u_error)
return;
u.u_error =
copyout((caddr_t)&fp->f_offset, (caddr_t)uap->basep, sizeof (long));
u.u_r.r_val1 = uap->count - auio.uio_resid;
fp->f_offset = auio.uio_offset;
}
#endif
/*
* get directory entries in a file system independent format.
* This call returns directory entries in the new format specified
* in sys/dirent.h which is also the format returned by the vn_readdir
* interface. This call supersedes getdirentries(), which will disappear
* in 5-?.0.
*/
getdents(uap)
register struct a {
int fd;
char *buf;
unsigned count;
} *uap;
{
struct file *fp;
struct uio auio;
struct iovec aiov;
struct vnode *vp;
if (uap->count < sizeof (struct dirent)) {
u.u_error = EINVAL;
return;
}
u.u_error = getvnodefp(uap->fd, &fp);
if (u.u_error)
return;
vp = (struct vnode *)fp->f_data;
if ((fp->f_flag & FREAD) == 0) {
u.u_error = EBADF;
return;
}
if (vp->v_type != VDIR) {
u.u_error = ENOTDIR;
return;
}
aiov.iov_base = uap->buf;
aiov.iov_len = uap->count;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = fp->f_offset;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_resid = uap->count;
u.u_error = VOP_READDIR(vp, &auio, fp->f_cred);
if (u.u_error)
return;
u.u_r.r_val1 = uap->count - auio.uio_resid;
fp->f_offset = auio.uio_offset;
}
/*
* Seek on file. Only hard operation is seek relative to
* end which must apply to vnode for current file size.
*
* Note: lseek(0, 0, L_XTND) costs much more than it did before.
*/
lseek(uap)
register struct a {
int fd;
off_t off;
int sbase;
} *uap;
{
register struct file *fp;
register struct vnode *vp;
GETF(fp, uap->fd);
if (fp->f_type != DTYPE_VNODE) {
u.u_error = ESPIPE;
return;
}
vp = (struct vnode *)fp->f_data;
if (vp->v_type == VFIFO) {
u.u_error = ESPIPE;
return;
}
u.u_error = lseek1(fp, uap->off, uap->sbase);
u.u_r.r_off = fp->f_offset;
}
/*
* Common code called from lseek and arw
*/
#define TWO_GB_BLKOFFSET \
(1 << sizeof (off_t) * NBBY - DEV_BSHIFT - 1)
int
lseek1(fp, delta, whence)
struct file *fp;
off_t delta;
int whence;
{
off_t offset0 = fp->f_offset;
int blockmode = ((fp->f_flag & FSETBLK) != 0);
struct vnode *vp = (struct vnode *)fp->f_data;
struct vattr vattr;
int error = 0;
/*
* Block-mode seeks are limited to character special files
*/
if (whence == LB_SET || whence == LB_INCR || whence == LB_XTND) {
if (vp->v_type != VCHR)
return (EINVAL);
fp->f_flag |= FSETBLK;
} else {
fp->f_flag &= ~FSETBLK;
}
switch (whence) {
case L_INCR:
if (!blockmode) {
fp->f_offset += delta;
} else if ((u_long)offset0 >= TWO_GB_BLKOFFSET && delta >= 0) {
error = EFBIG;
break;
} else {
off_t dstblk = offset0 +
delta / DEV_BSIZE - (delta % DEV_BSIZE < 0);
if ((u_long)dstblk >= TWO_GB_BLKOFFSET) {
error = EFBIG;
break;
}
fp->f_offset = dbtob(dstblk) + delta % DEV_BSIZE;
if (delta % DEV_BSIZE < 0)
fp->f_offset += DEV_BSIZE;
}
break;
case L_XTND:
error = VOP_GETATTR(vp, &vattr, u.u_cred);
if (error)
break;
fp->f_offset = delta + vattr.va_size;
break;
case L_SET:
case LB_SET:
fp->f_offset = delta;
break;
case LB_INCR:
/*
* LB_INCR succeeds only if the file is already in block
* mode or the current byte offset is on a block boundary
*/
if (!blockmode) {
if (offset0 % DEV_BSIZE != 0) {
error = EINVAL;
break;
}
fp->f_offset = btodb(offset0);
}
fp->f_offset += delta;
break;
case LB_XTND:
error = VOP_GETATTR(vp, &vattr, u.u_cred);
if (error)
break;
if (vattr.va_size % DEV_BSIZE != 0) {
error = EINVAL;
break;
}
fp->f_offset = delta + btodb(vattr.va_size);
break;
default:
error = EINVAL;
}
/*
* only char special devices are allowed negative offsets
*/
if (fp->f_offset < 0 && vp->v_type != VCHR)
error = EINVAL;
if (error) {
fp->f_offset = offset0;
if (blockmode)
fp->f_flag |= FSETBLK;
else
fp->f_flag &= ~FSETBLK;
}
return (error);
}
/*
* Determine accessibility of file, by
* reading its attributes and then checking
* against our protection policy.
*/
access(uap)
register struct a {
char *fname;
int fmode;
} *uap;
{
struct vnode *vp;
u_short mode;
struct ucred *tmpcred;
struct ucred *savecred;
/*
* posix check for invalid mode, weird because F_OK == 0
*/
if (uap->fmode != F_OK && (uap->fmode & ~(R_OK|W_OK|X_OK))) {
u.u_error = EINVAL;
return;
}
/*
* Use the real uid and gid and check access
*/
if (u.u_ruid == u.u_uid && u.u_rgid == u.u_gid) {
tmpcred = u.u_cred;
savecred = NULL;
} else {
savecred = u.u_cred;
u.u_cred = tmpcred = crdup(savecred);
u.u_uid = u.u_ruid;
u.u_gid = u.u_rgid;
}
/*
* Lookup file
*/
u.u_error = lookupname(uap->fname, UIO_USERSPACE, FOLLOW_LINK,
(struct vnode **)0, &vp);
if (u.u_error) {
if (savecred != NULL) {
u.u_cred = savecred;
crfree(tmpcred);
}
return;
}
mode = 0;
/*
* fmode == 0 means only check for exist
*/
if (uap->fmode) {
if (uap->fmode & R_OK)
mode |= VREAD;
if (uap->fmode & W_OK) {
if (isrofile(vp)) {
u.u_error = EROFS;
goto out;
}
mode |= VWRITE;
}
if (uap->fmode & X_OK)
mode |= VEXEC;
u.u_error = VOP_ACCESS(vp, mode, tmpcred);
}
/*
* release the vnode and free the temporary credential
*/
out:
VN_RELE(vp);
if (savecred != NULL) {
u.u_cred = savecred;
crfree(tmpcred);
}
}
/*
* Get attributes from file or file descriptor.
* Argument says whether to follow links, and is
* passed through in flags.
*/
stat(uap)
caddr_t uap;
{
u.u_error = stat1(uap, FOLLOW_LINK);
}
lstat(uap)
caddr_t uap;
{
u.u_error = stat1(uap, NO_FOLLOW);
}
static int
stat1(uap0, follow)
caddr_t uap0;
enum symfollow follow;
{
struct vnode *vp;
struct stat sb;
register int error;
register struct a {
char *fname;
struct stat *ub;
} *uap = (struct a *)uap0;
error =
lookupname(uap->fname, UIO_USERSPACE, follow,
(struct vnode **)0, &vp);
if (error)
return (error);
error = vno_stat(vp, &sb);
VN_RELE(vp);
if (error)
return (error);
return (copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)));
}
/*
* Read contents of symbolic link.
*/
readlink(uap)
register struct a {
char *name;
char *buf;
int count;
} *uap;
{
struct vnode *vp;
struct iovec aiov;
struct uio auio;
u.u_error = lookupname(uap->name, UIO_USERSPACE, NO_FOLLOW,
(struct vnode **)0, &vp);
if (u.u_error)
return;
if (vp->v_type != VLNK) {
u.u_error = EINVAL;
goto out;
}
aiov.iov_base = uap->buf;
aiov.iov_len = uap->count;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_resid = uap->count;
u.u_error = VOP_READLINK(vp, &auio, u.u_cred);
out:
VN_RELE(vp);
u.u_r.r_val1 = uap->count - auio.uio_resid;
}
/*
* Change mode of file given path name.
*/
chmod(uap)
register struct a {
char *fname;
int fmode;
} *uap;
{
struct vattr vattr;
vattr_null(&vattr);
vattr.va_mode = uap->fmode & 07777;
u.u_error = namesetattr(uap->fname, FOLLOW_LINK, &vattr, 0);
}
/*
* Change mode of file given file descriptor.
*/
fchmod(uap)
register struct a {
int fd;
int fmode;
} *uap;
{
struct vattr vattr;
vattr_null(&vattr);
vattr.va_mode = uap->fmode & 07777;
u.u_error = fdsetattr(uap->fd, &vattr);
}
/*
* Change ownership of file given file name.
*/
chown(uap)
register struct a {
char *fname;
int uid;
int gid;
} *uap;
{
struct vattr vattr;
vattr_null(&vattr);
vattr.va_uid = uap->uid;
vattr.va_gid = uap->gid;
u.u_error = namesetattr(uap->fname, NO_FOLLOW, &vattr, 0);
}
/*
* Change ownership of file given file descriptor.
*/
fchown(uap)
register struct a {
int fd;
int uid;
int gid;
} *uap;
{
struct vattr vattr;
vattr_null(&vattr);
vattr.va_uid = uap->uid;
vattr.va_gid = uap->gid;
u.u_error = fdsetattr(uap->fd, &vattr);
}
/*
* Set access/modify times on named file.
*/
utimes(uap)
register struct a {
char *fname;
struct timeval *tptr;
} *uap;
{
struct timeval tv[2];
struct vattr vattr;
int flags = 0;
vattr_null(&vattr);
if (uap->tptr != NULL) {
u.u_error =
copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv));
if (u.u_error)
return;
if (tv[0].tv_usec < 0 || tv[0].tv_usec >= 1000000 ||
tv[1].tv_usec < 0 || tv[1].tv_usec >= 1000000) {
u.u_error = EINVAL;
return;
}
vattr.va_atime = tv[0];
vattr.va_mtime = tv[1];
flags |= ATTR_UTIME;
} else {
/*
* If the timeval ptr is NULL, then this is the
* SystemV-compatible call to set the access
* and modified times to the present.
*
* XXX - For now, flag this with -1 in vattr.va_mtime.tv_usec.
*/
vattr.va_mtime.tv_sec = 0;
vattr.va_mtime.tv_usec = -1;
}
u.u_error = namesetattr(uap->fname, FOLLOW_LINK, &vattr, flags);
}
/*
* Truncate a file given its path name.
*/
truncate(uap)
register struct a {
char *fname;
off_t length;
} *uap;
{
struct vattr vattr;
if (uap->length < 0) {
u.u_error = EINVAL;
return;
}
vattr_null(&vattr);
vattr.va_size = (u_long)uap->length;
u.u_error = namesetattr(uap->fname, FOLLOW_LINK, &vattr, 0);
}
/*
* Truncate a file given a file descriptor.
*/
ftruncate(uap)
register struct a {
int fd;
off_t length;
} *uap;
{
register struct vnode *vp;
struct file *fp;
if (uap->length < 0) {
u.u_error = EINVAL;
return;
}
u.u_error = getvnodefp(uap->fd, &fp);
if (u.u_error)
return;
vp = (struct vnode *)fp->f_data;
if ((fp->f_flag & FWRITE) == 0) {
u.u_error = EINVAL;
} else if (vp->v_vfsp->vfs_flag & VFS_RDONLY) {
u.u_error = EROFS;
} else {
struct vattr vattr;
vattr_null(&vattr);
vattr.va_size = (u_long)uap->length;
u.u_error = VOP_SETATTR(vp, &vattr, fp->f_cred);
}
}
/*
* Common routine for modifying attributes
* of named files.
*/
int
namesetattr(fnamep, followlink, vap, flags)
char *fnamep;
enum symfollow followlink;
struct vattr *vap;
int flags;
{
struct vnode *vp;
register int error;
error = lookupname(fnamep, UIO_USERSPACE, followlink,
(struct vnode **)0, &vp);
if (error)
return (error);
/*
* XXX - Allow either owner or root to set access and
* modification times if flags is ATTR_UTIME.
*/
if (flags & ATTR_UTIME) {
struct vattr vtmp;
error = VOP_GETATTR(vp, &vtmp, u.u_cred);
if (error)
goto out;
if (u.u_cred->cr_uid != vtmp.va_uid && u.u_cred->cr_uid != 0) {
error = EPERM;
goto out;
}
}
if (vp->v_vfsp->vfs_flag & VFS_RDONLY) {
error = EROFS;
goto out;
}
/* If truncating, check for write permission */
if (vap->va_size != (u_long)-1) {
error = VOP_ACCESS(vp, VWRITE, u.u_cred);
if (error)
goto out;
}
error = VOP_SETATTR(vp, vap, u.u_cred);
out:
VN_RELE(vp);
return (error);
}
/*
* Common routine for modifying attributes
* of file referenced by descriptor.
*/
static int
fdsetattr(fd, vap)
int fd;
struct vattr *vap;
{
struct file *fp;
register struct vnode *vp;
register int error;
error = getvnodefp(fd, &fp);
if (error == 0) {
vp = (struct vnode *)fp->f_data;
if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
return (EROFS);
/* If truncating, check for write permission */
if (vap->va_size != (u_long)-1) {
if ((fp->f_flag & FWRITE) == 0)
return (EINVAL);
}
error = VOP_SETATTR(vp, vap, u.u_cred);
}
return (error);
}
/*
* Flush output pending for file.
*/
fsync(uap)
struct a {
int fd;
} *uap;
{
struct file *fp;
u.u_error = getvnodefp(uap->fd, &fp);
if (u.u_error == 0)
u.u_error = VOP_FSYNC((struct vnode *)fp->f_data, fp->f_cred);
}
/*
* Set file creation mask.
*/
umask(uap)
register struct a {
int mask;
} *uap;
{
u.u_r.r_val1 = u.u_cmask;
u.u_cmask = uap->mask & 07777;
}
/*
* Revoke access the current tty by all processes.
* Used only by the super-user in init
* to give ``clean'' terminals at login.
*/
vhangup()
{
if (!suser())
return;
if (u.u_procp->p_sessp->s_ttyp == NULL)
return;
forceclose(u.u_procp->p_sessp->s_ttyd);
gsignal(*u.u_procp->p_sessp->s_ttyp, SIGHUP);
}
forceclose(dev)
dev_t dev;
{
register struct file *fp;
register struct vnode *vp;
for (fp = file; fp < fileNFILE; fp++) {
if (fp->f_count == 0)
continue;
if (fp->f_type != DTYPE_VNODE)
continue;
vp = (struct vnode *)fp->f_data;
if (vp == 0)
continue;
if (vp->v_type != VCHR)
continue;
if (vp->v_rdev != dev)
continue;
/*
* Note that while this prohibits further I/O on the
* descriptor, it does not prohibit closing the
* descriptor.
*/
fp->f_flag &= ~(FREAD|FWRITE);
}
}
/*
* Get the file structure entry for the file descriptor, but make sure
* it's a vnode.
*/
int
getvnodefp(fd, fpp)
int fd;
struct file **fpp;
{
register struct file *fp;
if (fd < 0 || fd > u.u_lastfile || (fp = u.u_ofile[fd]) == NULL)
return (EBADF);
if (fp->f_type != DTYPE_VNODE)
return (EINVAL);
*fpp = fp;
return (0);
}