Beispiel #1
0
int
set_utimes(const char *file, struct stat *fs)
{
    struct timespec ts[2];

    ts[0] = fs->st_atimespec;
    ts[1] = fs->st_mtimespec;

    if (lutimens(file, ts)) {
	warn("lutimens: %s", file);
	return (1);
    }
    return (0);
}
Beispiel #2
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));
    }
}
Beispiel #3
0
int
utimecmp (char const *dst_name,
          struct stat const *dst_stat,
          struct stat const *src_stat,
          int options)
{
  /* Things to watch out for:

     The code uses a static hash table internally and is not safe in the
     presence of signals, multiple threads, etc.  However, memory pressure
     that prevents use of the hash table is not fatal - we just fall back
     to redoing the computations on every call in that case.

     int and long int might be 32 bits.  Many of the calculations store
     numbers up to 2 billion, and multiply by 10; they have to avoid
     multiplying 2 billion by 10, as this exceeds 32-bit capabilities.

     time_t might be unsigned.  */

  verify (TYPE_IS_INTEGER (time_t));
  verify (TYPE_TWOS_COMPLEMENT (int));

  /* Destination and source time stamps.  */
  time_t dst_s = dst_stat->st_mtime;
  time_t src_s = src_stat->st_mtime;
  int dst_ns = get_stat_mtime_ns (dst_stat);
  int src_ns = get_stat_mtime_ns (src_stat);

  if (options & UTIMECMP_TRUNCATE_SOURCE)
    {
      /* Look up the time stamp resolution for the destination device.  */

      /* Hash table for caching information learned about devices.  */
      static Hash_table *ht;

      /* Information about the destination file system.  */
      static struct fs_res *new_dst_res;
      struct fs_res *dst_res = NULL;
      struct fs_res tmp_dst_res;

      /* Time stamp resolution in nanoseconds.  */
      int res;

      /* Quick exit, if possible.  Since the worst resolution is 2
         seconds, anything that differs by more than that does not
         needs source truncation.  */
      if (dst_s == src_s && dst_ns == src_ns)
        return 0;
      if (dst_s <= src_s - 2)
        return -1;
      if (src_s <= dst_s - 2)
        return 1;

      /* Try to do a hash lookup, but fall back to stack variables and
         recomputation on low memory situations.  */
      if (! ht)
        ht = hash_initialize (16, NULL, dev_info_hash, dev_info_compare, free);
      if (ht)
        {
          if (! new_dst_res)
            {
              new_dst_res = malloc (sizeof *new_dst_res);
              if (!new_dst_res)
                goto low_memory;
              new_dst_res->resolution = 2 * BILLION;
              new_dst_res->exact = false;
            }
          new_dst_res->dev = dst_stat->st_dev;
          dst_res = hash_insert (ht, new_dst_res);
          if (! dst_res)
            goto low_memory;

          if (dst_res == new_dst_res)
            {
              /* NEW_DST_RES is now in use in the hash table, so allocate a
                 new entry next time.  */
              new_dst_res = NULL;
            }
        }
      else
        {
        low_memory:
          if (ht)
            {
              tmp_dst_res.dev = dst_stat->st_dev;
              dst_res = hash_lookup (ht, &tmp_dst_res);
            }
          if (!dst_res)
            {
              dst_res = &tmp_dst_res;
              dst_res->resolution = 2 * BILLION;
              dst_res->exact = false;
            }
        }

      res = dst_res->resolution;

#ifdef _PC_TIMESTAMP_RESOLUTION
      /* If the system will tell us the resolution, we're set!  */
      if (! dst_res->exact)
        {
          res = pathconf (dst_name, _PC_TIMESTAMP_RESOLUTION);
          if (0 < res)
            {
              dst_res->resolution = res;
              dst_res->exact = true;
            }
        }
#endif

      if (! dst_res->exact)
        {
          /* This file system's resolution is not known exactly.
             Deduce it, and store the result in the hash table.  */

          time_t dst_a_s = dst_stat->st_atime;
          time_t dst_c_s = dst_stat->st_ctime;
          time_t dst_m_s = dst_s;
          int dst_a_ns = get_stat_atime_ns (dst_stat);
          int dst_c_ns = get_stat_ctime_ns (dst_stat);
          int dst_m_ns = dst_ns;

          /* Set RES to an upper bound on the file system resolution
             (after truncation due to SYSCALL_RESOLUTION) by inspecting
             the atime, ctime and mtime of the existing destination.
             We don't know of any file system that stores atime or
             ctime with a higher precision than mtime, so it's valid to
             look at them too.  */
          {
            bool odd_second = (dst_a_s | dst_c_s | dst_m_s) & 1;

            if (SYSCALL_RESOLUTION == BILLION)
              {
                if (odd_second | dst_a_ns | dst_c_ns | dst_m_ns)
                  res = BILLION;
              }
            else
              {
                int a = dst_a_ns;
                int c = dst_c_ns;
                int m = dst_m_ns;

                /* Write it this way to avoid mistaken GCC warning
                   about integer overflow in constant expression.  */
                int SR10 = SYSCALL_RESOLUTION;  SR10 *= 10;

                if ((a % SR10 | c % SR10 | m % SR10) != 0)
                  res = SYSCALL_RESOLUTION;
                else
                  for (res = SR10, a /= SR10, c /= SR10, m /= SR10;
                       (res < dst_res->resolution
                        && (a % 10 | c % 10 | m % 10) == 0);
                       res *= 10, a /= 10, c /= 10, m /= 10)
                    if (res == BILLION)
                      {
                        if (! odd_second)
                          res *= 2;
                        break;
                      }
              }

            dst_res->resolution = res;
          }

          if (SYSCALL_RESOLUTION < res)
            {
              struct timespec timespec[2];
              struct stat dst_status;

              /* Ignore source time stamp information that must necessarily
                 be lost when filtered through utimens.  */
              src_ns -= src_ns % SYSCALL_RESOLUTION;

              /* If the time stamps disagree widely enough, there's no need
                 to interrogate the file system to deduce the exact time
                 stamp resolution; return the answer directly.  */
              {
                time_t s = src_s & ~ (res == 2 * BILLION);
                if (src_s < dst_s || (src_s == dst_s && src_ns <= dst_ns))
                  return 1;
                if (dst_s < s
                    || (dst_s == s && dst_ns < src_ns - src_ns % res))
                  return -1;
              }

              /* Determine the actual time stamp resolution for the
                 destination file system (after truncation due to
                 SYSCALL_RESOLUTION) by setting the access time stamp of the
                 destination to the existing access time, except with
                 trailing nonzero digits.  */

              timespec[0].tv_sec = dst_a_s;
              timespec[0].tv_nsec = dst_a_ns;
              timespec[1].tv_sec = dst_m_s | (res == 2 * BILLION);
              timespec[1].tv_nsec = dst_m_ns + res / 9;

              /* Set the modification time.  But don't try to set the
                 modification time of symbolic links; on many hosts this sets
                 the time of the pointed-to file.  */
              if ((S_ISLNK (dst_stat->st_mode)
                   ? lutimens (dst_name, timespec)
                   : utimens (dst_name, timespec)) != 0)
                return -2;

              /* Read the modification time that was set.  */
              {
                int stat_result = (S_ISLNK (dst_stat->st_mode)
                                   ? lstat (dst_name, &dst_status)
                                   : stat (dst_name, &dst_status));

                if (stat_result
                    | (dst_status.st_mtime ^ dst_m_s)
                    | (get_stat_mtime_ns (&dst_status) ^ dst_m_ns))
                  {
                    /* The modification time changed, or we can't tell whether
                       it changed.  Change it back as best we can.  */
                    timespec[1].tv_sec = dst_m_s;
                    timespec[1].tv_nsec = dst_m_ns;
                    if (S_ISLNK (dst_stat->st_mode))
                      lutimens (dst_name, timespec);
                    else
                      utimens (dst_name, timespec);
                  }

                if (stat_result != 0)
                  return -2;
              }

              /* Determine the exact resolution from the modification time
                 that was read back.  */
              {
                int old_res = res;
                int a = (BILLION * (dst_status.st_mtime & 1)
                         + get_stat_mtime_ns (&dst_status));

                res = SYSCALL_RESOLUTION;

                for (a /= res; a % 10 == 0; a /= 10)
                  {
                    if (res == BILLION)
                      {
                        res *= 2;
                        break;
                      }
                    res *= 10;
                    if (res == old_res)
                      break;
                  }
              }
            }

          dst_res->resolution = res;
          dst_res->exact = true;
        }

      /* Truncate the source's time stamp according to the resolution.  */
      src_s &= ~ (res == 2 * BILLION);
      src_ns -= src_ns % res;
    }

  /* Compare the time stamps and return -1, 0, 1 accordingly.  */
  return (dst_s < src_s ? -1
          : dst_s > src_s ? 1
          : dst_ns < src_ns ? -1
          : dst_ns > src_ns);
}