Exemplo n.º 1
0
/* Normalize any UTIME_NOW or UTIME_OMIT values in *TS, using stat
   buffer STATBUF to obtain the current timestamps of the file.  If
   both times are UTIME_NOW, set *TS to NULL (as this can avoid some
   permissions issues).  If both times are UTIME_OMIT, return true
   (nothing further beyond the prior collection of STATBUF is
   necessary); otherwise return false.  */
static bool
update_timespec (struct stat const *statbuf, struct timespec *ts[2])
{
    struct timespec *timespec = *ts;
    if (timespec[0].tv_nsec == UTIME_OMIT
            && timespec[1].tv_nsec == UTIME_OMIT)
        return true;
    if (timespec[0].tv_nsec == UTIME_NOW
            && timespec[1].tv_nsec == UTIME_NOW)
    {
        *ts = NULL;
        return false;
    }

    if (timespec[0].tv_nsec == UTIME_OMIT)
        timespec[0] = get_stat_atime (statbuf);
    else if (timespec[0].tv_nsec == UTIME_NOW)
        gettime (&timespec[0]);

    if (timespec[1].tv_nsec == UTIME_OMIT)
        timespec[1] = get_stat_mtime (statbuf);
    else if (timespec[1].tv_nsec == UTIME_NOW)
        gettime (&timespec[1]);

    return false;
}
Exemplo n.º 2
0
static void
test_mtime (const struct stat *statinfo, struct timespec *modtimes)
{
  int i;

  /* Use the struct stat fields directly. */
  /* mtime(stamp1) < mtime(stamp2) */
  ASSERT (statinfo[0].st_mtime < statinfo[2].st_mtime
          || (statinfo[0].st_mtime == statinfo[2].st_mtime
              && (get_stat_mtime_ns (&statinfo[0])
                  < get_stat_mtime_ns (&statinfo[2]))));
  /* mtime(stamp2) < mtime(stamp3) */
  ASSERT (statinfo[2].st_mtime < statinfo[3].st_mtime
          || (statinfo[2].st_mtime == statinfo[3].st_mtime
              && (get_stat_mtime_ns (&statinfo[2])
                  < get_stat_mtime_ns (&statinfo[3]))));

  /* Now check the result of the access functions. */
  /* mtime(stamp1) < mtime(stamp2) */
  ASSERT (modtimes[0].tv_sec < modtimes[2].tv_sec
          || (modtimes[0].tv_sec == modtimes[2].tv_sec
              && modtimes[0].tv_nsec < modtimes[2].tv_nsec));
  /* mtime(stamp2) < mtime(stamp3) */
  ASSERT (modtimes[2].tv_sec < modtimes[3].tv_sec
          || (modtimes[2].tv_sec == modtimes[3].tv_sec
              && modtimes[2].tv_nsec < modtimes[3].tv_nsec));

  /* verify equivalence */
  for (i = 0; i < NFILES; ++i)
    {
      struct timespec ts;
      ts = get_stat_mtime (&statinfo[i]);
      ASSERT (ts.tv_sec == statinfo[i].st_mtime);
    }
}
Exemplo n.º 3
0
static void
prepare_test (struct stat *statinfo, struct timespec *modtimes)
{
  int i;

  create_file ("t-stt-stamp1");
  nap ();
  create_file ("t-stt-testfile");
  nap ();
  create_file ("t-stt-stamp2");
  nap ();
  ASSERT (chmod ("t-stt-testfile", 0400) == 0);
  nap ();
  create_file ("t-stt-stamp3");

  do_stat ("t-stt-stamp1",  &statinfo[0]);
  do_stat ("t-stt-testfile", &statinfo[1]);
  do_stat ("t-stt-stamp2",  &statinfo[2]);
  do_stat ("t-stt-stamp3",  &statinfo[3]);

  /* Now use our access functions. */
  for (i = 0; i < NFILES; ++i)
    {
      modtimes[i] = get_stat_mtime (&statinfo[i]);
    }
}
Exemplo n.º 4
0
/* Some filesystems can slightly offset the timestamps of newly created files.
   To compensate for it, tar testsuite waits at least 1 second before creating
   next level of incremental backups.

   However, NFS mounts can offset the timestamps by bigger amounts.  
   
   This program returns with success (0) if a newly created file is assigned
   mtime matching the system time to the nearest second.
*/
int
main (int argc, char **argv)
{
  int fd;
  char name[sizeof(TEMPLATE)];
  struct stat st;
  struct timespec ts, td;
  double diff;
  
  gettime (&ts);
  
  strcpy (name, TEMPLATE);
  umask (077);
  fd = mkstemp (name);
  assert (fd != -1);
  unlink (name);
  assert (fstat (fd, &st) == 0);
  close (fd);

  td = timespec_sub (get_stat_mtime (&st), ts);
  diff = td.tv_sec * BILLION + td.tv_nsec;
  if (diff < 0)
    diff = - diff;
  if (diff / BILLION >= 1)
    {
      fprintf (stderr, "file timestamp unreliable\n");
      return 1;
    }
  return 0;
}
Exemplo n.º 5
0
void
update_parent_directory (struct tar_stat_info *parent)
{
  struct directory *directory = find_directory (parent->orig_file_name);
  if (directory)
    {
      struct stat st;
      if (fstat (parent->fd, &st) != 0)
	stat_diag (directory->name);
      else
	directory->mtime = get_stat_mtime (&st);
    }
}
Exemplo n.º 6
0
Arquivo: incremen.c Projeto: xrg/tar
void
update_parent_directory (const char *name)
{
  struct directory *directory;
  char *p;

  p = dir_name (name);
  directory = find_directory (p);
  if (directory)
    {
      struct stat st;
      if (deref_stat (dereference_option, p, &st) != 0)
	stat_diag (name);
      else
	directory->mtime = get_stat_mtime (&st);
    }
  free (p);
}
Exemplo n.º 7
0
void
update_parent_directory (const char *name)
{
  struct directory *directory;
  char *p;

  p = dir_name (name);
  directory = find_directory (p);
  if (directory)
    {
      struct stat st;
      if (deref_stat (dereference_option, p, &st) != 0)
	{
	  if (errno != ENOENT) 
	    stat_diag (directory->name);
	  /* else: should have been already reported */
	}
      else
	directory->mtime = get_stat_mtime (&st);
    }
  free (p);
}
Exemplo n.º 8
0
void
set_file_attributes (char const *to, enum file_attributes attr,
		     char const *from, const struct stat *st, mode_t mode,
		     struct timespec *new_time)
{
  if (attr & FA_TIMES)
    {
      struct timespec times[2];
      if (new_time)
	times[0] = times[1] = *new_time;
      else
        {
	  times[0] = get_stat_atime (st);
	  times[1] = get_stat_mtime (st);
	}
      if (lutimens (to, times) != 0)
	pfatal ("Failed to set the timestamps of %s %s",
		S_ISLNK (mode) ? "symbolic link" : "file",
		quotearg (to));
    }
  if (attr & FA_IDS)
    {
      static uid_t euid = -1;
      static gid_t egid = -1;
      uid_t uid;
      uid_t gid;

      if (euid == -1)
        {
	  euid = geteuid ();
	  egid = getegid ();
	}
      uid = (euid == st->st_uid) ? -1 : st->st_uid;
      gid = (egid == st->st_gid) ? -1 : st->st_gid;

      /* May fail if we are not privileged to set the file owner, or we are
         not in group instat.st_gid.  Ignore those errors.  */
      if ((uid != -1 || gid != -1)
	  && lchown (to, uid, gid) != 0
	  && (errno != EPERM
	      || (uid != -1
		  && lchown (to, (uid = -1), gid) != 0
		  && errno != EPERM)))
	pfatal ("Failed to set the %s of %s %s",
		(uid == -1) ? "owner" : "owning group",
		S_ISLNK (mode) ? "symbolic link" : "file",
		quotearg (to));
    }
  if (attr & FA_XATTRS)
    if (copy_attr (from, to) != 0
	&& errno != ENOSYS && errno != ENOTSUP && errno != EPERM)
      fatal_exit (0);
  if (attr & FA_MODE)
    {
#if 0 && defined HAVE_LCHMOD
      /* The "diff --git" format does not store the file permissions of
	 symlinks, so don't try to set symlink file permissions even on
	 systems where we could.  */
      if (lchmod (to, mode))
#else
      if (! S_ISLNK (mode) && chmod (to, mode) != 0)
#endif
	pfatal ("Failed to set the permissions of %s %s",
		S_ISLNK (mode) ? "symbolic link" : "file",
		quotearg (to));
    }
}
Exemplo n.º 9
0
/* Set the access and modification time stamps of FILE to be
   TIMESPEC[0] and TIMESPEC[1], respectively, without dereferencing
   symlinks.  Fail with ENOSYS if the platform does not support
   changing symlink timestamps, but FILE was a symlink.  */
