ufs_vnops.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,066 行 · 第 1/4 页

C
2,066
字号
		ip->i_mode &= ~ISGID;	return (0);}/* ARGSUSED */intufs_ioctl(v)	void *v;{#if 0	struct vop_ioctl_args /* {		struct vnode *a_vp;		u_long a_command;		caddr_t  a_data;		int  a_fflag;		struct ucred *a_cred;		struct proc *a_p;	} */ *ap = v;#endif	return (ENOTTY);}/* ARGSUSED */intufs_select(v)	void *v;{#if 0	struct vop_select_args /* {		struct vnode *a_vp;		int  a_which;		int  a_fflags;		struct ucred *a_cred;		struct proc *a_p;	} */ *ap = v;#endif	/*	 * We should really check to see if I/O is possible.	 */	return (1);}/* * Mmap a file * * NB Currently unsupported. *//* ARGSUSED */intufs_mmap(v)	void *v;{#if 0	struct vop_mmap_args /* {		struct vnode *a_vp;		int  a_fflags;		struct ucred *a_cred;		struct proc *a_p;	} */ *ap = v;#endif	return (EINVAL);}/* * Seek on a file * * Nothing to do, so just return. *//* ARGSUSED */intufs_seek(v)	void *v;{#if 0	struct vop_seek_args /* {		struct vnode *a_vp;		off_t  a_oldoff;		off_t  a_newoff;		struct ucred *a_cred;	} */ *ap = v;#endif	return (0);}intufs_remove(v)	void *v;{	struct vop_remove_args /* {		struct vnode *a_dvp;		struct vnode *a_vp;		struct componentname *a_cnp;	} */ *ap = v;	register struct inode *ip;	register struct vnode *vp = ap->a_vp;	register struct vnode *dvp = ap->a_dvp;	int error;	if (vp->v_type == VDIR) {		error = EISDIR;		goto out;	}	ip = VTOI(vp);	if ((ip->i_flags & (IMMUTABLE | APPEND)) ||	    (VTOI(dvp)->i_flags & APPEND)) {		error = EPERM;		goto out;	}	if ((error = ufs_dirremove(dvp, ap->a_cnp)) == 0) {		ip->i_nlink--;		ip->i_flag |= IN_CHANGE;	}out:	if (dvp == vp)		vrele(vp);	else		vput(vp);	vput(dvp);	return (error);}/* * link vnode call */intufs_link(v)	void *v;{	struct vop_link_args /* {		struct vnode *a_dvp;		struct vnode *a_vp;		struct componentname *a_cnp;	} */ *ap = v;	register struct vnode *dvp = ap->a_dvp;	register struct vnode *vp = ap->a_vp;	register struct componentname *cnp = ap->a_cnp;	register struct inode *ip;	struct timespec ts;	int error;#ifdef DIAGNOSTIC	if ((cnp->cn_flags & HASBUF) == 0)		panic("ufs_link: no name");#endif	if (vp->v_type == VDIR) {		VOP_ABORTOP(dvp, cnp);		error = EISDIR;		goto out2;	}	if (dvp->v_mount != vp->v_mount) {		VOP_ABORTOP(dvp, cnp);		error = EXDEV;		goto out2;	}	if (dvp != vp && (error = VOP_LOCK(vp))) {		VOP_ABORTOP(dvp, cnp);		goto out2;	}	ip = VTOI(vp);	if ((nlink_t)ip->i_nlink >= LINK_MAX) {		VOP_ABORTOP(dvp, cnp);		error = EMLINK;		goto out1;	}	if (ip->i_flags & (IMMUTABLE | APPEND)) {		VOP_ABORTOP(dvp, cnp);		error = EPERM;		goto out1;	}	ip->i_nlink++;	ip->i_flag |= IN_CHANGE;	TIMEVAL_TO_TIMESPEC(&time, &ts);	error = VOP_UPDATE(vp, &ts, &ts, 1);	if (!error)		error = ufs_direnter(ip, dvp, cnp);	if (error) {		ip->i_nlink--;		ip->i_flag |= IN_CHANGE;	}	FREE(cnp->cn_pnbuf, M_NAMEI);out1:	if (dvp != vp)		VOP_UNLOCK(vp);out2:	vput(dvp);	return (error);}/* * whiteout vnode call */intufs_whiteout(v)	void *v;{	struct vop_whiteout_args /* {		struct vnode *a_dvp;		struct componentname *a_cnp;		int a_flags;	} */ *ap = v;	struct vnode *dvp = ap->a_dvp;	struct componentname *cnp = ap->a_cnp;	struct direct newdir;	int error = 0;	switch (ap->a_flags) {	case LOOKUP:		/* 4.4 format directories support whiteout operations */		if (dvp->v_mount->mnt_maxsymlinklen > 0)			return (0);		return (EOPNOTSUPP);	case CREATE:		/* create a new directory whiteout */#ifdef DIAGNOSTIC		if ((cnp->cn_flags & SAVENAME) == 0)			panic("ufs_whiteout: missing name");		if (dvp->v_mount->mnt_maxsymlinklen <= 0)			panic("ufs_whiteout: old format filesystem");#endif		newdir.d_ino = WINO;		newdir.d_namlen = cnp->cn_namelen;		bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1);		newdir.d_type = DT_WHT;		error = ufs_direnter2(dvp, &newdir, cnp->cn_cred, cnp->cn_proc);		break;	case DELETE:		/* remove an existing directory whiteout */#ifdef DIAGNOSTIC		if (dvp->v_mount->mnt_maxsymlinklen <= 0)			panic("ufs_whiteout: old format filesystem");#endif		cnp->cn_flags &= ~DOWHITEOUT;		error = ufs_dirremove(dvp, cnp);		break;	}	if (cnp->cn_flags & HASBUF) {		FREE(cnp->cn_pnbuf, M_NAMEI);		cnp->cn_flags &= ~HASBUF;	}	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. */intufs_rename(v)	void *v;{	struct vop_rename_args  /* {		struct vnode *a_fdvp;		struct vnode *a_fvp;		struct componentname *a_fcnp;		struct vnode *a_tdvp;		struct vnode *a_tvp;		struct componentname *a_tcnp;	} */ *ap = v;	struct vnode *tvp = ap->a_tvp;	register struct vnode *tdvp = ap->a_tdvp;	struct vnode *fvp = ap->a_fvp;	register struct vnode *fdvp = ap->a_fdvp;	register struct componentname *tcnp = ap->a_tcnp;	register struct componentname *fcnp = ap->a_fcnp;	register struct inode *ip, *xp, *dp;	struct dirtemplate dirbuf;	struct timespec ts;	int doingdirectory = 0, oldparent = 0, newparent = 0;	int error = 0;	u_char namlen;#ifdef DIAGNOSTIC	if ((tcnp->cn_flags & HASBUF) == 0 ||	    (fcnp->cn_flags & HASBUF) == 0)		panic("ufs_rename: no name");#endif	/*	 * Check for cross-device rename.	 */	if ((fvp->v_mount != tdvp->v_mount) ||	    (tvp && (fvp->v_mount != tvp->v_mount))) {		error = EXDEV;abortit:		VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */		if (tdvp == tvp)			vrele(tdvp);		else			vput(tdvp);		if (tvp)			vput(tvp);		VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */		vrele(fdvp);		vrele(fvp);		return (error);	}	/*	 * Check if just deleting a link name.	 */	if (tvp && ((VTOI(tvp)->i_flags & (IMMUTABLE | APPEND)) ||	    (VTOI(tdvp)->i_flags & APPEND))) {		error = EPERM;		goto abortit;	}	if (fvp == tvp) {		if (fvp->v_type == VDIR) {			error = EINVAL;			goto abortit;		}		/* Release destination completely. */		VOP_ABORTOP(tdvp, tcnp);		vput(tdvp);		vput(tvp);		/* Delete source. */		vrele(fdvp);		vrele(fvp);		fcnp->cn_flags &= ~MODMASK;		fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;		if ((fcnp->cn_flags & SAVESTART) == 0)			panic("ufs_rename: lost from startdir");		fcnp->cn_nameiop = DELETE;		(void) relookup(fdvp, &fvp, fcnp);		return (VOP_REMOVE(fdvp, fvp, fcnp));	}	if ((error = VOP_LOCK(fvp)) != 0)		goto abortit;	dp = VTOI(fdvp);	ip = VTOI(fvp);	if ((ip->i_flags & (IMMUTABLE | APPEND)) || (dp->i_flags & APPEND)) {		VOP_UNLOCK(fvp);		error = EPERM;		goto abortit;	}	if ((ip->i_mode & IFMT) == IFDIR) {		/*		 * Avoid ".", "..", and aliases of "." for obvious reasons.		 */		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||		    dp == ip || (fcnp->cn_flags&ISDOTDOT) ||		    (ip->i_flag & IN_RENAME)) {			VOP_UNLOCK(fvp);			error = EINVAL;			goto abortit;		}		ip->i_flag |= IN_RENAME;		oldparent = dp->i_number;		doingdirectory++;	}	vrele(fdvp);	/*	 * When the target exists, both the directory	 * and target vnodes are returned locked.	 */	dp = VTOI(tdvp);	xp = NULL;	if (tvp)		xp = VTOI(tvp);	/*	 * 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 |= IN_CHANGE;	TIMEVAL_TO_TIMESPEC(&time, &ts);	if ((error = VOP_UPDATE(fvp, &ts, &ts, 1)) != 0) {		VOP_UNLOCK(fvp);		goto bad;	}	/*	 * 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().	 */	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);	VOP_UNLOCK(fvp);	if (oldparent != dp->i_number)		newparent = dp->i_number;	if (doingdirectory && newparent) {		if (error)	/* write access check above */			goto bad;		if (xp != NULL)			vput(tvp);		if ((error = ufs_checkpath(ip, dp, tcnp->cn_cred)) != 0)			goto out;		if ((tcnp->cn_flags & SAVESTART) == 0)			panic("ufs_rename: lost to startdir");		if ((error = relookup(tdvp, &tvp, tcnp)) != 0)			goto out;		dp = VTOI(tdvp);		xp = NULL;		if (tvp)			xp = VTOI(tvp);	}	/*	 * 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) {			if ((nlink_t)dp->i_nlink >= LINK_MAX) {				error = EMLINK;				goto bad;			}			dp->i_nlink++;			dp->i_flag |= IN_CHANGE;			if ((error = VOP_UPDATE(tdvp, &ts, &ts, 1)) != 0)				goto bad;		}		if ((error = ufs_direnter(ip, tdvp, tcnp)) != 0) {			if (doingdirectory && newparent) {				dp->i_nlink--;				dp->i_flag |= IN_CHANGE;				(void)VOP_UPDATE(tdvp, &ts, &ts, 1);			}			goto bad;		}		vput(tdvp);	} else {		if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)			panic("rename: EXDEV");		/*		 * Short circuit rename(foo, foo).		 */		if (xp->i_number == ip->i_number)			panic("rename: same file");		/*		 * 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 & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&		    tcnp->cn_cred->cr_uid != dp->i_uid &&		    xp->i_uid != tcnp->cn_cred->cr_uid) {			error = EPERM;			goto bad;		}		/*		 * Target must be empty if a directory and have no links		 * to it. Also, ensure source and target are compatible		 * (both directories, or both not directories).		 */		if ((xp->i_mode&IFMT) == IFDIR) {			if (!ufs_dirempty(xp, dp->i_number, tcnp->cn_cred) || 			    xp->i_nlink > 2) {				error = ENOTEMPTY;				goto bad;			}			if (!doingdirectory) {				error = ENOTDIR;				goto bad;			}			cache_purge(tdvp);		} else if (doingdirectory) {			error = EISDIR;			goto bad;		}		if ((error = ufs_dirrewrite(dp, ip, tcnp)) != 0)			goto bad;		/*		 * If the target directory is in the same		 * directory as the source directory,		 * decrement the link count on the parent		 * of the target directory.		 */		 if (doingdirectory && !newparent) {			dp->i_nlink--;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?