6677148ea5ecd976d4a9aa3bbecac42747cf1951
[debian/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 }