+ update();
+}
+
+/*
+ * Apply an advisory lock on a file descriptor.
+ */
+flock()
+{
+ register struct a {
+ int fd;
+ int how;
+ } *uap = (struct a *)u.u_ap;
+ register struct file *fp;
+ register int cmd, flags;
+
+ fp = getinode(uap->fd);
+ if (fp == NULL)
+ return;
+ cmd = uap->how;
+ flags = u.u_pofile[uap->fd] & (UF_SHLOCK|UF_EXLOCK);
+ if (cmd&LOCK_UN) {
+ if (flags == 0) {
+ u.u_error = EINVAL;
+ return;
+ }
+ funlocki((struct inode *)fp->f_data, flags);
+ u.u_pofile[uap->fd] &= ~(UF_SHLOCK|UF_EXLOCK);
+ return;
+ }
+ /*
+ * No reason to write lock a file we've already
+ * write locked, similarly with a read lock.
+ */
+ if ((flags&UF_EXLOCK) && (cmd&LOCK_EX) ||
+ (flags&UF_SHLOCK) && (cmd&LOCK_SH))
+ return;
+ u.u_pofile[uap->fd] =
+ flocki((struct inode *)fp->f_data, u.u_pofile[uap->fd], cmd);
+}
+
+/*
+ * Truncate a file given its path name.
+ */
+truncate()
+{
+ struct a {
+ char *fname;
+ u_long length;
+ } *uap = (struct a *)u.u_ap;
+ struct inode *ip;
+
+ ip = namei(uchar, LOOKUP, 1);
+ 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.
+ * 2) Link source to destination. If destination already exists,
+ * delete it first.
+ * 3) Unlink source reference to inode if still around.
+ * 4) 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;
+ register struct inode *ip, *xp, *dp;
+ int oldparent, parentdifferent, doingdirectory;
+ int error = 0;
+
+ uap = (struct a *)u.u_ap;
+ ip = namei(uchar, DELETE | LOCKPARENT, 0);
+ if (ip == NULL)
+ return;
+ dp = u.u_pdir;
+ oldparent = 0, doingdirectory = 0;
+ if ((ip->i_mode&IFMT) == IFDIR) {
+ register struct direct *d;
+
+ d = &u.u_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)) {
+ iput(dp);
+ if (dp == ip)
+ irele(ip);
+ else
+ iput(ip);
+ u.u_error = EINVAL;
+ return;
+ }
+ 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.
+ */
+ u.u_dirp = (caddr_t)uap->to;
+ xp = namei(uchar, CREATE | LOCKPARENT, 0);
+ if (u.u_error) {
+ error = u.u_error;
+ goto out;
+ }
+ dp = u.u_pdir;
+ /*
+ * If ".." must be changed (ie the directory gets a new
+ * parent) then the user must have write permission.
+ */
+ parentdifferent = oldparent != dp->i_number;
+ if (doingdirectory && parentdifferent && access(ip, IWRITE))
+ goto bad;
+ /*
+ * 2) If target doesn't exist, link the target
+ * to the source and unlink the source.
+ * Otherwise, rewrite the target directory
+ * entry to reference the source inode and
+ * expunge the original entry's existence.
+ */
+ if (xp == NULL) {
+ if (dp->i_dev != ip->i_dev) {
+ error = EXDEV;
+ goto bad;
+ }
+ /*
+ * Disallow rename(foo, foo/bar).
+ */
+ if (dp->i_number == ip->i_number) {
+ error = EEXIST;
+ goto bad;
+ }
+ /*
+ * Account for ".." in directory.
+ * When source and destination have the
+ * same parent we don't fool with the
+ * link count -- this isn't required
+ * because we do a similar check below.
+ */
+ if (doingdirectory && parentdifferent) {
+ dp->i_nlink++;
+ dp->i_flag |= ICHG;
+ iupdat(dp, &time, &time, 1);
+ }
+ error = direnter(ip);
+ if (error)
+ goto out;
+ } else {
+ if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) {
+ error = EXDEV;
+ goto bad;
+ }
+ /*
+ * Short circuit rename(foo, foo).
+ */
+ if (xp->i_number == ip->i_number)
+ goto bad;
+ /*
+ * Target must be empty if a directory
+ * and have no links to it.
+ * Also, insure source and target are
+ * compatible (both directories, or both
+ * not directories).
+ */
+ if ((xp->i_mode&IFMT) == IFDIR) {
+ if (!dirempty(xp) || xp->i_nlink > 2) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ if (!doingdirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ } else if (doingdirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+ dirrewrite(dp, ip);
+ if (u.u_error) {
+ error = u.u_error;
+ goto bad1;
+ }
+ /*
+ * Adjust the link count of the target to
+ * reflect the dirrewrite above. If this is
+ * a directory it is empty and there are
+ * no links to it, so we can squash the inode and
+ * any space associated with it. We disallowed
+ * renaming over top of a directory with links to
+ * it above, as we've no way to determine if
+ * we've got a link or the directory itself, and
+ * if we get a link, then ".." will be screwed up.
+ */
+ xp->i_nlink--;
+ if (doingdirectory) {
+ if (--xp->i_nlink != 0)
+ panic("rename: linked directory");
+ itrunc(xp, (u_long)0);
+ }
+ xp->i_flag |= ICHG;
+ iput(xp);
+ xp = NULL;
+ }
+
+ /*
+ * 3) Unlink the source.
+ */
+ u.u_dirp = uap->from;
+ dp = namei(uchar, DELETE, 0);
+ /*
+ * Insure directory entry still exists and
+ * has not changed since the start of all
+ * this. If either has occured, forget about
+ * about deleting the original entry and just
+ * adjust the link count in the inode.
+ */
+ if (dp == NULL || u.u_dent.d_ino != ip->i_number) {
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ } else {
+ /*
+ * If source is a directory, must adjust
+ * link count of parent directory also.
+ * If target didn't exist and source and
+ * target have the same parent, then we
+ * needn't touch the link count, it all
+ * balances out in the end. Otherwise, we
+ * must do so to reflect deletion of ".."
+ * done above.
+ */
+ if (doingdirectory && (xp != NULL || parentdifferent)) {
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ }
+ if (dirremove()) {
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ }
+ if (error == 0) /* conservative */
+ error = u.u_error;
+ }
+ irele(ip);
+ if (dp)
+ iput(dp);
+
+ /*
+ * 4) Renaming a directory with the parent
+ * different requires ".." to be rewritten.
+ * The window is still there for ".." to
+ * be inconsistent, but this is unavoidable,
+ * and a lot shorter than when it was done
+ * in a user process.
+ */
+ if (doingdirectory && parentdifferent && error == 0) {
+ struct dirtemplate dirbuf;
+
+ u.u_dirp = uap->to;
+ ip = namei(uchar, LOOKUP | LOCKPARENT, 0);
+ if (ip == NULL) {
+ printf("rename: .. went away\n");
+ return;
+ }
+ dp = u.u_pdir;
+ if ((ip->i_mode&IFMT) != IFDIR) {
+ printf("rename: .. not a directory\n");
+ goto stuck;
+ }
+ error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
+ if (error == 0) {
+ dirbuf.dotdot_ino = dp->i_number;
+ (void) rdwri(UIO_WRITE, ip, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
+ }
+stuck:
+ irele(dp);
+ iput(ip);
+ }
+ goto done;
+
+bad:
+ iput(dp);
+bad1:
+ if (xp)
+ iput(xp);
+out:
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ irele(ip);
+done:
+ if (error)
+ u.u_error = error;
+}
+
+/*
+ * Make a new file.
+ */
+struct inode *
+maknode(mode)
+ int mode;
+{
+ register struct inode *ip;
+ ino_t ipref;
+
+ if ((mode & IFMT) == IFDIR)
+ ipref = dirpref(u.u_pdir->i_fs);
+ else
+ ipref = u.u_pdir->i_number;
+ ip = ialloc(u.u_pdir, ipref, mode);
+ if (ip == NULL) {
+ iput(u.u_pdir);
+ return (NULL);
+ }
+#ifdef QUOTA
+ if (ip->i_dquot != NODQUOT)
+ panic("maknode: dquot");
+#endif
+ ip->i_flag |= IACC|IUPD|ICHG;
+ if ((mode & IFMT) == 0)
+ mode |= IFREG;
+ ip->i_mode = mode & ~u.u_cmask;
+ ip->i_nlink = 1;
+ ip->i_uid = u.u_uid;
+ ip->i_gid = u.u_pdir->i_gid;
+ if (ip->i_mode & ISGID && !groupmember(ip->i_gid))
+ ip->i_mode &= ~ISGID;
+#ifdef QUOTA
+ ip->i_dquot = inoquota(ip);
+#endif
+
+ /*
+ * Make sure inode goes to disk before directory entry.
+ */
+ iupdat(ip, &time, &time, 1);
+ u.u_error = direnter(ip);
+ if (u.u_error) {
+ /*
+ * Write error occurred trying to update directory
+ * so must deallocate the inode.
+ */
+ ip->i_nlink = 0;
+ ip->i_flag |= ICHG;
+ iput(ip);
+ return (NULL);
+ }
+ return (ip);
+}
+
+/*
+ * A virgin directory (no blushing please).
+ */
+struct dirtemplate mastertemplate = {
+ 0, 12, 1, ".",
+ 0, DIRBLKSIZ - 12, 2, ".."
+};
+
+/*
+ * Mkdir system call
+ */
+mkdir()
+{
+ struct a {
+ char *name;
+ int dmode;
+ } *uap;
+ register struct inode *ip, *dp;
+ struct dirtemplate dirtemplate;
+
+ uap = (struct a *)u.u_ap;
+ ip = namei(uchar, CREATE, 0);
+ if (u.u_error)
+ return;
+ if (ip != NULL) {
+ iput(ip);
+ u.u_error = EEXIST;
+ return;
+ }
+ dp = u.u_pdir;
+ uap->dmode &= 0777;
+ uap->dmode |= IFDIR;
+ /*
+ * Must simulate part of maknode here
+ * in order to acquire the inode, but
+ * not have it entered in the parent
+ * directory. The entry is made later
+ * after writing "." and ".." entries out.
+ */
+ ip = ialloc(dp, dirpref(dp->i_fs), uap->dmode);
+ if (ip == NULL) {
+ iput(dp);
+ return;
+ }
+#ifdef QUOTA
+ if (ip->i_dquot != NODQUOT)
+ panic("mkdir: dquot");
+#endif
+ ip->i_flag |= IACC|IUPD|ICHG;
+ ip->i_mode = uap->dmode & ~u.u_cmask;
+ ip->i_nlink = 2;
+ ip->i_uid = u.u_uid;
+ ip->i_gid = dp->i_gid;
+#ifdef QUOTA
+ ip->i_dquot = inoquota(ip);
+#endif
+ iupdat(ip, &time, &time, 1);
+
+ /*
+ * Bump link count in parent directory
+ * to reflect work done below. Should
+ * be done before reference is created
+ * so reparation is possible if we crash.
+ */
+ dp->i_nlink++;
+ dp->i_flag |= ICHG;
+ iupdat(dp, &time, &time, 1);
+
+ /*
+ * Initialize directory with "."
+ * and ".." from static template.
+ */
+ dirtemplate = mastertemplate;
+ dirtemplate.dot_ino = ip->i_number;
+ dirtemplate.dotdot_ino = dp->i_number;
+ u.u_error = rdwri(UIO_WRITE, ip, (caddr_t)&dirtemplate,
+ sizeof (dirtemplate), (off_t)0, 1, (int *)0);
+ if (u.u_error) {
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ goto bad;
+ }
+ /*
+ * Directory all set up, now
+ * install the entry for it in
+ * the parent directory.
+ */
+ u.u_error = direnter(ip);
+ dp = NULL;
+ if (u.u_error) {
+ u.u_dirp = uap->name;
+ dp = namei(uchar, LOOKUP, 0);
+ if (dp) {
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ }
+ }
+bad:
+ /*
+ * No need to do an explicit itrunc here,
+ * irele will do this for us because we set
+ * the link count to 0.
+ */
+ if (u.u_error) {
+ ip->i_nlink = 0;
+ ip->i_flag |= ICHG;
+ }
+ if (dp)
+ iput(dp);
+ iput(ip);
+}
+
+/*
+ * Rmdir system call.
+ */
+rmdir()
+{
+ struct a {
+ char *name;
+ };
+ register struct inode *ip, *dp;
+
+ ip = namei(uchar, DELETE | LOCKPARENT, 0);
+ if (ip == NULL)
+ return;
+ dp = u.u_pdir;
+ /*
+ * No rmdir "." please.
+ */
+ if (dp == ip) {
+ irele(dp);
+ iput(ip);
+ u.u_error = EINVAL;
+ return;
+ }
+ if ((ip->i_mode&IFMT) != IFDIR) {
+ u.u_error = ENOTDIR;
+ goto out;
+ }
+ /*
+ * Don't remove a mounted on directory.
+ */
+ if (ip->i_dev != dp->i_dev) {
+ u.u_error = EBUSY;
+ goto out;
+ }
+ /*
+ * Verify the directory is empty (and valid).
+ * (Rmdir ".." won't be valid since
+ * ".." will contain a reference to
+ * the current directory and thus be
+ * non-empty.)
+ */
+ if (ip->i_nlink != 2 || !dirempty(ip)) {
+ u.u_error = ENOTEMPTY;
+ goto out;
+ }
+ /*
+ * Delete reference to directory before purging
+ * inode. If we crash in between, the directory
+ * will be reattached to lost+found,
+ */
+ if (dirremove() == 0)
+ goto out;
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ iput(dp);
+ dp = NULL;
+ /*
+ * Truncate inode. The only stuff left
+ * in the directory is "." and "..". The
+ * "." reference is inconsequential since
+ * we're quashing it. The ".." reference
+ * has already been adjusted above. We've
+ * removed the "." reference and the reference
+ * in the parent directory, but there may be
+ * other hard links so decrement by 2 and
+ * worry about them later.
+ */
+ ip->i_nlink -= 2;
+ itrunc(ip, (u_long)0);
+out:
+ if (dp)
+ iput(dp);
+ iput(ip);
+}
+
+struct file *
+getinode(fdes)
+ int fdes;
+{
+ register struct file *fp;
+
+ fp = getf(fdes);
+ if (fp == 0)
+ return (0);
+ if (fp->f_type != DTYPE_INODE) {
+ u.u_error = EINVAL;
+ return (0);
+ }
+ return (fp);
+}
+
+/*
+ * mode mask for creation of files
+ */
+umask()
+{
+ register struct a {
+ int mask;
+ } *uap;
+
+ uap = (struct a *)u.u_ap;
+ u.u_r.r_val1 = u.u_cmask;
+ u.u_cmask = uap->mask & 07777;