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