diff options
author | peter <peter@FreeBSD.org> | 1998-03-10 13:40:57 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1998-03-10 13:40:57 +0000 |
commit | 0c111e2b51cac7eead56494b30c5977e4ec9a8ea (patch) | |
tree | f60b0014663435c30f2efea2b10ca4f8ecc0208c /contrib/cvs/src/hardlink.c | |
parent | c3a8ee0e80a59793349940056dfd14746ebd4905 (diff) | |
download | FreeBSD-src-0c111e2b51cac7eead56494b30c5977e4ec9a8ea.zip FreeBSD-src-0c111e2b51cac7eead56494b30c5977e4ec9a8ea.tar.gz |
Import cvs-1.9.26 onto vendor branch
Diffstat (limited to 'contrib/cvs/src/hardlink.c')
-rw-r--r-- | contrib/cvs/src/hardlink.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/contrib/cvs/src/hardlink.c b/contrib/cvs/src/hardlink.c new file mode 100644 index 0000000..51bd2a6 --- /dev/null +++ b/contrib/cvs/src/hardlink.c @@ -0,0 +1,298 @@ +/* This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* Collect and manage hardlink info associated with a particular file. */ + +#include "cvs.h" +#include "hardlink.h" + +/* The structure currently used to manage hardlink info is a list. + Therefore, most of the functions which manipulate hardlink data + are walklist procedures. This is not a very efficient implementation; + if someone decides to use a real hash table (for instance), then + much of this code can be rewritten to be a little less arcane. + + Each element of `hardlist' represents an inode. It is keyed on the + inode number, and points to a list of files. This is to make it + easy to find out what files are linked to a given file FOO: find + FOO's inode, look it up in hardlist, and retrieve the list of files + associated with that inode. + + Each file node, in turn, is represented by a `hardlink_info' struct, + which includes `status' and `links' fields. The `status' field should + be used by a procedure like commit_fileproc or update_fileproc to + record each file's status; that way, after all file links have been + recorded, CVS can check the linkage of files which are in doubt + (i.e. T_NEEDS_MERGE files). + + TODO: a diagram of an example hardlist would help here. */ + +/* TODO: change this to something with a marginal degree of + efficiency, like maybe a hash table. Yeah. */ + +List *hardlist; /* Record hardlink information for working files */ +char *working_dir; /* The top-level working directory, used for + constructing full pathnames. */ + +/* For check_link_proc: list all of the files named in an inode list. */ +static int +list_files_proc (node, vstrp) + Node *node; + void *vstrp; +{ + char **strp, *file; + int len; + + /* Get the file's basename. This is because -- VERY IMPORTANT -- + the `hardlinks' field is presently defined only to include links + within a directory. So the hardlinks field might be `foo' or + `mumble grump flink', but not `foo bar com/baz' or `wham ../bam + ../thank/you'. Someday it would be nice to extend this to + permit cross-directory links, but the issues involved are + hideous. */ + + file = strrchr (node->key, '/'); + if (file) + ++file; + else + file = node->key; + + /* Is it safe to cast vstrp to (char **) here, and then play with + the contents? I think so, since vstrp will have started out + a char ** to begin with, so we should not have alignment bugs. */ + strp = (char **) vstrp; + len = (*strp == NULL ? 0 : strlen (*strp)); + *strp = (char *) xrealloc (*strp, len + strlen (file) + 2); + if (*strp == NULL) + { + error (0, errno, "could not allocate memory"); + return 1; + } + if (sprintf (*strp + len, "%s ", file) < 0) + { + error (0, errno, "could not compile file list"); + return 1; + } + + return 0; +} + +/* Set the link field of each hardlink_info node to `data', which is a + list of linked files. */ +static int +set_hardlink_field_proc (node, data) + Node *node; + void *data; +{ + struct hardlink_info *hlinfo = (struct hardlink_info *) node->data; + hlinfo->links = xstrdup ((char *) data); + + return 0; +} + +/* For each file being checked in, compile a list of the files linked + to it, and cache the list in the file's hardlink_info field. */ +int +cache_hardlinks_proc (node, data) + Node *node; + void *data; +{ + List *inode_links; + char *p, *linked_files = NULL; + int err; + + inode_links = (List *) node->data; + + /* inode->data is a list of hardlink_info structures: all the + files linked to this inode. We compile a string of each file + named in this list, in alphabetical order, separated by spaces. + Then store this string in the `links' field of each + hardlink_info structure, so that RCS_checkin can easily add + it to the `hardlinks' field of a new delta node. */ + + sortlist (inode_links, fsortcmp); + err = walklist (inode_links, list_files_proc, &linked_files); + if (err) + return err; + + /* Trim trailing whitespace. */ + p = linked_files + strlen(linked_files) - 1; + while (p > linked_files && isspace (*p)) + *p-- = '\0'; + + err = walklist (inode_links, set_hardlink_field_proc, linked_files); + return err; +} + +/* Return a pointer to FILEPATH's node in the hardlist. This means + looking up its inode, retrieving the list of files linked to that + inode, and then looking up FILE in that list. If the file doesn't + seem to exist, return NULL. */ +Node * +lookup_file_by_inode (filepath) + const char *filepath; +{ + char *inodestr, *file; + struct stat sb; + Node *hp, *p; + + /* Get file's basename, so that we can stat it. */ + file = strrchr (filepath, '/'); + if (file) + ++file; + else + file = (char *) filepath; + + /* inodestr contains the hexadecimal representation of an + inode, so it requires two bytes of text to represent + each byte of the inode number. */ + inodestr = (char *) xmalloc (2*sizeof(ino_t)*sizeof(char) + 1); + if (stat (file, &sb) < 0) + { + if (errno == ENOENT) + { + /* The file doesn't exist; we may be doing an update on a + file that's been removed. A nonexistent file has no + link information, so return without changing hardlist. */ + free (inodestr); + return NULL; + } + error (1, errno, "cannot stat %s", file); + } + + sprintf (inodestr, "%lx", (unsigned long) sb.st_ino); + + /* Find out if this inode is already in the hardlist, adding + a new entry to the list if not. */ + hp = findnode (hardlist, inodestr); + if (hp == NULL) + { + hp = getnode (); + hp->type = UNKNOWN; + hp->key = inodestr; + hp->data = (char *) getlist(); + hp->delproc = dellist; + (void) addnode (hardlist, hp); + } + else + { + free (inodestr); + } + + p = findnode ((List *) hp->data, filepath); + if (p == NULL) + { + p = getnode(); + p->type = UNKNOWN; + p->key = xstrdup (filepath); + p->data = NULL; + (void) addnode ((List *) hp->data, p); + } + + return p; +} + +/* After a file has been checked out, add a node for it to the hardlist + (if necessary) and mark it as checked out. */ +void +update_hardlink_info (file) + const char *file; +{ + char *path; + Node *n; + struct hardlink_info *hlinfo; + + if (file[0] == '/') + { + path = xstrdup (file); + } + else + { + /* file is a relative pathname; assume it's from the current + working directory. */ + char *dir = xgetwd(); + path = xmalloc (sizeof(char) * (strlen(dir) + strlen(file) + 2)); + sprintf (path, "%s/%s", dir, file); + free (dir); + } + + n = lookup_file_by_inode (path); + if (n == NULL) + { + /* Something is *really* wrong if the file doesn't exist here; + update_hardlink_info should be called only when a file has + just been checked out to a working directory. */ + error (1, 0, "lost hardlink info for %s", file); + } + + if (n->data == NULL) + n->data = (char *) xmalloc (sizeof (struct hardlink_info)); + hlinfo = (struct hardlink_info *) n->data; + hlinfo->status = T_UPTODATE; + hlinfo->checked_out = 1; + hlinfo->links = NULL; +} + +/* Return a string listing all the files known to be linked to FILE in + the working directory. Used by special_file_mismatch, to determine + whether it is safe to merge two files. */ +char * +list_files_linked_to (file) + const char *file; +{ + char *inodestr, *filelist, *path; + struct stat sb; + Node *n; + int err; + + /* If hardlist is NULL, we have not been doing an operation that + would permit us to know anything about the file's hardlinks + (cvs update, cvs commit, etc). Return an empty string. */ + if (hardlist == NULL) + return xstrdup (""); + + /* Get the full pathname of file (assuming the working directory) */ + if (file[0] == '/') + path = xstrdup (file); + else + { + char *dir = xgetwd(); + path = (char *) xmalloc (sizeof(char) * + (strlen(dir) + strlen(file) + 2)); + sprintf (path, "%s/%s", dir, file); + free (dir); + } + + /* We do an extra lookup_file here just to make sure that there + is a node for `path' in the hardlist. If that were not so, + comparing the working directory linkage against the repository + linkage for a file would always fail. */ + (void) lookup_file_by_inode (path); + + if (stat (path, &sb) < 0) + error (1, errno, "cannot stat %s", file); + /* inodestr contains the hexadecimal representation of an + inode, so it requires two bytes of text to represent + each byte of the inode number. */ + inodestr = (char *) xmalloc (2*sizeof(ino_t)*sizeof(char) + 1); + sprintf (inodestr, "%lx", (unsigned long) sb.st_ino); + + /* Make sure the files linked to this inode are sorted. */ + n = findnode (hardlist, inodestr); + sortlist ((List *) n->data, fsortcmp); + + filelist = NULL; + err = walklist ((List *) n->data, list_files_proc, &filelist); + if (err) + error (1, 0, "cannot get list of hardlinks for %s", file); + + free (inodestr); + return filelist; +} |