xref: /dflybsd-src/contrib/cvs-1.12/src/hardlink.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /* This program is free software; you can redistribute it and/or modify
2*86d7f5d3SJohn Marino    it under the terms of the GNU General Public License as published by
3*86d7f5d3SJohn Marino    the Free Software Foundation; either version 2, or (at your option)
4*86d7f5d3SJohn Marino    any later version.
5*86d7f5d3SJohn Marino 
6*86d7f5d3SJohn Marino    This program is distributed in the hope that it will be useful,
7*86d7f5d3SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
8*86d7f5d3SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9*86d7f5d3SJohn Marino    GNU General Public License for more details.  */
10*86d7f5d3SJohn Marino 
11*86d7f5d3SJohn Marino /* Collect and manage hardlink info associated with a particular file.  */
12*86d7f5d3SJohn Marino 
13*86d7f5d3SJohn Marino #include "cvs.h"
14*86d7f5d3SJohn Marino 
15*86d7f5d3SJohn Marino #ifdef PRESERVE_PERMISSIONS_SUPPORT
16*86d7f5d3SJohn Marino # include "hardlink.h"
17*86d7f5d3SJohn Marino 
18*86d7f5d3SJohn Marino /* The structure currently used to manage hardlink info is a list.
19*86d7f5d3SJohn Marino    Therefore, most of the functions which manipulate hardlink data
20*86d7f5d3SJohn Marino    are walklist procedures.  This is not a very efficient implementation;
21*86d7f5d3SJohn Marino    if someone decides to use a real hash table (for instance), then
22*86d7f5d3SJohn Marino    much of this code can be rewritten to be a little less arcane.
23*86d7f5d3SJohn Marino 
24*86d7f5d3SJohn Marino    Each element of `hardlist' represents an inode.  It is keyed on the
25*86d7f5d3SJohn Marino    inode number, and points to a list of files.  This is to make it
26*86d7f5d3SJohn Marino    easy to find out what files are linked to a given file FOO: find
27*86d7f5d3SJohn Marino    FOO's inode, look it up in hardlist, and retrieve the list of files
28*86d7f5d3SJohn Marino    associated with that inode.
29*86d7f5d3SJohn Marino 
30*86d7f5d3SJohn Marino    Each file node, in turn, is represented by a `hardlink_info' struct,
31*86d7f5d3SJohn Marino    which includes `status' and `links' fields.  The `status' field should
32*86d7f5d3SJohn Marino    be used by a procedure like commit_fileproc or update_fileproc to
33*86d7f5d3SJohn Marino    record each file's status; that way, after all file links have been
34*86d7f5d3SJohn Marino    recorded, CVS can check the linkage of files which are in doubt
35*86d7f5d3SJohn Marino    (i.e. T_NEEDS_MERGE files).
36*86d7f5d3SJohn Marino 
37*86d7f5d3SJohn Marino    TODO: a diagram of an example hardlist would help here. */
38*86d7f5d3SJohn Marino 
39*86d7f5d3SJohn Marino /* TODO: change this to something with a marginal degree of
40*86d7f5d3SJohn Marino    efficiency, like maybe a hash table.  Yeah. */
41*86d7f5d3SJohn Marino 
42*86d7f5d3SJohn Marino 
43*86d7f5d3SJohn Marino 
44*86d7f5d3SJohn Marino static void
delhardlist(Node * p)45*86d7f5d3SJohn Marino delhardlist (Node *p)
46*86d7f5d3SJohn Marino {
47*86d7f5d3SJohn Marino     if (p->data)
48*86d7f5d3SJohn Marino 	dellist ((List **)&p->data);
49*86d7f5d3SJohn Marino }
50*86d7f5d3SJohn Marino 
51*86d7f5d3SJohn Marino 
52*86d7f5d3SJohn Marino 
53*86d7f5d3SJohn Marino List *hardlist;		/* Record hardlink information for working files */
54*86d7f5d3SJohn Marino char *working_dir;	/* The top-level working directory, used for
55*86d7f5d3SJohn Marino 			   constructing full pathnames. */
56*86d7f5d3SJohn Marino 
57*86d7f5d3SJohn Marino /* Return a pointer to FILEPATH's node in the hardlist.  This means
58*86d7f5d3SJohn Marino    looking up its inode, retrieving the list of files linked to that
59*86d7f5d3SJohn Marino    inode, and then looking up FILE in that list.  If the file doesn't
60*86d7f5d3SJohn Marino    seem to exist, return NULL. */
61*86d7f5d3SJohn Marino Node *
lookup_file_by_inode(const char * filepath)62*86d7f5d3SJohn Marino lookup_file_by_inode (const char *filepath)
63*86d7f5d3SJohn Marino {
64*86d7f5d3SJohn Marino     char *inodestr;
65*86d7f5d3SJohn Marino     const char *file;
66*86d7f5d3SJohn Marino     struct stat sb;
67*86d7f5d3SJohn Marino     Node *hp, *p;
68*86d7f5d3SJohn Marino 
69*86d7f5d3SJohn Marino     /* Get file's basename, so that we can stat it. */
70*86d7f5d3SJohn Marino     file = strrchr (filepath, '/');
71*86d7f5d3SJohn Marino     if (file)
72*86d7f5d3SJohn Marino 	++file;
73*86d7f5d3SJohn Marino     else
74*86d7f5d3SJohn Marino 	file = filepath;
75*86d7f5d3SJohn Marino 
76*86d7f5d3SJohn Marino     if (stat (file, &sb) < 0)
77*86d7f5d3SJohn Marino     {
78*86d7f5d3SJohn Marino 	if (existence_error (errno))
79*86d7f5d3SJohn Marino 	{
80*86d7f5d3SJohn Marino 	    /* The file doesn't exist; we may be doing an update on a
81*86d7f5d3SJohn Marino 	       file that's been removed.  A nonexistent file has no
82*86d7f5d3SJohn Marino 	       link information, so return without changing hardlist. */
83*86d7f5d3SJohn Marino 	    free (inodestr);
84*86d7f5d3SJohn Marino 	    return NULL;
85*86d7f5d3SJohn Marino 	}
86*86d7f5d3SJohn Marino 	error (1, errno, "cannot stat %s", file);
87*86d7f5d3SJohn Marino     }
88*86d7f5d3SJohn Marino 
89*86d7f5d3SJohn Marino     /* inodestr contains the hexadecimal representation of an
90*86d7f5d3SJohn Marino        inode. */
91*86d7f5d3SJohn Marino     inodestr = Xasprintf ("%lx", (unsigned long) sb.st_ino);
92*86d7f5d3SJohn Marino 
93*86d7f5d3SJohn Marino     /* Find out if this inode is already in the hardlist, adding
94*86d7f5d3SJohn Marino        a new entry to the list if not. */
95*86d7f5d3SJohn Marino     hp = findnode (hardlist, inodestr);
96*86d7f5d3SJohn Marino     if (hp == NULL)
97*86d7f5d3SJohn Marino     {
98*86d7f5d3SJohn Marino 	hp = getnode ();
99*86d7f5d3SJohn Marino 	hp->type = NT_UNKNOWN;
100*86d7f5d3SJohn Marino 	hp->key = inodestr;
101*86d7f5d3SJohn Marino 	hp->data = getlist ();
102*86d7f5d3SJohn Marino 	hp->delproc = delhardlist;
103*86d7f5d3SJohn Marino 	(void) addnode (hardlist, hp);
104*86d7f5d3SJohn Marino     }
105*86d7f5d3SJohn Marino     else
106*86d7f5d3SJohn Marino     {
107*86d7f5d3SJohn Marino 	free (inodestr);
108*86d7f5d3SJohn Marino     }
109*86d7f5d3SJohn Marino 
110*86d7f5d3SJohn Marino     p = findnode (hp->data, filepath);
111*86d7f5d3SJohn Marino     if (p == NULL)
112*86d7f5d3SJohn Marino     {
113*86d7f5d3SJohn Marino 	p = getnode ();
114*86d7f5d3SJohn Marino 	p->type = NT_UNKNOWN;
115*86d7f5d3SJohn Marino 	p->key = xstrdup (filepath);
116*86d7f5d3SJohn Marino 	p->data = NULL;
117*86d7f5d3SJohn Marino 	(void) addnode (hp->data, p);
118*86d7f5d3SJohn Marino     }
119*86d7f5d3SJohn Marino 
120*86d7f5d3SJohn Marino     return p;
121*86d7f5d3SJohn Marino }
122*86d7f5d3SJohn Marino 
123*86d7f5d3SJohn Marino /* After a file has been checked out, add a node for it to the hardlist
124*86d7f5d3SJohn Marino    (if necessary) and mark it as checked out. */
125*86d7f5d3SJohn Marino void
update_hardlink_info(const char * file)126*86d7f5d3SJohn Marino update_hardlink_info (const char *file)
127*86d7f5d3SJohn Marino {
128*86d7f5d3SJohn Marino     char *path;
129*86d7f5d3SJohn Marino     Node *n;
130*86d7f5d3SJohn Marino     struct hardlink_info *hlinfo;
131*86d7f5d3SJohn Marino 
132*86d7f5d3SJohn Marino     if (file[0] == '/')
133*86d7f5d3SJohn Marino     {
134*86d7f5d3SJohn Marino 	path = xstrdup (file);
135*86d7f5d3SJohn Marino     }
136*86d7f5d3SJohn Marino     else
137*86d7f5d3SJohn Marino     {
138*86d7f5d3SJohn Marino 	/* file is a relative pathname; assume it's from the current
139*86d7f5d3SJohn Marino 	   working directory. */
140*86d7f5d3SJohn Marino 	char *dir = xgetcwd ();
141*86d7f5d3SJohn Marino 	path = Xasprintf ("%s/%s", dir, file);
142*86d7f5d3SJohn Marino 	free (dir);
143*86d7f5d3SJohn Marino     }
144*86d7f5d3SJohn Marino 
145*86d7f5d3SJohn Marino     n = lookup_file_by_inode (path);
146*86d7f5d3SJohn Marino     if (n == NULL)
147*86d7f5d3SJohn Marino     {
148*86d7f5d3SJohn Marino 	/* Something is *really* wrong if the file doesn't exist here;
149*86d7f5d3SJohn Marino 	   update_hardlink_info should be called only when a file has
150*86d7f5d3SJohn Marino 	   just been checked out to a working directory. */
151*86d7f5d3SJohn Marino 	error (1, 0, "lost hardlink info for %s", file);
152*86d7f5d3SJohn Marino     }
153*86d7f5d3SJohn Marino 
154*86d7f5d3SJohn Marino     if (n->data == NULL)
155*86d7f5d3SJohn Marino 	n->data = xmalloc (sizeof (struct hardlink_info));
156*86d7f5d3SJohn Marino     hlinfo = n->data;
157*86d7f5d3SJohn Marino     hlinfo->status = T_UPTODATE;
158*86d7f5d3SJohn Marino     hlinfo->checked_out = 1;
159*86d7f5d3SJohn Marino }
160*86d7f5d3SJohn Marino 
161*86d7f5d3SJohn Marino /* Return a List with all the files known to be linked to FILE in
162*86d7f5d3SJohn Marino    the working directory.  Used by special_file_mismatch, to determine
163*86d7f5d3SJohn Marino    whether it is safe to merge two files.
164*86d7f5d3SJohn Marino 
165*86d7f5d3SJohn Marino    FIXME: What is the memory allocation for the return value?  We seem
166*86d7f5d3SJohn Marino    to sometimes allocate a new list (getlist() call below) and sometimes
167*86d7f5d3SJohn Marino    return an existing list (where we return n->data).  */
168*86d7f5d3SJohn Marino List *
list_linked_files_on_disk(char * file)169*86d7f5d3SJohn Marino list_linked_files_on_disk (char *file)
170*86d7f5d3SJohn Marino {
171*86d7f5d3SJohn Marino     char *inodestr, *path;
172*86d7f5d3SJohn Marino     struct stat sb;
173*86d7f5d3SJohn Marino     Node *n;
174*86d7f5d3SJohn Marino 
175*86d7f5d3SJohn Marino     /* If hardlist is NULL, we have not been doing an operation that
176*86d7f5d3SJohn Marino        would permit us to know anything about the file's hardlinks
177*86d7f5d3SJohn Marino        (cvs update, cvs commit, etc).  Return an empty list. */
178*86d7f5d3SJohn Marino     if (hardlist == NULL)
179*86d7f5d3SJohn Marino 	return getlist ();
180*86d7f5d3SJohn Marino 
181*86d7f5d3SJohn Marino     /* Get the full pathname of file (assuming the working directory) */
182*86d7f5d3SJohn Marino     if (file[0] == '/')
183*86d7f5d3SJohn Marino 	path = xstrdup (file);
184*86d7f5d3SJohn Marino     else
185*86d7f5d3SJohn Marino     {
186*86d7f5d3SJohn Marino 	char *dir = xgetcwd ();
187*86d7f5d3SJohn Marino 	path = Xasprintf ("%s/%s", dir, file);
188*86d7f5d3SJohn Marino 	free (dir);
189*86d7f5d3SJohn Marino     }
190*86d7f5d3SJohn Marino 
191*86d7f5d3SJohn Marino     /* We do an extra lookup_file here just to make sure that there
192*86d7f5d3SJohn Marino        is a node for `path' in the hardlist.  If that were not so,
193*86d7f5d3SJohn Marino        comparing the working directory linkage against the repository
194*86d7f5d3SJohn Marino        linkage for a file would always fail. */
195*86d7f5d3SJohn Marino     (void) lookup_file_by_inode (path);
196*86d7f5d3SJohn Marino 
197*86d7f5d3SJohn Marino     if (stat (path, &sb) < 0)
198*86d7f5d3SJohn Marino 	error (1, errno, "cannot stat %s", file);
199*86d7f5d3SJohn Marino     /* inodestr contains the hexadecimal representation of an
200*86d7f5d3SJohn Marino        inode. */
201*86d7f5d3SJohn Marino     inodestr = Xasprintf ("%lx", (unsigned long) sb.st_ino);
202*86d7f5d3SJohn Marino 
203*86d7f5d3SJohn Marino     /* Make sure the files linked to this inode are sorted. */
204*86d7f5d3SJohn Marino     n = findnode (hardlist, inodestr);
205*86d7f5d3SJohn Marino     sortlist (n->data, fsortcmp);
206*86d7f5d3SJohn Marino 
207*86d7f5d3SJohn Marino     free (inodestr);
208*86d7f5d3SJohn Marino     return n->data;
209*86d7f5d3SJohn Marino }
210*86d7f5d3SJohn Marino 
211*86d7f5d3SJohn Marino /* Compare the files in the `key' fields of two lists, returning 1 if
212*86d7f5d3SJohn Marino    the lists are equivalent and 0 otherwise.
213*86d7f5d3SJohn Marino 
214*86d7f5d3SJohn Marino    Only the basenames of each file are compared. This is an awful hack
215*86d7f5d3SJohn Marino    that exists because list_linked_files_on_disk returns full paths
216*86d7f5d3SJohn Marino    and the `hardlinks' structure of a RCSVers node contains only
217*86d7f5d3SJohn Marino    basenames.  That in turn is a result of the awful hack that only
218*86d7f5d3SJohn Marino    basenames are stored in the RCS file.  If anyone ever solves the
219*86d7f5d3SJohn Marino    problem of correctly managing cross-directory hardlinks, this
220*86d7f5d3SJohn Marino    function (along with most functions in this file) must be fixed. */
221*86d7f5d3SJohn Marino 
222*86d7f5d3SJohn Marino int
compare_linkage_lists(List * links1,List * links2)223*86d7f5d3SJohn Marino compare_linkage_lists (List *links1, List *links2)
224*86d7f5d3SJohn Marino {
225*86d7f5d3SJohn Marino     Node *n1, *n2;
226*86d7f5d3SJohn Marino     char *p1, *p2;
227*86d7f5d3SJohn Marino 
228*86d7f5d3SJohn Marino     sortlist (links1, fsortcmp);
229*86d7f5d3SJohn Marino     sortlist (links2, fsortcmp);
230*86d7f5d3SJohn Marino 
231*86d7f5d3SJohn Marino     n1 = links1->list->next;
232*86d7f5d3SJohn Marino     n2 = links2->list->next;
233*86d7f5d3SJohn Marino 
234*86d7f5d3SJohn Marino     while (n1 != links1->list && n2 != links2->list)
235*86d7f5d3SJohn Marino     {
236*86d7f5d3SJohn Marino 	/* Get the basenames of both files. */
237*86d7f5d3SJohn Marino 	p1 = strrchr (n1->key, '/');
238*86d7f5d3SJohn Marino 	if (p1 == NULL)
239*86d7f5d3SJohn Marino 	    p1 = n1->key;
240*86d7f5d3SJohn Marino 	else
241*86d7f5d3SJohn Marino 	    ++p1;
242*86d7f5d3SJohn Marino 
243*86d7f5d3SJohn Marino 	p2 = strrchr (n2->key, '/');
244*86d7f5d3SJohn Marino 	if (p2 == NULL)
245*86d7f5d3SJohn Marino 	    p2 = n2->key;
246*86d7f5d3SJohn Marino 	else
247*86d7f5d3SJohn Marino 	    ++p2;
248*86d7f5d3SJohn Marino 
249*86d7f5d3SJohn Marino 	/* Compare the files' basenames. */
250*86d7f5d3SJohn Marino 	if (strcmp (p1, p2) != 0)
251*86d7f5d3SJohn Marino 	    return 0;
252*86d7f5d3SJohn Marino 
253*86d7f5d3SJohn Marino 	n1 = n1->next;
254*86d7f5d3SJohn Marino 	n2 = n2->next;
255*86d7f5d3SJohn Marino     }
256*86d7f5d3SJohn Marino 
257*86d7f5d3SJohn Marino     /* At this point we should be at the end of both lists; if not,
258*86d7f5d3SJohn Marino        one file has more links than the other, and return 1. */
259*86d7f5d3SJohn Marino     return (n1 == links1->list && n2 == links2->list);
260*86d7f5d3SJohn Marino }
261*86d7f5d3SJohn Marino 
262*86d7f5d3SJohn Marino /* Find a checked-out file in a list of filenames.  Used by RCS_checkout
263*86d7f5d3SJohn Marino    when checking out a new hardlinked file, to decide whether this file
264*86d7f5d3SJohn Marino    can be linked to any others that already exist.  The return value
265*86d7f5d3SJohn Marino    is not currently used. */
266*86d7f5d3SJohn Marino 
267*86d7f5d3SJohn Marino int
find_checkedout_proc(Node * node,void * data)268*86d7f5d3SJohn Marino find_checkedout_proc (Node *node, void *data)
269*86d7f5d3SJohn Marino {
270*86d7f5d3SJohn Marino     Node **uptodate = data;
271*86d7f5d3SJohn Marino     Node *link;
272*86d7f5d3SJohn Marino     char *dir = xgetcwd ();
273*86d7f5d3SJohn Marino     char *path;
274*86d7f5d3SJohn Marino     struct hardlink_info *hlinfo;
275*86d7f5d3SJohn Marino 
276*86d7f5d3SJohn Marino     /* If we have already found a file, don't do anything. */
277*86d7f5d3SJohn Marino     if (*uptodate != NULL)
278*86d7f5d3SJohn Marino 	return 0;
279*86d7f5d3SJohn Marino 
280*86d7f5d3SJohn Marino     /* Look at this file in the hardlist and see whether the checked_out
281*86d7f5d3SJohn Marino        field is 1, meaning that it has been checked out during this CVS run. */
282*86d7f5d3SJohn Marino     path = Xasprintf ("%s/%s", dir, node->key);
283*86d7f5d3SJohn Marino     link = lookup_file_by_inode (path);
284*86d7f5d3SJohn Marino     free (path);
285*86d7f5d3SJohn Marino     free (dir);
286*86d7f5d3SJohn Marino 
287*86d7f5d3SJohn Marino     if (link == NULL)
288*86d7f5d3SJohn Marino     {
289*86d7f5d3SJohn Marino 	/* We haven't seen this file -- maybe it hasn't been checked
290*86d7f5d3SJohn Marino 	   out yet at all. */
291*86d7f5d3SJohn Marino 	return 0;
292*86d7f5d3SJohn Marino     }
293*86d7f5d3SJohn Marino 
294*86d7f5d3SJohn Marino     hlinfo = link->data;
295*86d7f5d3SJohn Marino     if (hlinfo->checked_out)
296*86d7f5d3SJohn Marino     {
297*86d7f5d3SJohn Marino 	/* This file has been checked out recently, so it's safe to
298*86d7f5d3SJohn Marino            link to it. */
299*86d7f5d3SJohn Marino 	*uptodate = link;
300*86d7f5d3SJohn Marino     }
301*86d7f5d3SJohn Marino 
302*86d7f5d3SJohn Marino     return 0;
303*86d7f5d3SJohn Marino }
304*86d7f5d3SJohn Marino #endif /* PRESERVE_PERMISSIONS_SUPPORT */
305