]> Dogcows Code - chaz/tar/blob - src/unlink.c
6677148ea5ecd976d4a9aa3bbecac42747cf1951
[chaz/tar] / src / unlink.c
1 /* Unlink files.
2
3 Copyright 2009, 2013 Free Software Foundation, Inc.
4
5 This file is part of GNU tar.
6
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.
11
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.
16
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/>. */
19
20 #include <system.h>
21 #include "common.h"
22 #include <quotearg.h>
23
24 struct deferred_unlink
25 {
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
29 to dir_idx */
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 */
33 };
34
35 /* The unlink queue */
36 static struct deferred_unlink *dunlink_head, *dunlink_tail;
37
38 /* Number of entries in the queue */
39 static size_t dunlink_count;
40
41 /* List of entries available for allocation */
42 static struct deferred_unlink *dunlink_avail;
43
44 /* Delay (number of records written) between adding entry to the
45 list and its actual removal. */
46 static size_t deferred_unlink_delay = 0;
47
48 static struct deferred_unlink *
49 dunlink_alloc (void)
50 {
51 struct deferred_unlink *p;
52 if (dunlink_avail)
53 {
54 p = dunlink_avail;
55 dunlink_avail = p->next;
56 p->next = NULL;
57 }
58 else
59 p = xmalloc (sizeof (*p));
60 return p;
61 }
62
63 static void
64 dunlink_reclaim (struct deferred_unlink *p)
65 {
66 free (p->file_name);
67 p->next = dunlink_avail;
68 dunlink_avail = p;
69 }
70
71 static void
72 flush_deferred_unlinks (bool force)
73 {
74 struct deferred_unlink *p, *prev = NULL;
75 int saved_chdir = chdir_current;
76
77 for (p = dunlink_head; p; )
78 {
79 struct deferred_unlink *next = p->next;
80
81 if (force
82 || records_written > p->records_written + deferred_unlink_delay)
83 {
84 chdir_do (p->dir_idx);
85 if (p->is_dir)
86 {
87 const char *fname;
88
89 if (p->file_name[0] == 0 ||
90 strcmp (p->file_name, ".") == 0)
91 {
92 fname = tar_dirname ();
93 chdir_do (p->dir_idx - 1);
94 }
95 else
96 fname = p->file_name;
97
98 if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
99 {
100 switch (errno)
101 {
102 case ENOENT:
103 /* nothing to worry about */
104 break;
105 case ENOTEMPTY:
106 if (!force)
107 {
108 /* Keep the record in list, in the hope we'll
109 be able to remove it later */
110 prev = p;
111 p = next;
112 continue;
113 }
114 /* fall through */
115 default:
116 rmdir_error (fname);
117 }
118 }
119 }
120 else
121 {
122 if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
123 unlink_error (p->file_name);
124 }
125 dunlink_reclaim (p);
126 dunlink_count--;
127 p = next;
128 if (prev)
129 prev->next = p;
130 else
131 dunlink_head = p;
132 }
133 else
134 {
135 prev = p;
136 p = next;
137 }
138 }
139 if (!dunlink_head)
140 dunlink_tail = NULL;
141 chdir_do (saved_chdir);
142 }
143
144 void
145 finish_deferred_unlinks (void)
146 {
147 flush_deferred_unlinks (true);
148 while (dunlink_avail)
149 {
150 struct deferred_unlink *next = dunlink_avail->next;
151 free (dunlink_avail);
152 dunlink_avail = next;
153 }
154 }
155
156 void
157 queue_deferred_unlink (const char *name, bool is_dir)
158 {
159 struct deferred_unlink *p;
160
161 if (dunlink_head
162 && records_written > dunlink_head->records_written + deferred_unlink_delay)
163 flush_deferred_unlinks (false);
164
165 p = dunlink_alloc ();
166 p->next = NULL;
167 p->dir_idx = chdir_current;
168 p->file_name = xstrdup (name);
169 normalize_filename_x (p->file_name);
170 p->is_dir = is_dir;
171 p->records_written = records_written;
172
173 if (dunlink_tail)
174 dunlink_tail->next = p;
175 else
176 dunlink_head = p;
177 dunlink_tail = p;
178 dunlink_count++;
179 }
This page took 0.036887 seconds and 3 git commands to generate.