unlink.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /* Unlink files.
  2. Copyright 2009-2023 Free Software Foundation, Inc.
  3. This file is part of GNU tar.
  4. GNU tar is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3 of the License, or
  7. (at your option) any later version.
  8. GNU tar is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  14. #include <system.h>
  15. #include "common.h"
  16. #include <quotearg.h>
  17. struct deferred_unlink
  18. {
  19. struct deferred_unlink *next; /* Next unlink in the queue */
  20. int dir_idx; /* Directory index in wd */
  21. char *file_name; /* Name of the file to unlink, relative
  22. to dir_idx */
  23. bool is_dir; /* True if file_name is a directory */
  24. off_t records_written; /* Number of records written when this
  25. entry got added to the queue */
  26. };
  27. #define IS_CWD(p) \
  28. ((p)->is_dir \
  29. && ((p)->file_name[0] == 0 || strcmp ((p)->file_name, ".") == 0))
  30. /* The unlink queue */
  31. static struct deferred_unlink *dunlink_head, *dunlink_tail;
  32. /* Number of entries in the queue */
  33. static size_t dunlink_count;
  34. /* List of entries available for allocation */
  35. static struct deferred_unlink *dunlink_avail;
  36. /* Delay (number of records written) between adding entry to the
  37. list and its actual removal. */
  38. static size_t deferred_unlink_delay = 0;
  39. static struct deferred_unlink *
  40. dunlink_alloc (void)
  41. {
  42. struct deferred_unlink *p;
  43. if (dunlink_avail)
  44. {
  45. p = dunlink_avail;
  46. dunlink_avail = p->next;
  47. p->next = NULL;
  48. }
  49. else
  50. p = xmalloc (sizeof (*p));
  51. return p;
  52. }
  53. static void
  54. dunlink_insert (struct deferred_unlink *anchor, struct deferred_unlink *p)
  55. {
  56. if (anchor)
  57. {
  58. p->next = anchor->next;
  59. anchor->next = p;
  60. }
  61. else
  62. {
  63. p->next = dunlink_head;
  64. dunlink_head = p;
  65. }
  66. if (!p->next)
  67. dunlink_tail = p;
  68. dunlink_count++;
  69. }
  70. static void
  71. dunlink_reclaim (struct deferred_unlink *p)
  72. {
  73. free (p->file_name);
  74. p->next = dunlink_avail;
  75. dunlink_avail = p;
  76. }
  77. static void
  78. flush_deferred_unlinks (bool force)
  79. {
  80. struct deferred_unlink *p, *prev = NULL;
  81. int saved_chdir = chdir_current;
  82. for (p = dunlink_head; p; )
  83. {
  84. struct deferred_unlink *next = p->next;
  85. if (force
  86. || records_written > p->records_written + deferred_unlink_delay)
  87. {
  88. chdir_do (p->dir_idx);
  89. if (p->is_dir)
  90. {
  91. const char *fname;
  92. if (p->dir_idx && IS_CWD (p))
  93. {
  94. prev = p;
  95. p = next;
  96. continue;
  97. }
  98. else
  99. fname = p->file_name;
  100. if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
  101. {
  102. switch (errno)
  103. {
  104. case ENOENT:
  105. /* nothing to worry about */
  106. break;
  107. case EEXIST:
  108. /* OpenSolaris >=10 sets EEXIST instead of ENOTEMPTY
  109. if trying to remove a non-empty directory */
  110. #if defined ENOTEMPTY && ENOTEMPTY != EEXIST
  111. case ENOTEMPTY:
  112. #endif
  113. /* Keep the record in list, in the hope we'll
  114. be able to remove it later */
  115. prev = p;
  116. p = next;
  117. continue;
  118. default:
  119. rmdir_error (fname);
  120. }
  121. }
  122. }
  123. else
  124. {
  125. if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
  126. unlink_error (p->file_name);
  127. }
  128. dunlink_reclaim (p);
  129. dunlink_count--;
  130. p = next;
  131. if (prev)
  132. prev->next = p;
  133. else
  134. dunlink_head = p;
  135. }
  136. else
  137. {
  138. prev = p;
  139. p = next;
  140. }
  141. }
  142. if (!dunlink_head)
  143. dunlink_tail = NULL;
  144. else if (force)
  145. {
  146. for (p = dunlink_head; p; )
  147. {
  148. struct deferred_unlink *next = p->next;
  149. const char *fname;
  150. chdir_do (p->dir_idx);
  151. if (p->dir_idx && IS_CWD (p))
  152. {
  153. fname = tar_dirname ();
  154. chdir_do (p->dir_idx - 1);
  155. }
  156. else
  157. fname = p->file_name;
  158. if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
  159. {
  160. if (errno != ENOENT)
  161. rmdir_error (fname);
  162. }
  163. dunlink_reclaim (p);
  164. dunlink_count--;
  165. p = next;
  166. }
  167. dunlink_head = dunlink_tail = NULL;
  168. }
  169. chdir_do (saved_chdir);
  170. }
  171. void
  172. finish_deferred_unlinks (void)
  173. {
  174. flush_deferred_unlinks (true);
  175. while (dunlink_avail)
  176. {
  177. struct deferred_unlink *next = dunlink_avail->next;
  178. free (dunlink_avail);
  179. dunlink_avail = next;
  180. }
  181. }
  182. void
  183. queue_deferred_unlink (const char *name, bool is_dir)
  184. {
  185. struct deferred_unlink *p;
  186. if (dunlink_head
  187. && records_written > dunlink_head->records_written + deferred_unlink_delay)
  188. flush_deferred_unlinks (false);
  189. p = dunlink_alloc ();
  190. p->next = NULL;
  191. p->dir_idx = chdir_current;
  192. p->file_name = xstrdup (name);
  193. normalize_filename_x (p->file_name);
  194. p->is_dir = is_dir;
  195. p->records_written = records_written;
  196. if (IS_CWD (p))
  197. {
  198. struct deferred_unlink *q, *prev;
  199. for (q = dunlink_head, prev = NULL; q; prev = q, q = q->next)
  200. if (IS_CWD (q) && q->dir_idx < p->dir_idx)
  201. break;
  202. if (q)
  203. dunlink_insert (prev, p);
  204. else
  205. dunlink_insert (dunlink_tail, p);
  206. }
  207. else
  208. dunlink_insert (dunlink_tail, p);
  209. }