diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /gnu/usr.bin/tar/gnu.c |
initial import of NetBSD tree
Diffstat (limited to 'gnu/usr.bin/tar/gnu.c')
-rw-r--r-- | gnu/usr.bin/tar/gnu.c | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/gnu/usr.bin/tar/gnu.c b/gnu/usr.bin/tar/gnu.c new file mode 100644 index 00000000000..bf539b38e00 --- /dev/null +++ b/gnu/usr.bin/tar/gnu.c @@ -0,0 +1,681 @@ +/* GNU dump extensions to tar. + Copyright (C) 1988, 1992, 1993 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar 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, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef lint +static char rcsid[] = "$Id: gnu.c,v 1.1 1995/10/18 08:41:09 deraadt Exp $"; +#endif /* not lint */ + +#include <stdio.h> +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> +#ifndef STDC_HEADERS +extern int errno; +#endif +#include <time.h> +time_t time (); + +#include "tar.h" +#include "port.h" + +#ifndef S_ISLNK +#define lstat stat +#endif + +extern time_t new_time; +extern FILE *msg_file; + +void addname (); +int check_exclude (); +extern PTR ck_malloc (); +extern PTR ck_realloc (); +int confirm (); +extern PTR init_buffer (); +extern char *get_buffer (); +int is_dot_or_dotdot (); +extern void add_buffer (); +extern void flush_buffer (); +void name_gather (); +int recursively_delete (); +void skip_file (); +char *un_quote_string (); + +extern char *new_name (); + +static void add_dir_name (); + +struct dirname + { + struct dirname *next; + char *name; + char *dir_text; + int dev; + int ino; + int allnew; + }; +static struct dirname *dir_list; +static time_t this_time; + +void +add_dir (name, dev, ino, text) + char *name; + char *text; + dev_t dev; + ino_t ino; +{ + struct dirname *dp; + + dp = (struct dirname *) ck_malloc (sizeof (struct dirname)); + if (!dp) + abort (); + dp->next = dir_list; + dir_list = dp; + dp->dev = dev; + dp->ino = ino; + dp->name = ck_malloc (strlen (name) + 1); + strcpy (dp->name, name); + dp->dir_text = text; + dp->allnew = 0; +} + +void +read_dir_file () +{ + int dev; + int ino; + char *strp; + FILE *fp; + char buf[512]; + static char *path = 0; + + if (path == 0) + path = ck_malloc (PATH_MAX); + time (&this_time); + if (gnu_dumpfile[0] != '/') + { +#if defined(__MSDOS__) || defined(HAVE_GETCWD) || defined(_POSIX_VERSION) + if (!getcwd (path, PATH_MAX)) + { + msg ("Couldn't get current directory."); + exit (EX_SYSTEM); + } +#else + char *getwd (); + + if (!getwd (path)) + { + msg ("Couldn't get current directory: %s", path); + exit (EX_SYSTEM); + } +#endif + /* If this doesn't fit, we're in serious trouble */ + strcat (path, "/"); + strcat (path, gnu_dumpfile); + gnu_dumpfile = path; + } + fp = fopen (gnu_dumpfile, "r"); + if (fp == 0 && errno != ENOENT) + { + msg_perror ("Can't open %s", gnu_dumpfile); + return; + } + if (!fp) + return; + fgets (buf, sizeof (buf), fp); + if (!f_new_files) + { + f_new_files++; + new_time = atol (buf); + } + while (fgets (buf, sizeof (buf), fp)) + { + strp = &buf[strlen (buf)]; + if (strp[-1] == '\n') + strp[-1] = '\0'; + strp = buf; + dev = atol (strp); + while (isdigit (*strp)) + strp++; + ino = atol (strp); + while (isspace (*strp)) + strp++; + while (isdigit (*strp)) + strp++; + strp++; + add_dir (un_quote_string (strp), dev, ino, (char *) 0); + } + fclose (fp); +} + +void +write_dir_file () +{ + FILE *fp; + struct dirname *dp; + char *str; + extern char *quote_copy_string (); + + fp = fopen (gnu_dumpfile, "w"); + if (fp == 0) + { + msg_perror ("Can't write to %s", gnu_dumpfile); + return; + } + fprintf (fp, "%lu\n", this_time); + for (dp = dir_list; dp; dp = dp->next) + { + if (!dp->dir_text) + continue; + str = quote_copy_string (dp->name); + if (str) + { + fprintf (fp, "%u %u %s\n", dp->dev, dp->ino, str); + free (str); + } + else + fprintf (fp, "%u %u %s\n", dp->dev, dp->ino, dp->name); + } + fclose (fp); +} + +struct dirname * +get_dir (name) + char *name; +{ + struct dirname *dp; + + for (dp = dir_list; dp; dp = dp->next) + { + if (!strcmp (dp->name, name)) + return dp; + } + return 0; +} + + +/* Collect all the names from argv[] (or whatever), then expand them into + a directory tree, and put all the directories at the beginning. */ +void +collect_and_sort_names () +{ + struct name *n, *n_next; + int num_names; + struct stat statbuf; + int name_cmp (); + char *merge_sort (); + + name_gather (); + + if (gnu_dumpfile) + read_dir_file (); + if (!namelist) + addname ("."); + for (n = namelist; n; n = n_next) + { + n_next = n->next; + if (n->found || n->dir_contents) + continue; + if (n->regexp) /* FIXME just skip regexps for now */ + continue; + if (n->change_dir) + if (chdir (n->change_dir) < 0) + { + msg_perror ("can't chdir to %s", n->change_dir); + continue; + } + +#ifdef AIX + if (statx (n->name, &statbuf, STATSIZE, STX_HIDDEN | STX_LINK)) +#else + if (lstat (n->name, &statbuf) < 0) +#endif /* AIX */ + { + msg_perror ("can't stat %s", n->name); + continue; + } + if (S_ISDIR (statbuf.st_mode)) + { + n->found++; + add_dir_name (n->name, statbuf.st_dev); + } + } + + num_names = 0; + for (n = namelist; n; n = n->next) + num_names++; + namelist = (struct name *) merge_sort ((PTR) namelist, num_names, (char *) (&(namelist->next)) - (char *) namelist, name_cmp); + + for (n = namelist; n; n = n->next) + { + n->found = 0; + } + if (gnu_dumpfile) + write_dir_file (); +} + +int +name_cmp (n1, n2) + struct name *n1, *n2; +{ + if (n1->found) + { + if (n2->found) + return strcmp (n1->name, n2->name); + else + return -1; + } + else if (n2->found) + return 1; + else + return strcmp (n1->name, n2->name); +} + +int +dirent_cmp (p1, p2) + const PTR p1; + const PTR p2; +{ + char *frst, *scnd; + + frst = (*(char **) p1) + 1; + scnd = (*(char **) p2) + 1; + + return strcmp (frst, scnd); +} + +char * +get_dir_contents (p, device) + char *p; + int device; +{ + DIR *dirp; + register struct dirent *d; + char *new_buf; + char *namebuf; + int bufsiz; + int len; + PTR the_buffer; + char *buf; + size_t n_strs; + /* int n_size;*/ + char *p_buf; + char **vec, **p_vec; + + extern int errno; + + errno = 0; + dirp = opendir (p); + bufsiz = strlen (p) + NAMSIZ; + namebuf = ck_malloc (bufsiz + 2); + if (!dirp) + { + if (errno) + msg_perror ("can't open directory %s", p); + else + msg ("error opening directory %s", p); + new_buf = NULL; + } + else + { + struct dirname *dp; + int all_children; + + dp = get_dir (p); + all_children = dp ? dp->allnew : 0; + (void) strcpy (namebuf, p); + if (p[strlen (p) - 1] != '/') + (void) strcat (namebuf, "/"); + len = strlen (namebuf); + + the_buffer = init_buffer (); + while (d = readdir (dirp)) + { + struct stat hs; + + /* Skip . and .. */ + if (is_dot_or_dotdot (d->d_name)) + continue; + if (NLENGTH (d) + len >= bufsiz) + { + bufsiz += NAMSIZ; + namebuf = ck_realloc (namebuf, bufsiz + 2); + } + (void) strcpy (namebuf + len, d->d_name); +#ifdef AIX + if (0 != f_follow_links ? + statx (namebuf, &hs, STATSIZE, STX_HIDDEN) : + statx (namebuf, &hs, STATSIZE, STX_HIDDEN | STX_LINK)) +#else + if (0 != f_follow_links ? stat (namebuf, &hs) : lstat (namebuf, &hs)) +#endif + { + msg_perror ("can't stat %s", namebuf); + continue; + } + if ((f_local_filesys && device != hs.st_dev) + || (f_exclude && check_exclude (namebuf))) + add_buffer (the_buffer, "N", 1); +#ifdef AIX + else if (S_ISHIDDEN (hs.st_mode)) + { + add_buffer (the_buffer, "D", 1); + strcat (d->d_name, "A"); + d->d_namlen++; + } +#endif /* AIX */ + else if (S_ISDIR (hs.st_mode)) + { + if (dp = get_dir (namebuf)) + { + if (dp->dev != hs.st_dev + || dp->ino != hs.st_ino) + { + if (f_verbose) + msg ("directory %s has been renamed.", namebuf); + dp->allnew = 1; + dp->dev = hs.st_dev; + dp->ino = hs.st_ino; + } + dp->dir_text = ""; + } + else + { + if (f_verbose) + msg ("Directory %s is new", namebuf); + add_dir (namebuf, hs.st_dev, hs.st_ino, ""); + dp = get_dir (namebuf); + dp->allnew = 1; + } + if (all_children) + dp->allnew = 1; + + add_buffer (the_buffer, "D", 1); + } + else if (!all_children + && f_new_files + && new_time > hs.st_mtime + && (f_new_files > 1 + || new_time > hs.st_ctime)) + add_buffer (the_buffer, "N", 1); + else + add_buffer (the_buffer, "Y", 1); + add_buffer (the_buffer, d->d_name, (int) (NLENGTH (d) + 1)); + } + add_buffer (the_buffer, "\000\000", 2); + closedir (dirp); + + /* Well, we've read in the contents of the dir, now sort them */ + buf = get_buffer (the_buffer); + if (buf[0] == '\0') + { + flush_buffer (the_buffer); + new_buf = NULL; + } + else + { + n_strs = 0; + for (p_buf = buf; *p_buf;) + { + int tmp; + + tmp = strlen (p_buf) + 1; + n_strs++; + p_buf += tmp; + } + vec = (char **) ck_malloc (sizeof (char *) * (n_strs + 1)); + for (p_vec = vec, p_buf = buf; *p_buf; p_buf += strlen (p_buf) + 1) + *p_vec++ = p_buf; + *p_vec = 0; + qsort ((PTR) vec, n_strs, sizeof (char *), dirent_cmp); + new_buf = (char *) ck_malloc (p_buf - buf + 2); + for (p_vec = vec, p_buf = new_buf; *p_vec; p_vec++) + { + char *p_tmp; + + for (p_tmp = *p_vec; *p_buf++ = *p_tmp++;) + ; + } + *p_buf++ = '\0'; + free (vec); + flush_buffer (the_buffer); + } + } + free (namebuf); + return new_buf; +} + +/* p is a directory. Add all the files in P to the namelist. If any of the + files is a directory, recurse on the subdirectory. . . */ +static void +add_dir_name (p, device) + char *p; + int device; +{ + char *new_buf; + char *p_buf; + + char *namebuf; + int buflen; + register int len; + int sublen; + + /* PTR the_buffer;*/ + + /* char *buf;*/ + /* char **vec,**p_vec;*/ + /* int n_strs,n_size;*/ + + struct name *n; + + int dirent_cmp (); + + new_buf = get_dir_contents (p, device); + + for (n = namelist; n; n = n->next) + { + if (!strcmp (n->name, p)) + { + n->dir_contents = new_buf ? new_buf : "\0\0\0\0"; + break; + } + } + + if (new_buf) + { + len = strlen (p); + buflen = NAMSIZ <= len ? len + NAMSIZ : NAMSIZ; + namebuf = ck_malloc (buflen + 1); + + (void) strcpy (namebuf, p); + if (namebuf[len - 1] != '/') + { + namebuf[len++] = '/'; + namebuf[len] = '\0'; + } + for (p_buf = new_buf; *p_buf; p_buf += sublen + 1) + { + sublen = strlen (p_buf); + if (*p_buf == 'D') + { + if (len + sublen >= buflen) + { + buflen += NAMSIZ; + namebuf = ck_realloc (namebuf, buflen + 1); + } + (void) strcpy (namebuf + len, p_buf + 1); + addname (namebuf); + add_dir_name (namebuf, device); + } + } + free (namebuf); + } +} + +/* Returns non-zero if p is . or .. This could be a macro for speed. */ +int +is_dot_or_dotdot (p) + char *p; +{ + return (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))); +} + + + + + + +void +gnu_restore (skipcrud) + int skipcrud; +{ + char *current_dir; + /* int current_dir_length; */ + + char *archive_dir; + /* int archive_dir_length; */ + PTR the_buffer; + char *p; + DIR *dirp; + struct dirent *d; + char *cur, *arc; + extern struct stat hstat; /* Stat struct corresponding */ + long size, copied; + char *from, *to; + extern union record *head; + + dirp = opendir (skipcrud + current_file_name); + + if (!dirp) + { + /* The directory doesn't exist now. It'll be created. + In any case, we don't have to delete any files out + of it */ + skip_file ((long) hstat.st_size); + return; + } + + the_buffer = init_buffer (); + while (d = readdir (dirp)) + { + if (is_dot_or_dotdot (d->d_name)) + continue; + + add_buffer (the_buffer, d->d_name, (int) (NLENGTH (d) + 1)); + } + closedir (dirp); + add_buffer (the_buffer, "", 1); + + current_dir = get_buffer (the_buffer); + archive_dir = (char *) ck_malloc (hstat.st_size); + if (archive_dir == 0) + { + msg ("Can't allocate %qd bytes for restore", hstat.st_size); + skip_file ((long) hstat.st_size); + return; + } + to = archive_dir; + for (size = hstat.st_size; size > 0; size -= copied) + { + from = findrec ()->charptr; + if (!from) + { + msg ("Unexpected EOF in archive\n"); + break; + } + copied = endofrecs ()->charptr - from; + if (copied > size) + copied = size; + bcopy ((PTR) from, (PTR) to, (int) copied); + to += copied; + userec ((union record *) (from + copied - 1)); + } + + for (cur = current_dir; *cur; cur += strlen (cur) + 1) + { + for (arc = archive_dir; *arc; arc += strlen (arc) + 1) + { + arc++; + if (!strcmp (arc, cur)) + break; + } + if (*arc == '\0') + { + p = new_name (skipcrud + current_file_name, cur); + if (f_confirm && !confirm ("delete", p)) + { + free (p); + continue; + } + if (f_verbose) + fprintf (msg_file, "%s: deleting %s\n", tar, p); + if (recursively_delete (p)) + { + msg ("%s: Error while deleting %s\n", tar, p); + } + free (p); + } + + } + flush_buffer (the_buffer); + free (archive_dir); +} + +int +recursively_delete (path) + char *path; +{ + struct stat sbuf; + DIR *dirp; + struct dirent *dp; + char *path_buf; + /* int path_len; */ + + + if (lstat (path, &sbuf) < 0) + return 1; + if (S_ISDIR (sbuf.st_mode)) + { + + /* path_len=strlen(path); */ + dirp = opendir (path); + if (dirp == 0) + return 1; + while (dp = readdir (dirp)) + { + if (is_dot_or_dotdot (dp->d_name)) + continue; + path_buf = new_name (path, dp->d_name); + if (recursively_delete (path_buf)) + { + free (path_buf); + closedir (dirp); + return 1; + } + free (path_buf); + } + closedir (dirp); + + if (rmdir (path) < 0) + return 1; + return 0; + } + if (unlink (path) < 0) + return 1; + return 0; +} |