1 /* This program is free software; you can redistribute it and/or modify
2 it under the terms of the GNU General Public License as published by
3 the Free Software Foundation; either version 2, or (at your option)
6 This program is distributed in the hope that it will be useful,
7 but WITHOUT ANY WARRANTY; without even the implied warranty of
8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 GNU General Public License for more details. */
11 /* Collect and manage hardlink info associated with a particular file. */
16 /* The structure currently used to manage hardlink info is a list.
17 Therefore, most of the functions which manipulate hardlink data
18 are walklist procedures. This is not a very efficient implementation;
19 if someone decides to use a real hash table (for instance), then
20 much of this code can be rewritten to be a little less arcane.
22 Each element of `hardlist' represents an inode. It is keyed on the
23 inode number, and points to a list of files. This is to make it
24 easy to find out what files are linked to a given file FOO: find
25 FOO's inode, look it up in hardlist, and retrieve the list of files
26 associated with that inode.
28 Each file node, in turn, is represented by a `hardlink_info' struct,
29 which includes `status' and `links' fields. The `status' field should
30 be used by a procedure like commit_fileproc or update_fileproc to
31 record each file's status; that way, after all file links have been
32 recorded, CVS can check the linkage of files which are in doubt
33 (i.e. T_NEEDS_MERGE files).
35 TODO: a diagram of an example hardlist would help here. */
37 /* TODO: change this to something with a marginal degree of
38 efficiency, like maybe a hash table. Yeah. */
40 List *hardlist; /* Record hardlink information for working files */
41 char *working_dir; /* The top-level working directory, used for
42 constructing full pathnames. */
44 /* Return a pointer to FILEPATH's node in the hardlist. This means
45 looking up its inode, retrieving the list of files linked to that
46 inode, and then looking up FILE in that list. If the file doesn't
47 seem to exist, return NULL. */
49 lookup_file_by_inode (const char *filepath)
51 char *inodestr, *file;
55 /* Get file's basename, so that we can stat it. */
56 file = strrchr (filepath, '/');
60 file = (char *) filepath;
62 /* inodestr contains the hexadecimal representation of an
63 inode, so it requires two bytes of text to represent
64 each byte of the inode number. */
65 inodestr = (char *) xmalloc (2*sizeof(ino_t) + 1);
66 if( CVS_STAT( file, &sb ) < 0 )
68 if (existence_error (errno))
70 /* The file doesn't exist; we may be doing an update on a
71 file that's been removed. A nonexistent file has no
72 link information, so return without changing hardlist. */
76 error (1, errno, "cannot stat %s", file);
79 sprintf (inodestr, "%lx", (unsigned long) sb.st_ino);
81 /* Find out if this inode is already in the hardlist, adding
82 a new entry to the list if not. */
83 hp = findnode (hardlist, inodestr);
87 hp->type = NT_UNKNOWN;
90 hp->delproc = dellist;
91 (void) addnode (hardlist, hp);
98 p = findnode (hp->data, filepath);
102 p->type = NT_UNKNOWN;
103 p->key = xstrdup (filepath);
105 (void) addnode (hp->data, p);
111 /* After a file has been checked out, add a node for it to the hardlist
112 (if necessary) and mark it as checked out. */
114 update_hardlink_info (const char *file)
118 struct hardlink_info *hlinfo;
122 path = xstrdup (file);
126 /* file is a relative pathname; assume it's from the current
127 working directory. */
128 char *dir = xgetwd();
129 path = xmalloc (strlen(dir) + strlen(file) + 2);
130 sprintf (path, "%s/%s", dir, file);
134 n = lookup_file_by_inode (path);
137 /* Something is *really* wrong if the file doesn't exist here;
138 update_hardlink_info should be called only when a file has
139 just been checked out to a working directory. */
140 error (1, 0, "lost hardlink info for %s", file);
144 n->data = xmalloc (sizeof (struct hardlink_info));
146 hlinfo->status = T_UPTODATE;
147 hlinfo->checked_out = 1;
150 /* Return a List with all the files known to be linked to FILE in
151 the working directory. Used by special_file_mismatch, to determine
152 whether it is safe to merge two files.
154 FIXME: What is the memory allocation for the return value? We seem
155 to sometimes allocate a new list (getlist() call below) and sometimes
156 return an existing list (where we return n->data). */
158 list_linked_files_on_disk (char *file)
160 char *inodestr, *path;
164 /* If hardlist is NULL, we have not been doing an operation that
165 would permit us to know anything about the file's hardlinks
166 (cvs update, cvs commit, etc). Return an empty list. */
167 if (hardlist == NULL)
170 /* Get the full pathname of file (assuming the working directory) */
172 path = xstrdup (file);
175 char *dir = xgetwd();
176 path = (char *) xmalloc (strlen(dir) + strlen(file) + 2);
177 sprintf (path, "%s/%s", dir, file);
181 /* We do an extra lookup_file here just to make sure that there
182 is a node for `path' in the hardlist. If that were not so,
183 comparing the working directory linkage against the repository
184 linkage for a file would always fail. */
185 (void) lookup_file_by_inode (path);
187 if( CVS_STAT( path, &sb ) < 0 )
188 error (1, errno, "cannot stat %s", file);
189 /* inodestr contains the hexadecimal representation of an
190 inode, so it requires two bytes of text to represent
191 each byte of the inode number. */
192 inodestr = (char *) xmalloc (2*sizeof(ino_t) + 1);
193 sprintf (inodestr, "%lx", (unsigned long) sb.st_ino);
195 /* Make sure the files linked to this inode are sorted. */
196 n = findnode (hardlist, inodestr);
197 sortlist (n->data, fsortcmp);
203 /* Compare the files in the `key' fields of two lists, returning 1 if
204 the lists are equivalent and 0 otherwise.
206 Only the basenames of each file are compared. This is an awful hack
207 that exists because list_linked_files_on_disk returns full paths
208 and the `hardlinks' structure of a RCSVers node contains only
209 basenames. That in turn is a result of the awful hack that only
210 basenames are stored in the RCS file. If anyone ever solves the
211 problem of correctly managing cross-directory hardlinks, this
212 function (along with most functions in this file) must be fixed. */
215 compare_linkage_lists (List *links1, List *links2)
220 sortlist (links1, fsortcmp);
221 sortlist (links2, fsortcmp);
223 n1 = links1->list->next;
224 n2 = links2->list->next;
226 while (n1 != links1->list && n2 != links2->list)
228 /* Get the basenames of both files. */
229 p1 = strrchr (n1->key, '/');
235 p2 = strrchr (n2->key, '/');
241 /* Compare the files' basenames. */
242 if (strcmp (p1, p2) != 0)
249 /* At this point we should be at the end of both lists; if not,
250 one file has more links than the other, and return 1. */
251 return (n1 == links1->list && n2 == links2->list);
254 /* Find a checked-out file in a list of filenames. Used by RCS_checkout
255 when checking out a new hardlinked file, to decide whether this file
256 can be linked to any others that already exist. The return value
257 is not currently used. */
260 find_checkedout_proc (Node *node, void *data)
262 Node **uptodate = (Node **) data;
264 char *dir = xgetwd();
266 struct hardlink_info *hlinfo;
268 /* If we have already found a file, don't do anything. */
269 if (*uptodate != NULL)
272 /* Look at this file in the hardlist and see whether the checked_out
273 field is 1, meaning that it has been checked out during this CVS run. */
275 xmalloc (strlen (dir) + strlen (node->key) + 2);
276 sprintf (path, "%s/%s", dir, node->key);
277 link = lookup_file_by_inode (path);
283 /* We haven't seen this file -- maybe it hasn't been checked
289 if (hlinfo->checked_out)
291 /* This file has been checked out recently, so it's safe to