+ register struct inode *ip = VTOI(vp);
+ int error;
+
+ if (cred->cr_uid != ip->i_uid &&
+ (error = suser(cred, &u.u_acflag)))
+ return (error);
+ ip->i_mode &= ~07777;
+ if (cred->cr_uid) {
+ if (vp->v_type != VDIR)
+ mode &= ~ISVTX;
+ if (!groupmember(ip->i_gid, cred))
+ mode &= ~ISGID;
+ }
+ ip->i_mode |= mode & 07777;
+ ip->i_flag |= ICHG;
+ if ((vp->v_flag & VTEXT) && (ip->i_mode & ISVTX) == 0)
+ xrele(vp);
+ return (0);
+}
+
+/*
+ * Perform chown operation on inode ip;
+ * inode must be locked prior to call.
+ */
+chown1(vp, uid, gid, cred)
+ register struct vnode *vp;
+ uid_t uid;
+ gid_t gid;
+ struct ucred *cred;
+{
+ register struct inode *ip = VTOI(vp);
+#ifdef QUOTA
+ register long change;
+#endif
+ int error;
+
+ if (uid == (u_short)VNOVAL)
+ uid = ip->i_uid;
+ if (gid == (u_short)VNOVAL)
+ gid = ip->i_gid;
+ /*
+ * If we don't own the file, are trying to change the owner
+ * of the file, or are not a member of the target group,
+ * the caller must be superuser or the call fails.
+ */
+ if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid ||
+ !groupmember((gid_t)gid, cred)) &&
+ (error = suser(cred, &u.u_acflag)))
+ return (error);
+#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;
+ 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_t)uid, 1);
+ return (u.u_error); /* should == 0 ALWAYS !! */
+#else
+ return (0);
+#endif
+}
+
+/* ARGSUSED */
+ufs_ioctl(vp, com, data, fflag, cred)
+ struct vnode *vp;
+ int com;
+ caddr_t data;
+ int fflag;
+ struct ucred *cred;
+{
+
+ printf("ufs_ioctl called with type %d\n", vp->v_type);
+ return (ENOTTY);
+}
+
+/* ARGSUSED */
+ufs_select(vp, which, cred)
+ struct vnode *vp;
+ int which;
+ struct ucred *cred;
+{
+
+ printf("ufs_select called with type %d\n", vp->v_type);
+ return (1); /* XXX */
+}
+
+/*
+ * Mmap a file
+ *
+ * NB Currently unsupported.
+ */
+/* ARGSUSED */
+ufs_mmap(vp, fflags, cred)
+ struct vnode *vp;
+ int fflags;
+ struct ucred *cred;
+{
+
+ return (EINVAL);
+}
+
+/*
+ * Synch an open file.
+ */
+/* ARGSUSED */
+ufs_fsync(vp, fflags, cred)
+ struct vnode *vp;
+ int fflags;
+ struct ucred *cred;
+{
+ register struct inode *ip = VTOI(vp);
+ int error;
+
+ ILOCK(ip);
+ if (fflags&FWRITE)
+ ip->i_flag |= ICHG;
+ error = syncip(ip);
+ IUNLOCK(ip);
+ return (error);
+}
+
+/*
+ * Seek on a file
+ *
+ * Nothing to do, so just return.
+ */
+/* ARGSUSED */
+ufs_seek(vp, oldoff, newoff, cred)
+ struct vnode *vp;
+ off_t oldoff, newoff;
+ struct ucred *cred;
+{
+
+ return (0);
+}
+
+/*
+ * ufs remove
+ * Hard to avoid races here, especially
+ * in unlinking directories.
+ */
+ufs_remove(ndp)
+ struct nameidata *ndp;
+{
+ register struct inode *ip, *dp;
+ int error;
+
+ ip = VTOI(ndp->ni_vp);
+ dp = VTOI(ndp->ni_dvp);
+ error = dirremove(ndp);
+ if (!error) {
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ }
+ if (dp == ip)
+ vrele(ITOV(ip));
+ else
+ iput(ip);
+ iput(dp);
+ return (error);
+}
+
+/*
+ * link vnode call
+ */
+ufs_link(vp, ndp)
+ register struct vnode *vp;
+ register struct nameidata *ndp;
+{
+ register struct inode *ip = VTOI(vp);
+ int error;
+
+ if (ndp->ni_dvp != vp)
+ ILOCK(ip);
+ if (ip->i_nlink == LINK_MAX - 1) {
+ error = EMLINK;
+ goto out;
+ }
+ ip->i_nlink++;
+ ip->i_flag |= ICHG;
+ error = iupdat(ip, &time, &time, 1);
+ if (!error)
+ error = direnter(ip, ndp);
+out:
+ if (ndp->ni_dvp != vp)
+ IUNLOCK(ip);
+ if (error) {
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ }
+ return (error);
+}
+
+/*
+ * 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 ensure 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.
+ */
+ufs_rename(fndp, tndp)
+ register struct nameidata *fndp, *tndp;
+{
+ register struct inode *ip, *xp, *dp;
+ struct dirtemplate dirbuf;
+ int doingdirectory = 0, oldparent = 0, newparent = 0;
+ int error = 0;
+
+ dp = VTOI(fndp->ni_dvp);
+ ip = VTOI(fndp->ni_vp);
+ ILOCK(ip);
+ if ((ip->i_mode&IFMT) == IFDIR) {
+ register struct direct *d = &fndp->ni_dent;
+
+ /*
+ * Avoid ".", "..", and aliases of "." for obvious reasons.
+ */
+ if ((d->d_namlen == 1 && d->d_name[0] == '.') || dp == ip ||
+ fndp->ni_isdotdot || (ip->i_flag & IRENAME)) {
+ IUNLOCK(ip);
+ ufs_abortop(fndp);
+ ufs_abortop(tndp);
+ return (EINVAL);
+ }
+ ip->i_flag |= IRENAME;
+ oldparent = dp->i_number;
+ doingdirectory++;
+ }
+ vrele(fndp->ni_dvp);
+
+ /*
+ * 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;
+ error = iupdat(ip, &time, &time, 1);
+ IUNLOCK(ip);
+
+ /*
+ * When the target exists, both the directory
+ * and target vnodes are returned locked.
+ */
+ dp = VTOI(tndp->ni_dvp);
+ xp = NULL;
+ if (tndp->ni_vp)
+ xp = VTOI(tndp->ni_vp);
+ /*
+ * 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 (error = iaccess(ip, IWRITE, tndp->ni_cred))
+ goto bad;
+ tndp->ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE;
+ do {
+ dp = VTOI(tndp->ni_dvp);
+ if (xp != NULL)
+ iput(xp);
+ if (error = checkpath(ip, dp, tndp->ni_cred))
+ goto out;
+ if (error = namei(tndp))
+ goto out;
+ xp = NULL;
+ if (tndp->ni_vp)
+ xp = VTOI(tndp->ni_vp);
+ } while (dp != VTOI(tndp->ni_dvp));
+ }
+ /*
+ * 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)
+ panic("rename: EXDEV");
+ /*
+ * 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;
+ error = iupdat(dp, &time, &time, 1);
+ }
+ if (error = direnter(ip, tndp))
+ goto out;