tfs_ops.c

来自「操作系统SunOS 4.1.3版本的源码」· C语言 代码 · 共 567 行

C
567
字号
#ifndef lintstatic char sccsid[] = "@(#)tfs_ops.c 1.1 92/07/30 Copyr 1988 Sun Micro";#endif/* * Copyright (c) 1987 Sun Microsystems, Inc. */#include <nse/types.h>#include "headers.h"#include "vnode.h"#include "tfs.h"#include "subr.h"#include <nse/param.h>#include <nse/util.h>#include <nse/hash.h>#include <nse/searchlink.h>/* * This file contains all the TFS protocol routines, except for tfs_mount(), * tfs_umount(), and tfs_flush(), which live in tfs_mount.c.  For these * routines, we assume that the parent directory always exists in the * frontmost writeable layer. */int		tfs_unwhiteout();int		tfs_get_wo_entries();int		tfs_sync();int		tfs_push();int		tfs_pull();int		tfs_set_searchlink();int		tfs_clear_front();int		tfs_wo_readonly_files();/* * Utility routines */int		set_whiteout();int		unwhiteout();static struct vnode *get_wo_entries();int		clear_file();int		push_to_sub_layer();static int	move_file();/* * Procedure 22.  Unwhiteout. *//* ARGSUSED */inttfs_unwhiteout(pvp, args, status)	struct vnode	*pvp;	Tfs_name_args	args;	enum nfsstat	*status;{	struct vnode	*vp;	struct pnode	*pp;	vp = lookup_vnode(pvp, args->name, (struct nfsfattr *) NULL, TRUE);	if (vp == NULL || !vp->v_whited_out || !IS_WRITEABLE(vp)) {		return (0);	}	pp = get_pnode_at_layer(pvp, vp->v_layer);	return (update_directory(pp, args->name, DIR_CLEAR_FILE));}/* * Procedure 23.  Get white-out entries for a directory. */inttfs_get_wo_entries(pvp, args, res)	struct vnode	*pvp;	Tfs_get_wo_args	args;	Tfs_get_wo_res	res;{	/*	 * Maintain a one-entry cache, so that when a get_wo_entries	 * request comes in for the same directory at the current offset,	 * we can use 'last_vp' instead of seeking to the correct position	 * in the directory.	 */	static struct vnode *last_pvp = NULL;	static int	last_offset = 0;	static struct vnode *last_vp = NULL;	struct vnode	*vp;	int		offset;	int		result;	res->buf = read_result_buffer;	res->offset = args->offset;	res->count = 0;	res->eof = 0;	if (result = validate_directory(pvp)) {		return (result);	}	if ((pvp == last_pvp) && (args->offset == last_offset)) {		vp = last_vp;	} else {		vp = CHILD_VNODE(pvp);		if (vp != NULL && (!vp->v_whited_out || !IS_WRITEABLE(vp))) {			vp = next_whiteout(vp);		}		if (args->offset > 0) {			offset = 0;			while ((vp != NULL) && (offset < args->offset)) {				/*				 * Seek to correct offset within directory				 */				offset++;				vp = next_whiteout(vp);			}		}	}	last_vp = get_wo_entries(vp, res, args->nbytes);	if (res->eof) {		last_pvp = NULL;	} else {		last_offset = res->offset;		last_pvp = pvp;	}	return (0);}/* * Procedure 21.  Synchronize the TFS's write cache with disk. */inttfs_sync(){	sync_entire_wcache();	flush_all_attrs();}/* * Procedure 25. * Push the file with vnode 'vp' from the front sub-layer to the next * sub-layer back.  Needed for variants. */inttfs_push(vp)	struct vnode	*vp;{	struct pnode	*front_pp;	struct pnode	*back_pp;	char		pn[MAXPATHLEN];	char		*name = vp->v_name;	int		result;	if (vp->v_pnode->p_sub_layer == MAX_SUB_LAYER) {		return (0);	}	if (vp->v_pnode->p_type == PTYPE_DIR) {		return (EISDIR);	}	if (result = promote_file(vp)) {		return (result);	}	if (vp->v_pnode->p_fd != 0) {		close_fd(vp->v_pnode);	}	front_pp = PARENT_PNODE(vp->v_pnode);	back_pp = get_next_pnode(PARENT_VNODE(vp), front_pp);	if (back_pp == NULL) {		return (0);	}	if (result = change_to_dir(back_pp)) {		return (result);	}	ptopn(vp->v_pnode, pn);	if ((result = move_file(pn, name)) ||	    (result = update_directory(front_pp, name, DIR_CLEAR_FILE)) ||	    (result = update_directory(back_pp, name, DIR_ADD_FILE))) {		return (result);	}	return (0);}/* * Pull the file with vnode 'vp' from the file system just in back of * the front file system to the front file system .  Needed for variants. */inttfs_pull(vp)	struct vnode	*vp;{	struct pnode	*front_pp;	struct pnode	*back_pp;	char		pn[MAXPATHLEN];	char		*name = vp->v_name;	int		result;	if (vp->v_pnode->p_sub_layer == FIRST_SUBL(vp)) {		return (0);	}	if (vp->v_pnode->p_type == PTYPE_DIR) {		return (EISDIR);	}	if (result = promote_file(vp)) {		return (result);	}	if (vp->v_pnode->p_fd != 0) {		close_fd(vp->v_pnode);	}	back_pp = PARENT_PNODE(vp->v_pnode);	front_pp = PARENT_VNODE(vp)->v_pnode;	if (result = change_to_dir(front_pp)) {		return (result);	}	ptopn(vp->v_pnode, pn);	if ((result = move_file(pn, name)) ||	    (result = update_directory(back_pp, name, DIR_CLEAR_FILE)) ||	    (result = update_directory(front_pp, name, DIR_ADD_FILE)) ||	    (result = validate_directory(PARENT_VNODE(vp)))) {		return (result);	}	if (vp->v_back_layer != INVALID_LAYER &&	    (result = whiteout_other_dirs(back_pp, name, front_pp))) {		return result;	}	return (0);}/* * Procedure 26. * Set the searchlink of the directory with vnode 'vp' to the value of * args->value. */inttfs_set_searchlink(vp, args)	struct vnode	*vp;	Tfs_searchlink_args args;{	char		*name = args->value;	struct pnode	*back_pp;	char		pn[MAXPATHLEN];	char		full_name[MAXPATHLEN];	char		*cp;	int		result;	if (result = validate_directory(vp)) {		return (result);	}	back_pp = get_next_pnode(vp, vp->v_pnode);	if (back_pp != NULL) {		ptopn(back_pp, pn);		if (NSE_STREQ(name, pn)) {			/*			 * Directory already has a searchlink set to			 * the correct value.			 */			return args->conditional ? NSE_SL_ALREADY_SET_SAME : 0;		}		cp = nse_find_substring(name, NSE_TFS_WILDCARD);		if (cp != NULL) {			/*			 * The searchlink has the wildcard string in it, so			 * the existing searchlink may still match.  (This			 * match will succeed incorrectly if the existing			 * searchlink doesn't contain the wildcard string.)			 */			if (expand_searchlink(name, vp->v_environ_id, cp,					      full_name, TRUE) &&			    NSE_STREQ(full_name, pn)) {				return args->conditional ?						NSE_SL_ALREADY_SET_SAME : 0;			}		}		if (args->conditional) {			return (NSE_SL_ALREADY_SET_DIFF);		}	}	if ((result = change_to_dir(vp->v_pnode)) ||	    (result = tfsd_set_searchlink(".", name, vp->v_pnode)) ||	    (result = update_directory(vp->v_pnode, (char *) NULL,				       DIR_FLUSH))) {		return (result);	}	return (0);}/* * Procedure 27. * Remove the file 'args->name' from the frontmost sub-layer of the front * file system (and remove the white-out entry for it, if there is one.) */inttfs_clear_front(pvp, args)	struct vnode	*pvp;	Tfs_name_args	args;{	struct vnode	*vp;	vp = lookup_vnode(pvp, args->name, (struct nfsfattr *) NULL, TRUE);	if (vp == NULL || vp->v_layer > 0) {		return (0);	}	return (clear_file(pvp->v_pnode, vp));}/* * Procedure 28. * Whiteout all files in the directory with vnode 'pvp' that are showing * through from read-only filesystems.  (Whiteout only those files which * are in the first sub-layer.) */inttfs_wo_readonly_files(pvp)	struct vnode	*pvp;{	struct vnode	*vp;	struct vnode	*next_vp;	struct pnode	*parentp;	char		searchlink[MAXPATHLEN];	Nse_whiteout	wo;	Nse_whiteout	bl;	int		err;	int		result;	if ((result = validate_directory(pvp)) ||	    (result = change_to_dir(pvp->v_pnode))) {		return result;	}	if (result = tfsd_get_tfs_info(".", searchlink, &bl, &wo, TRUE,				       pvp->v_pnode, FALSE)) {		return result;	}	for (vp = CHILD_VNODE(pvp); vp != NULL; vp = next_vp) {		next_vp = NEXT_VNODE(vp);		if (vp->v_layer > pvp->v_writeable && !vp->v_whited_out) {			parentp = get_front_parent_pnode(pvp, vp->v_layer);			if (parentp->p_sub_layer == FIRST_SUBL(pvp) &&			    (result = update_directory(parentp, vp->v_name,						       DIR_REMOVE_FILE,						       TRUE, FALSE, &wo))) {				goto error;			}		}	}error:	if ((err = tfsd_set_tfs_info(".", searchlink, bl, wo, pvp->v_pnode)) &&	    result == 0) {		return err;	}	return result;}/* * Utility routines *//* * Set whiteout entry for 'vp' in the directory with vnode 'pvp'. * precondition:  'vp' is a vnode in a writeable layer. */intset_whiteout(pvp, vp, listp)	struct vnode	*pvp;	struct vnode	*vp;	Nse_whiteout	*listp;{	struct pnode	*pp;	int		result;	if (listp) {		Nse_whiteout	wo;		wo = NSE_NEW(Nse_whiteout);		wo->name = NSE_STRDUP(vp->v_name);		wo->next = *listp;		*listp = wo;		return (0);	}	pp = get_pnode_at_layer(pvp, vp->v_layer);	if (result = change_to_dir(pp)) {		return (result);	}	return (tfsd_set_whiteout(".", vp->v_name, pp));}/* * Remove whiteout entry for vnode 'vp' in the directory with vnode 'pvp'. */intunwhiteout(pvp, vp)	struct vnode	*pvp;	struct vnode	*vp;{	struct pnode	*pp;	int		result;	pp = get_pnode_at_layer(pvp, vp->v_layer);	if ((result = change_to_dir(pp)) ||	    (result = tfsd_clear_whiteout(".", vp->v_name, pp))) {		return (result);	}	return (0);}/* * Stuff whiteout entries from the vnode list 'vp' into the directory * buffer in gr->buf.  Returns pointer to the next entry in the list, if * the list is too long to fit in one buffer; otherwise NULL. */static struct vnode *get_wo_entries(vp, gr, maxcount)	struct vnode	*vp;	Tfs_get_wo_res	gr;	int		maxcount;{	struct direct	*dp;	char		*buffer = gr->buf;	int		len;	int		reclen;	do {		if (vp == NULL) {			gr->eof = 1;			break;		}		len = strlen(vp->v_name);		reclen = ENTRYSIZ(len);		if (gr->count + reclen >= maxcount) {			break;		}		dp = (struct direct *) buffer;		dp->d_fileno = 1;		strcpy(dp->d_name, vp->v_name);		dp->d_namlen = len;		dp->d_reclen = reclen;		gr->count += reclen;		buffer += reclen;		gr->offset++;#ifdef SUN_OS_4		dp->d_off = gr->offset;#endif		vp = next_whiteout(vp);	} while (1);	return (vp);}/* * Remove the file/whiteout entry 'vp' from the directory with pnode * 'parentp'. */intclear_file(parentp, vp)	struct pnode	*parentp;	struct vnode	*vp;{	int		result;	if (!vp->v_whited_out) {		if (vp->v_pnode->p_needs_write) {			sync_wcache(vp->v_pnode);		}		if (result = change_to_dir(parentp)) {			return (result);		}		if (unlink(vp->v_name) < 0) {			return (errno);		}	}	return (update_directory(parentp, vp->v_name, DIR_CLEAR_FILE));}/* * Push the file with vnode 'vp' to sub-layer 'sub_layer'.  Assumes that * 'vp' is currently at or in front of the specified sub-layer. */intpush_to_sub_layer(vp, sub_layer)	struct vnode	*vp;	int		sub_layer;{	int		result;	while (1) {		if (!lookup_pnode(PARENT_VNODE(vp), vp,		    (struct nfsfattr *) NULL)) {			return (errno);		}		if (vp->v_pnode->p_sub_layer == sub_layer) {			break;		}		if (result = tfs_push(vp)) {			return (result);		}	}	return (0);}/* * Try to move 'from' file to 'to' file accounting for all manner * of unusual error conditions. */static intmove_file(from, to)	char		*from;	char		*to;{	int		result;	struct stat	tostat;	struct stat	fromstat;	Nse_err		err;	result = rename(from, to);	if (result == -1) {		if (errno != EXDEV) {			return errno;		}		err = _nse_copy_file(to, from, 1);		if (err) {			if (err->code != EPERM) {				return err->code;			}			/*			 * If copy failed because we are not the owner of the			 * destination, make sure we can read the source as			 * anybody, change uid to the owner of the			 * destination, and try again.			 */			if (lstat(to, &tostat) < 0) {				return errno;			}			if ((lstat(from, &fromstat) < 0) ||			    (chmod(from, 0444) < 0)) {				return errno;			}			change_user_id((int)tostat.st_uid);			err = _nse_copy_file(to, from, 1);			if (!err && (chmod(to, (int)fromstat.st_mode) < 0)) {				err = nse_err_format_errno(					   "Can't change mode of %s\n", to);			}			change_user_id(current_user_id());			if (err) {				/*				 * It didn't work, so try to set the modes				 * back as if nothing had happened.				 */				(void)chmod(from, (int)fromstat.st_mode);				return err->code;			}		}		/*		 * Success! get rid of the source file.		 */		if (unlink(from) < 0) {			return (errno);		}	}	return (0);}

⌨️ 快捷键说明

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