X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fmisc.c;h=ca2921b0e0e6ad597f8967c41a40df3efcf92140;hb=098ad10c71e87f8003227aa10ad8c88ae43dce12;hp=b56a916b1557a87175c5ea57e308a186c3259b67;hpb=1bcbbcf1ff2c537ffa970dbf82e3843d4ad110e5;p=chaz%2Ftar diff --git a/src/misc.c b/src/misc.c index b56a916..ca2921b 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1,7 +1,7 @@ /* Miscellaneous functions, not really specific to GNU tar. Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001, - 2003, 2004, 2005, 2006, 2007, 2009 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 @@ -24,14 +24,9 @@ #include #include #include -#include -#include -#if HAVE_STROPTS_H -# include -#endif -#if HAVE_SYS_FILIO_H -# include +#ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT +# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0 #endif @@ -47,6 +42,9 @@ assign_string (char **string, const char *value) *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 @@ -59,7 +57,7 @@ assign_string (char **string, const char *value) 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; @@ -100,6 +98,7 @@ quote_copy_string (const char *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 @@ -230,10 +229,98 @@ zap_slashes (char *name) 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) { - return zap_slashes (canonicalize_filename_mode (name, CAN_MISSING)); + 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; +} + + +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); + } } @@ -279,6 +366,10 @@ code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND]) char *np; bool negative = s < 0; + /* ignore invalid values of ns */ + if (BILLION <= ns || ns < 0) + ns = 0; + if (negative && ns != 0) { s++; @@ -299,7 +390,7 @@ static char *before_backup_name; 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); @@ -522,31 +613,31 @@ deref_stat (bool deref, char const *name, struct stat *buf) 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 gl_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; }; @@ -585,7 +676,7 @@ chdir_arg (char const *dir) if (! wd_count) { wd[wd_count].name = "."; - wd[wd_count].saved = 0; + wd[wd_count].err = -1; wd_count++; } } @@ -602,28 +693,28 @@ chdir_arg (char const *dir) } wd[wd_count].name = dir; - wd[wd_count].saved = 0; + wd[wd_count].err = -1; return wd_count++; } +/* Index of current directory. */ +int chdir_current; + /* 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; - - 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) { - int err = 0; - prev->saved = 1; + prev->err = 0; if (save_cwd (&prev->saved_cwd) != 0) - err = errno; + prev->err = errno; else if (0 <= prev->saved_cwd.desc) { /* Make sure we still have at least one descriptor available. */ @@ -637,19 +728,21 @@ chdir_do (int i) close (fd1); prev->saved_cwd.desc = -1; prev->saved_cwd.name = xgetcwd (); + if (! prev->saved_cwd.name) + prev->err = errno; } else - err = errno; + prev->err = errno; } - - if (err) - FATAL_ERROR ((0, err, _("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 { @@ -659,7 +752,7 @@ chdir_do (int i) chdir_fatal (curr->name); } - previous = i; + chdir_current = i; } } @@ -726,6 +819,36 @@ stat_diag (char const *name) 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) { @@ -777,3 +900,42 @@ page_aligned_alloc (void **ptr, size_t size) *ptr = xmalloc (size1); return ptr_align (*ptr, alignment); } + + + +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; +}