123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677 |
- /* 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. */
- #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 %d 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;
- }
|