+ u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, 0, 0, &resid);
+out:
+ iput(ip);
+ u.u_r.r_val1 = uap->count - resid;
+}
+
+/*
+ * Change mode of a file given path name.
+ */
+chmod()
+{
+ struct inode *ip;
+ struct a {
+ char *fname;
+ int fmode;
+ } *uap = (struct a *)u.u_ap;
+
+ if ((ip = owner(uap->fname, FOLLOW)) == NULL)
+ return;
+ chmod1(ip, uap->fmode);
+ iput(ip);
+}
+
+/*
+ * Change mode of a file given a file descriptor.
+ */
+fchmod()
+{
+ struct a {
+ int fd;
+ int fmode;
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip;
+ register struct file *fp;
+
+ fp = getinode(uap->fd);
+ if (fp == NULL)
+ return;
+ ip = (struct inode *)fp->f_data;
+ if (u.u_uid != ip->i_uid && !suser())
+ return;
+ ILOCK(ip);
+ chmod1(ip, uap->fmode);
+ IUNLOCK(ip);
+}
+
+/*
+ * Change the mode on a file.
+ * Inode must be locked before calling.
+ */
+chmod1(ip, mode)
+ register struct inode *ip;
+ register int mode;
+{
+
+ ip->i_mode &= ~07777;
+ if (u.u_uid) {
+ mode &= ~ISVTX;
+ if (!groupmember(ip->i_gid))
+ mode &= ~ISGID;
+ }
+ ip->i_mode |= mode&07777;
+ ip->i_flag |= ICHG;
+ if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0)
+ xrele(ip);
+}
+
+/*
+ * Set ownership given a path name.
+ */
+chown()
+{
+ struct inode *ip;
+ struct a {
+ char *fname;
+ int uid;
+ int gid;
+ } *uap = (struct a *)u.u_ap;
+
+ if (!suser() || (ip = owner(uap->fname, NOFOLLOW)) == NULL)
+ return;
+ u.u_error = chown1(ip, uap->uid, uap->gid);
+ iput(ip);
+}
+
+/*
+ * Set ownership given a file descriptor.
+ */
+fchown()
+{
+ struct a {
+ int fd;
+ int uid;
+ int gid;
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip;
+ register struct file *fp;
+
+ fp = getinode(uap->fd);
+ if (fp == NULL)
+ return;
+ ip = (struct inode *)fp->f_data;
+ if (!suser())
+ return;
+ ILOCK(ip);
+ u.u_error = chown1(ip, uap->uid, uap->gid);
+ IUNLOCK(ip);
+}
+
+/*
+ * Perform chown operation on inode ip;
+ * inode must be locked prior to call.
+ */
+chown1(ip, uid, gid)
+ register struct inode *ip;
+ int uid, gid;
+{
+#ifdef QUOTA
+ register long change;
+#endif
+
+ if (uid == -1)
+ uid = ip->i_uid;
+ if (gid == -1)
+ gid = ip->i_gid;
+#ifdef QUOTA
+ if (ip->i_uid == uid) /* this just speeds things a little */
+ change = 0;
+ else
+ change = ip->i_blocks;
+ (void) chkdq(ip, -change, 1);
+ (void) chkiq(ip->i_dev, ip, ip->i_uid, 1);
+ dqrele(ip->i_dquot);
+#endif
+ ip->i_uid = uid;
+ ip->i_gid = gid;
+ ip->i_flag |= ICHG;
+ if (u.u_ruid != 0)
+ ip->i_mode &= ~(ISUID|ISGID);
+#ifdef QUOTA
+ ip->i_dquot = inoquota(ip);
+ (void) chkdq(ip, change, 1);
+ (void) chkiq(ip->i_dev, (struct inode *)NULL, uid, 1);
+ return (u.u_error); /* should == 0 ALWAYS !! */
+#else
+ return (0);
+#endif
+}
+
+utimes()
+{
+ register struct a {
+ char *fname;
+ struct timeval *tptr;
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip;
+ struct timeval tv[2];
+
+ if ((ip = owner(uap->fname, FOLLOW)) == NULL)
+ return;
+ u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv));
+ if (u.u_error == 0) {
+ ip->i_flag |= IACC|IUPD|ICHG;
+ iupdat(ip, &tv[0], &tv[1], 0);
+ }
+ iput(ip);
+}
+
+/*
+ * Flush any pending I/O.
+ */
+sync()
+{
+
+ update();
+}
+
+/*
+ * Truncate a file given its path name.
+ */
+truncate()
+{
+ struct a {
+ char *fname;
+ u_long length;
+ } *uap = (struct a *)u.u_ap;
+ struct inode *ip;
+ register struct nameidata *ndp = &u.u_nd;
+
+ ndp->ni_nameiop = LOOKUP | FOLLOW;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->fname;
+ ip = namei(ndp);
+ if (ip == NULL)
+ return;
+ if (access(ip, IWRITE))
+ goto bad;
+ if ((ip->i_mode&IFMT) == IFDIR) {
+ u.u_error = EISDIR;
+ goto bad;
+ }
+ itrunc(ip, uap->length);
+bad:
+ iput(ip);
+}
+
+/*
+ * Truncate a file given a file descriptor.
+ */
+ftruncate()
+{
+ struct a {
+ int fd;
+ u_long length;
+ } *uap = (struct a *)u.u_ap;
+ struct inode *ip;
+ struct file *fp;
+
+ fp = getinode(uap->fd);
+ if (fp == NULL)
+ return;
+ if ((fp->f_flag&FWRITE) == 0) {
+ u.u_error = EINVAL;
+ return;
+ }
+ ip = (struct inode *)fp->f_data;
+ ILOCK(ip);
+ itrunc(ip, uap->length);
+ IUNLOCK(ip);
+}
+
+/*
+ * Synch an open file.
+ */
+fsync()
+{
+ struct a {
+ int fd;
+ } *uap = (struct a *)u.u_ap;
+ struct inode *ip;
+ struct file *fp;
+
+ fp = getinode(uap->fd);
+ if (fp == NULL)
+ return;
+ ip = (struct inode *)fp->f_data;
+ ILOCK(ip);
+ syncip(ip);
+ IUNLOCK(ip);
+}
+
+/*
+ * Rename system call.
+ * rename("foo", "bar");
+ * is essentially
+ * unlink("bar");
+ * link("foo", "bar");
+ * unlink("foo");
+ * but ``atomically''. Can't do full commit without saving state in the
+ * inode on disk which isn't feasible at this time. Best we can do is
+ * always guarantee the target exists.
+ *
+ * Basic algorithm is:
+ *
+ * 1) Bump link count on source while we're linking it to the
+ * target. This also insure the inode won't be deleted out
+ * from underneath us while we work (it may be truncated by
+ * a concurrent `trunc' or `open' for creation).
+ * 2) Link source to destination. If destination already exists,
+ * delete it first.
+ * 3) Unlink source reference to inode if still around. If a
+ * directory was moved and the parent of the destination
+ * is different from the source, patch the ".." entry in the
+ * directory.
+ *
+ * Source and destination must either both be directories, or both
+ * not be directories. If target is a directory, it must be empty.
+ */
+rename()
+{
+ struct a {
+ char *from;
+ char *to;
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip, *xp, *dp;
+ struct dirtemplate dirbuf;
+ int doingdirectory = 0, oldparent = 0, newparent = 0;
+ register struct nameidata *ndp = &u.u_nd;
+ int error = 0;
+
+ ndp->ni_nameiop = DELETE | LOCKPARENT;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->from;
+ ip = namei(ndp);
+ if (ip == NULL)
+ return;
+ dp = ndp->ni_pdir;
+ if ((ip->i_mode&IFMT) == IFDIR) {
+ register struct direct *d;
+
+ d = &ndp->ni_dent;
+ /*
+ * Avoid ".", "..", and aliases of "." for obvious reasons.
+ */
+ if ((d->d_namlen == 1 && d->d_name[0] == '.') ||
+ (d->d_namlen == 2 && bcmp(d->d_name, "..", 2) == 0) ||
+ (dp == ip) || (ip->i_flag & IRENAME)) {
+ iput(dp);
+ if (dp == ip)
+ irele(ip);
+ else
+ iput(ip);
+ u.u_error = EINVAL;
+ return;
+ }
+ ip->i_flag |= IRENAME;
+ oldparent = dp->i_number;
+ doingdirectory++;
+ }
+ iput(dp);
+
+ /*
+ * 1) Bump link count while we're moving stuff
+ * around. If we crash somewhere before
+ * completing our work, the link count
+ * may be wrong, but correctable.
+ */
+ ip->i_nlink++;
+ ip->i_flag |= ICHG;
+ iupdat(ip, &time, &time, 1);
+ IUNLOCK(ip);
+
+ /*
+ * When the target exists, both the directory
+ * and target inodes are returned locked.
+ */
+ ndp->ni_nameiop = CREATE | LOCKPARENT | NOCACHE;
+ ndp->ni_dirp = (caddr_t)uap->to;
+ xp = namei(ndp);