]> Dogcows Code - chaz/tar/blob - src/unlink.c
tar: do not dereference NULL pointer with '--remove-files .'
[chaz/tar] / src / unlink.c
1 /* Unlink files.
2
3 Copyright 2009, 2013-2014 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->dir_idx
90 && (p->file_name[0] == 0
91 || strcmp (p->file_name, ".") == 0))
92 {
93 fname = tar_dirname ();
94 chdir_do (p->dir_idx - 1);
95 }
96 else
97 fname = p->file_name;
98
99 if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
100 {
101 switch (errno)
102 {
103 case ENOENT:
104 /* nothing to worry about */
105 break;
106 case ENOTEMPTY:
107 if (!force)
108 {
109 /* Keep the record in list, in the hope we'll
110 be able to remove it later */
111 prev = p;
112 p = next;
113 continue;
114 }
115 /* fall through */
116 default:
117 rmdir_error (fname);
118 }
119 }
120 }
121 else
122 {
123 if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
124 unlink_error (p->file_name);
125 }
126 dunlink_reclaim (p);
127 dunlink_count--;
128 p = next;
129 if (prev)
130 prev->next = p;
131 else
132 dunlink_head = p;
133 }
134 else
135 {
136 prev = p;
137 p = next;
138 }
139 }
140 if (!dunlink_head)
141 dunlink_tail = NULL;
142 chdir_do (saved_chdir);
143 }
144
145 void
146 finish_deferred_unlinks (void)
147 {
148 flush_deferred_unlinks (true);
149 while (dunlink_avail)
150 {
151 struct deferred_unlink *next = dunlink_avail->next;
152 free (dunlink_avail);
153 dunlink_avail = next;
154 }
155 }
156
157 void
158 queue_deferred_unlink (const char *name, bool is_dir)
159 {
160 struct deferred_unlink *p;
161
162 if (dunlink_head
163 && records_written > dunlink_head->records_written + deferred_unlink_delay)
164 flush_deferred_unlinks (false);
165
166 p = dunlink_alloc ();
167 p->next = NULL;
168 p->dir_idx = chdir_current;
169 p->file_name = xstrdup (name);
170 normalize_filename_x (p->file_name);
171 p->is_dir = is_dir;
172 p->records_written = records_written;
173
174 if (dunlink_tail)
175 dunlink_tail->next = p;
176 else
177 dunlink_head = p;
178 dunlink_tail = p;
179 dunlink_count++;
180 }
This page took 0.035749 seconds and 4 git commands to generate.