]> Dogcows Code - chaz/tar/blob - src/unlink.c
8c58b8d1e2c223f6d937006d668a48e8a61f728a
[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 char *file_name; /* Absolute name of the file to unlink */
28 bool is_dir; /* True if file_name is a directory */
29 off_t records_written; /* Number of records written when this
30 entry got added to the queue */
31 };
32
33 /* The unlink queue */
34 static struct deferred_unlink *dunlink_head, *dunlink_tail;
35
36 /* Number of entries in the queue */
37 static size_t dunlink_count;
38
39 /* List of entries available for allocation */
40 static struct deferred_unlink *dunlink_avail;
41
42 /* Delay (number of records written) between adding entry to the
43 list and its actual removal. */
44 static size_t deferred_unlink_delay = 0;
45
46 static struct deferred_unlink *
47 dunlink_alloc (void)
48 {
49 struct deferred_unlink *p;
50 if (dunlink_avail)
51 {
52 p = dunlink_avail;
53 dunlink_avail = p->next;
54 p->next = NULL;
55 }
56 else
57 p = xmalloc (sizeof (*p));
58 return p;
59 }
60
61 static void
62 dunlink_reclaim (struct deferred_unlink *p)
63 {
64 free (p->file_name);
65 p->next = dunlink_avail;
66 dunlink_avail = p;
67 }
68
69 static void
70 flush_deferred_unlinks (bool force)
71 {
72 struct deferred_unlink *p, *prev = NULL;
73
74 for (p = dunlink_head; p; )
75 {
76 struct deferred_unlink *next = p->next;
77 if (force
78 || records_written > p->records_written + deferred_unlink_delay)
79 {
80 if (p->is_dir)
81 {
82 if (unlinkat (chdir_fd, p->file_name, AT_REMOVEDIR) != 0)
83 {
84 switch (errno)
85 {
86 case ENOENT:
87 /* nothing to worry about */
88 break;
89 case ENOTEMPTY:
90 if (!force)
91 {
92 /* Keep the record in list, in the hope we'll
93 be able to remove it later */
94 prev = p;
95 p = next;
96 continue;
97 }
98 /* fall through */
99 default:
100 rmdir_error (p->file_name);
101 }
102 }
103 }
104 else
105 {
106 if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
107 unlink_error (p->file_name);
108 }
109 dunlink_reclaim (p);
110 dunlink_count--;
111 p = next;
112 if (prev)
113 prev->next = p;
114 else
115 dunlink_head = p;
116 }
117 else
118 {
119 prev = p;
120 p = next;
121 }
122 }
123 if (!dunlink_head)
124 dunlink_tail = NULL;
125 }
126
127 void
128 finish_deferred_unlinks (void)
129 {
130 flush_deferred_unlinks (true);
131 while (dunlink_avail)
132 {
133 struct deferred_unlink *next = dunlink_avail->next;
134 free (dunlink_avail);
135 dunlink_avail = next;
136 }
137 }
138
139 void
140 queue_deferred_unlink (const char *name, bool is_dir)
141 {
142 struct deferred_unlink *p;
143
144 if (dunlink_head
145 && records_written > dunlink_head->records_written + deferred_unlink_delay)
146 flush_deferred_unlinks (false);
147
148 p = dunlink_alloc ();
149 p->next = NULL;
150 p->file_name = normalize_filename (name);
151 p->is_dir = is_dir;
152 p->records_written = records_written;
153
154 if (dunlink_tail)
155 dunlink_tail->next = p;
156 else
157 dunlink_head = p;
158 dunlink_tail = p;
159 dunlink_count++;
160 }
This page took 0.039211 seconds and 3 git commands to generate.