1  /* futimesat basic tests.
       2     Copyright (C) 2021-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <dirent.h>
      20  #include <errno.h>
      21  #include <fcntl.h>
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <unistd.h>
      26  #include <sys/stat.h>
      27  #include <sys/time.h>
      28  
      29  #include <support/test-driver.h>
      30  #include <support/temp_file.h>
      31  #include <support/xunistd.h>
      32  
      33  #ifndef struct_stat
      34  # define struct_stat struct stat64
      35  # define fstat       fstat64
      36  # define fstatat     fstatat64
      37  #endif
      38  
      39  static int dir_fd;
      40  
      41  static void
      42  prepare (int argc, char *argv[])
      43  {
      44    size_t test_dir_len = strlen (test_dir);
      45    static const char dir_name[] = "/tst-futimesat.XXXXXX";
      46  
      47    size_t dirbuflen = test_dir_len + sizeof (dir_name);
      48    char *dirbuf = malloc (dirbuflen);
      49    if (dirbuf == NULL)
      50      {
      51        puts ("out of memory");
      52        exit (1);
      53      }
      54  
      55    snprintf (dirbuf, dirbuflen, "%s%s", test_dir, dir_name);
      56    if (mkdtemp (dirbuf) == NULL)
      57      {
      58        puts ("cannot create temporary directory");
      59        exit (1);
      60      }
      61  
      62    add_temp_file (dirbuf);
      63  
      64    dir_fd = open (dirbuf, O_RDONLY | O_DIRECTORY);
      65    if (dir_fd == -1)
      66      {
      67        puts ("cannot open directory");
      68        exit (1);
      69      }
      70  }
      71  #define PREPARE prepare
      72  
      73  static int
      74  do_test (void)
      75  {
      76    /* fdopendir takes over the descriptor, make a copy.  */
      77    int dupfd = dup (dir_fd);
      78    if (dupfd == -1)
      79      {
      80        puts ("dup failed");
      81        return 1;
      82      }
      83    if (lseek (dupfd, 0, SEEK_SET) != 0)
      84      {
      85        puts ("1st lseek failed");
      86        return 1;
      87      }
      88  
      89    /* The directory should be empty safe the . and .. files.  */
      90    DIR *dir = fdopendir (dupfd);
      91    if (dir == NULL)
      92      {
      93        puts ("fdopendir failed");
      94        return 1;
      95      }
      96    struct dirent64 *d;
      97    while ((d = readdir64 (dir)) != NULL)
      98      if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0)
      99        {
     100  	printf ("temp directory contains file \"%s\"\n", d->d_name);
     101  	return 1;
     102        }
     103    closedir (dir);
     104  
     105    /* Try to create a file.  */
     106    int fd = openat (dir_fd, "some-file", O_CREAT|O_RDWR|O_EXCL, 0666);
     107    if (fd == -1)
     108      {
     109        if (errno == ENOSYS)
     110  	{
     111  	  puts ("*at functions not supported");
     112  	  return 0;
     113  	}
     114  
     115        puts ("file creation failed");
     116        return 1;
     117      }
     118    xwrite (fd, "hello", 5);
     119    puts ("file created");
     120  
     121    struct_stat st1;
     122    if (fstat (fd, &st1) != 0)
     123      {
     124        puts ("fstat64 failed");
     125        return 1;
     126      }
     127  
     128    close (fd);
     129  
     130    struct timeval tv[2];
     131    tv[0].tv_sec = st1.st_atime + 1;
     132    tv[0].tv_usec = 0;
     133    tv[1].tv_sec = st1.st_mtime + 1;
     134    tv[1].tv_usec = 0;
     135    if (futimesat (dir_fd, "some-file", tv) != 0)
     136      {
     137        puts ("futimesat failed");
     138        return 1;
     139      }
     140  
     141    struct_stat st2;
     142    if (fstatat (dir_fd, "some-file", &st2, 0) != 0)
     143      {
     144        puts ("fstatat64 failed");
     145        return 1;
     146      }
     147  
     148    if (st2.st_mtime != tv[1].tv_sec
     149  #ifdef _STATBUF_ST_NSEC
     150        || st2.st_mtim.tv_nsec != 0
     151  #endif
     152        )
     153      {
     154        puts ("stat shows different mtime");
     155        return 1;
     156      }
     157  
     158  
     159    if (unlinkat (dir_fd, "some-file", 0) != 0)
     160      {
     161        puts ("unlinkat failed");
     162        return 1;
     163      }
     164  
     165    close (dir_fd);
     166  
     167    return 0;
     168  }
     169  
     170  #include <support/test-driver.c>