/* Miscellaneous functions, not really specific to GNU tar.
Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001,
- 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
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, or (at your option) any later
+ Free Software Foundation; either version 3, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
#include <system.h>
-#include <sys/time.h>
-#include <sys/resource.h>
#include <rmt.h>
#include "common.h"
#include <quotearg.h>
#include <save-cwd.h>
#include <xgetcwd.h>
#include <unlinkdir.h>
-#include <utimens.h>
-#if HAVE_STROPTS_H
-# include <stropts.h>
-#endif
-#if HAVE_SYS_FILIO_H
-# include <sys/filio.h>
+#ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
+# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
#endif
\f
*string = value ? xstrdup (value) : 0;
}
+#if 0
+/* This function is currently unused; perhaps it should be removed? */
+
/* Allocate a copy of the string quoted as in C, and returns that. If
the string does not have to be quoted, it returns a null pointer.
The allocated copy should normally be freed with free() after the
when reading directory files. This means that we can't use
quotearg, as quotearg is locale-dependent and is meant for human
consumption. */
-char *
+static char *
quote_copy_string (const char *string)
{
const char *source = string;
}
return 0;
}
+#endif
/* Takes a quoted C string (like those produced by quote_copy_string)
and turns it back into the un-quoted original. This is done in
*destination = '\0';
return result;
}
+
+/* Zap trailing slashes. */
+char *
+zap_slashes (char *name)
+{
+ char *q;
+
+ if (!name || *name == 0)
+ return name;
+ q = name + strlen (name) - 1;
+ while (q > name && ISSLASH (*q))
+ *q-- = '\0';
+ return name;
+}
+
+/* Normalize FILE_NAME by removing redundant slashes and "."
+ components, including redundant trailing slashes. Leave ".."
+ alone, as it may be significant in the presence of symlinks and on
+ platforms where "/.." != "/". Destructive version: modifies its
+ argument. */
+static void
+normalize_filename_x (char *file_name)
+{
+ char *name = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
+ char *p;
+ char const *q;
+ char c;
+
+ /* Don't squeeze leading "//" to "/", on hosts where they're distinct. */
+ name += (DOUBLE_SLASH_IS_DISTINCT_ROOT
+ && ISSLASH (*name) && ISSLASH (name[1]) && ! ISSLASH (name[2]));
+
+ /* Omit redundant leading "." components. */
+ for (q = p = name; (*p = *q) == '.' && ISSLASH (q[1]); p += !*q)
+ for (q += 2; ISSLASH (*q); q++)
+ continue;
+
+ /* Copy components from Q to P, omitting redundant slashes and
+ internal "." components. */
+ while ((*p++ = c = *q++) != '\0')
+ if (ISSLASH (c))
+ while (ISSLASH (q[*q == '.']))
+ q += (*q == '.') + 1;
+
+ /* Omit redundant trailing "." component and slash. */
+ if (2 < p - name)
+ {
+ p -= p[-2] == '.' && ISSLASH (p[-3]);
+ p -= 2 < p - name && ISSLASH (p[-2]);
+ p[-1] = '\0';
+ }
+}
+
+/* Normalize NAME by removing redundant slashes and "." components,
+ including redundant trailing slashes. Return a normalized
+ newly-allocated copy. */
+
+char *
+normalize_filename (const char *name)
+{
+ char *copy = NULL;
+
+ if (IS_RELATIVE_FILE_NAME (name))
+ {
+ /* Set COPY to the absolute file name if possible.
+
+ FIXME: There should be no need to get the absolute file name.
+ getcwd is slow, it might fail, and it does not necessarily
+ return a canonical name even when it succeeds. Perhaps we
+ can use dev+ino pairs instead of names? */
+ copy = xgetcwd ();
+ if (copy)
+ {
+ size_t copylen = strlen (copy);
+ bool need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT
+ && copylen == 2 && ISSLASH (copy[1]));
+ copy = xrealloc (copy, copylen + need_separator + strlen (name) + 1);
+ copy[copylen] = DIRECTORY_SEPARATOR;
+ strcpy (copy + copylen + need_separator, name);
+ }
+ else
+ WARN ((0, errno, _("Cannot get working directory")));
+ }
+
+ if (! copy)
+ copy = xstrdup (name);
+ normalize_filename_x (copy);
+ return copy;
+}
+
+\f
+void
+replace_prefix (char **pname, const char *samp, size_t slen,
+ const char *repl, size_t rlen)
+{
+ char *name = *pname;
+ size_t nlen = strlen (name);
+ if (nlen > slen && memcmp (name, samp, slen) == 0 && ISSLASH (name[slen]))
+ {
+ if (rlen > slen)
+ {
+ name = xrealloc (name, nlen - slen + rlen + 1);
+ *pname = name;
+ }
+ memmove (name + rlen, name + slen, nlen - slen + 1);
+ memcpy (name, repl, rlen);
+ }
+}
+
\f
/* Handling numbers. */
char *np;
bool negative = s < 0;
+ /* ignore invalid values of ns */
+ if (BILLION <= ns || ns < 0)
+ ns = 0;
+
if (negative && ns != 0)
{
s++;
static char *after_backup_name;
/* Return 1 if FILE_NAME is obviously "." or "/". */
-static bool
+bool
must_be_dot_or_slash (char const *file_name)
{
file_name += FILE_SYSTEM_PREFIX_LEN (file_name);
{
struct stat file_stat;
+ assign_string (&before_backup_name, file_name);
+
+ /* A run situation may exist between Emacs or other GNU programs trying to
+ make a backup for the same file simultaneously. If theoretically
+ possible, real problems are unlikely. Doing any better would require a
+ convention, GNU-wide, for all programs doing backups. */
+
+ assign_string (&after_backup_name, 0);
+
/* Check if we really need to backup the file. */
if (this_is_the_archive && _remdev (file_name))
&& (S_ISBLK (file_stat.st_mode) || S_ISCHR (file_stat.st_mode)))
return true;
- assign_string (&before_backup_name, file_name);
-
- /* A run situation may exist between Emacs or other GNU programs trying to
- make a backup for the same file simultaneously. If theoretically
- possible, real problems are unlikely. Doing any better would require a
- convention, GNU-wide, for all programs doing backups. */
-
- assign_string (&after_backup_name, 0);
after_backup_name = find_backup_file_name (file_name, backup_type);
if (! after_backup_name)
xalloc_die ();
return deref ? stat (name, buf) : lstat (name, buf);
}
-/* Set FD's (i.e., FILE's) access time to TIMESPEC[0]. If that's not
- possible to do by itself, set its access and data modification
- times to TIMESPEC[0] and TIMESPEC[1], respectively. */
+/* Set FD's (i.e., assuming the working directory is PARENTFD, FILE's)
+ access time to ATIME. ATFLAG controls symbolic-link following, in
+ the style of openat. */
int
-set_file_atime (int fd, char const *file, struct timespec const timespec[2])
+set_file_atime (int fd, int parentfd, char const *file, struct timespec atime,
+ int atflag)
{
-#ifdef _FIOSATIME
- if (0 <= fd)
- {
- struct timeval timeval;
- timeval.tv_sec = timespec[0].tv_sec;
- timeval.tv_usec = timespec[0].tv_nsec / 1000;
- if (ioctl (fd, _FIOSATIME, &timeval) == 0)
- return 0;
- }
-#endif
-
- return futimens (fd, file, timespec);
+ struct timespec ts[2];
+ ts[0] = atime;
+ ts[1].tv_nsec = UTIME_OMIT;
+ return fdutimensat (fd, parentfd, file, ts, atflag);
}
/* A description of a working directory. */
struct wd
{
+ /* The directory's name. */
char const *name;
- int saved;
+
+ /* A negative value if no attempt has been made to save the
+ directory, 0 if it was saved successfully, and a positive errno
+ value if it was not saved successfully. */
+ int err;
+
+ /* The saved version of the directory, if ERR == 0. */
struct saved_cwd saved_cwd;
};
static struct wd *wd;
/* The number of working directories in the vector. */
-static size_t wds;
+static size_t wd_count;
/* The allocated size of the vector. */
static size_t wd_alloc;
+int
+chdir_count ()
+{
+ if (wd_count == 0)
+ return wd_count;
+ return wd_count - 1;
+}
+
/* DIR is the operand of a -C option; add it to vector of chdir targets,
and return the index of its location. */
int
chdir_arg (char const *dir)
{
- if (wds == wd_alloc)
+ if (wd_count == wd_alloc)
{
if (wd_alloc == 0)
{
else
wd = x2nrealloc (wd, &wd_alloc, sizeof *wd);
- if (! wds)
+ if (! wd_count)
{
- wd[wds].name = ".";
- wd[wds].saved = 0;
- wds++;
+ wd[wd_count].name = ".";
+ wd[wd_count].err = -1;
+ wd_count++;
}
}
for (dir += 2; ISSLASH (*dir); dir++)
continue;
if (! dir[dir[0] == '.'])
- return wds - 1;
+ return wd_count - 1;
}
- wd[wds].name = dir;
- wd[wds].saved = 0;
- return wds++;
-}
-
-/* Return maximum number of open files */
-int
-get_max_open_files ()
-{
-#if defined _SC_OPEN_MAX
- return sysconf (_SC_OPEN_MAX);
-#elif defined RLIMIT_NOFILE
- struct rlimit rlim;
-
- if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
- return rlim.rlim_max;
-#elif defined HAVE_GETDTABLESIZE
- return getdtablesize ();
-#endif
- return -1;
+ wd[wd_count].name = dir;
+ wd[wd_count].err = -1;
+ return wd_count++;
}
-/* Close all descriptors, except the first three */
-void
-closeopen ()
-{
- int i;
+/* Index of current directory. */
+int chdir_current;
- for (i = get_max_open_files () - 1; i > 2; i--)
- close (i);
-}
-
/* Change to directory I. If I is 0, change to the initial working
directory; otherwise, I must be a value returned by chdir_arg. */
void
chdir_do (int i)
{
- static int previous;
- static int saved_count;
-
- if (previous != i)
+ if (chdir_current != i)
{
- struct wd *prev = &wd[previous];
+ struct wd *prev = &wd[chdir_current];
struct wd *curr = &wd[i];
- if (! prev->saved)
+ if (prev->err < 0)
{
- prev->saved = 1;
- saved_count++;
- /* Make sure we still have at least one descriptor available */
- if (saved_count >= get_max_open_files () - 4)
+ prev->err = 0;
+ if (save_cwd (&prev->saved_cwd) != 0)
+ prev->err = errno;
+ else if (0 <= prev->saved_cwd.desc)
{
- /* Force restore_cwd to use chdir_long */
- prev->saved_cwd.desc = -1;
- prev->saved_cwd.name = xgetcwd ();
+ /* Make sure we still have at least one descriptor available. */
+ int fd1 = prev->saved_cwd.desc;
+ int fd2 = dup (fd1);
+ if (0 <= fd2)
+ close (fd2);
+ else if (errno == EMFILE)
+ {
+ /* Force restore_cwd to use chdir_long. */
+ close (fd1);
+ prev->saved_cwd.desc = -1;
+ prev->saved_cwd.name = xgetcwd ();
+ if (! prev->saved_cwd.name)
+ prev->err = errno;
+ }
+ else
+ prev->err = errno;
}
- else if (save_cwd (&prev->saved_cwd) != 0)
- FATAL_ERROR ((0, 0, _("Cannot save working directory")));
}
- if (curr->saved)
+ if (0 <= curr->err)
{
- if (restore_cwd (&curr->saved_cwd))
- FATAL_ERROR ((0, 0, _("Cannot change working directory")));
+ int err = curr->err;
+ if (err == 0 && restore_cwd (&curr->saved_cwd) != 0)
+ err = errno;
+ if (err)
+ FATAL_ERROR ((0, err, _("Cannot restore working directory")));
}
else
{
chdir_fatal (curr->name);
}
- previous = i;
+ chdir_current = i;
}
}
\f
stat_error (name);
}
+void
+file_removed_diag (const char *name, bool top_level,
+ void (*diagfn) (char const *name))
+{
+ if (!top_level && errno == ENOENT)
+ {
+ WARNOPT (WARN_FILE_REMOVED,
+ (0, 0, _("%s: File removed before we read it"),
+ quotearg_colon (name)));
+ set_exit_status (TAREXIT_DIFFERS);
+ }
+ else
+ diagfn (name);
+}
+
+void
+dir_removed_diag (const char *name, bool top_level,
+ void (*diagfn) (char const *name))
+{
+ if (!top_level && errno == ENOENT)
+ {
+ WARNOPT (WARN_FILE_REMOVED,
+ (0, 0, _("%s: Directory removed before we read it"),
+ quotearg_colon (name)));
+ set_exit_status (TAREXIT_DIFFERS);
+ }
+ else
+ diagfn (name);
+}
+
void
write_fatal_details (char const *name, ssize_t status, size_t size)
{
*ptr = xmalloc (size1);
return ptr_align (*ptr, alignment);
}
+
+\f
+
+struct namebuf
+{
+ char *buffer; /* directory, `/', and directory member */
+ size_t buffer_size; /* allocated size of name_buffer */
+ size_t dir_length; /* length of directory part in buffer */
+};
+
+namebuf_t
+namebuf_create (const char *dir)
+{
+ namebuf_t buf = xmalloc (sizeof (*buf));
+ buf->buffer_size = strlen (dir) + 2;
+ buf->buffer = xmalloc (buf->buffer_size);
+ strcpy (buf->buffer, dir);
+ buf->dir_length = strlen (buf->buffer);
+ if (!ISSLASH (buf->buffer[buf->dir_length - 1]))
+ buf->buffer[buf->dir_length++] = DIRECTORY_SEPARATOR;
+ return buf;
+}
+
+void
+namebuf_free (namebuf_t buf)
+{
+ free (buf->buffer);
+ free (buf);
+}
+
+char *
+namebuf_name (namebuf_t buf, const char *name)
+{
+ size_t len = strlen (name);
+ while (buf->dir_length + len + 1 >= buf->buffer_size)
+ buf->buffer = x2realloc (buf->buffer, &buf->buffer_size);
+ strcpy (buf->buffer + buf->dir_length, name);
+ return buf->buffer;
+}