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