xref: /openbsd/gnu/usr.bin/cvs/src/hardlink.c (revision 36ac2e2a)
15e617892Stholo /* This program is free software; you can redistribute it and/or modify
25e617892Stholo    it under the terms of the GNU General Public License as published by
35e617892Stholo    the Free Software Foundation; either version 2, or (at your option)
45e617892Stholo    any later version.
55e617892Stholo 
65e617892Stholo    This program is distributed in the hope that it will be useful,
75e617892Stholo    but WITHOUT ANY WARRANTY; without even the implied warranty of
85e617892Stholo    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
95e617892Stholo    GNU General Public License for more details.  */
105e617892Stholo 
115e617892Stholo /* Collect and manage hardlink info associated with a particular file.  */
125e617892Stholo 
135e617892Stholo #include "cvs.h"
145e617892Stholo #include "hardlink.h"
155e617892Stholo 
165e617892Stholo /* The structure currently used to manage hardlink info is a list.
175e617892Stholo    Therefore, most of the functions which manipulate hardlink data
185e617892Stholo    are walklist procedures.  This is not a very efficient implementation;
195e617892Stholo    if someone decides to use a real hash table (for instance), then
205e617892Stholo    much of this code can be rewritten to be a little less arcane.
215e617892Stholo 
225e617892Stholo    Each element of `hardlist' represents an inode.  It is keyed on the
235e617892Stholo    inode number, and points to a list of files.  This is to make it
245e617892Stholo    easy to find out what files are linked to a given file FOO: find
255e617892Stholo    FOO's inode, look it up in hardlist, and retrieve the list of files
265e617892Stholo    associated with that inode.
275e617892Stholo 
285e617892Stholo    Each file node, in turn, is represented by a `hardlink_info' struct,
295e617892Stholo    which includes `status' and `links' fields.  The `status' field should
305e617892Stholo    be used by a procedure like commit_fileproc or update_fileproc to
315e617892Stholo    record each file's status; that way, after all file links have been
325e617892Stholo    recorded, CVS can check the linkage of files which are in doubt
335e617892Stholo    (i.e. T_NEEDS_MERGE files).
345e617892Stholo 
355e617892Stholo    TODO: a diagram of an example hardlist would help here. */
365e617892Stholo 
375e617892Stholo /* TODO: change this to something with a marginal degree of
385e617892Stholo    efficiency, like maybe a hash table.  Yeah. */
395e617892Stholo 
405e617892Stholo List *hardlist;		/* Record hardlink information for working files */
415e617892Stholo char *working_dir;	/* The top-level working directory, used for
425e617892Stholo 			   constructing full pathnames. */
435e617892Stholo 
445e617892Stholo /* Return a pointer to FILEPATH's node in the hardlist.  This means
455e617892Stholo    looking up its inode, retrieving the list of files linked to that
465e617892Stholo    inode, and then looking up FILE in that list.  If the file doesn't
475e617892Stholo    seem to exist, return NULL. */
485e617892Stholo Node *
lookup_file_by_inode(filepath)495e617892Stholo lookup_file_by_inode (filepath)
505e617892Stholo     const char *filepath;
515e617892Stholo {
525e617892Stholo     char *inodestr, *file;
535e617892Stholo     struct stat sb;
545e617892Stholo     Node *hp, *p;
555e617892Stholo 
565e617892Stholo     /* Get file's basename, so that we can stat it. */
575e617892Stholo     file = strrchr (filepath, '/');
585e617892Stholo     if (file)
595e617892Stholo 	++file;
605e617892Stholo     else
615e617892Stholo 	file = (char *) filepath;
625e617892Stholo 
635e617892Stholo     /* inodestr contains the hexadecimal representation of an
645e617892Stholo        inode, so it requires two bytes of text to represent
655e617892Stholo        each byte of the inode number. */
66e77048c1Stholo     inodestr = (char *) xmalloc (2*sizeof(ino_t) + 1);
675e617892Stholo     if (stat (file, &sb) < 0)
685e617892Stholo     {
69c71bc7e2Stholo 	if (existence_error (errno))
705e617892Stholo 	{
715e617892Stholo 	    /* The file doesn't exist; we may be doing an update on a
725e617892Stholo 	       file that's been removed.  A nonexistent file has no
735e617892Stholo 	       link information, so return without changing hardlist. */
745e617892Stholo 	    free (inodestr);
755e617892Stholo 	    return NULL;
765e617892Stholo 	}
775e617892Stholo 	error (1, errno, "cannot stat %s", file);
785e617892Stholo     }
795e617892Stholo 
80*36ac2e2aSguenther     sprintf (inodestr, "%llx", (unsigned long long) sb.st_ino);
815e617892Stholo 
825e617892Stholo     /* Find out if this inode is already in the hardlist, adding
835e617892Stholo        a new entry to the list if not. */
845e617892Stholo     hp = findnode (hardlist, inodestr);
855e617892Stholo     if (hp == NULL)
865e617892Stholo     {
875e617892Stholo 	hp = getnode ();
88e77048c1Stholo 	hp->type = NT_UNKNOWN;
895e617892Stholo 	hp->key = inodestr;
905e617892Stholo 	hp->data = (char *) getlist();
915e617892Stholo 	hp->delproc = dellist;
925e617892Stholo 	(void) addnode (hardlist, hp);
935e617892Stholo     }
945e617892Stholo     else
955e617892Stholo     {
965e617892Stholo 	free (inodestr);
975e617892Stholo     }
985e617892Stholo 
995e617892Stholo     p = findnode ((List *) hp->data, filepath);
1005e617892Stholo     if (p == NULL)
1015e617892Stholo     {
1025e617892Stholo 	p = getnode();
103e77048c1Stholo 	p->type = NT_UNKNOWN;
1045e617892Stholo 	p->key = xstrdup (filepath);
1055e617892Stholo 	p->data = NULL;
1065e617892Stholo 	(void) addnode ((List *) hp->data, p);
1075e617892Stholo     }
1085e617892Stholo 
1095e617892Stholo     return p;
1105e617892Stholo }
1115e617892Stholo 
1125e617892Stholo /* After a file has been checked out, add a node for it to the hardlist
1135e617892Stholo    (if necessary) and mark it as checked out. */
1145e617892Stholo void
update_hardlink_info(file)1155e617892Stholo update_hardlink_info (file)
1165e617892Stholo     const char *file;
1175e617892Stholo {
1185e617892Stholo     char *path;
1195e617892Stholo     Node *n;
1205e617892Stholo     struct hardlink_info *hlinfo;
1215e617892Stholo 
1225e617892Stholo     if (file[0] == '/')
1235e617892Stholo     {
1245e617892Stholo 	path = xstrdup (file);
1255e617892Stholo     }
1265e617892Stholo     else
1275e617892Stholo     {
1285e617892Stholo 	/* file is a relative pathname; assume it's from the current
1295e617892Stholo 	   working directory. */
1305e617892Stholo 	char *dir = xgetwd();
131e77048c1Stholo 	path = xmalloc (strlen(dir) + strlen(file) + 2);
1325e617892Stholo 	sprintf (path, "%s/%s", dir, file);
1335e617892Stholo 	free (dir);
1345e617892Stholo     }
1355e617892Stholo 
1365e617892Stholo     n = lookup_file_by_inode (path);
1375e617892Stholo     if (n == NULL)
1385e617892Stholo     {
1395e617892Stholo 	/* Something is *really* wrong if the file doesn't exist here;
1405e617892Stholo 	   update_hardlink_info should be called only when a file has
1415e617892Stholo 	   just been checked out to a working directory. */
1425e617892Stholo 	error (1, 0, "lost hardlink info for %s", file);
1435e617892Stholo     }
1445e617892Stholo 
1455e617892Stholo     if (n->data == NULL)
1465e617892Stholo 	n->data = (char *) xmalloc (sizeof (struct hardlink_info));
1475e617892Stholo     hlinfo = (struct hardlink_info *) n->data;
1485e617892Stholo     hlinfo->status = T_UPTODATE;
1495e617892Stholo     hlinfo->checked_out = 1;
1505e617892Stholo }
1515e617892Stholo 
152b6f6614eStholo /* Return a List with all the files known to be linked to FILE in
1535e617892Stholo    the working directory.  Used by special_file_mismatch, to determine
154b6f6614eStholo    whether it is safe to merge two files.
155b6f6614eStholo 
156b6f6614eStholo    FIXME: What is the memory allocation for the return value?  We seem
157b6f6614eStholo    to sometimes allocate a new list (getlist() call below) and sometimes
158b6f6614eStholo    return an existing list (where we return n->data).  */
159b6f6614eStholo List *
list_linked_files_on_disk(file)160b6f6614eStholo list_linked_files_on_disk (file)
161b6f6614eStholo     char *file;
1625e617892Stholo {
163b6f6614eStholo     char *inodestr, *path;
1645e617892Stholo     struct stat sb;
1655e617892Stholo     Node *n;
1665e617892Stholo 
1675e617892Stholo     /* If hardlist is NULL, we have not been doing an operation that
1685e617892Stholo        would permit us to know anything about the file's hardlinks
169b6f6614eStholo        (cvs update, cvs commit, etc).  Return an empty list. */
1705e617892Stholo     if (hardlist == NULL)
171b6f6614eStholo 	return getlist();
1725e617892Stholo 
1735e617892Stholo     /* Get the full pathname of file (assuming the working directory) */
1745e617892Stholo     if (file[0] == '/')
1755e617892Stholo 	path = xstrdup (file);
1765e617892Stholo     else
1775e617892Stholo     {
1785e617892Stholo 	char *dir = xgetwd();
179e77048c1Stholo 	path = (char *) xmalloc (strlen(dir) + strlen(file) + 2);
1805e617892Stholo 	sprintf (path, "%s/%s", dir, file);
1815e617892Stholo 	free (dir);
1825e617892Stholo     }
1835e617892Stholo 
1845e617892Stholo     /* We do an extra lookup_file here just to make sure that there
1855e617892Stholo        is a node for `path' in the hardlist.  If that were not so,
1865e617892Stholo        comparing the working directory linkage against the repository
1875e617892Stholo        linkage for a file would always fail. */
1885e617892Stholo     (void) lookup_file_by_inode (path);
1895e617892Stholo 
1905e617892Stholo     if (stat (path, &sb) < 0)
1915e617892Stholo 	error (1, errno, "cannot stat %s", file);
1925e617892Stholo     /* inodestr contains the hexadecimal representation of an
1935e617892Stholo        inode, so it requires two bytes of text to represent
1945e617892Stholo        each byte of the inode number. */
195e77048c1Stholo     inodestr = (char *) xmalloc (2*sizeof(ino_t) + 1);
196*36ac2e2aSguenther     sprintf (inodestr, "%llx", (unsigned long long) sb.st_ino);
1975e617892Stholo 
1985e617892Stholo     /* Make sure the files linked to this inode are sorted. */
1995e617892Stholo     n = findnode (hardlist, inodestr);
2005e617892Stholo     sortlist ((List *) n->data, fsortcmp);
2015e617892Stholo 
2025e617892Stholo     free (inodestr);
203b6f6614eStholo     return (List *) n->data;
2045e617892Stholo }
205b6f6614eStholo 
206b6f6614eStholo /* Compare the files in the `key' fields of two lists, returning 1 if
207b6f6614eStholo    the lists are equivalent and 0 otherwise.
208b6f6614eStholo 
209b6f6614eStholo    Only the basenames of each file are compared. This is an awful hack
210b6f6614eStholo    that exists because list_linked_files_on_disk returns full paths
211b6f6614eStholo    and the `hardlinks' structure of a RCSVers node contains only
212b6f6614eStholo    basenames.  That in turn is a result of the awful hack that only
213b6f6614eStholo    basenames are stored in the RCS file.  If anyone ever solves the
214b6f6614eStholo    problem of correctly managing cross-directory hardlinks, this
215b6f6614eStholo    function (along with most functions in this file) must be fixed. */
216b6f6614eStholo 
217b6f6614eStholo int
compare_linkage_lists(links1,links2)218b6f6614eStholo compare_linkage_lists (links1, links2)
219b6f6614eStholo     List *links1;
220b6f6614eStholo     List *links2;
221b6f6614eStholo {
222b6f6614eStholo     Node *n1, *n2;
223b6f6614eStholo     char *p1, *p2;
224b6f6614eStholo 
225b6f6614eStholo     sortlist (links1, fsortcmp);
226b6f6614eStholo     sortlist (links2, fsortcmp);
227b6f6614eStholo 
228b6f6614eStholo     n1 = links1->list->next;
229b6f6614eStholo     n2 = links2->list->next;
230b6f6614eStholo 
231b6f6614eStholo     while (n1 != links1->list && n2 != links2->list)
232b6f6614eStholo     {
233b6f6614eStholo 	/* Get the basenames of both files. */
234b6f6614eStholo 	p1 = strrchr (n1->key, '/');
235b6f6614eStholo 	if (p1 == NULL)
236b6f6614eStholo 	    p1 = n1->key;
237b6f6614eStholo 	else
238b6f6614eStholo 	    ++p1;
239b6f6614eStholo 
240b6f6614eStholo 	p2 = strrchr (n2->key, '/');
241b6f6614eStholo 	if (p2 == NULL)
242b6f6614eStholo 	    p2 = n2->key;
243b6f6614eStholo 	else
244b6f6614eStholo 	    ++p2;
245b6f6614eStholo 
246b6f6614eStholo 	/* Compare the files' basenames. */
247b6f6614eStholo 	if (strcmp (p1, p2) != 0)
248b6f6614eStholo 	    return 0;
249b6f6614eStholo 
250b6f6614eStholo 	n1 = n1->next;
251b6f6614eStholo 	n2 = n2->next;
252b6f6614eStholo     }
253b6f6614eStholo 
254b6f6614eStholo     /* At this point we should be at the end of both lists; if not,
255b6f6614eStholo        one file has more links than the other, and return 1. */
256b6f6614eStholo     return (n1 == links1->list && n2 == links2->list);
257b6f6614eStholo }
258b6f6614eStholo 
259b6f6614eStholo /* Find a checked-out file in a list of filenames.  Used by RCS_checkout
260b6f6614eStholo    when checking out a new hardlinked file, to decide whether this file
261b6f6614eStholo    can be linked to any others that already exist.  The return value
262b6f6614eStholo    is not currently used. */
263b6f6614eStholo 
264b6f6614eStholo int
find_checkedout_proc(node,data)265b6f6614eStholo find_checkedout_proc (node, data)
266b6f6614eStholo     Node *node;
267b6f6614eStholo     void *data;
268b6f6614eStholo {
269b6f6614eStholo     Node **uptodate = (Node **) data;
270b6f6614eStholo     Node *link;
271b6f6614eStholo     char *dir = xgetwd();
272b6f6614eStholo     char *path;
273b6f6614eStholo     struct hardlink_info *hlinfo;
274b6f6614eStholo 
275b6f6614eStholo     /* If we have already found a file, don't do anything. */
276b6f6614eStholo     if (*uptodate != NULL)
277b6f6614eStholo 	return 0;
278b6f6614eStholo 
279b6f6614eStholo     /* Look at this file in the hardlist and see whether the checked_out
280b6f6614eStholo        field is 1, meaning that it has been checked out during this CVS run. */
281b6f6614eStholo     path = (char *)
282e77048c1Stholo 	xmalloc (strlen (dir) + strlen (node->key) + 2);
283b6f6614eStholo     sprintf (path, "%s/%s", dir, node->key);
284b6f6614eStholo     link = lookup_file_by_inode (path);
285b6f6614eStholo     free (path);
286b6f6614eStholo     free (dir);
287b6f6614eStholo 
288b6f6614eStholo     if (link == NULL)
289b6f6614eStholo     {
290b6f6614eStholo 	/* We haven't seen this file -- maybe it hasn't been checked
291b6f6614eStholo 	   out yet at all. */
292b6f6614eStholo 	return 0;
293b6f6614eStholo     }
294b6f6614eStholo 
295b6f6614eStholo     hlinfo = (struct hardlink_info *) link->data;
296b6f6614eStholo     if (hlinfo->checked_out)
297b6f6614eStholo     {
298b6f6614eStholo 	/* This file has been checked out recently, so it's safe to
299b6f6614eStholo            link to it. */
300b6f6614eStholo 	*uptodate = link;
301b6f6614eStholo     }
302b6f6614eStholo 
303b6f6614eStholo     return 0;
304b6f6614eStholo }
305b6f6614eStholo 
306