int
lutimens (char const *file, struct timespec const timespec[2])
{
    struct timespec adjusted_timespec[2];
    struct timespec *ts = timespec ? adjusted_timespec : NULL;
    int adjustment_needed = 0;
    struct stat st;

    if (ts)
    {
        adjusted_timespec[0] = timespec[0];
        adjusted_timespec[1] = timespec[1];
        adjustment_needed = validate_timespec (ts);
    }
    if (adjustment_needed < 0)
        return -1;

    /* The Linux kernel did not support symlink timestamps until
       utimensat, in version 2.6.22, so we don't need to mimic
       fdutimens' worry about buggy NFS clients.  But we do have to
       worry about bogus return values.  */

#if HAVE_UTIMENSAT
    if (0 <= lutimensat_works_really)
    {
        int result;
# if __linux__
        /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
           systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
           but work if both times are either explicitly specified or
           UTIME_NOW.  Work around it with a preparatory lstat prior to
           calling utimensat; fortunately, there is not much timing
           impact due to the extra syscall even on file systems where
           UTIME_OMIT would have worked.  FIXME: Simplify this in 2012,
           when file system bugs are no longer common.  */
        if (adjustment_needed == 2)
        {
            if (lstat (file, &st))
                return -1;
            if (ts[0].tv_nsec == UTIME_OMIT)
                ts[0] = get_stat_atime (&st);
            else if (ts[1].tv_nsec == UTIME_OMIT)
                ts[1] = get_stat_mtime (&st);
            /* Note that st is good, in case utimensat gives ENOSYS.  */
            adjustment_needed++;
        }
# endif /* __linux__ */
        result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW);
# ifdef __linux__
        /* Work around a kernel bug:
           http://bugzilla.redhat.com/442352
           http://bugzilla.redhat.com/449910
           It appears that utimensat can mistakenly return 280 rather
           than -1 upon ENOSYS failure.
           FIXME: remove in 2010 or whenever the offending kernels
           are no longer in common use.  */
        if (0 < result)
            errno = ENOSYS;
# endif
        if (result == 0 || errno != ENOSYS)
        {
            utimensat_works_really = 1;
            lutimensat_works_really = 1;
            return result;
        }
    }
    lutimensat_works_really = -1;
#endif /* HAVE_UTIMENSAT */

    /* The platform lacks an interface to set file timestamps with
       nanosecond resolution, so do the best we can, discarding any
       fractional part of the timestamp.  */

    if (adjustment_needed || REPLACE_FUNC_STAT_FILE)
    {
        if (adjustment_needed != 3 && lstat (file, &st))
            return -1;
        if (ts && update_timespec (&st, &ts))
            return 0;
    }

    /* On Linux, lutimes is a thin wrapper around utimensat, so there is
       no point trying lutimes if utimensat failed with ENOSYS.  */
#if HAVE_LUTIMES && !HAVE_UTIMENSAT
    {
        struct timeval timeval[2];
        struct timeval *t;
        int result;
        if (ts)
        {
            timeval[0].tv_sec = ts[0].tv_sec;
            timeval[0].tv_usec = ts[0].tv_nsec / 1000;
            timeval[1].tv_sec = ts[1].tv_sec;
            timeval[1].tv_usec = ts[1].tv_nsec / 1000;
            t = timeval;
        }
        else
            t = NULL;

        result = lutimes (file, t);
        if (result == 0 || errno != ENOSYS)
            return result;
    }
