3 Copyright 2009, 2013-2014, 2016 Free Software Foundation, Inc.
5 This file is part of GNU tar.
7 GNU tar is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 GNU tar is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
24 struct deferred_unlink
26 struct deferred_unlink *next; /* Next unlink in the queue */
27 int dir_idx; /* Directory index in wd */
28 char *file_name; /* Name of the file to unlink, relative
30 bool is_dir; /* True if file_name is a directory */
31 off_t records_written; /* Number of records written when this
32 entry got added to the queue */
37 && ((p)->file_name[0] == 0 || strcmp ((p)->file_name, ".") == 0))
39 /* The unlink queue */
40 static struct deferred_unlink *dunlink_head, *dunlink_tail;
42 /* Number of entries in the queue */
43 static size_t dunlink_count;
45 /* List of entries available for allocation */
46 static struct deferred_unlink *dunlink_avail;
48 /* Delay (number of records written) between adding entry to the
49 list and its actual removal. */
50 static size_t deferred_unlink_delay = 0;
52 static struct deferred_unlink *
55 struct deferred_unlink *p;
59 dunlink_avail = p->next;
63 p = xmalloc (sizeof (*p));
68 dunlink_insert (struct deferred_unlink *anchor, struct deferred_unlink *p)
72 p->next = anchor->next;
77 p->next = dunlink_head;
86 dunlink_reclaim (struct deferred_unlink *p)
89 p->next = dunlink_avail;
94 flush_deferred_unlinks (bool force)
96 struct deferred_unlink *p, *prev = NULL;
97 int saved_chdir = chdir_current;
99 for (p = dunlink_head; p; )
101 struct deferred_unlink *next = p->next;
104 || records_written > p->records_written + deferred_unlink_delay)
106 chdir_do (p->dir_idx);
111 if (p->dir_idx && IS_CWD (p))
118 fname = p->file_name;
120 if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
125 /* nothing to worry about */
128 /* OpenSolaris >=10 sets EEXIST instead of ENOTEMPTY
129 if trying to remove a non-empty directory */
131 /* Keep the record in list, in the hope we'll
132 be able to remove it later */
144 if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
145 unlink_error (p->file_name);
165 for (p = dunlink_head; p; )
167 struct deferred_unlink *next = p->next;
170 chdir_do (p->dir_idx);
171 if (p->dir_idx && IS_CWD (p))
173 fname = tar_dirname ();
174 chdir_do (p->dir_idx - 1);
177 fname = p->file_name;
179 if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
188 dunlink_head = dunlink_tail = NULL;
191 chdir_do (saved_chdir);
195 finish_deferred_unlinks (void)
197 flush_deferred_unlinks (true);
199 while (dunlink_avail)
201 struct deferred_unlink *next = dunlink_avail->next;
202 free (dunlink_avail);
203 dunlink_avail = next;
208 queue_deferred_unlink (const char *name, bool is_dir)
210 struct deferred_unlink *p;
213 && records_written > dunlink_head->records_written + deferred_unlink_delay)
214 flush_deferred_unlinks (false);
216 p = dunlink_alloc ();
218 p->dir_idx = chdir_current;
219 p->file_name = xstrdup (name);
220 normalize_filename_x (p->file_name);
222 p->records_written = records_written;
226 struct deferred_unlink *q, *prev;
227 for (q = dunlink_head, prev = NULL; q; prev = q, q = q->next)
228 if (IS_CWD (q) && q->dir_idx < p->dir_idx)
231 dunlink_insert (prev, p);
233 dunlink_insert (dunlink_tail, p);
236 dunlink_insert (dunlink_tail, p);