• Ulrich Drepper's avatar
    utimensat implementation · 1c710c89
    Ulrich Drepper authored
    Implement utimensat(2) which is an extension to futimesat(2) in that it
    
    a) supports nano-second resolution for the timestamps
    b) allows to selectively ignore the atime/mtime value
    c) allows to selectively use the current time for either atime or mtime
    d) supports changing the atime/mtime of a symlink itself along the lines
       of the BSD lutimes(3) functions
    
    For this change the internally used do_utimes() functions was changed to
    accept a timespec time value and an additional flags parameter.
    
    Additionally the sys_utime function was changed to match compat_sys_utime
    which already use do_utimes instead of duplicating the work.
    
    Also, the completely missing futimensat() functionality is added.  We have
    such a function in glibc but we have to resort to using /proc/self/fd/* which
    not everybody likes (chroot etc).
    
    Test application (the syscall number will need per-arch editing):
    
    #include <errno.h>
    #include <fcntl.h>
    #include <time.h>
    #include <sys/time.h>
    #include <stddef.h>
    #include <syscall.h>
    
    #define __NR_utimensat 280
    
    #define UTIME_NOW       ((1l << 30) - 1l)
    #define UTIME_OMIT      ((1l << 30) - 2l)
    
    int
    main(void)
    {
      int status = 0;
    
      int fd = open("ttt", O_RDWR|O_CREAT|O_EXCL, 0666);
      if (fd == -1)
        error (1, errno, "failed to create test file \"ttt\"");
    
      struct stat64 st1;
      if (fstat64 (fd, &st1) != 0)
        error (1, errno, "fstat failed");
    
      struct timespec t[2];
      t[0].tv_sec = 0;
      t[0].tv_nsec = 0;
      t[1].tv_sec = 0;
      t[1].tv_nsec = 0;
      if (syscall(__NR_utimensat, AT_FDCWD, "ttt", t, 0) != 0)
        error (1, errno, "utimensat failed");
    
      struct stat64 st2;
      if (fstat64 (fd, &st2) != 0)
        error (1, errno, "fstat failed");
    
      if (st2.st_atim.tv_sec != 0 || st2.st_atim.tv_nsec != 0)
        {
          puts ("atim not reset to zero");
          status = 1;
        }
      if (st2.st_mtim.tv_sec != 0 || st2.st_mtim.tv_nsec != 0)
        {
          puts ("mtim not reset to zero");
          status = 1;
        }
      if (status != 0)
        goto out;
    
      t[0] = st1.st_atim;
      t[1].tv_sec = 0;
      t[1].tv_nsec = UTIME_OMIT;
      if (syscall(__NR_utimensat, AT_FDCWD, "ttt", t, 0) != 0)
        error (1, errno, "utimensat failed");
    
      if (fstat64 (fd, &st2) != 0)
        error (1, errno, "fstat failed");
    
      if (st2.st_atim.tv_sec != st1.st_atim.tv_sec
          || st2.st_atim.tv_nsec != st1.st_atim.tv_nsec)
        {
          puts ("atim not set");
          status = 1;
        }
      if (st2.st_mtim.tv_sec != 0 || st2.st_mtim.tv_nsec != 0)
        {
          puts ("mtim changed from zero");
          status = 1;
        }
      if (status != 0)
        goto out;
    
      t[0].tv_sec = 0;
      t[0].tv_nsec = UTIME_OMIT;
      t[1] = st1.st_mtim;
      if (syscall(__NR_utimensat, AT_FDCWD, "ttt", t, 0) != 0)
        error (1, errno, "utimensat failed");
    
      if (fstat64 (fd, &st2) != 0)
        error (1, errno, "fstat failed");
    
      if (st2.st_atim.tv_sec != st1.st_atim.tv_sec
          || st2.st_atim.tv_nsec != st1.st_atim.tv_nsec)
        {
          puts ("mtim changed from original time");
          status = 1;
        }
      if (st2.st_mtim.tv_sec != st1.st_mtim.tv_sec
          || st2.st_mtim.tv_nsec != st1.st_mtim.tv_nsec)
        {
          puts ("mtim not set");
          status = 1;
        }
      if (status != 0)
        goto out;
    
      sleep (2);
    
      t[0].tv_sec = 0;
      t[0].tv_nsec = UTIME_NOW;
      t[1].tv_sec = 0;
      t[1].tv_nsec = UTIME_NOW;
      if (syscall(__NR_utimensat, AT_FDCWD, "ttt", t, 0) != 0)
        error (1, errno, "utimensat failed");
    
      if (fstat64 (fd, &st2) != 0)
        error (1, errno, "fstat failed");
    
      struct timeval tv;
      gettimeofday(&tv,NULL);
    
      if (st2.st_atim.tv_sec <= st1.st_atim.tv_sec
          || st2.st_atim.tv_sec > tv.tv_sec)
        {
          puts ("atim not set to NOW");
          status = 1;
        }
      if (st2.st_mtim.tv_sec <= st1.st_mtim.tv_sec
          || st2.st_mtim.tv_sec > tv.tv_sec)
        {
          puts ("mtim not set to NOW");
          status = 1;
        }
    
      if (symlink ("ttt", "tttsym") != 0)
        error (1, errno, "cannot create symlink");
    
      t[0].tv_sec = 0;
      t[0].tv_nsec = 0;
      t[1].tv_sec = 0;
      t[1].tv_nsec = 0;
      if (syscall(__NR_utimensat, AT_FDCWD, "tttsym", t, AT_SYMLINK_NOFOLLOW) != 0)
        error (1, errno, "utimensat failed");
    
      if (lstat64 ("tttsym", &st2) != 0)
        error (1, errno, "lstat failed");
    
      if (st2.st_atim.tv_sec != 0 || st2.st_atim.tv_nsec != 0)
        {
          puts ("symlink atim not reset to zero");
          status = 1;
        }
      if (st2.st_mtim.tv_sec != 0 || st2.st_mtim.tv_nsec != 0)
        {
          puts ("symlink mtim not reset to zero");
          status = 1;
        }
      if (status != 0)
        goto out;
    
      t[0].tv_sec = 1;
      t[0].tv_nsec = 0;
      t[1].tv_sec = 1;
      t[1].tv_nsec = 0;
      if (syscall(__NR_utimensat, fd, NULL, t, 0) != 0)
        error (1, errno, "utimensat failed");
    
      if (fstat64 (fd, &st2) != 0)
        error (1, errno, "fstat failed");
    
      if (st2.st_atim.tv_sec != 1 || st2.st_atim.tv_nsec != 0)
        {
          puts ("atim not reset to one");
          status = 1;
        }
      if (st2.st_mtim.tv_sec != 1 || st2.st_mtim.tv_nsec != 0)
        {
          puts ("mtim not reset to one");
          status = 1;
        }
    
      if (status == 0)
         puts ("all OK");
    
     out:
      close (fd);
      unlink ("ttt");
      unlink ("tttsym");
    
      return status;
    }
    
    [akpm@linux-foundation.org: add missing i386 syscall table entry]
    Signed-off-by: default avatarUlrich Drepper <drepper@redhat.com>
    Cc: Alexey Dobriyan <adobriyan@openvz.org>
    Cc: Michael Kerrisk <mtk-manpages@gmx.net>
    Cc: <linux-arch@vger.kernel.org>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    1c710c89
stat.h 1.65 KB