#endif /* HAVE_LUTIMES && !HAVE_UTIMENSAT */

    /* Out of luck for symlinks, but we still handle regular files.  */
    if (!(adjustment_needed || REPLACE_FUNC_STAT_FILE) && lstat (file, &st))
        return -1;
    if (!S_ISLNK (st.st_mode))
        return fdutimens (-1, file, ts);
    errno = ENOSYS;
    return -1;
}
Exemplo n.º 10
0
int
fdutimens (int fd, char const *file, struct timespec const timespec[2])
{
    struct timespec adjusted_timespec[2];
    struct timespec *ts = timespec ? adjusted_timespec : NULL;
    int adjustment_needed = 0;
    struct stat st;

    if (ts)
    {
        adjusted_timespec[0] = timespec[0];
        adjusted_timespec[1] = timespec[1];
        adjustment_needed = validate_timespec (ts);
    }
    if (adjustment_needed < 0)
        return -1;

    /* Require that at least one of FD or FILE are potentially valid, to avoid
       a Linux bug where futimens (AT_FDCWD, NULL) changes "." rather
       than failing.  */
    if (fd < 0 && !file)
    {
        errno = EBADF;
        return -1;
    }

    /* Some Linux-based NFS clients are buggy, and mishandle time stamps
       of files in NFS file systems in some cases.  We have no
       configure-time test for this, but please see
       <http://bugs.gentoo.org/show_bug.cgi?id=132673> for references to
       some of the problems with Linux 2.6.16.  If this affects you,
       compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to
       help in some cases, albeit at a cost in performance.  But you
       really should upgrade your kernel to a fixed version, since the
       problem affects many applications.  */

#if HAVE_BUGGY_NFS_TIME_STAMPS
    if (fd < 0)
        sync ();
    else
        fsync (fd);
#endif

    /* POSIX 2008 added two interfaces to set file timestamps with
       nanosecond resolution; newer Linux implements both functions via
       a single syscall.  We provide a fallback for ENOSYS (for example,
       compiling against Linux 2.6.25 kernel headers and glibc 2.7, but
       running on Linux 2.6.18 kernel).  */
#if HAVE_UTIMENSAT || HAVE_FUTIMENS
    if (0 <= utimensat_works_really)
    {
        int result;
# if __linux__
        /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
           systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
           but work if both times are either explicitly specified or
           UTIME_NOW.  Work around it with a preparatory [f]stat prior
           to calling futimens/utimensat; fortunately, there is not much
           timing impact due to the extra syscall even on file systems
           where UTIME_OMIT would have worked.  FIXME: Simplify this in
           2012, when file system bugs are no longer common.  */
        if (adjustment_needed == 2)
        {
            if (fd < 0 ? stat (file, &st) : fstat (fd, &st))
                return -1;
            if (ts[0].tv_nsec == UTIME_OMIT)
                ts[0] = get_stat_atime (&st);
            else if (ts[1].tv_nsec == UTIME_OMIT)
                ts[1] = get_stat_mtime (&st);
            /* Note that st is good, in case utimensat gives ENOSYS.  */
            adjustment_needed++;
        }
# endif /* __linux__ */
# if HAVE_UTIMENSAT
        if (fd < 0)
        {
            result = utimensat (AT_FDCWD, file, ts, 0);
#  ifdef __linux__
            /* Work around a kernel bug:
               http://bugzilla.redhat.com/442352
               http://bugzilla.redhat.com/449910
               It appears that utimensat can mistakenly return 280 rather
               than -1 upon ENOSYS failure.
               FIXME: remove in 2010 or whenever the offending kernels
               are no longer in common use.  */
            if (0 < result)
                errno = ENOSYS;
#  endif /* __linux__ */
            if (result == 0 || errno != ENOSYS)
            {
                utimensat_works_really = 1;
                return result;
            }
        }
# endif /* HAVE_UTIMENSAT */
# if HAVE_FUTIMENS
        if (0 <= fd)
        {
            result = futimens (fd, ts);
#  ifdef __linux__
            /* Work around the same bug as above.  */
            if (0 < result)
                errno = ENOSYS;
#  endif /* __linux__ */
            if (result == 0 || errno != ENOSYS)
            {
                utimensat_works_really = 1;
                return result;
            }
        }
# endif /* HAVE_FUTIMENS */
    }
    utimensat_works_really = -1;
    lutimensat_works_really = -1;
#endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */

    /* The platform lacks an interface to set file timestamps with
       nanosecond resolution, so do the best we can, discarding any
       fractional part of the timestamp.  */

    if (adjustment_needed || (REPLACE_FUNC_STAT_FILE && fd < 0))
    {
        if (adjustment_needed != 3
                && (fd < 0 ? stat (file, &st) : fstat (fd, &st)))
            return -1;
        if (ts && update_timespec (&st, &ts))
            return 0;
    }

    {
#if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
        struct timeval timeval[2];
        struct timeval *t;
        if (ts)
        {
            timeval[0].tv_sec = ts[0].tv_sec;
            timeval[0].tv_usec = ts[0].tv_nsec / 1000;
            timeval[1].tv_sec = ts[1].tv_sec;
            timeval[1].tv_usec = ts[1].tv_nsec / 1000;
            t = timeval;
        }
        else
            t = NULL;

        if (fd < 0)
        {
# if HAVE_FUTIMESAT
            return futimesat (AT_FDCWD, file, t);
# endif
        }
        else
        {
            /* If futimesat or futimes fails here, don't try to speed things
               up by returning right away.  glibc can incorrectly fail with
               errno == ENOENT if /proc isn't mounted.  Also, Mandrake 10.0
               in high security mode doesn't allow ordinary users to read
               /proc/self, so glibc incorrectly fails with errno == EACCES.
               If errno == EIO, EPERM, or EROFS, it's probably safe to fail
               right away, but these cases are rare enough that they're not
               worth optimizing, and who knows what other messed-up systems
               are out there?  So play it safe and fall back on the code
               below.  */

# if (HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) || HAVE_FUTIMES
#  if HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG
#   undef futimes
#   define futimes(fd, t) futimesat (fd, NULL, t)
#  endif
            if (futimes (fd, t) == 0)
            {
#  if __linux__ && __GLIBC__
                /* Work around a longstanding glibc bug, still present as
                   of 2010-12-27.  On older Linux kernels that lack both
                   utimensat and utimes, glibc's futimes rounds instead of
                   truncating when falling back on utime.  The same bug
                   occurs in futimesat with a null 2nd arg.  */
                if (t)
                {
                    bool abig = 500000 <= t[0].tv_usec;
                    bool mbig = 500000 <= t[1].tv_usec;
                    if ((abig | mbig) && fstat (fd, &st) == 0)
                    {
                        /* If these two subtractions overflow, they'll
                           track the overflows inside the buggy glibc.  */
                        time_t adiff = st.st_atime - t[0].tv_sec;
                        time_t mdiff = st.st_mtime - t[1].tv_sec;

                        struct timeval *tt = NULL;
                        struct timeval truncated_timeval[2];
                        truncated_timeval[0] = t[0];
                        truncated_timeval[1] = t[1];
                        if (abig && adiff == 1 && get_stat_atime_ns (&st) == 0)
                        {
                            tt = truncated_timeval;
                            tt[0].tv_usec = 0;
                        }
                        if (mbig && mdiff == 1 && get_stat_mtime_ns (&st) == 0)
                        {
                            tt = truncated_timeval;
                            tt[1].tv_usec = 0;
                        }
                        if (tt)
                            futimes (fd, tt);
                    }
                }
#  endif

                return 0;
            }
# endif
        }
#endif /* HAVE_FUTIMESAT || HAVE_WORKING_UTIMES */

        if (!file)
        {
#if ! ((HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG)          \
        || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
            errno = ENOSYS;
#endif
            return -1;
        }

#if HAVE_WORKING_UTIMES
        return utimes (file, t);
#else
        {
            struct utimbuf utimbuf;
            struct utimbuf *ut;
            if (ts)
            {
                utimbuf.actime = ts[0].tv_sec;
                utimbuf.modtime = ts[1].tv_sec;
                ut = &utimbuf;
            }
            else
                ut = NULL;

            return utime (file, ut);
        }
#endif /* !HAVE_WORKING_UTIMES */
    }
}
Exemplo n.º 11
0
static struct directory *
procdir (const char *name_buffer, struct stat *stat_data,
	 dev_t device,
	 int flag,
	 char *entry)
{
  struct directory *directory;
  bool nfs = NFS_FILE_STAT (*stat_data);

  if ((directory = find_directory (name_buffer)) != NULL)
    {
      if (DIR_IS_INITED (directory))
	{
	  if (flag & PD_FORCE_INIT)
	    {
	      assign_string (&directory->name, name_buffer);
	    }
	  else
	    {
	      *entry = 'N'; /* Avoid duplicating this directory */
	      return directory;
	    }
	}

      if (strcmp (directory->name, name_buffer))
	{
	  *entry = 'N';
	  return directory;
	}
      
      /* With NFS, the same file can have two different devices
	 if an NFS directory is mounted in multiple locations,
	 which is relatively common when automounting.
	 To avoid spurious incremental redumping of
	 directories, consider all NFS devices as equal,
	 relying on the i-node to establish differences.  */
      
      if (! ((!check_device_option
	      || (DIR_IS_NFS (directory) && nfs)
	      || directory->device_number == stat_data->st_dev)
	     && directory->inode_number == stat_data->st_ino))
	{
	  /* FIXME: find_directory_meta ignores nfs */
	  struct directory *d = find_directory_meta (stat_data->st_dev,
						     stat_data->st_ino);
	  if (d)
	    {
	      if (strcmp (d->name, name_buffer))
		{
		  WARNOPT (WARN_RENAME_DIRECTORY,
			   (0, 0,
			    _("%s: Directory has been renamed from %s"),
			    quotearg_colon (name_buffer),
			    quote_n (1, d->name)));
		  directory->orig = d;
		  DIR_SET_FLAG (directory, DIRF_RENAMED);
		  dirlist_replace_prefix (d->name, name_buffer);
		}
	      directory->children = CHANGED_CHILDREN;
	    }
	  else
	    {
	      WARNOPT (WARN_RENAME_DIRECTORY,
		       (0, 0, _("%s: Directory has been renamed"),
			quotearg_colon (name_buffer)));
	      directory->children = ALL_CHILDREN;
	      directory->device_number = stat_data->st_dev;
	      directory->inode_number = stat_data->st_ino;
	    }
	  if (nfs)
	    DIR_SET_FLAG (directory, DIRF_NFS);
	}
      else
	directory->children = CHANGED_CHILDREN;
      
      DIR_SET_FLAG (directory, DIRF_FOUND);
    }
  else
    {
      struct directory *d = find_directory_meta (stat_data->st_dev,
						 stat_data->st_ino);
      
      directory = note_directory (name_buffer,
				  get_stat_mtime(stat_data),
				  stat_data->st_dev,
				  stat_data->st_ino,
				  nfs,
				  true,
				  NULL);

      if (d)
	{
	  if (strcmp (d->name, name_buffer))
	    {
	      WARNOPT (WARN_RENAME_DIRECTORY,
		       (0, 0, _("%s: Directory has been renamed from %s"),
			quotearg_colon (name_buffer),
			quote_n (1, d->name)));
	      directory->orig = d;
	      DIR_SET_FLAG (directory, DIRF_RENAMED);
	      dirlist_replace_prefix (d->name, name_buffer);
	    }
	  directory->children = CHANGED_CHILDREN;
	}
      else
	{
	  DIR_SET_FLAG (directory, DIRF_NEW);
	  WARNOPT (WARN_NEW_DIRECTORY,
		   (0, 0, _("%s: Directory is new"),
		    quotearg_colon (name_buffer)));
	  directory->children =
	    (listed_incremental_option
	     || (OLDER_STAT_TIME (*stat_data, m)
		 || (after_date_option
		     && OLDER_STAT_TIME (*stat_data, c))))
	    ? ALL_CHILDREN
	    : CHANGED_CHILDREN;
	}
    }

  /* If the directory is on another device and --one-file-system was given,
     omit it... */
  if (one_file_system_option && device != stat_data->st_dev
      /* ... except if it was explicitely given in the command line */
      && !is_individual_file (name_buffer))
    /* FIXME: 
	WARNOPT (WARN_XDEV,
		 (0, 0,
		  _("%s: directory is on a different filesystem; not dumped"),
		  quotearg_colon (directory->name)));
    */
    directory->children = NO_CHILDREN;
  else if (flag & PD_FORCE_CHILDREN)
    {
      directory->children = PD_CHILDREN(flag);
      if (directory->children == NO_CHILDREN)
	*entry = 'N';
    }
	  
  DIR_SET_FLAG (directory, DIRF_INIT);

  if (directory->children != NO_CHILDREN)
    {
      const char *tag_file_name;

      switch (check_exclusion_tags (name_buffer, &tag_file_name))
	{
	case exclusion_tag_all:
	  /* This warning can be duplicated by code in dump_file0, but only
	     in case when the topmost directory being archived contains
	     an exclusion tag. */
	  exclusion_tag_warning (name_buffer, tag_file_name,
				 _("directory not dumped"));
	  *entry = 'N';
	  directory->children = NO_CHILDREN;
	  break;

	case exclusion_tag_contents:
	  exclusion_tag_warning (name_buffer, tag_file_name,
				 _("contents not dumped"));
	  directory->children = NO_CHILDREN;
	  break;
	  
	case exclusion_tag_under:
	  exclusion_tag_warning (name_buffer, tag_file_name,
				 _("contents not dumped"));
	  directory->tagfile = tag_file_name;
	  break;
	  
	case exclusion_tag_none:
	  break;
	}
    }

  return directory;
}
Exemplo n.º 12
0
int
fdutimens (int fd, char const *file, struct timespec const timespec[2])
{
  struct timespec adjusted_timespec[2];
  struct timespec *ts = timespec ? adjusted_timespec : NULL;
  int adjustment_needed = 0;
  struct stat st;

  if (ts)
    {
      adjusted_timespec[0] = timespec[0];
      adjusted_timespec[1] = timespec[1];
      adjustment_needed = validate_timespec (ts);
    }
  if (adjustment_needed < 0)
    return -1;

  /* Require that at least one of FD or FILE are potentially valid, to avoid
     a Linux bug where futimens (AT_FDCWD, NULL) changes "." rather
     than failing.  */
  if (fd < 0 && !file)
    {
      errno = EBADF;
      return -1;
    }

  /* Some Linux-based NFS clients are buggy, and mishandle timestamps
     of files in NFS file systems in some cases.  We have no
     configure-time test for this, but please see
     <https://bugs.gentoo.org/show_bug.cgi?id=132673> for references to
     some of the problems with Linux 2.6.16.  If this affects you,
     compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to
     help in some cases, albeit at a cost in performance.  But you
     really should upgrade your kernel to a fixed version, since the
     problem affects many applications.  */

#if HAVE_BUGGY_NFS_TIME_STAMPS
  if (fd < 0)
    sync ();
  else
    fsync (fd);
#endif

  /* POSIX 2008 added two interfaces to set file timestamps with
     nanosecond resolution; newer Linux implements both functions via
     a single syscall.  We provide a fallback for ENOSYS (for example,
     compiling against Linux 2.6.25 kernel headers and glibc 2.7, but
     running on Linux 2.6.18 kernel).  */
#if HAVE_UTIMENSAT || HAVE_FUTIMENS
  if (0 <= utimensat_works_really)
    {
      int result;
# if __linux__ || __sun
      /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
         systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
         but work if both times are either explicitly specified or
         UTIME_NOW.  Work around it with a preparatory [f]stat prior
         to calling futimens/utimensat; fortunately, there is not much
         timing impact due to the extra syscall even on file systems
         where UTIME_OMIT would have worked.

         The same bug occurs in Solaris 11.1 (Apr 2013).

         FIXME: Simplify this for Linux in 2016 and for Solaris in
         2024, when file system bugs are no longer common.  */
      if (adjustment_needed == 2)
        {
          if (fd < 0 ? stat (file, &st) : fstat (fd, &st))
            return -1;
          if (ts[0].tv_nsec == UTIME_OMIT)
            ts[0] = get_stat_atime (&st);
          else if (ts[1].tv_nsec == UTIME_OMIT)
            ts[1] = get_stat_mtime (&st);
          /* Note that st is good, in case utimensat gives ENOSYS.  */
          adjustment_needed++;
        }
# endif
# if HAVE_UTIMENSAT
      if (fd < 0)
        {
          result = utimensat (AT_FDCWD, file, ts, 0);
#  ifdef __linux__
          /* Work around a kernel bug:
             https://bugzilla.redhat.com/show_bug.cgi?id=442352
             https://bugzilla.redhat.com/show_bug.cgi?id=449910
             It appears that utimensat can mistakenly return 280 rather
             than -1 upon ENOSYS failure.
             FIXME: remove in 2010 or whenever the offending kernels
             are no longer in common use.  */
          if (0 < result)
            errno = ENOSYS;
#  endif /* __linux__ */
          if (result == 0 || errno != ENOSYS)
            {
              utimensat_works_really = 1;
              return result;
            }
        }
# endif /* HAVE_UTIMENSAT */
# if HAVE_FUTIMENS
      if (0 <= fd)
        {
          result = futimens (fd, ts);
#  ifdef __linux__
          /* Work around the same bug as above.  */
          if (0 < result)
            errno = ENOSYS;
#  endif /* __linux__ */
          if (result == 0 || errno != ENOSYS)
            {
              utimensat_works_really = 1;
              return result;
            }
        }
# endif /* HAVE_FUTIMENS */
    }
  utimensat_works_really = -1;
  lutimensat_works_really = -1;
#endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */

#ifdef USE_SETFILETIME
  /* On native Windows, use SetFileTime(). See
     <https://msdn.microsoft.com/en-us/library/ms724933.aspx>
     <https://msdn.microsoft.com/en-us/library/ms724284.aspx>  */
  if (0 <= fd)
    {
      HANDLE handle;
      FILETIME current_time;
      FILETIME last_access_time;
      FILETIME last_write_time;

      handle = (HANDLE) _get_osfhandle (fd);
      if (handle == INVALID_HANDLE_VALUE)
        {
          errno = EBADF;
          return -1;
        }

      if (ts == NULL || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW)
        {
          /* GetSystemTimeAsFileTime
             <https://msdn.microsoft.com/en-us/library/ms724397.aspx>.
             It would be overkill to use
             GetSystemTimePreciseAsFileTime
             <https://msdn.microsoft.com/en-us/library/hh706895.aspx>.  */
          GetSystemTimeAsFileTime (&current_time);
        }

      if (ts == NULL || ts[0].tv_nsec == UTIME_NOW)
        {
          last_access_time = current_time;
        }
      else if (ts[0].tv_nsec == UTIME_OMIT)
        {
          last_access_time.dwLowDateTime = 0;
          last_access_time.dwHighDateTime = 0;
        }
      else
        {
          ULONGLONG time_since_16010101 =
            (ULONGLONG) ts[0].tv_sec * 10000000 + ts[0].tv_nsec / 100 + 116444736000000000LL;
          last_access_time.dwLowDateTime = (DWORD) time_since_16010101;
          last_access_time.dwHighDateTime = time_since_16010101 >> 32;
        }

      if (ts == NULL || ts[1].tv_nsec == UTIME_NOW)
        {
          last_write_time = current_time;
        }
      else if (ts[1].tv_nsec == UTIME_OMIT)
        {
          last_write_time.dwLowDateTime = 0;
          last_write_time.dwHighDateTime = 0;
        }
      else
        {
          ULONGLONG time_since_16010101 =
            (ULONGLONG) ts[1].tv_sec * 10000000 + ts[1].tv_nsec / 100 + 116444736000000000LL;
          last_write_time.dwLowDateTime = (DWORD) time_since_16010101;
          last_write_time.dwHighDateTime = time_since_16010101 >> 32;
        }

      if (SetFileTime (handle, NULL, &last_access_time, &last_write_time))
        return 0;
      else
        {
          DWORD sft_error = GetLastError ();
          #if 0
          fprintf (stderr, "fdutimens SetFileTime error 0x%x\n", (unsigned int) sft_error);
          #endif
          switch (sft_error)
            {
            case ERROR_ACCESS_DENIED: /* fd was opened without O_RDWR */
              errno = EACCES; /* not specified by POSIX */
              break;
            default:
              errno = EINVAL;
              break;
            }
          return -1;
        }
    }
Exemplo n.º 13
0
int
rpl_utimensat (int fd, char const *file, struct timespec const times[2],
               int flag)
{
# if defined __linux__ || defined __sun
  struct timespec ts[2];
# endif

  /* See comments in utimens.c for details.  */
  static int utimensat_works_really; /* 0 = unknown, 1 = yes, -1 = no.  */
  if (0 <= utimensat_works_really)
    {
      int result;
# if defined __linux__ || defined __sun
      struct stat st;
      /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
         systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
         but work if both times are either explicitly specified or
         UTIME_NOW.  Work around it with a preparatory [l]stat prior
         to calling utimensat; fortunately, there is not much timing
         impact due to the extra syscall even on file systems where
         UTIME_OMIT would have worked.

         The same bug occurs in Solaris 11.1 (Apr 2013).

         FIXME: Simplify this for Linux in 2016 and for Solaris in
         2024, when file system bugs are no longer common.  */
      if (times && (times[0].tv_nsec == UTIME_OMIT
                    || times[1].tv_nsec == UTIME_OMIT))
        {
          if (fstatat (fd, file, &st, flag))
            return -1;
          if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT)
            return 0;
          if (times[0].tv_nsec == UTIME_OMIT)
            ts[0] = get_stat_atime (&st);
          else
            ts[0] = times[0];
          if (times[1].tv_nsec == UTIME_OMIT)
            ts[1] = get_stat_mtime (&st);
          else
            ts[1] = times[1];
          times = ts;
        }
#  ifdef __hppa__
      /* Linux kernel 2.6.22.19 on hppa does not reject invalid tv_nsec
         values.  */
      else if (times
               && ((times[0].tv_nsec != UTIME_NOW
                    && (times[0].tv_nsec < 0
                        || times[0].tv_nsec >= 1000000000))
                   || (times[1].tv_nsec != UTIME_NOW
                       && (times[1].tv_nsec < 0
                           || times[1].tv_nsec >= 1000000000))))
        {
          errno = EINVAL;
          return -1;
        }
#  endif
# endif
      result = utimensat (fd, file, times, flag);
      /* Linux kernel 2.6.25 has a bug where it returns EINVAL for
         UTIME_NOW or UTIME_OMIT with non-zero tv_sec, which
         local_utimensat works around.  Meanwhile, EINVAL for a bad
         flag is indeterminate whether the native utimensat works, but
         local_utimensat will also reject it.  */
      if (result == -1 && errno == EINVAL && (flag & ~AT_SYMLINK_NOFOLLOW))
        return result;
      if (result == 0 || (errno != ENOSYS && errno != EINVAL))
        {
          utimensat_works_really = 1;
          return result;
        }
    }
  /* No point in trying openat/futimens, since on Linux, futimens is
     implemented with the same syscall as utimensat.  Only avoid the
     native utimensat due to an ENOSYS failure; an EINVAL error was
     data-dependent, and the next caller may pass valid data.  */
  if (0 <= utimensat_works_really && errno == ENOSYS)
    utimensat_works_really = -1;
  return local_utimensat (fd, file, times, flag);
}
Exemplo n.º 14
0
static void
do_fprintf (struct format_val *dest,
            struct segment *segment,
            const char *pathname,
            const struct stat *stat_buf)
{
  char hbuf[LONGEST_HUMAN_READABLE + 1];
  const char *cp;

  switch (segment->segkind)
    {
    case KIND_PLAIN:    /* Plain text string (no % conversion). */
      /* trusted */
      checked_fwrite(segment->text, 1, segment->text_len, dest);
      break;

    case KIND_STOP:             /* Terminate argument and flush output. */
      /* trusted */
      checked_fwrite (segment->text, 1, segment->text_len, dest);
      checked_fflush (dest);
      break;

    case KIND_FORMAT:
      switch (segment->format_char[0])
        {
        case 'a':               /* atime in `ctime' format. */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text, ctime_format (get_stat_atime (stat_buf)));
          break;
        case 'b':               /* size in 512-byte blocks */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text,
                           human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
                                           hbuf, human_ceiling,
                                           ST_NBLOCKSIZE, 512));
          break;
        case 'c':               /* ctime in `ctime' format */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text, ctime_format (get_stat_ctime (stat_buf)));
          break;
        case 'd':               /* depth in search tree */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text, state.curdepth);
          break;
        case 'D':               /* Device on which file exists (stat.st_dev) */
          /* trusted */
          checked_fprintf (dest, segment->text,
                           human_readable ((uintmax_t) stat_buf->st_dev, hbuf,
                                           human_ceiling, 1, 1));
          break;
        case 'f':               /* base name of path */
          /* sanitised */
          {
            char *base = base_name (pathname);
            checked_print_quoted (dest, segment->text, base);
            free (base);
          }
          break;
        case 'F':               /* file system type */
          /* trusted */
          checked_print_quoted (dest, segment->text, filesystem_type (stat_buf, pathname));
          break;
        case 'g':               /* group name */
          /* trusted */
          /* (well, the actual group is selected by the user but
           * its name was selected by the system administrator)
           */
          {
            struct group *g;

            g = getgrgid (stat_buf->st_gid);
            if (g)
              {
                segment->text[segment->text_len] = 's';
                checked_fprintf (dest, segment->text, g->gr_name);
                break;
              }
            else
              {
                /* Do nothing. */
                /*FALLTHROUGH*/
              }
          }
          /*FALLTHROUGH*/ /*...sometimes, so 'G' case.*/

        case 'G':               /* GID number */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text,
                           human_readable ((uintmax_t) stat_buf->st_gid, hbuf,
                                           human_ceiling, 1, 1));
          break;
        case 'h':               /* leading directories part of path */
          /* sanitised */
          {
            cp = strrchr (pathname, '/');
            if (cp == NULL)     /* No leading directories. */
              {
                /* If there is no slash in the pathname, we still
                 * print the string because it contains characters
                 * other than just '%s'.  The %h expands to ".".
                 */
                checked_print_quoted (dest, segment->text, ".");
              }
            else
              {
                char *s = strdup (pathname);
                s[cp - pathname] = 0;
                checked_print_quoted (dest, segment->text, s);
                free (s);
              }
          }
          break;

        case 'H':               /* ARGV element file was found under */
          /* trusted */
          {
            char *s = xmalloc (state.starting_path_length+1);
            memcpy (s, pathname, state.starting_path_length);
            s[state.starting_path_length] = 0;
            checked_fprintf (dest, segment->text, s);
            free (s);
          }
          break;

        case 'i':               /* inode number */
          /* UNTRUSTED, but not exploitable I think */
          /* POSIX does not guarantee that ino_t is unsigned or even
           * integral (except as an XSI extension), but we'll work on
           * fixing that if we ever get a report of a system where
           * ino_t is indeed a signed integral type or a non-integral
           * arithmetic type. */
          checked_fprintf (dest, segment->text,
                           human_readable ((uintmax_t) stat_buf->st_ino, hbuf,
                                           human_ceiling,
                                           1, 1));
          break;
        case 'k':               /* size in 1K blocks */
          /* UNTRUSTED, but not exploitable I think */
          checked_fprintf (dest, segment->text,
                           human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
                                           hbuf, human_ceiling,
                                           ST_NBLOCKSIZE, 1024));
          break;
        case 'l':               /* object of symlink */
          /* sanitised */
