/contrib/cvs/src/hardlink.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 307 lines · 170 code · 40 blank · 97 comment · 36 complexity · 53cc7728f1a19e0295d7614077a27e8c MD5 · raw file

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