+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);
+ if (fp->f_flag&FWRITE)
+ ip->i_flag |= ICHG;
+ 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);
+ if (u.u_error) {
+ error = u.u_error;
+ goto out;
+ }
+ dp = ndp->ni_pdir;
+ /*
+ * If ".." must be changed (ie the directory gets a new
+ * parent) then the source directory must not be in the
+ * directory heirarchy above the target, as this would
+ * orphan everything below the source directory. Also
+ * the user must have write permission in the source so
+ * as to be able to change "..". We must repeat the call
+ * to namei, as the parent directory is unlocked by the
+ * call to checkpath().
+ */
+ if (oldparent != dp->i_number)
+ newparent = dp->i_number;
+ if (doingdirectory && newparent) {
+ if (access(ip, IWRITE))
+ goto bad;
+ do {
+ dp = ndp->ni_pdir;
+ if (xp != NULL)
+ iput(xp);
+ u.u_error = checkpath(ip, dp);
+ if (u.u_error)
+ goto out;
+ xp = namei(ndp);
+ if (u.u_error) {
+ error = u.u_error;
+ goto out;
+ }
+ } while (dp != ndp->ni_pdir);
+ }
+ /*
+ * 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;
+ }
+ /*
+ * Account for ".." in new directory.
+ * When source and destination have the same
+ * parent we don't fool with the link count.
+ */
+ if (doingdirectory && newparent) {
+ dp->i_nlink++;
+ dp->i_flag |= ICHG;
+ iupdat(dp, &time, &time, 1);
+ }
+ error = direnter(ip, ndp);
+ 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;
+ /*
+ * If the parent directory is "sticky", then the user must
+ * own the parent directory, or the destination of the rename,
+ * otherwise the destination may not be changed (except by
+ * root). This implements append-only directories.
+ */
+ if ((dp->i_mode & ISVTX) && u.u_uid != 0 &&
+ u.u_uid != dp->i_uid && xp->i_uid != u.u_uid) {
+ error = EPERM;
+ 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, dp->i_number) || xp->i_nlink > 2) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ if (!doingdirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ cacheinval(dp);
+ } else if (doingdirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+ dirrewrite(dp, ip, ndp);
+ 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 the remaining link would point to
+ * a directory without "." or ".." entries.
+ */
+ 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.
+ */
+ ndp->ni_nameiop = DELETE | LOCKPARENT;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->from;
+ xp = namei(ndp);
+ if (xp != NULL)
+ dp = ndp->ni_pdir;
+ else
+ dp = NULL;
+ /*
+ * Insure that the directory entry still exists and has not
+ * changed while the new name has been entered. If the source is
+ * a file then the entry may have been unlinked or renamed. In
+ * either case there is no further work to be done. If the source
+ * is a directory then it cannot have been rmdir'ed; its link
+ * count of three would cause a rmdir to fail with ENOTEMPTY.
+ * The IRENAME flag insures that it cannot be moved by another
+ * rename.
+ */
+ if (xp != ip) {
+ if (doingdirectory)
+ panic("rename: lost dir entry");
+ } else {
+ /*
+ * If the source is a directory with a
+ * new parent, the link count of the old
+ * parent directory must be decremented
+ * and ".." set to point to the new parent.
+ */
+ if (doingdirectory && newparent) {
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ error = rdwri(UIO_READ, xp, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0, 1,
+ (int *)0);
+ if (error == 0) {
+ if (dirbuf.dotdot_namlen != 2 ||
+ dirbuf.dotdot_name[0] != '.' ||
+ dirbuf.dotdot_name[1] != '.') {
+ printf("rename: mangled dir\n");
+ } else {
+ dirbuf.dotdot_ino = newparent;
+ (void) rdwri(UIO_WRITE, xp,
+ (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate),
+ (off_t)0, 1, (int *)0);
+ cacheinval(dp);
+ }
+ }
+ }
+ if (dirremove(ndp)) {
+ xp->i_nlink--;
+ xp->i_flag |= ICHG;
+ }
+ xp->i_flag &= ~IRENAME;
+ if (error == 0) /* XXX conservative */
+ error = u.u_error;
+ }
+ if (dp)
+ iput(dp);
+ if (xp)
+ iput(xp);
+ irele(ip);
+ if (error)
+ u.u_error = error;
+ return;
+
+bad:
+ iput(dp);
+bad1:
+ if (xp)
+ iput(xp);
+out:
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ irele(ip);
+ if (error)
+ u.u_error = error;
+}
+
+/*
+ * Make a new file.
+ */
+struct inode *
+maknode(mode, ndp)
+ int mode;
+ register struct nameidata *ndp;