summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/hardlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/hardlink.c')
-rw-r--r--contrib/cvs/src/hardlink.c298
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;
+}
OpenPOWER on IntegriCloud