X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Funlink.c;fp=src%2Funlink.c;h=2af6f9957b2887eea74b9fe0b394207d60937e32;hb=22f1eb8bc17e5be72dd23d42d6aaa60196ac22e6;hp=0000000000000000000000000000000000000000;hpb=00fa13ff3f2d5b6e2a94c5e948c38616ff7ad37a;p=debian%2Ftar diff --git a/src/unlink.c b/src/unlink.c new file mode 100644 index 00000000..2af6f995 --- /dev/null +++ b/src/unlink.c @@ -0,0 +1,158 @@ +/* This file is part of GNU tar. + Copyright (C) 2009 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include +#include "common.h" +#include + +struct deferred_unlink + { + struct deferred_unlink *next; /* Next unlink in the queue */ + char *file_name; /* Absolute name of the file to unlink */ + bool is_dir; /* True if file_name is a directory */ + off_t records_written; /* Number of records written when this + entry got added to the queue */ + }; + +/* The unlink queue */ +static struct deferred_unlink *dunlink_head, *dunlink_tail; + +/* Number of entries in the queue */ +static size_t dunlink_count; + +/* List of entries available for allocation */ +static struct deferred_unlink *dunlink_avail; + +/* Delay (number of records written) between adding entry to the + list and its actual removal. */ +size_t deferred_unlink_delay = 0; + +static struct deferred_unlink * +dunlink_alloc () +{ + struct deferred_unlink *p; + if (dunlink_avail) + { + p = dunlink_avail; + dunlink_avail = p->next; + p->next = NULL; + } + else + p = xmalloc (sizeof (*p)); + return p; +} + +static void +dunlink_reclaim (struct deferred_unlink *p) +{ + free (p->file_name); + p->next = dunlink_avail; + dunlink_avail = p; +} + +static void +flush_deferred_unlinks (bool force) +{ + struct deferred_unlink *p, *prev = NULL; + + for (p = dunlink_head; p; ) + { + struct deferred_unlink *next = p->next; + if (force + || records_written > p->records_written + deferred_unlink_delay) + { + if (p->is_dir) + { + if (rmdir (p->file_name) != 0) + { + switch (errno) + { + case ENOENT: + /* nothing to worry about */ + break; + case ENOTEMPTY: + if (!force) + { + /* Keep the record in list, in the hope we'll + be able to remove it later */ + prev = p; + p = next; + continue; + } + /* fall through */ + default: + rmdir_error (p->file_name); + } + } + } + else + { + if (unlink (p->file_name) != 0 && errno != ENOENT) + unlink_error (p->file_name); + } + dunlink_reclaim (p); + dunlink_count--; + p = next; + if (prev) + prev->next = p; + else + dunlink_head = p; + } + else + { + prev = p; + p = next; + } + } + if (!dunlink_head) + dunlink_tail = NULL; +} + +void +finish_deferred_unlinks () +{ + flush_deferred_unlinks (true); + while (dunlink_avail) + { + struct deferred_unlink *next = dunlink_avail->next; + free (dunlink_avail); + dunlink_avail = next; + } +} + +void +queue_deferred_unlink (const char *name, bool is_dir) +{ + struct deferred_unlink *p; + + if (dunlink_head + && records_written > dunlink_head->records_written + deferred_unlink_delay) + flush_deferred_unlinks (false); + + p = dunlink_alloc (); + p->next = NULL; + p->file_name = normalize_filename (name); + p->is_dir = is_dir; + p->records_written = records_written; + + if (dunlink_tail) + dunlink_tail->next = p; + else + dunlink_head = p; + dunlink_tail = p; + dunlink_count++; +}