/* Miscellaneous functions, not really specific to GNU tar.
Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001,
- 2003 Free Software Foundation, Inc.
+ 2003, 2004, 2005 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
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-#include "system.h"
-#include "rmt.h"
+#include <system.h>
+#include <rmt.h>
#include "common.h"
#include <quotearg.h>
#include <save-cwd.h>
+#include <unlinkdir.h>
+#include <utimens.h>
+
+#if HAVE_STROPTS_H
+# include <stropts.h>
+#endif
+#if HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
-static void call_arg_fatal (char const *, char const *)
- __attribute__ ((noreturn));
\f
/* Handling strings. */
source++;
break;
- case 'n':
- *destination++ = '\n';
+ case 'a':
+ *destination++ = '\a';
source++;
break;
- case 't':
- *destination++ = '\t';
+ case 'b':
+ *destination++ = '\b';
source++;
break;
source++;
break;
- case 'b':
- *destination++ = '\b';
+ case 'n':
+ *destination++ = '\n';
source++;
break;
source++;
break;
+ case 't':
+ *destination++ = '\t';
+ source++;
+ break;
+
+ case 'v':
+ *destination++ = '\v';
+ source++;
+ break;
+
case '?':
*destination++ = 0177;
source++;
return result;
}
\f
+/* Handling numbers. */
+
+/* Output fraction and trailing digits appropriate for a nanoseconds
+ count equal to NS, but don't output unnecessary '.' or trailing
+ zeros. */
+
+void
+code_ns_fraction (int ns, char *p)
+{
+ if (ns == 0)
+ *p = '\0';
+ else
+ {
+ int i = 9;
+ *p++ = '.';
+
+ while (ns % 10 == 0)
+ {
+ ns /= 10;
+ i--;
+ }
+
+ p[i] = '\0';
+
+ for (;;)
+ {
+ p[--i] = '0' + ns % 10;
+ if (i == 0)
+ break;
+ ns /= 10;
+ }
+ }
+}
+
+char const *
+code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND])
+{
+ time_t s = t.tv_sec;
+ int ns = t.tv_nsec;
+ char *np;
+ bool negative = s < 0;
+
+ if (negative && ns != 0)
+ {
+ s++;
+ ns = BILLION - ns;
+ }
+
+ np = umaxtostr (negative ? - (uintmax_t) s : (uintmax_t) s, sbuf + 1);
+ if (negative)
+ *--np = '-';
+ code_ns_fraction (ns, sbuf + UINTMAX_STRSIZE_BOUND);
+ return np;
+}
+\f
/* File handling. */
/* Saved names in case backup needs to be undone. */
static char *before_backup_name;
static char *after_backup_name;
-/* Return 1 if PATH is obviously "." or "/". */
+/* Return 1 if FILE_NAME is obviously "." or "/". */
static bool
-must_be_dot_or_slash (char const *path)
+must_be_dot_or_slash (char const *file_name)
{
- path += FILESYSTEM_PREFIX_LEN (path);
+ file_name += FILE_SYSTEM_PREFIX_LEN (file_name);
- if (ISSLASH (path[0]))
+ if (ISSLASH (file_name[0]))
{
for (;;)
- if (ISSLASH (path[1]))
- path++;
- else if (path[1] == '.' && ISSLASH (path[2 + (path[2] == '.')]))
- path += 2 + (path[2] == '.');
+ if (ISSLASH (file_name[1]))
+ file_name++;
+ else if (file_name[1] == '.'
+ && ISSLASH (file_name[2 + (file_name[2] == '.')]))
+ file_name += 2 + (file_name[2] == '.');
else
- return ! path[1];
+ return ! file_name[1];
}
else
{
- while (path[0] == '.' && ISSLASH (path[1]))
+ while (file_name[0] == '.' && ISSLASH (file_name[1]))
{
- path += 2;
- while (ISSLASH (*path))
- path++;
+ file_name += 2;
+ while (ISSLASH (*file_name))
+ file_name++;
}
- return ! path[0] || (path[0] == '.' && ! path[1]);
+ return ! file_name[0] || (file_name[0] == '.' && ! file_name[1]);
}
}
Report an error with errno set to zero for obvious cases of this;
otherwise call rmdir. */
static int
-safer_rmdir (const char *path)
+safer_rmdir (const char *file_name)
{
- if (must_be_dot_or_slash (path))
+ if (must_be_dot_or_slash (file_name))
{
errno = 0;
return -1;
}
- return rmdir (path);
+ return rmdir (file_name);
}
-/* Remove PATH, returning 1 on success. If PATH is a directory, then
- if OPTION is RECURSIVE_REMOVE_OPTION is set remove PATH
- recursively; otherwise, remove it only if it is empty. If PATH is
+/* Remove FILE_NAME, returning 1 on success. If FILE_NAME is a directory,
+ then if OPTION is RECURSIVE_REMOVE_OPTION is set remove FILE_NAME
+ recursively; otherwise, remove it only if it is empty. If FILE_NAME is
a directory that cannot be removed (e.g., because it is nonempty)
and if OPTION is WANT_DIRECTORY_REMOVE_OPTION, then return -1.
- Return 0 on error, with errno set; if PATH is obviously the working
+ Return 0 on error, with errno set; if FILE_NAME is obviously the working
directory return zero with errno set to zero. */
int
-remove_any_file (const char *path, enum remove_option option)
+remove_any_file (const char *file_name, enum remove_option option)
{
- /* Try unlink first if we are not root, as this saves us a system
- call in the common case where we're removing a non-directory. */
- if (! we_are_root)
+ /* Try unlink first if we cannot unlink directories, as this saves
+ us a system call in the common case where we're removing a
+ non-directory. */
+ bool try_unlink_first = cannot_unlink_dir ();
+
+ if (try_unlink_first)
{
- if (unlink (path) == 0)
+ if (unlink (file_name) == 0)
return 1;
/* POSIX 1003.1-2001 requires EPERM when attempting to unlink a
return 0;
}
- if (safer_rmdir (path) == 0)
+ if (safer_rmdir (file_name) == 0)
return 1;
switch (errno)
{
case ENOTDIR:
- return we_are_root && unlink (path) == 0;
+ return !try_unlink_first && unlink (file_name) == 0;
case 0:
case EEXIST:
case RECURSIVE_REMOVE_OPTION:
{
- char *directory = savedir (path);
+ char *directory = savedir (file_name);
char const *entry;
size_t entrylen;
(entrylen = strlen (entry)) != 0;
entry += entrylen + 1)
{
- char *path_buffer = new_name (path, entry);
- int r = remove_any_file (path_buffer, 1);
+ char *file_name_buffer = new_name (file_name, entry);
+ int r = remove_any_file (file_name_buffer,
+ RECURSIVE_REMOVE_OPTION);
int e = errno;
- free (path_buffer);
+ free (file_name_buffer);
if (! r)
{
}
free (directory);
- return safer_rmdir (path) == 0;
+ return safer_rmdir (file_name) == 0;
}
}
break;
return 0;
}
-/* Check if PATH already exists and make a backup of it right now.
- Return success (nonzero) only if the backup in either unneeded, or
+/* Check if FILE_NAME already exists and make a backup of it right now.
+ Return success (nonzero) only if the backup is either unneeded, or
successful. For now, directories are considered to never need
- backup. If ARCHIVE is nonzero, this is the archive and so, we do
- not have to backup block or character devices, nor remote entities. */
-int
-maybe_backup_file (const char *path, int archive)
+ backup. If THIS_IS_THE_ARCHIVE is nonzero, this is the archive and
+ so, we do not have to backup block or character devices, nor remote
+ entities. */
+bool
+maybe_backup_file (const char *file_name, int this_is_the_archive)
{
struct stat file_stat;
/* Check if we really need to backup the file. */
- if (archive && _remdev (path))
- return 1;
+ if (this_is_the_archive && _remdev (file_name))
+ return true;
- if (stat (path, &file_stat))
+ if (stat (file_name, &file_stat))
{
if (errno == ENOENT)
- return 1;
+ return true;
- stat_error (path);
- return 0;
+ stat_error (file_name);
+ return false;
}
if (S_ISDIR (file_stat.st_mode))
- return 1;
+ return true;
- if (archive && (S_ISBLK (file_stat.st_mode) || S_ISCHR (file_stat.st_mode)))
- return 1;
+ if (this_is_the_archive
+ && (S_ISBLK (file_stat.st_mode) || S_ISCHR (file_stat.st_mode)))
+ return true;
- assign_string (&before_backup_name, path);
+ 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
convention, GNU-wide, for all programs doing backups. */
assign_string (&after_backup_name, 0);
- after_backup_name = find_backup_file_name (path, backup_type);
+ after_backup_name = find_backup_file_name (file_name, backup_type);
if (! after_backup_name)
xalloc_die ();
fprintf (stdlis, _("Renaming %s to %s\n"),
quote_n (0, before_backup_name),
quote_n (1, after_backup_name));
- return 1;
+ return true;
}
else
{
quotearg_colon (before_backup_name),
quote_n (1, after_backup_name)));
assign_string (&after_backup_name, 0);
- return 0;
+ return false;
}
}
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. */
+int
+set_file_atime (int fd, char const *file, struct timespec const timespec[2])
+{
+#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);
+}
+
/* A description of a working directory. */
struct wd
{
}
}
\f
-/* Decode MODE from its binary form in a stat structure, and encode it
- into a 9-byte string STRING, terminated with a NUL. */
-
-void
-decode_mode (mode_t mode, char *string)
-{
- *string++ = mode & S_IRUSR ? 'r' : '-';
- *string++ = mode & S_IWUSR ? 'w' : '-';
- *string++ = (mode & S_ISUID
- ? (mode & S_IXUSR ? 's' : 'S')
- : (mode & S_IXUSR ? 'x' : '-'));
- *string++ = mode & S_IRGRP ? 'r' : '-';
- *string++ = mode & S_IWGRP ? 'w' : '-';
- *string++ = (mode & S_ISGID
- ? (mode & S_IXGRP ? 's' : 'S')
- : (mode & S_IXGRP ? 'x' : '-'));
- *string++ = mode & S_IROTH ? 'r' : '-';
- *string++ = mode & S_IWOTH ? 'w' : '-';
- *string++ = (mode & S_ISVTX
- ? (mode & S_IXOTH ? 't' : 'T')
- : (mode & S_IXOTH ? 'x' : '-'));
- *string = '\0';
-}
-
-/* Report an error associated with the system call CALL and the
- optional name NAME. */
-static void
-call_arg_error (char const *call, char const *name)
-{
- int e = errno;
- ERROR ((0, e, _("%s: Cannot %s"), quotearg_colon (name), call));
-}
-
-/* Report a fatal error associated with the system call CALL and
- the optional file name NAME. */
-static void
-call_arg_fatal (char const *call, char const *name)
-{
- int e = errno;
- FATAL_ERROR ((0, e, _("%s: Cannot %s"), quotearg_colon (name), call));
-}
-
-/* Report a warning associated with the system call CALL and
- the optional file name NAME. */
-static void
-call_arg_warn (char const *call, char const *name)
-{
- int e = errno;
- WARN ((0, e, _("%s: Warning: Cannot %s"), quotearg_colon (name), call));
-}
-
-void
-chdir_fatal (char const *name)
-{
- call_arg_fatal ("chdir", name);
-}
-
-void
-chmod_error_details (char const *name, mode_t mode)
-{
- int e = errno;
- char buf[10];
- decode_mode (mode, buf);
- ERROR ((0, e, _("%s: Cannot change mode to %s"),
- quotearg_colon (name), buf));
-}
-
-void
-chown_error_details (char const *name, uid_t uid, gid_t gid)
-{
- int e = errno;
- ERROR ((0, e, _("%s: Cannot change ownership to uid %lu, gid %lu"),
- quotearg_colon (name), (unsigned long) uid, (unsigned long) gid));
-}
-
-void
-close_error (char const *name)
-{
- call_arg_error ("close", name);
-}
-
-void
-close_fatal (char const *name)
-{
- call_arg_fatal ("close", name);
-}
-
-void
-close_warn (char const *name)
-{
- call_arg_warn ("close", name);
-}
-
-void
-exec_fatal (char const *name)
-{
- call_arg_fatal ("exec", name);
-}
-
-void
-link_error (char const *target, char const *source)
-{
- int e = errno;
- ERROR ((0, e, _("%s: Cannot hard link to %s"),
- quotearg_colon (source), quote_n (1, target)));
-}
-
-void
-mkdir_error (char const *name)
-{
- call_arg_error ("mkdir", name);
-}
-
-void
-mkfifo_error (char const *name)
-{
- call_arg_error ("mkfifo", name);
-}
-
-void
-mknod_error (char const *name)
-{
- call_arg_error ("mknod", name);
-}
-
-void
-open_error (char const *name)
-{
- call_arg_error ("open", name);
-}
-
-void
-open_fatal (char const *name)
-{
- call_arg_fatal ("open", name);
-}
-
-void
-open_warn (char const *name)
-{
- call_arg_warn ("open", name);
-}
-
-void
-read_error (char const *name)
-{
- call_arg_error ("read", name);
-}
-
-void
-read_error_details (char const *name, off_t offset, size_t size)
-{
- char buf[UINTMAX_STRSIZE_BOUND];
- int e = errno;
- ERROR ((0, e,
- _("%s: Read error at byte %s, reading %lu bytes"),
- quotearg_colon (name), STRINGIFY_BIGINT (offset, buf),
- (unsigned long) size));
-}
-
-void
-read_warn_details (char const *name, off_t offset, size_t size)
-{
- char buf[UINTMAX_STRSIZE_BOUND];
- int e = errno;
- WARN ((0, e,
- _("%s: Warning: Read error at byte %s, reading %lu bytes"),
- quotearg_colon (name), STRINGIFY_BIGINT (offset, buf),
- (unsigned long) size));
-}
-
-void
-read_fatal (char const *name)
-{
- call_arg_fatal ("read", name);
-}
-
-void
-read_fatal_details (char const *name, off_t offset, size_t size)
-{
- char buf[UINTMAX_STRSIZE_BOUND];
- int e = errno;
- FATAL_ERROR ((0, e,
- _("%s: Read error at byte %s, reading %lu bytes"),
- quotearg_colon (name), STRINGIFY_BIGINT (offset, buf),
- (unsigned long) size));
-}
-
-void
-readlink_error (char const *name)
-{
- call_arg_error ("readlink", name);
-}
-
-void
-readlink_warn (char const *name)
-{
- call_arg_warn ("readlink", name);
-}
-
-void
-savedir_error (char const *name)
-{
- call_arg_error ("savedir", name);
-}
-
-void
-savedir_warn (char const *name)
-{
- call_arg_warn ("savedir", name);
-}
-
void
-seek_error (char const *name)
+close_diag (char const *name)
{
- call_arg_error ("seek", name);
-}
-
-void
-seek_error_details (char const *name, off_t offset)
-{
- char buf[UINTMAX_STRSIZE_BOUND];
- int e = errno;
- ERROR ((0, e, _("%s: Cannot seek to %s"),
- quotearg_colon (name),
- STRINGIFY_BIGINT (offset, buf)));
-}
-
-void
-seek_warn (char const *name)
-{
- call_arg_warn ("seek", name);
-}
-
-void
-seek_warn_details (char const *name, off_t offset)
-{
- char buf[UINTMAX_STRSIZE_BOUND];
- int e = errno;
- WARN ((0, e, _("%s: Warning: Cannot seek to %s"),
- quotearg_colon (name),
- STRINGIFY_BIGINT (offset, buf)));
-}
-
-void
-symlink_error (char const *contents, char const *name)
-{
- int e = errno;
- ERROR ((0, e, _("%s: Cannot create symlink to %s"),
- quotearg_colon (name), quote_n (1, contents)));
-}
-
-void
-stat_error (char const *name)
-{
- call_arg_error ("stat", name);
-}
-
-void
-stat_warn (char const *name)
-{
- call_arg_warn ("stat", name);
-}
-
-void
-truncate_error (char const *name)
-{
- call_arg_error ("truncate", name);
-}
-
-void
-truncate_warn (char const *name)
-{
- call_arg_warn ("truncate", name);
+ if (ignore_failed_read_option)
+ close_warn (name);
+ else
+ close_error (name);
}
void
-unlink_error (char const *name)
+open_diag (char const *name)
{
- call_arg_error ("unlink", name);
+ if (ignore_failed_read_option)
+ open_warn (name);
+ else
+ open_error (name);
}
void
-utime_error (char const *name)
+read_diag_details (char const *name, off_t offset, size_t size)
{
- call_arg_error ("utime", name);
+ if (ignore_failed_read_option)
+ read_warn_details (name, offset, size);
+ else
+ read_error_details (name, offset, size);
}
void
-waitpid_error (char const *name)
+readlink_diag (char const *name)
{
- call_arg_error ("waitpid", name);
+ if (ignore_failed_read_option)
+ readlink_warn (name);
+ else
+ readlink_error (name);
}
void
-write_error (char const *name)
+savedir_diag (char const *name)
{
- call_arg_error ("write", name);
+ if (ignore_failed_read_option)
+ savedir_warn (name);
+ else
+ savedir_error (name);
}
void
-write_error_details (char const *name, ssize_t status, size_t size)
+seek_diag_details (char const *name, off_t offset)
{
- if (status < 0)
- write_error (name);
+ if (ignore_failed_read_option)
+ seek_warn_details (name, offset);
else
- ERROR ((0, 0, _("%s: Wrote only %lu of %lu bytes"),
- name, (unsigned long) status, (unsigned long) record_size));
+ seek_error_details (name, offset);
}
void
-write_fatal (char const *name)
+stat_diag (char const *name)
{
- call_arg_fatal ("write", name);
+ if (ignore_failed_read_option)
+ stat_warn (name);
+ else
+ stat_error (name);
}
void
fatal_exit ();
}
-
/* Fork, aborting if unsuccessful. */
pid_t
xfork (void)
call_arg_fatal ("pipe", _("interprocess channel"));
}
-/* Return an unambiguous printable representation, allocated in slot N,
- for NAME, suitable for diagnostics. */
-char const *
-quote_n (int n, char const *name)
+/* Return PTR, aligned upward to the next multiple of ALIGNMENT.
+ ALIGNMENT must be nonzero. The caller must arrange for ((char *)
+ PTR) through ((char *) PTR + ALIGNMENT - 1) to be addressable
+ locations. */
+
+static inline void *
+ptr_align (void *ptr, size_t alignment)
{
- return quotearg_n_style (n, locale_quoting_style, name);
+ char *p0 = ptr;
+ char *p1 = p0 + alignment - 1;
+ return p1 - (size_t) p1 % alignment;
}
-/* Return an unambiguous printable representation of NAME, suitable
- for diagnostics. */
-char const *
-quote (char const *name)
+/* Return the address of a page-aligned buffer of at least SIZE bytes.
+ The caller should free *PTR when done with the buffer. */
+
+void *
+page_aligned_alloc (void **ptr, size_t size)
{
- return quote_n (0, name);
+ size_t alignment = getpagesize ();
+ size_t size1 = size + alignment;
+ if (size1 < size)
+ xalloc_die ();
+ *ptr = xmalloc (size1);
+ return ptr_align (*ptr, alignment);
}