mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 09:36:25 +00:00 
			
		
		
		
	 1c710c896e
			
		
	
	
		1c710c896e
		
	
	
	
	
		
			
			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: Ulrich 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
		
	
			
		
			
				
	
	
		
			81 lines
		
	
	
		
			1.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			81 lines
		
	
	
		
			1.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #ifndef _LINUX_STAT_H
 | |
| #define _LINUX_STAT_H
 | |
| 
 | |
| #ifdef __KERNEL__
 | |
| 
 | |
| #include <asm/stat.h>
 | |
| 
 | |
| #endif
 | |
| 
 | |
| #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
 | |
| 
 | |
| #define S_IFMT  00170000
 | |
| #define S_IFSOCK 0140000
 | |
| #define S_IFLNK	 0120000
 | |
| #define S_IFREG  0100000
 | |
| #define S_IFBLK  0060000
 | |
| #define S_IFDIR  0040000
 | |
| #define S_IFCHR  0020000
 | |
| #define S_IFIFO  0010000
 | |
| #define S_ISUID  0004000
 | |
| #define S_ISGID  0002000
 | |
| #define S_ISVTX  0001000
 | |
| 
 | |
| #define S_ISLNK(m)	(((m) & S_IFMT) == S_IFLNK)
 | |
| #define S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
 | |
| #define S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
 | |
| #define S_ISCHR(m)	(((m) & S_IFMT) == S_IFCHR)
 | |
| #define S_ISBLK(m)	(((m) & S_IFMT) == S_IFBLK)
 | |
| #define S_ISFIFO(m)	(((m) & S_IFMT) == S_IFIFO)
 | |
| #define S_ISSOCK(m)	(((m) & S_IFMT) == S_IFSOCK)
 | |
| 
 | |
| #define S_IRWXU 00700
 | |
| #define S_IRUSR 00400
 | |
| #define S_IWUSR 00200
 | |
| #define S_IXUSR 00100
 | |
| 
 | |
| #define S_IRWXG 00070
 | |
| #define S_IRGRP 00040
 | |
| #define S_IWGRP 00020
 | |
| #define S_IXGRP 00010
 | |
| 
 | |
| #define S_IRWXO 00007
 | |
| #define S_IROTH 00004
 | |
| #define S_IWOTH 00002
 | |
| #define S_IXOTH 00001
 | |
| 
 | |
| #endif
 | |
| 
 | |
| #ifdef __KERNEL__
 | |
| #define S_IRWXUGO	(S_IRWXU|S_IRWXG|S_IRWXO)
 | |
| #define S_IALLUGO	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
 | |
| #define S_IRUGO		(S_IRUSR|S_IRGRP|S_IROTH)
 | |
| #define S_IWUGO		(S_IWUSR|S_IWGRP|S_IWOTH)
 | |
| #define S_IXUGO		(S_IXUSR|S_IXGRP|S_IXOTH)
 | |
| 
 | |
| #define UTIME_NOW	((1l << 30) - 1l)
 | |
| #define UTIME_OMIT	((1l << 30) - 2l)
 | |
| 
 | |
| #include <linux/types.h>
 | |
| #include <linux/time.h>
 | |
| 
 | |
| struct kstat {
 | |
| 	u64		ino;
 | |
| 	dev_t		dev;
 | |
| 	umode_t		mode;
 | |
| 	unsigned int	nlink;
 | |
| 	uid_t		uid;
 | |
| 	gid_t		gid;
 | |
| 	dev_t		rdev;
 | |
| 	loff_t		size;
 | |
| 	struct timespec  atime;
 | |
| 	struct timespec	mtime;
 | |
| 	struct timespec	ctime;
 | |
| 	unsigned long	blksize;
 | |
| 	unsigned long long	blocks;
 | |
| };
 | |
| 
 | |
| #endif
 | |
| 
 | |
| #endif
 |