3 Copyright 2009-2024 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 */
130 #if defined ENOTEMPTY && ENOTEMPTY != EEXIST
133 /* Keep the record in list, in the hope we'll
134 be able to remove it later */
146 if (unlinkat (chdir_fd
, p
->file_name
, 0) != 0 && errno
!= ENOENT
)
147 unlink_error (p
->file_name
);
167 for (p
= dunlink_head
; p
; )
169 struct deferred_unlink
*next
= p
->next
;
172 chdir_do (p
->dir_idx
);
173 if (p
->dir_idx
&& IS_CWD (p
))
175 fname
= tar_dirname ();
176 chdir_do (p
->dir_idx
- 1);
179 fname
= p
->file_name
;
181 if (unlinkat (chdir_fd
, fname
, AT_REMOVEDIR
) != 0)
190 dunlink_head
= dunlink_tail
= NULL
;
193 chdir_do (saved_chdir
);
197 finish_deferred_unlinks (void)
199 flush_deferred_unlinks (true);
201 while (dunlink_avail
)
203 struct deferred_unlink
*next
= dunlink_avail
->next
;
204 free (dunlink_avail
);
205 dunlink_avail
= next
;
210 queue_deferred_unlink (const char *name
, bool is_dir
)
212 struct deferred_unlink
*p
;
215 && records_written
> dunlink_head
->records_written
+ deferred_unlink_delay
)
216 flush_deferred_unlinks (false);
218 p
= dunlink_alloc ();
220 p
->dir_idx
= chdir_current
;
221 p
->file_name
= xstrdup (name
);
222 normalize_filename_x (p
->file_name
);
224 p
->records_written
= records_written
;
228 struct deferred_unlink
*q
, *prev
;
229 for (q
= dunlink_head
, prev
= NULL
; q
; prev
= q
, q
= q
->next
)
230 if (IS_CWD (q
) && q
->dir_idx
< p
->dir_idx
)
233 dunlink_insert (prev
, p
);
235 dunlink_insert (dunlink_tail
, p
);
238 dunlink_insert (dunlink_tail
, p
);