]> git.cworth.org Git - tar/blob - src/unlink.c
Imported Upstream version 1.24
[tar] / src / unlink.c
1 /* This file is part of GNU tar.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3, or (at your option) any later
7    version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
12    Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation, Inc.,
16    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 #include <system.h>
19 #include "common.h"
20 #include <quotearg.h>
21
22 struct deferred_unlink
23   {
24     struct deferred_unlink *next;   /* Next unlink in the queue */
25     char *file_name;                /* Absolute name of the file to unlink */
26     bool is_dir;                    /* True if file_name is a directory */
27     off_t records_written;          /* Number of records written when this
28                                        entry got added to the queue */
29   };
30
31 /* The unlink queue */
32 static struct deferred_unlink *dunlink_head, *dunlink_tail;
33
34 /* Number of entries in the queue */
35 static size_t dunlink_count;
36
37 /* List of entries available for allocation */
38 static struct deferred_unlink *dunlink_avail;
39
40 /* Delay (number of records written) between adding entry to the
41    list and its actual removal. */
42 size_t deferred_unlink_delay = 0;
43
44 static struct deferred_unlink *
45 dunlink_alloc (void)
46 {
47   struct deferred_unlink *p;
48   if (dunlink_avail)
49     {
50       p = dunlink_avail;
51       dunlink_avail = p->next;
52       p->next  = NULL;
53     }
54   else
55     p = xmalloc (sizeof (*p));
56   return p;
57 }
58
59 static void
60 dunlink_reclaim (struct deferred_unlink *p)
61 {
62   free (p->file_name);
63   p->next = dunlink_avail;
64   dunlink_avail = p;
65 }
66
67 static void
68 flush_deferred_unlinks (bool force)
69 {
70   struct deferred_unlink *p, *prev = NULL;
71
72   for (p = dunlink_head; p; )
73     {
74       struct deferred_unlink *next = p->next;
75       if (force
76           || records_written > p->records_written + deferred_unlink_delay)
77         {
78           if (p->is_dir)
79             {
80               if (unlinkat (chdir_fd, p->file_name, AT_REMOVEDIR) != 0)
81                 {
82                   switch (errno)
83                     {
84                     case ENOENT:
85                       /* nothing to worry about */
86                       break;
87                     case ENOTEMPTY:
88                       if (!force)
89                         {
90                           /* Keep the record in list, in the hope we'll
91                              be able to remove it later */
92                           prev = p;
93                           p = next;
94                           continue;
95                         }
96                       /* fall through */
97                     default:
98                       rmdir_error (p->file_name);
99                     }
100                 }
101             }
102           else
103             {
104               if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
105                 unlink_error (p->file_name);
106             }
107           dunlink_reclaim (p);
108           dunlink_count--;
109           p = next;
110           if (prev)
111             prev->next = p;
112           else
113             dunlink_head = p;
114         }
115       else
116         {
117           prev = p;
118           p = next;
119         }
120     }
121   if (!dunlink_head)
122     dunlink_tail = NULL;
123 }
124
125 void
126 finish_deferred_unlinks ()
127 {
128   flush_deferred_unlinks (true);
129   while (dunlink_avail)
130     {
131       struct deferred_unlink *next = dunlink_avail->next;
132       free (dunlink_avail);
133       dunlink_avail = next;
134     }
135 }
136
137 void
138 queue_deferred_unlink (const char *name, bool is_dir)
139 {
140   struct deferred_unlink *p;
141
142   if (dunlink_head
143       && records_written > dunlink_head->records_written + deferred_unlink_delay)
144     flush_deferred_unlinks (false);
145
146   p = dunlink_alloc ();
147   p->next = NULL;
148   p->file_name = normalize_filename (name);
149   p->is_dir = is_dir;
150   p->records_written = records_written;
151
152   if (dunlink_tail)
153     dunlink_tail->next = p;
154   else
155     dunlink_head = p;
156   dunlink_tail = p;
157   dunlink_count++;
158 }