#ifdef S_ISLNK
          {
            char *linkname = 0;

            if (S_ISLNK (stat_buf->st_mode))
              {
                linkname = areadlinkat (state.cwd_dir_fd, state.rel_pathname);
                if (linkname == NULL)
                  {
                    nonfatal_target_file_error (errno, pathname);
                    state.exit_status = 1;
                  }
              }
            if (linkname)
              {
                checked_print_quoted (dest, segment->text, linkname);
              }
            else
              {
                /* We still need to honour the field width etc., so this is
                 * not a no-op.
                 */
                checked_print_quoted (dest, segment->text, "");
              }
            free (linkname);
          }
#endif                          /* S_ISLNK */
          break;

        case 'M':               /* mode as 10 chars (eg., "-rwxr-x--x" */
          /* UNTRUSTED, probably unexploitable */
          {
            char modestring[16] ;
            filemodestring (stat_buf, modestring);
            modestring[10] = '\0';
            checked_fprintf (dest, segment->text, modestring);
          }
          break;

        case 'm':               /* mode as octal number (perms only) */
          /* UNTRUSTED, probably unexploitable */
          {
            /* Output the mode portably using the traditional numbers,
               even if the host unwisely uses some other numbering
               scheme.  But help the compiler in the common case where
               the host uses the traditional numbering scheme.  */
            mode_t m = stat_buf->st_mode;
            bool traditional_numbering_scheme =
              (S_ISUID == 04000 && S_ISGID == 02000 && S_ISVTX == 01000
               && S_IRUSR == 00400 && S_IWUSR == 00200 && S_IXUSR == 00100
               && S_IRGRP == 00040 && S_IWGRP == 00020 && S_IXGRP == 00010
               && S_IROTH == 00004 && S_IWOTH == 00002 && S_IXOTH == 00001);
            checked_fprintf (dest, segment->text,
                     (traditional_numbering_scheme
                      ? m & MODE_ALL
                      : ((m & S_ISUID ? 04000 : 0)
                         | (m & S_ISGID ? 02000 : 0)
                         | (m & S_ISVTX ? 01000 : 0)
                         | (m & S_IRUSR ? 00400 : 0)
                         | (m & S_IWUSR ? 00200 : 0)
                         | (m & S_IXUSR ? 00100 : 0)
                         | (m & S_IRGRP ? 00040 : 0)
                         | (m & S_IWGRP ? 00020 : 0)
                         | (m & S_IXGRP ? 00010 : 0)
                         | (m & S_IROTH ? 00004 : 0)
                         | (m & S_IWOTH ? 00002 : 0)
                         | (m & S_IXOTH ? 00001 : 0))));
          }
          break;

        case 'n':               /* number of links */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text,
                   human_readable ((uintmax_t) stat_buf->st_nlink,
                                   hbuf,
                                   human_ceiling,
                                   1, 1));
          break;

        case 'p':               /* pathname */
          /* sanitised */
          checked_print_quoted (dest, segment->text, pathname);
          break;

        case 'P':               /* pathname with ARGV element stripped */
          /* sanitised */
          if (state.curdepth > 0)
            {
              cp = pathname + state.starting_path_length;
              if (*cp == '/')
                /* Move past the slash between the ARGV element
                   and the rest of the pathname.  But if the ARGV element
                   ends in a slash, we didn't add another, so we've
                   already skipped past it.  */
                cp++;
            }
          else
            {
              cp = "";
            }
          checked_print_quoted (dest, segment->text, cp);
          break;

        case 's':               /* size in bytes */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text,
                   human_readable ((uintmax_t) stat_buf->st_size,
                                   hbuf, human_ceiling, 1, 1));
          break;

        case 'S':               /* sparseness */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text, file_sparseness (stat_buf));
          break;

        case 't':               /* mtime in `ctime' format */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text,
                           ctime_format (get_stat_mtime (stat_buf)));
          break;

        case 'u':               /* user name */
          /* trusted */
          /* (well, the actual user is selected by the user on systems
           * where chown is not restricted, but the user name was
           * selected by the system administrator)
           */
          {
            struct passwd *p;

            p = getpwuid (stat_buf->st_uid);
            if (p)
              {
                segment->text[segment->text_len] = 's';
                checked_fprintf (dest, segment->text, p->pw_name);
                break;
              }
            /* else fallthru */
          }
          /* FALLTHROUGH*/ /* .. to case U */

        case 'U':               /* UID number */
          /* UNTRUSTED, probably unexploitable */
          checked_fprintf (dest, segment->text,
                           human_readable ((uintmax_t) stat_buf->st_uid, hbuf,
                                           human_ceiling, 1, 1));
          break;

          /* %Y: type of file system entry like `ls -l`:
           *     (d,-,l,s,p,b,c,n) n=nonexistent (symlink)
           */
        case 'Y':               /* in case of symlink */
          /* trusted */
          {
#ifdef S_ISLNK
            if (S_ISLNK (stat_buf->st_mode))
              {
                struct stat sbuf;
                /* If we would normally follow links, do not do so.
                 * If we would normally not follow links, do so.
                 */
                if ((following_links () ? optionp_stat : optionl_stat)
                    (state.rel_pathname, &sbuf) != 0)
                  {
                    if ( errno == ENOENT )
                      {
                        checked_fprintf (dest, segment->text, "N");
                        break;
                      }
                    else if ( errno == ELOOP )
                      {
                        checked_fprintf (dest, segment->text, "L");
                        break;
                      }
                    else
                      {
                        checked_fprintf (dest, segment->text, "?");
                        error (0, errno, "%s",
                               safely_quote_err_filename (0, pathname));
                        /* exit_status = 1;
                           return ; */
                        break;
                      }
                  }
                checked_fprintf (dest, segment->text,
                                 mode_to_filetype (sbuf.st_mode & S_IFMT));
              }
#endif /* S_ISLNK */
            else
              {
                checked_fprintf (dest, segment->text,
                                 mode_to_filetype (stat_buf->st_mode & S_IFMT));
              }
          }
          break;

        case 'y':
          /* trusted */
          {
            checked_fprintf (dest, segment->text,
                             mode_to_filetype (stat_buf->st_mode & S_IFMT));
          }
          break;

        case 'Z':               /* SELinux security context */
          {
            security_context_t scontext;
            int rv = (*options.x_getfilecon) (state.cwd_dir_fd, state.rel_pathname,
                                              &scontext);
            if (rv < 0)
              {
                /* If getfilecon fails, there will in the general case
                   still be some text to print.   We just make %Z expand
                   to an empty string. */
                checked_fprintf (dest, segment->text, "");

                error (0, errno, _("getfilecon failed: %s"),
                    safely_quote_err_filename (0, pathname));
                state.exit_status = 1;
              }
            else
              {
                checked_fprintf (dest, segment->text, scontext);
                freecon (scontext);
              }
          }
          break;

        case 0:
        case '%':
          checked_fprintf (dest, segment->text);
          break;
        }
      /* end of KIND_FORMAT case */
      break;
    }
}
Exemplo n.º 15
0
bool
pred_fprintf (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
  struct format_val *dest = &pred_ptr->args.printf_vec;
  struct segment *segment;

  for (segment = dest->segment; segment; segment = segment->next)
    {
      if ( (KIND_FORMAT == segment->segkind) && segment->format_char[1]) /* Component of date. */
        {
          struct timespec ts;
          int valid = 0;

          switch (segment->format_char[0])
            {
            case 'A':
              ts = get_stat_atime (stat_buf);
              valid = 1;
              break;
            case 'B':
              ts = get_stat_birthtime (stat_buf);
              if ('@' == segment->format_char[1])
                valid = 1;
              else
                valid = (ts.tv_nsec >= 0);
              break;
            case 'C':
              ts = get_stat_ctime (stat_buf);
              valid = 1;
              break;
            case 'T':
              ts = get_stat_mtime (stat_buf);
              valid = 1;
              break;
            default:
              assert (0);
              abort ();
            }
          /* We trust the output of format_date not to contain
           * nasty characters, though the value of the date
           * is itself untrusted data.
           */
          if (valid)
            {
              /* trusted */
              checked_fprintf (dest, segment->text,
                               format_date (ts, segment->format_char[1]));
            }
          else
            {
              /* The specified timestamp is not available, output
               * nothing for the timestamp, but use the rest (so that
               * for example find foo -printf '[%Bs] %p\n' can print
               * "[] foo").
               */
              /* trusted */
              checked_fprintf (dest, segment->text, "");
            }
        }
      else
        {
          /* Print a segment which is not a date. */
          do_fprintf (dest, segment, pathname, stat_buf);
        }
    }
  return true;
}