diff options
Diffstat (limited to 'tarlist.c')
-rw-r--r-- | tarlist.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/tarlist.c b/tarlist.c new file mode 100644 index 000000000..917efdcb9 --- /dev/null +++ b/tarlist.c @@ -0,0 +1,232 @@ +/* tarfs - A GNU tar filesystem for the Hurd. + Copyright (C) 2002, Ludovic Courtès <ludo@type-z.org> + + 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 2 of the + License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA */ + +/* Tar list management functions. This is used as a model representing + the contents of a tar file, i.e. the items in the order in which they + appear (or should appear) in the tar file. */ + +#include <hurd/netfs.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <error.h> + +#include "tarfs.h" +#include "fs.h" +#include "debug.h" + + +/* Initialize LIST. */ +void +tar_list_init (struct tar_list *list) +{ + list->head = NULL; + mutex_init (&list->lock); +} + +/* Make a tar item containing the given information. NEW points to the + newly created item. */ +error_t +tar_make_item (struct tar_item **new_item, + struct node *node, size_t orig_size, off_t offset) +{ + struct tar_item *new; + + new = calloc (1, sizeof (struct tar_item)); + if (! new) + return ENOMEM; + + assert (node != NULL); + new->orig_size = orig_size; + new->offset = offset; + new->node = node; + NODE_INFO(node)->tar = new; + + *new_item = new; + + return 0; +} + +/* Insert tar item NEW right after PREV in LIST. */ +error_t +tar_insert_item (struct tar_list *list, + struct tar_item *prev, struct tar_item *new) +{ + struct tar_item **head, *next; + + assert (prev != new); + + mutex_lock (&list->lock); + head = &list->head; + + if (! prev) + if (! *head) + { + *head = new; + next = NULL; + } + else + { + prev = *head; + next = (*head)->next; + (*head)->next = new; + } + else + { + next = prev->next; + prev->next = new; + } + + new->prev = prev; + new->next = next; + if (next) + next->prev = new; + + mutex_unlock (&list->lock); + + return 0; +} + +/* Remove ITEM from LIST. */ +void +tar_unlink_item_safe (struct tar_list *list, struct tar_item *item) +{ + struct tar_item **head; + + /* The corresponding node should have been destroyed first. */ + assert (item->node == NULL); + + head = &list->head; + + /* Make sure LIST is not already empty */ + assert (*head != NULL); + + if (! item->prev) + { + *head = item->next; + if (*head) + (*head)->prev = NULL; + } + else + { + item->prev->next = item->next; + if (item->next) + item->next->prev = item->prev; + } + + /* Free ITEM. */ + free (item); +} + +void +tar_unlink_item (struct tar_list *list, struct tar_item *tar) +{ + mutex_lock (&list->lock); + tar_unlink_item_safe (list, tar); + mutex_unlock (&list->lock); +} + + +/* Attempt to find a place for TAR, an new yet unlinked tar item, into the + tar list in an optimal way. Returns in PREV_ITEM the item after which + TAR should be inserted but don't actually insert it. */ +void +tar_put_item (struct tar_item **prev_tar, struct tar_item *tar) +{ + struct node *node = tar->node; + struct node *dir, *last_entry; + + /* TAR has to be linked to the filesystem. */ + assert (tar->node); + + dir = node->nn->dir; + + /* Try to insert the node inside the tar list in such a way that it + appears: + - after its parent directory; + - after all the entries of its parent dir; + - after the last entry of its parent dir's last entry. + + The 1st thing is needed to have a consistent tar archive, and the two + latter requirements are needed for better performance at sync time + (it avoids having to sync everything before NEWNODE). + + So that we have, for instance: + 0: file + 1: dir/ + 2: dir/file1 + 3: dir/file2 + 4: NEWNODE. */ + + last_entry = dir->nn->entries; + + if (last_entry) + { + /* Get a reference to DIR's last entry. */ + struct node *next; + + for ( ; + (next = last_entry->next) != NULL; + last_entry = next) + { + if (next == node) + { + /* If LAST_ENTRY is TAR's node... */ + if (node->next) + /* ... skip it */ + next = node->next; + else + /* ... or keep the next-to-last entry */ + break; + } + } + + if (last_entry == node) + last_entry = NULL; + + /* Jump to the last node of LAST_ENTRY's deepest subdir */ + while (last_entry) + { + /* If it's a directory, get its last entry. */ + if ( (S_ISDIR (last_entry->nn_stat.st_mode)) + && (last_entry->nn->entries) ) + { + for (last_entry = last_entry->nn->entries; + last_entry->next; + last_entry = last_entry->next); + } + else + break; + } + } + + if ((last_entry) && (last_entry != node)) + { + assert (NODE_INFO(last_entry)->tar); + *prev_tar = NODE_INFO(last_entry)->tar; + } + else + { + if (dir == netfs_root_node) + *prev_tar = NULL; + else + *prev_tar = NODE_INFO(dir)->tar; + } + + return; +} |