#include "system.h"
#if !MSDOS
# include <pwd.h>
# include <grp.h>
#endif
#if HAVE_UTIME_H
# include <utime.h>
#else
struct utimbuf
{
long actime;
long modtime;
};
#endif
#include "common.h"
#ifndef MSDOS
extern dev_t ar_dev;
extern ino_t ar_ino;
#endif
extern struct name *gnu_list_name;
struct link
{
struct link *next;
dev_t dev;
ino_t ino;
short linkcount;
char name[1];
};
struct link *linklist = NULL;
static void
to_oct (uintmax_t value, uintmax_t substitute, char *where, size_t size, const char *type)
{
uintmax_t v = value;
size_t i = size;
# define MAX_OCTAL_VAL_WITH_DIGITS(digits) \
((digits) * 3 < sizeof (uintmax_t) * CHAR_BIT \
? ((uintmax_t) 1 << ((digits) * 3)) - 1 \
: (uintmax_t) -1)
if (value <= MAX_OCTAL_VAL_WITH_DIGITS (size - 1))
where[--i] = '\0';
do
{
where[--i] = '0' + (int) (v & 7);
v >>= 3;
}
while (i != 0 && v != 0);
while (i != 0)
where[--i] = '0';
if (v != 0)
{
uintmax_t maxval = MAX_OCTAL_VAL_WITH_DIGITS (size);
char buf1[UINTMAX_STRSIZE_BOUND];
char buf2[UINTMAX_STRSIZE_BOUND];
char buf3[UINTMAX_STRSIZE_BOUND];
char *value_string = STRINGIFY_BIGINT (value, buf1);
char *maxval_string = STRINGIFY_BIGINT (maxval, buf2);
if (substitute)
{
substitute &= maxval;
WARN ((0, 0, _("%s value %s too large (max=%s); substituting %s"),
type, value_string, maxval_string,
STRINGIFY_BIGINT (substitute, buf3)));
to_oct (substitute, (uintmax_t) 0, where, size, type);
}
else
ERROR ((0, 0, _("%s value %s too large (max=%s)"),
type, value_string, maxval_string));
}
}
#ifndef GID_NOBODY
#define GID_NOBODY 0
#endif
void
gid_to_oct (gid_t v, char *p, size_t s)
{
to_oct ((uintmax_t) v, (uintmax_t) GID_NOBODY, p, s, "gid_t");
}
void
major_to_oct (major_t v, char *p, size_t s)
{
to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "major_t");
}
void
minor_to_oct (minor_t v, char *p, size_t s)
{
to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "minor_t");
}
void
mode_to_oct (mode_t v, char *p, size_t s)
{
uintmax_t u =
((S_ISUID == TSUID && S_ISGID == TSGID && S_ISVTX == TSVTX
&& S_IRUSR == TUREAD && S_IWUSR == TUWRITE && S_IXUSR == TUEXEC
&& S_IRGRP == TGREAD && S_IWGRP == TGWRITE && S_IXGRP == TGEXEC
&& S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC)
? v
: ((v & S_ISUID ? TSUID : 0)
| (v & S_ISGID ? TSGID : 0)
| (v & S_ISVTX ? TSVTX : 0)
| (v & S_IRUSR ? TUREAD : 0)
| (v & S_IWUSR ? TUWRITE : 0)
| (v & S_IXUSR ? TUEXEC : 0)
| (v & S_IRGRP ? TGREAD : 0)
| (v & S_IWGRP ? TGWRITE : 0)
| (v & S_IXGRP ? TGEXEC : 0)
| (v & S_IROTH ? TOREAD : 0)
| (v & S_IWOTH ? TOWRITE : 0)
| (v & S_IXOTH ? TOEXEC : 0)));
to_oct (u, (uintmax_t) 0, p, s, "mode_t");
}
void
off_to_oct (off_t v, char *p, size_t s)
{
to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "off_t");
}
void
size_to_oct (size_t v, char *p, size_t s)
{
to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "size_t");
}
void
time_to_oct (time_t v, char *p, size_t s)
{
to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "time_t");
}
#ifndef UID_NOBODY
#define UID_NOBODY 0
#endif
void
uid_to_oct (uid_t v, char *p, size_t s)
{
to_oct ((uintmax_t) v, (uintmax_t) UID_NOBODY, p, s, "uid_t");
}
void
uintmax_to_oct (uintmax_t v, char *p, size_t s)
{
to_oct (v, (uintmax_t) 0, p, s, "uintmax_t");
}
static void
clear_buffer (char *buffer)
{
memset (buffer, 0, BLOCKSIZE);
}
void
write_eot (void)
{
union block *pointer = find_next_block ();
if (pointer)
{
size_t space = available_space_after (pointer);
memset (pointer->buffer, 0, space);
set_next_block_after (pointer);
}
}
static union block *start_header PARAMS ((const char *, struct stat *));
static void
write_long (const char *p, char type)
{
size_t size = strlen (p) + 1;
size_t bufsize;
union block *header;
struct stat foo;
memset (&foo, 0, sizeof foo);
foo.st_size = size;
header = start_header ("././@LongLink", &foo);
header->header.typeflag = type;
finish_header (header);
header = find_next_block ();
bufsize = available_space_after (header);
while (bufsize < size)
{
memcpy (header->buffer, p, bufsize);
p += bufsize;
size -= bufsize;
set_next_block_after (header + (bufsize - 1) / BLOCKSIZE);
header = find_next_block ();
bufsize = available_space_after (header);
}
memcpy (header->buffer, p, size);
memset (header->buffer + size, 0, bufsize - size);
set_next_block_after (header + (size - 1) / BLOCKSIZE);
}
static union block *
start_header (const char *name, struct stat *st)
{
union block *header;
if (!absolute_names_option)
{
static int warned_once = 0;
#if MSDOS
if (name[1] == ':')
{
name += 2;
if (!warned_once)
{
warned_once = 1;
WARN ((0, 0, _("Removing drive spec from names in the archive")));
}
}
#endif
while (*name == '/')
{
name++;
if (!warned_once)
{
warned_once = 1;
WARN ((0, 0, _("\
Removing leading `/' from absolute path names in the archive")));
}
}
}
if (strlen (name) >= (size_t) NAME_FIELD_SIZE)
write_long (name, GNUTYPE_LONGNAME);
header = find_next_block ();
memset (header->buffer, 0, sizeof (union block));
assign_string (¤t_file_name, name);
strncpy (header->header.name, name, NAME_FIELD_SIZE);
header->header.name[NAME_FIELD_SIZE - 1] = '\0';
if (owner_option != (uid_t) -1)
st->st_uid = owner_option;
if (group_option != (gid_t) -1)
st->st_gid = group_option;
if (mode_option)
st->st_mode = ((st->st_mode & S_IFMT)
| mode_adjust (st->st_mode, mode_option));
if (archive_format == V7_FORMAT)
MODE_TO_OCT (st->st_mode & MODE_ALL, header->header.mode);
else
MODE_TO_OCT (st->st_mode, header->header.mode);
UID_TO_OCT (st->st_uid, header->header.uid);
GID_TO_OCT (st->st_gid, header->header.gid);
OFF_TO_OCT (st->st_size, header->header.size);
TIME_TO_OCT (st->st_mtime, header->header.mtime);
if (incremental_option)
if (archive_format == OLDGNU_FORMAT)
{
TIME_TO_OCT (st->st_atime, header->oldgnu_header.atime);
TIME_TO_OCT (st->st_ctime, header->oldgnu_header.ctime);
}
header->header.typeflag = archive_format == V7_FORMAT ? AREGTYPE : REGTYPE;
switch (archive_format)
{
case DEFAULT_FORMAT:
case V7_FORMAT:
break;
case OLDGNU_FORMAT:
strcpy (header->header.magic, OLDGNU_MAGIC);
break;
case POSIX_FORMAT:
case GNU_FORMAT:
strncpy (header->header.magic, TMAGIC, TMAGLEN);
strncpy (header->header.version, TVERSION, TVERSLEN);
break;
}
if (archive_format == V7_FORMAT || numeric_owner_option)
{
}
else
{
uid_to_uname (st->st_uid, header->header.uname);
gid_to_gname (st->st_gid, header->header.gname);
}
return header;
}
void
finish_header (union block *header)
{
size_t i;
int sum;
char *p;
memcpy (header->header.chksum, CHKBLANKS, sizeof (header->header.chksum));
sum = 0;
p = header->buffer;
for (i = sizeof (*header); i-- != 0; )
sum += 0xFF & *p++;
uintmax_to_oct ((uintmax_t) sum, header->header.chksum, 7);
set_next_block_after (header);
if (verbose_option
&& header->header.typeflag != GNUTYPE_LONGLINK
&& header->header.typeflag != GNUTYPE_LONGNAME)
{
current_header = header;
current_format = archive_format;
print_header ();
}
}
static int
zero_block_p (char *buffer)
{
int counter;
for (counter = 0; counter < BLOCKSIZE; counter++)
if (buffer[counter] != '\0')
return 0;
return 1;
}
static void
init_sparsearray (void)
{
int counter;
sp_array_size = 10;
sparsearray = (struct sp_array *)
xmalloc (sp_array_size * sizeof (struct sp_array));
for (counter = 0; counter < sp_array_size; counter++)
{
sparsearray[counter].offset = 0;
sparsearray[counter].numbytes = 0;
}
}
static void
find_new_file_size (off_t *filesize, int highest_index)
{
int counter;
*filesize = 0;
for (counter = 0;
sparsearray[counter].numbytes && counter <= highest_index;
counter++)
*filesize += sparsearray[counter].numbytes;
}
static int
deal_with_sparse (char *name, union block *header)
{
size_t numbytes = 0;
off_t offset = 0;
int file;
int sparse_index = 0;
ssize_t count;
char buffer[BLOCKSIZE];
if (archive_format == OLDGNU_FORMAT)
header->oldgnu_header.isextended = 0;
if (file = open (name, O_RDONLY), file < 0)
return 0;
init_sparsearray ();
clear_buffer (buffer);
while (count = safe_read (file, buffer, sizeof buffer), count != 0)
{
if (sparse_index > sp_array_size - 1)
{
sparsearray = (struct sp_array *)
xrealloc (sparsearray,
2 * sp_array_size * sizeof (struct sp_array));
sp_array_size *= 2;
}
if (count == sizeof buffer)
if (zero_block_p (buffer))
{
if (numbytes)
{
sparsearray[sparse_index++].numbytes = numbytes;
numbytes = 0;
}
}
else
{
if (!numbytes)
sparsearray[sparse_index].offset = offset;
numbytes += count;
}
else
if (!zero_block_p (buffer))
{
if (!numbytes)
sparsearray[sparse_index].offset = offset;
numbytes += count;
}
else
if (numbytes)
numbytes += count;
offset += count;
clear_buffer (buffer);
}
if (numbytes)
sparsearray[sparse_index++].numbytes = numbytes;
else
{
sparsearray[sparse_index].offset = offset - 1;
sparsearray[sparse_index++].numbytes = 1;
}
close (file);
return sparse_index - 1;
}
static int
finish_sparse_file (int file, off_t *sizeleft, off_t fullsize, char *name)
{
union block *start;
size_t bufsize;
int sparse_index = 0;
ssize_t count;
while (*sizeleft > 0)
{
start = find_next_block ();
memset (start->buffer, 0, BLOCKSIZE);
bufsize = sparsearray[sparse_index].numbytes;
if (!bufsize)
{
char buf1[UINTMAX_STRSIZE_BOUND];
char buf2[UINTMAX_STRSIZE_BOUND];
ERROR ((0, 0, _("Wrote %s of %s bytes to file %s"),
STRINGIFY_BIGINT (fullsize - *sizeleft, buf1),
STRINGIFY_BIGINT (fullsize, buf2),
name));
break;
}
if (lseek (file, sparsearray[sparse_index++].offset, SEEK_SET) < 0)
{
char buf[UINTMAX_STRSIZE_BOUND];
ERROR ((0, errno, _("lseek error at byte %s in file %s"),
STRINGIFY_BIGINT (sparsearray[sparse_index - 1].offset, buf),
name));
break;
}
while (bufsize > BLOCKSIZE)
{
#if 0
if (amount_read)
{
count = safe_read (file, start->buffer + amount_read,
BLOCKSIZE - amount_read);
bufsize -= BLOCKSIZE - amount_read;
amount_read = 0;
set_next_block_after (start);
start = find_next_block ();
memset (start->buffer, 0, BLOCKSIZE);
}
#endif
count = safe_read (file, start->buffer, BLOCKSIZE);
if (count < 0)
{
char buf[UINTMAX_STRSIZE_BOUND];
ERROR ((0, errno, _("\
Read error at byte %s, reading %lu bytes, in file %s"),
STRINGIFY_BIGINT (fullsize - *sizeleft, buf),
(unsigned long) bufsize, name));
return 1;
}
bufsize -= count;
*sizeleft -= count;
set_next_block_after (start);
start = find_next_block ();
memset (start->buffer, 0, BLOCKSIZE);
}
{
char buffer[BLOCKSIZE];
clear_buffer (buffer);
count = safe_read (file, buffer, bufsize);
memcpy (start->buffer, buffer, BLOCKSIZE);
}
if (count < 0)
{
char buf[UINTMAX_STRSIZE_BOUND];
ERROR ((0, errno,
_("Read error at byte %s, reading %lu bytes, in file %s"),
STRINGIFY_BIGINT (fullsize - *sizeleft, buf),
(unsigned long) bufsize, name));
return 1;
}
#if 0
if (amount_read >= BLOCKSIZE)
{
amount_read = 0;
set_next_block_after (start + (count - 1) / BLOCKSIZE);
if (count != bufsize)
{
ERROR ((0, 0,
_("File %s shrunk, padding with zeros"),
name));
return 1;
}
start = find_next_block ();
}
else
amount_read += bufsize;
#endif
*sizeleft -= count;
set_next_block_after (start);
}
free (sparsearray);
#if 0
set_next_block_after (start + (count - 1) / BLOCKSIZE);
#endif
return 0;
}
void
create_archive (void)
{
char *p;
open_archive (ACCESS_WRITE);
if (incremental_option)
{
char *buffer = xmalloc (PATH_MAX);
const char *q;
char *bufp;
collect_and_sort_names ();
while (p = name_from_list (), p)
dump_file (p, (dev_t) -1, 1);
blank_name_list ();
while (p = name_from_list (), p)
{
strcpy (buffer, p);
if (p[strlen (p) - 1] != '/')
strcat (buffer, "/");
bufp = buffer + strlen (buffer);
for (q = gnu_list_name->dir_contents;
q && *q;
q += strlen (q) + 1)
{
if (*q == 'Y')
{
strcpy (bufp, q + 1);
dump_file (buffer, (dev_t) -1, 1);
}
}
}
free (buffer);
}
else
{
while (p = name_next (1), p)
dump_file (p, (dev_t) -1, 1);
}
write_eot ();
close_archive ();
if (listed_incremental_option)
write_dir_file ();
}
void
dump_file (char *p, dev_t parent_device, int top_level)
{
union block *header;
char type;
union block *exhdr;
char save_typeflag;
struct utimbuf restore_times;
off_t restore_size;
if (interactive_option && !confirm ("add", p))
return;
if (dereference_option != 0
#if STX_HIDDEN && !_LARGE_FILES
? statx (p, ¤t_stat, STATSIZE, STX_HIDDEN)
: statx (p, ¤t_stat, STATSIZE, STX_HIDDEN | STX_LINK)
#else
? stat (p, ¤t_stat) : lstat (p, ¤t_stat)
#endif
)
{
WARN ((0, errno, _("Cannot add file %s"), p));
if (!ignore_failed_read_option)
exit_status = TAREXIT_FAILURE;
return;
}
restore_times.actime = current_stat.st_atime;
restore_times.modtime = current_stat.st_mtime;
restore_size = current_stat.st_size;
#ifdef S_ISHIDDEN
if (S_ISHIDDEN (current_stat.st_mode))
{
char *new = (char *) alloca (strlen (p) + 2);
if (new)
{
strcpy (new, p);
strcat (new, "@");
p = new;
}
}
#endif
if (!incremental_option && !S_ISDIR (current_stat.st_mode)
&& current_stat.st_mtime < newer_mtime_option
&& (!after_date_option || current_stat.st_ctime < newer_ctime_option))
{
if (parent_device == (dev_t) -1)
WARN ((0, 0, _("%s: is unchanged; not dumped"), p));
return;
}
#if !MSDOS
if (ar_dev && current_stat.st_dev == ar_dev && current_stat.st_ino == ar_ino)
{
WARN ((0, 0, _("%s is the archive; not dumped"), p));
return;
}
#endif
if (current_stat.st_nlink > 1
&& (S_ISREG (current_stat.st_mode)
#ifdef S_ISCTG
|| S_ISCTG (current_stat.st_mode)
#endif
#ifdef S_ISCHR
|| S_ISCHR (current_stat.st_mode)
#endif
#ifdef S_ISBLK
|| S_ISBLK (current_stat.st_mode)
#endif
#ifdef S_ISFIFO
|| S_ISFIFO (current_stat.st_mode)
#endif
))
{
struct link *lp;
for (lp = linklist; lp; lp = lp->next)
if (lp->ino == current_stat.st_ino && lp->dev == current_stat.st_dev)
{
char *link_name = lp->name;
while (!absolute_names_option && *link_name == '/')
{
static int warned_once = 0;
if (!warned_once)
{
warned_once = 1;
WARN ((0, 0, _("\
Removing leading `/' from absolute links")));
}
link_name++;
}
if (strlen (link_name) >= NAME_FIELD_SIZE)
write_long (link_name, GNUTYPE_LONGLINK);
assign_string (¤t_link_name, link_name);
current_stat.st_size = 0;
header = start_header (p, ¤t_stat);
if (header == NULL)
{
exit_status = TAREXIT_FAILURE;
return;
}
strncpy (header->header.linkname,
link_name, NAME_FIELD_SIZE);
header->header.linkname[NAME_FIELD_SIZE - 1] = 0;
header->header.typeflag = LNKTYPE;
finish_header (header);
if (remove_files_option)
if (unlink (p) == -1)
ERROR ((0, errno, _("Cannot remove %s"), p));
return;
}
lp = (struct link *)
xmalloc ((size_t) (sizeof (struct link) + strlen (p)));
lp->ino = current_stat.st_ino;
lp->dev = current_stat.st_dev;
strcpy (lp->name, p);
lp->next = linklist;
linklist = lp;
}
if (S_ISREG (current_stat.st_mode)
#ifdef S_ISCTG
|| S_ISCTG (current_stat.st_mode)
#endif
)
{
int f;
size_t bufsize;
ssize_t count;
off_t sizeleft;
union block *start;
int header_moved;
char isextended = 0;
int upperbound;
#if 0
static int cried_once = 0;
#endif
header_moved = 0;
if (sparse_option)
{
if (ST_NBLOCKS (current_stat)
< (current_stat.st_size / ST_NBLOCKSIZE
+ (current_stat.st_size % ST_NBLOCKSIZE != 0)))
{
off_t filesize = current_stat.st_size;
int counter;
header = start_header (p, ¤t_stat);
if (header == NULL)
{
exit_status = TAREXIT_FAILURE;
return;
}
header->header.typeflag = GNUTYPE_SPARSE;
header_moved = 1;
upperbound = deal_with_sparse (p, header);
if (upperbound > SPARSES_IN_OLDGNU_HEADER - 1)
header->oldgnu_header.isextended = 1;
OFF_TO_OCT (current_stat.st_size,
header->oldgnu_header.realsize);
find_new_file_size (&filesize, upperbound);
current_stat.st_size = filesize;
OFF_TO_OCT (filesize, header->header.size);
for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
{
if (!sparsearray[counter].numbytes)
break;
OFF_TO_OCT (sparsearray[counter].offset,
header->oldgnu_header.sp[counter].offset);
SIZE_TO_OCT (sparsearray[counter].numbytes,
header->oldgnu_header.sp[counter].numbytes);
}
}
}
else
upperbound = SPARSES_IN_OLDGNU_HEADER - 1;
sizeleft = current_stat.st_size;
if (dev_null_output
|| (sizeleft == 0
&& MODE_R == (MODE_R & current_stat.st_mode)))
f = -1;
else
{
f = open (p, O_RDONLY | O_BINARY);
if (f < 0)
{
WARN ((0, errno, _("Cannot add file %s"), p));
if (!ignore_failed_read_option)
exit_status = TAREXIT_FAILURE;
return;
}
}
if (!header_moved)
{
header = start_header (p, ¤t_stat);
if (header == NULL)
{
if (f >= 0)
close (f);
exit_status = TAREXIT_FAILURE;
return;
}
}
#ifdef S_ISCTG
if (archive_format != V7_FORMAT && S_ISCTG (current_stat.st_mode))
header->header.typeflag = CONTTYPE;
#endif
isextended = header->oldgnu_header.isextended;
save_typeflag = header->header.typeflag;
finish_header (header);
if (isextended)
{
#if 0
int sum = 0;
#endif
int counter;
#if 0
union block *exhdr;
int arraybound = SPARSES_IN_SPARSE_HEADER;
#endif
int index_offset = SPARSES_IN_OLDGNU_HEADER;
extend:
exhdr = find_next_block ();
if (exhdr == NULL)
{
exit_status = TAREXIT_FAILURE;
return;
}
memset (exhdr->buffer, 0, BLOCKSIZE);
for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
{
if (counter + index_offset > upperbound)
break;
SIZE_TO_OCT (sparsearray[counter + index_offset].numbytes,
exhdr->sparse_header.sp[counter].numbytes);
OFF_TO_OCT (sparsearray[counter + index_offset].offset,
exhdr->sparse_header.sp[counter].offset);
}
set_next_block_after (exhdr);
#if 0
sum += counter;
if (sum < upperbound)
goto extend;
#endif
if (index_offset + counter <= upperbound)
{
index_offset += counter;
exhdr->sparse_header.isextended = 1;
goto extend;
}
}
if (save_typeflag == GNUTYPE_SPARSE)
{
if (f < 0
|| finish_sparse_file (f, &sizeleft, current_stat.st_size, p))
goto padit;
}
else
while (sizeleft > 0)
{
if (multi_volume_option)
{
assign_string (&save_name, p);
save_sizeleft = sizeleft;
save_totsize = current_stat.st_size;
}
start = find_next_block ();
bufsize = available_space_after (start);
if (sizeleft < bufsize)
{
bufsize = sizeleft;
count = bufsize % BLOCKSIZE;
if (count)
memset (start->buffer + sizeleft, 0,
(size_t) (BLOCKSIZE - count));
}
if (f < 0)
count = bufsize;
else
count = safe_read (f, start->buffer, bufsize);
if (count < 0)
{
char buf[UINTMAX_STRSIZE_BOUND];
ERROR ((0, errno, _("\
Read error at byte %s, reading %lu bytes, in file %s"),
STRINGIFY_BIGINT (current_stat.st_size - sizeleft,
buf),
(unsigned long) bufsize, p));
goto padit;
}
sizeleft -= count;
set_next_block_after (start + (count - 1) / BLOCKSIZE);
if (count == bufsize)
continue;
else
{
char buf[UINTMAX_STRSIZE_BOUND];
ERROR ((0, 0,
_("File %s shrunk by %s bytes, padding with zeros"),
p, STRINGIFY_BIGINT (sizeleft, buf)));
goto padit;
}
}
if (multi_volume_option)
assign_string (&save_name, NULL);
if (f >= 0)
{
struct stat final_stat;
if (fstat (f, &final_stat) != 0)
ERROR ((0, errno, "%s: fstat", p));
else if (final_stat.st_mtime != restore_times.modtime
|| final_stat.st_size != restore_size)
ERROR ((0, errno, _("%s: file changed as we read it"), p));
if (close (f) != 0)
ERROR ((0, errno, _("%s: close"), p));
if (atime_preserve_option)
utime (p, &restore_times);
}
if (remove_files_option)
{
if (unlink (p) == -1)
ERROR ((0, errno, _("Cannot remove %s"), p));
}
return;
padit:
while (sizeleft > 0)
{
save_sizeleft = sizeleft;
start = find_next_block ();
memset (start->buffer, 0, BLOCKSIZE);
set_next_block_after (start);
sizeleft -= BLOCKSIZE;
}
if (multi_volume_option)
assign_string (&save_name, NULL);
if (f >= 0)
{
close (f);
if (atime_preserve_option)
utime (p, &restore_times);
}
return;
}
#ifdef S_ISLNK
else if (S_ISLNK (current_stat.st_mode))
{
int size;
char *buffer = (char *) alloca (PATH_MAX + 1);
size = readlink (p, buffer, PATH_MAX + 1);
if (size < 0)
{
WARN ((0, errno, _("Cannot add file %s"), p));
if (!ignore_failed_read_option)
exit_status = TAREXIT_FAILURE;
return;
}
buffer[size] = '\0';
if (size >= NAME_FIELD_SIZE)
write_long (buffer, GNUTYPE_LONGLINK);
assign_string (¤t_link_name, buffer);
current_stat.st_size = 0;
header = start_header (p, ¤t_stat);
if (header == NULL)
{
exit_status = TAREXIT_FAILURE;
return;
}
strncpy (header->header.linkname, buffer, NAME_FIELD_SIZE);
header->header.linkname[NAME_FIELD_SIZE - 1] = '\0';
header->header.typeflag = SYMTYPE;
finish_header (header);
if (remove_files_option)
{
if (unlink (p) == -1)
ERROR ((0, errno, _("Cannot remove %s"), p));
}
return;
}
#endif
else if (S_ISDIR (current_stat.st_mode))
{
DIR *directory;
struct dirent *entry;
char *namebuf;
size_t buflen;
size_t len;
dev_t our_device = current_stat.st_dev;
if (access (p, R_OK) == -1 && geteuid () != 0)
{
WARN ((0, errno, _("Cannot add directory %s"), p));
if (!ignore_failed_read_option)
exit_status = TAREXIT_FAILURE;
return;
}
len = strlen (p);
buflen = len + NAME_FIELD_SIZE;
namebuf = xmalloc (buflen + 1);
strncpy (namebuf, p, buflen);
while (len >= 1 && namebuf[len - 1] == '/')
len--;
namebuf[len++] = '/';
namebuf[len] = '\0';
if (1)
{
current_stat.st_size = 0;
header = start_header (namebuf, ¤t_stat);
if (header == NULL)
{
exit_status = TAREXIT_FAILURE;
return;
}
if (incremental_option)
header->header.typeflag = GNUTYPE_DUMPDIR;
else
header->header.typeflag = DIRTYPE;
if (!incremental_option)
finish_header (header);
}
if (incremental_option && gnu_list_name->dir_contents)
{
off_t sizeleft;
off_t totsize;
size_t bufsize;
union block *start;
ssize_t count;
const char *buffer, *p_buffer;
buffer = gnu_list_name->dir_contents;
totsize = 0;
for (p_buffer = buffer; p_buffer && *p_buffer;)
{
size_t tmp;
tmp = strlen (p_buffer) + 1;
totsize += tmp;
p_buffer += tmp;
}
totsize++;
OFF_TO_OCT (totsize, header->header.size);
finish_header (header);
p_buffer = buffer;
sizeleft = totsize;
while (sizeleft > 0)
{
if (multi_volume_option)
{
assign_string (&save_name, p);
save_sizeleft = sizeleft;
save_totsize = totsize;
}
start = find_next_block ();
bufsize = available_space_after (start);
if (sizeleft < bufsize)
{
bufsize = sizeleft;
count = bufsize % BLOCKSIZE;
if (count)
memset (start->buffer + sizeleft, 0,
(size_t) (BLOCKSIZE - count));
}
memcpy (start->buffer, p_buffer, bufsize);
sizeleft -= bufsize;
p_buffer += bufsize;
set_next_block_after (start + (bufsize - 1) / BLOCKSIZE);
}
if (multi_volume_option)
assign_string (&save_name, NULL);
if (atime_preserve_option)
utime (p, &restore_times);
return;
}
if (no_recurse_option)
return;
if (one_file_system_option && !top_level
&& parent_device != current_stat.st_dev)
{
if (verbose_option)
WARN ((0, 0, _("%s: On a different filesystem; not dumped"), p));
return;
}
errno = 0;
directory = opendir (p);
if (!directory)
{
ERROR ((0, errno, _("Cannot open directory %s"), p));
return;
}
if (len == 2 && namebuf[0] == '.' && namebuf[1] == '/')
len = 0;
while (entry = readdir (directory), entry)
{
if (is_dot_or_dotdot (entry->d_name)
|| excluded_filename (excluded, entry->d_name))
continue;
if ((int) NAMLEN (entry) + len >= buflen)
{
buflen = len + NAMLEN (entry);
namebuf = (char *) xrealloc (namebuf, buflen + 1);
#if 0
namebuf[len] = '\0';
ERROR ((0, 0, _("File name %s%s too long"),
namebuf, entry->d_name));
continue;
#endif
}
strcpy (namebuf + len, entry->d_name);
dump_file (namebuf, our_device, 0);
}
closedir (directory);
free (namebuf);
if (atime_preserve_option)
utime (p, &restore_times);
return;
}
#ifdef S_ISCHR
else if (S_ISCHR (current_stat.st_mode))
type = CHRTYPE;
#endif
#ifdef S_ISBLK
else if (S_ISBLK (current_stat.st_mode))
type = BLKTYPE;
#endif
#if (_ISP__M68K == 0) && (_ISP__A88K == 0) && defined(S_ISFIFO)
else if (S_ISFIFO (current_stat.st_mode))
type = FIFOTYPE;
#endif
#ifdef S_ISSOCK
else if (S_ISSOCK (current_stat.st_mode))
type = FIFOTYPE;
#endif
else
goto unknown;
if (archive_format == V7_FORMAT)
goto unknown;
current_stat.st_size = 0;
header = start_header (p, ¤t_stat);
if (header == NULL)
{
exit_status = TAREXIT_FAILURE;
return;
}
header->header.typeflag = type;
#if defined(S_IFBLK) || defined(S_IFCHR)
if (type != FIFOTYPE)
{
MAJOR_TO_OCT (major (current_stat.st_rdev), header->header.devmajor);
MINOR_TO_OCT (minor (current_stat.st_rdev), header->header.devminor);
}
#endif
finish_header (header);
if (remove_files_option)
{
if (unlink (p) == -1)
ERROR ((0, errno, _("Cannot remove %s"), p));
}
return;
unknown:
ERROR ((0, 0, _("%s: Unknown file type; file ignored"), p));
}