📄 tfs_backlink.c
字号:
#ifndef lintstatic char sccsid[] = "@(#)tfs_backlink.c 1.1 92/07/30 Copyr 1988 Sun Micro";#endif/* * Copyright (c) 1988 Sun Microsystems, Inc. */#include <nse/param.h>#include <nse/util.h>#include "headers.h"#include "vnode.h"#include "tfs.h"#include "subr.h"#include "tfs_lists.h"#include <nse/searchlink.h>/* * Routines to remove and rename directories. These routines remove/rename * the directory in *all* variants, using the backlinks in .tfs_info to * determine the front layer in each variant. Renaming a directory can * potentially be a long-running operation, since all the searchlinks and * backlinks of sub-directories have to be renamed. Therefore, the old and * new name of the directory being renamed are recorded, to allow * completion of the rename later should the tfsd fail to finish the rename * (either because the tfsd is killed or the machine crashes.) * * The following rename records are written, to allow partially completed * directory renames to be recovered: * * !TFS_RENAME1 old_pattern new_pattern * Written into the source parent directory in the shared sub-layer. * 'old_pattern' and 'new_pattern' can be fed to replace_substring() * to determine the new directory name from the old name. * * !TFS_RENAME2 shared_path * Written into the source parent directories in the front sub-layers. * Also written into the destination parent directory, if the directory * is being renamed into a different parent directory. This record * points to the directory containing the !TFS_RENAME1 record. */int remove_directory();static int bl_verify_dir_empty();static int verify_dir_empty();static int bl_physical_rmdir();static int physical_rmdir();static void remove_tfs_file();int rename_directory();int finish_rename_directory();static int finish_rename_dir();int whiteout_other_dirs();static int bl_whiteout();static int get_backlinks();static int bl_rename_dir();static int rename_shared_dir();static int recursive_directory_op();static int rename_searchlink();static int rename_backlinks();static int create_parent_dir();static int clear_all_rename_records();static int bl_clear_rename_record();static int set_rename_record();static int clear_rename_record();Nse_whiteout bl_list_get_rename_elem();static void bl_list_remove_elem();static void replace_substring();static char *common_suffix();static int update_backlinks();int exists_in_variant();static int bl_verify_no_variant();/* * Remove the directory with vnode 'vp'. This routine returns 0 if the * directory is successfully removed, a positive error code otherwise. */intremove_directory(vp) struct vnode *vp;{ struct pnode *pp; char searchlink[MAXPATHLEN]; Nse_list backlinks; int result; if (result = validate_directory(vp)) { return result; } pp = vp->v_pnode; while (pp->p_sub_layer < MAX_SUB_LAYER) { pp = get_next_pnode(vp, pp); } if ((result = change_to_dir(PARENT_PNODE(pp))) || (result = get_backlinks(pp->p_name, searchlink, &backlinks, pp))) { return result; } if (nse_list_nelems(backlinks) == 0 && pp != vp->v_pnode) { vtovn(vp, searchlink); nse_log_message( "warning: can't remove directory %s, need Version 2 .tfs_info\n", searchlink); result = EACCES; goto error; } if ((result = (int) nse_list_iterate_or(backlinks, bl_verify_dir_empty)) || (result = verify_dir_empty(pp, (char *) NULL, (struct vnode **) NULL)) || (result = (int) nse_list_iterate_or(backlinks, bl_physical_rmdir)) || (result = physical_rmdir(pp))) { goto error; } result = update_directory(PARENT_PNODE(pp), pp->p_name, DIR_REMOVE_FILE, FALSE, TRUE, NULL);error: nse_list_destroy(backlinks); return result;}static intbl_verify_dir_empty(bp) Backlink bp;{ return verify_dir_empty(bp->pnode, bp->varname, &bp->dummy_vnode);}static intverify_dir_empty(pp, varname, dummy_vpp) struct pnode *pp; char *varname; struct vnode **dummy_vpp;{ struct vnode *vnodes[MAX_NAMES]; struct vnode *vp; struct vnode *childvp; int result; bzero((char *) vnodes, sizeof vnodes); get_vnodes(pp, vnodes); vp = vnodes[0]; if (vp == NULL) { if (dummy_vpp == NULL) { return 0; } vp = create_vnode((struct vnode *) NULL, "dummy", 0L); vp->v_layer = 0; vp->v_pnode = pp; *dummy_vpp = vp; /* * Create a tmp view so that validate_directory() * can follow searchlinks. */ if (create_tmp_view(vp, varname)) { return ENOTEMPTY; } } if (result = validate_directory(vp)) { return result; } for (childvp = CHILD_VNODE(vp); childvp; childvp = next_file(childvp)) { if (!childvp->v_whited_out && childvp->v_layer != INVALID_LAYER) { return ENOTEMPTY; } } return 0;}static intbl_physical_rmdir(bp) Backlink bp;{ return physical_rmdir(bp->pnode);}static intphysical_rmdir(pp) struct pnode *pp;{ char dir_name[MAXNAMLEN]; /* * Remove the .tfs_ files from the directory so that * the rmdir call will succeed. */ ptoname(pp, dir_name); remove_tfs_file(dir_name, NSE_TFS_FILE); remove_tfs_file(dir_name, NSE_TFS_BACK_FILE); remove_tfs_file(dir_name, NSE_TFS_SWAP_FILE); if (rmdir(dir_name) < 0) { return (errno); } return (0);}static voidremove_tfs_file(path, name) char *path; char *name;{ char tfs_name[MAXPATHLEN]; strcpy(tfs_name, path); nse_pathcat(tfs_name, name); (void) unlink(tfs_name);}/* * Rename the directory with vnode 'vp' to be named 'newname' in the * directory 'newpvp'. */intrename_directory(vp, newpvp, newname) struct vnode *vp; struct vnode *newpvp; char *newname;{ struct pnode *pp; struct pnode *new_parentp; char str[MAXPATHLEN]; char var[MAXPATHLEN]; Nse_list backlinks; Backlink bp; int result; if (result = validate_directory(vp)) { return result; } pp = vp->v_pnode; while (pp->p_sub_layer < MAX_SUB_LAYER) { pp = get_next_pnode(vp, pp); } new_parentp = newpvp->v_pnode; while (new_parentp->p_sub_layer < MAX_SUB_LAYER) { new_parentp = get_next_pnode(newpvp, new_parentp); } if (result = change_to_dir(new_parentp)) { return (result); } if (result = exists_in_variant(newname, var)) { vtovn(newpvp, str); nse_pathcat(str, newname); nse_log_message( "rename: %s already exists as a file in variant %s\n", str, var); return (ENOTDIR); } if ((result = change_to_dir(PARENT_PNODE(pp))) || (result = get_backlinks(pp->p_name, str, &backlinks, pp))) { return result; } if (nse_list_nelems(backlinks) == 0 && pp != vp->v_pnode) { vtovn(vp, str); nse_log_message( "warning: can't rename directory %s, need Version 2 .tfs_info\n", str); result = EACCES; goto error; } bp = (Backlink) nse_listelem_data(nse_list_first_elem(backlinks)); if (result = update_backlinks(bp, backlinks, pp, new_parentp, newname)) { goto error; } if ((result = update_directory(PARENT_PNODE(pp), pp->p_name, DIR_RENAME_FILE, new_parentp, newname, FALSE, TRUE)) || (result = update_directory(new_parentp, newname, DIR_ADD_FILE))) { /* * Another view may have a vnode for the destination parent * directory but not one for the source parent directory. * The second call to update_directory() guarantees that * the destination directory gets a vnode for the new * (renamed) directory. */ goto error; } if (PARENT_PNODE(pp) != new_parentp || !NSE_STREQ(pp->p_name, newname)) { rename_pnode(pp, new_parentp, newname); }error: nse_list_destroy(backlinks); return result;}/* * See if 'name' exists in any variant */intexists_in_variant(name, var) char *name; char *var;{ Nse_list backlinks; char str[MAXPATHLEN]; int result; if (result = get_backlinks(".", str, &backlinks, (struct pnode *)0)) { return result; } result = (int) nse_list_iterate_or(backlinks, bl_verify_no_variant, name, var); nse_list_destroy(backlinks); return result;}/* * Make sure that file 'name' does not exist in variant pointed * to by backlink 'bp'. */static intbl_verify_no_variant(bp, name, var) Backlink bp; char *name; char *var;{ char path[MAXPATHLEN]; struct stat statb; if (bp) { (void)strcpy(path, bp->path); nse_pathcat(path, name); if (lstat(path, &statb) == 0) { strcpy(var, bp->varname); return EEXIST; } } return 0;}/* * Update all tfs_info files with search links that point to 'pp' * using backlink information from 'bp' and 'backlinks' and physically * rename 'pp' to 'new_parentp'/'newname'. */static intupdate_backlinks(bp, backlinks, pp, new_parentp, newname) Backlink bp; Nse_list backlinks; struct pnode *pp; struct pnode *new_parentp; char *newname;{ char old_path[MAXPATHLEN]; char new_path[MAXPATHLEN]; char *old_pattern; char *new_pattern; char str[MAXPATHLEN]; char *cp; bool_t same_dir; int result; ptopn(pp, old_path); ptopn(new_parentp, new_path); nse_pathcat(new_path, newname); /* * If there was no .tfs_info file, and hence no backlink information * just do the rename. */ if (bp == NULL) { if (rename(old_path, new_path) == -1) { return errno; } return 0; } old_pattern = common_suffix(old_path, bp->path); new_pattern = &new_path[nse_find_substring(old_path, old_pattern) - old_path]; sprintf(str, "%s1 %s %s", NSE_TFS_RENAME_STR, old_pattern, new_pattern); if (result = set_rename_record(".", str)) { return result; } same_dir = (new_parentp == PARENT_PNODE(pp)); if (!same_dir) { sprintf(str, "%s2 %s", NSE_TFS_RENAME_STR, old_path); *rindex(str, '/') = '\0'; cp = rindex(new_path, '/'); *cp = '\0'; result = set_rename_record(new_path, str); *cp = '/'; if (result) { return result; } } if ((result = (int) nse_list_iterate_or(backlinks, bl_rename_dir, old_path, old_pattern, new_pattern, same_dir)) || (result = rename_shared_dir(pp, new_path, old_pattern, new_pattern, same_dir)) || (result = clear_all_rename_records(PARENT_PNODE(pp), new_parentp, old_pattern, new_pattern, backlinks, FALSE))) { return result; } return 0;}/* * Finish a partially-completed directory rename. (The rename wasn't * completed because the machine went down in the middle of it, for * example.) 'path' is the name of the parent directory of the directory * being renamed. */intfinish_rename_directory(path, rename_entry) char *path; char *rename_entry;{ char *cp; char type; Nse_whiteout bl; Nse_whiteout bl_rename; int result = 0; cp = rename_entry + strlen(NSE_TFS_RENAME_STR); type = *cp++; switch (type) { case '1': result = finish_rename_dir(path, cp); break; case '2': cp++; if (result = tfsd_get_backlink(cp, &bl)) { return result; } bl_rename = bl_list_get_rename_elem(bl); if (bl_rename) { result = finish_rename_directory(cp, bl_rename->name); } else { result = clear_rename_record(path); } nse_dispose_whiteout(bl); break; } return result;}static intfinish_rename_dir(path, rename_entry) char *path; char *rename_entry;{ struct pnode *pp; struct pnode *new_parentp; char old_path[MAXPATHLEN]; char new_path[MAXPATHLEN]; char old_pattern[MAXNAMLEN]; char new_pattern[MAXNAMLEN]; char searchlink[MAXPATHLEN]; char *cp; bool_t same_dir; Nse_list backlinks = NULL; int result; struct stat statb; sscanf(rename_entry, "%s %s", old_pattern, new_pattern); strcpy(old_path, path); nse_pathcat(old_path, rindex(old_pattern, '/') + 1); pp = path_to_pnode(old_path, MAX_SUB_LAYER); replace_substring(old_path, old_pattern, new_pattern, new_path); cp = rindex(new_path, '/'); *cp = '\0'; same_dir = NSE_STREQ(path, new_path); if (!same_dir) { new_parentp = path_to_pnode(new_path, MAX_SUB_LAYER); } else { new_parentp = PARENT_PNODE(pp); } *cp = '/'; if (stat(old_path, &statb) == 0) { /* * If the old name still exists, complete the rename */ if (result = get_backlinks(old_path, searchlink, &backlinks, (struct pnode *) NULL)) { goto error; } if ((result = (int) nse_list_iterate_or(backlinks, bl_rename_dir, old_path, old_pattern, new_pattern, same_dir)) || (result = rename_shared_dir(pp, new_path, old_pattern, new_pattern, same_dir))) { goto error; } nse_list_destroy(backlinks); backlinks = NULL; } /* * Necessary to get the backlinks again even if they've already * been read, because the backlinks may have pointed to invalid * directories if the tfsd crashed after some or all of the front * layers were renamed but before the backlinks were renamed. */ if (result = get_backlinks(new_path, searchlink, &backlinks, (struct pnode *) NULL)) { goto error; } result = clear_all_rename_records(PARENT_PNODE(pp), new_parentp, old_pattern, new_pattern, backlinks, TRUE);error: if (backlinks) { nse_list_destroy(backlinks); } if (!same_dir) { free_pnode(new_parentp); } free_pnode(pp); return result;}/* * Whiteout the file named 'name' in all directories, except directory * 'exclude_pp', which have searchlinks pointing to directory 'shared_pp'. * Needed for tfs_pull(). */intwhiteout_other_dirs(shared_pp, name, exclude_pp) struct pnode *shared_pp; char *name; struct pnode *exclude_pp;{ char searchlink[MAXPATHLEN]; Nse_list backlinks; int result; if ((result = change_to_dir(PARENT_PNODE(shared_pp))) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -