home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
tar-1.11.8-src.tgz
/
tar.out
/
fsf
/
tar
/
src
/
gnu.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-09-28
|
15KB
|
692 lines
/* GNU dump extensions to tar.
Copyright (C) 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
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 "system.h"
#include <time.h>
time_t time ();
#define ISDIGIT(Char) (ISASCII (Char) && isdigit (Char))
#define ISSPACE(Char) (ISASCII (Char) && isspace (Char))
#ifndef S_ISLNK
#define lstat stat
#endif
#include "tar.h"
extern time_t new_time;
static void add_dir __P ((char *, dev_t, ino_t, const char *));
static void add_dir_name __P ((char *, int));
static int dirent_cmp __P ((const voidstar, const voidstar));
static int name_cmp __P ((struct name *, struct name *));
static int recursively_delete __P ((char *));
struct dirname
{
struct dirname *next;
const char *name;
const char *dir_text;
int dev;
int ino;
int allnew;
};
static struct dirname *dir_list;
static time_t this_time;
/*---.
| ? |
`---*/
static void
add_dir (char *name, dev_t dev, ino_t ino, const char *text)
{
struct dirname *dp;
dp = (struct dirname *) xmalloc (sizeof (struct dirname));
dp->next = dir_list;
dir_list = dp;
dp->dev = dev;
dp->ino = ino;
dp->name = xstrdup (name);
dp->dir_text = text;
dp->allnew = 0;
}
/*---.
| ? |
`---*/
static void
read_dir_file (void)
{
dev_t dev;
ino_t ino;
char *strp;
FILE *fp;
char buf[512];
static char *path = 0;
if (path == 0)
path = xmalloc (PATH_MAX);
time (&this_time);
if (gnu_dumpfile[0] != '/')
{
#ifdef HAVE_GETCWD
if (!getcwd (path, PATH_MAX))
ERROR ((TAREXIT_FAILURE, 0, _("Could not get current directory")));
#else
char *getwd ();
if (!getwd (path))
ERROR ((TAREXIT_FAILURE, 0, _("Could not get current directory: %s"),
path));
#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)
{
ERROR ((0, errno, _("Cannot open %s"), gnu_dumpfile));
return;
}
if (!fp)
return;
fgets (buf, sizeof (buf), fp);
if (!flag_new_files)
{
flag_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 (void)
{
FILE *fp;
struct dirname *dp;
char *str;
fp = fopen (gnu_dumpfile, "w");
if (fp == 0)
{
ERROR ((0, errno, _("Cannot 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);
}
/*---.
| ? |
`---*/
static struct dirname *
get_dir (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 (void)
{
struct name *n, *n_next;
int num_names;
struct stat statbuf;
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)
{
ERROR ((0, errno, _("Cannot chdir to %s"), n->change_dir));
continue;
}
if (
#ifdef AIX
statx (n->name, &statbuf, STATSIZE, STX_HIDDEN | STX_LINK)
#else
lstat (n->name, &statbuf) < 0
#endif
)
{
ERROR ((0, errno, _("Cannot 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 ((voidstar) namelist, (unsigned) 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 ();
}
/*---.
| ? |
`---*/
static int
name_cmp (struct name *n1, struct name *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);
}
/*---.
| ? |
`---*/
static int
dirent_cmp (const voidstar p1, const voidstar p2)
{
char const *frst, *scnd;
frst = (*(char *const *) p1) + 1;
scnd = (*(char *const *) p2) + 1;
return strcmp (frst, scnd);
}
/*---.
| ? |
`---*/
char *
get_dir_contents (char *p, int device)
{
DIR *dirp;
register struct dirent *d;
char *new_buf;
char *namebuf;
int bufsiz;
int len;
voidstar the_buffer;
char *buf;
size_t n_strs;
#if 0
int n_size;
#endif
char *p_buf;
char **vec, **p_vec;
errno = 0;
dirp = opendir (p);
bufsiz = strlen (p) + NAMSIZ;
namebuf = xmalloc ((size_t) (bufsiz + 2));
if (!dirp)
{
ERROR ((0, errno, _("Cannot open directory %s"), p));
new_buf = NULL;
}
else
{
struct dirname *dp;
int all_children;
dp = get_dir (p);
all_children = dp ? dp->allnew : 0;
strcpy (namebuf, p);
if (p[strlen (p) - 1] != '/')
strcat (namebuf, "/");
len = strlen (namebuf);
the_buffer = init_buffer ();
while (d = readdir (dirp), d)
{
struct stat hs;
/* Skip `.' and `..'. */
if (is_dot_or_dotdot (d->d_name))
continue;
if ((int) NAMLEN (d) + len >= bufsiz)
{
bufsiz += NAMSIZ;
namebuf = (char *) xrealloc (namebuf, (size_t) (bufsiz + 2));
}
strcpy (namebuf + len, d->d_name);
#ifdef AIX
if (flag_follow_links ? statx (namebuf, &hs, STATSIZE, STX_HIDDEN)
: statx (namebuf, &hs, STATSIZE, STX_HIDDEN | STX_LINK))
#else
if (flag_follow_links ? stat (namebuf, &hs) : lstat (namebuf, &hs))
#endif
{
ERROR ((0, errno, _("Cannot stat %s"), namebuf));
continue;
}
if ((flag_local_filesys && device != hs.st_dev)
|| (flag_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), dp)
{
if (dp->dev != hs.st_dev
|| dp->ino != hs.st_ino)
{
if (flag_verbose)
WARN ((0, 0, _("Directory %s has been renamed"),
namebuf));
dp->allnew = 1;
dp->dev = hs.st_dev;
dp->ino = hs.st_ino;
}
dp->dir_text = "";
}
else
{
if (flag_verbose)
WARN ((0, 0, _("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)
dp->allnew = 1;
add_buffer (the_buffer, "D", 1);
}
else if (!all_children
&& flag_new_files
&& new_time > hs.st_mtime
&& (flag_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) (NAMLEN (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 **) xmalloc (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 ((voidstar) vec, n_strs, sizeof (char *), dirent_cmp);
new_buf = (char *) xmalloc ((size_t) (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 (char *p, int device)
{
char *new_buf;
char *p_buf;
char *namebuf;
int buflen;
register int len;
int sublen;
#if 0
voidstar the_buffer;
char *buf;
char **vec,**p_vec;
int n_strs,n_size;
#endif
struct name *n;
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 = xmalloc ((size_t) (buflen + 1));
strcpy (namebuf, p);
if (namebuf[len - 1] != '/')
{
namebuf[len++] = '/';
namebuf[len] = '\0';
}
for (p_buf = new_buf; p_buf && *p_buf; p_buf += sublen + 1)
{
sublen = strlen (p_buf);
if (*p_buf == 'D')
{
if (len + sublen >= buflen)
{
buflen += NAMSIZ;
namebuf = (char *) xrealloc (namebuf, (size_t) (buflen + 1));
}
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. |
`-------------------------------------------------------------------------*/
/* Early Solaris 2.4 readdir may return d->d_name as `' in NFS-mounted
directories. The workaround here skips `' just like `.'. Without it,
GNU tar would then treat `' much like `.' and loop endlessly. */
int
is_dot_or_dotdot (char *p)
{
return (p[0] == '\0'
|| (p[0] == '.' && (p[1] == '\0'
|| (p[1] == '.' && p[2] == '\0'))));
}
/*---.
| ? |
`---*/
void
gnu_restore (int skipcrud)
{
char *current_dir;
#if 0
int current_dir_length;
#endif
char *archive_dir;
#if 0
int archive_dir_length;
#endif
voidstar the_buffer;
char *p;
DIR *dirp;
struct dirent *d;
char *cur, *arc;
long size, copied;
char *from, *to;
#define CURRENT_FILE_NAME (skipcrud + current_file_name)
dirp = opendir (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), d)
{
if (is_dot_or_dotdot (d->d_name))
continue;
add_buffer (the_buffer, d->d_name, (int) (NAMLEN (d) + 1));
}
closedir (dirp);
add_buffer (the_buffer, "", 1);
current_dir = get_buffer (the_buffer);
archive_dir = (char *) ck_malloc ((size_t) hstat.st_size);
if (archive_dir == 0)
{
ERROR ((0, 0, _("Cannot 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)
{
ERROR ((0, 0, _("Unexpected EOF in archive")));
break;
}
copied = endofrecs ()->charptr - from;
if (copied > size)
copied = size;
memcpy ((voidstar) to, (voidstar) from, (size_t) 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 (CURRENT_FILE_NAME, cur);
if (flag_confirm && !confirm ("delete", p))
{
free (p);
continue;
}
if (flag_verbose)
fprintf (stdlis, _("%s: Deleting %s\n"), program_name, p);
if (recursively_delete (p))
ERROR ((0, 0, _("Error while deleting %s"), p));
free (p);
}
}
flush_buffer (the_buffer);
free (archive_dir);
#undef CURRENT_FILE_NAME
}
/*---.
| ? |
`---*/
static int
recursively_delete (char *path)
{
struct stat sbuf;
DIR *dirp;
struct dirent *dp;
char *path_buf;
#if 0
int path_len;
#endif
if (lstat (path, &sbuf) < 0)
return 1;
if (S_ISDIR (sbuf.st_mode))
{
#if 0
path_len = strlen (path);
#endif
dirp = opendir (path);
if (dirp == 0)
return 1;
while (dp = readdir (dirp), dp)
{
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;
}