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