(root)/
glibc-2.38/
posix/
tst-dir.c
       1  /* Copyright (C) 2000-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <dirent.h>
      19  #include <errno.h>
      20  #include <fcntl.h>
      21  #include <mcheck.h>
      22  #include <stddef.h>
      23  #include <stdio.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  #include <unistd.h>
      27  #include <sys/stat.h>
      28  #include <libc-diag.h>
      29  
      30  /* We expect four arguments:
      31     - source directory name
      32     - object directory
      33     - common object directory
      34     - the program name with path
      35  */
      36  int
      37  main (int argc, char *argv[])
      38  {
      39    const char *srcdir;
      40    const char *objdir;
      41    const char *common_objdir;
      42    const char *progpath;
      43    struct stat64 st1;
      44    struct stat64 st2;
      45    struct stat64 st3;
      46    DIR *dir1;
      47    DIR *dir2;
      48    int result = 0;
      49    struct dirent64 *d;
      50    union
      51      {
      52        struct dirent64 d;
      53        char room [offsetof (struct dirent64, d_name[0]) + NAME_MAX + 1];
      54      }
      55      direntbuf;
      56    char *objdir_copy1;
      57    char *objdir_copy2;
      58    char *buf;
      59    int fd;
      60  
      61    mtrace ();
      62  
      63    if (argc < 5)
      64      {
      65        puts ("not enough parameters");
      66        exit (1);
      67      }
      68  
      69    /* Make parameters available with nicer names.  */
      70    srcdir = argv[1];
      71    objdir = argv[2];
      72    common_objdir = argv[3];
      73    progpath = argv[4];
      74  
      75    /* First test the current source dir.  We cannot really compare the
      76       result of `getpwd' with the srcdir string but we have other means.  */
      77    if (stat64 (".", &st1) < 0)
      78      {
      79        printf ("cannot stat starting directory: %m\n");
      80        exit (1);
      81      }
      82  
      83    if (chdir (srcdir) < 0)
      84      {
      85        printf ("cannot change to source directory: %m\n");
      86        exit (1);
      87      }
      88    if (stat64 (".", &st2) < 0)
      89      {
      90        printf ("cannot stat source directory: %m\n");
      91        exit (1);
      92      }
      93  
      94    /* The two last stat64 calls better were for the same directory.  */
      95    if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
      96      {
      97        printf ("stat of source directory failed: (%lld,%lld) vs (%lld,%lld)\n",
      98  	      (long long int) st1.st_dev, (long long int) st1.st_ino,
      99  	      (long long int) st2.st_dev, (long long int) st2.st_ino);
     100        exit (1);
     101      }
     102  
     103    /* Change to the object directory.  */
     104    if (chdir (objdir) < 0)
     105      {
     106        printf ("cannot change to object directory: %m\n");
     107        exit (1);
     108      }
     109    if (stat64 (".", &st1) < 0)
     110      {
     111        printf ("cannot stat object directory: %m\n");
     112        exit (1);
     113      }
     114    /* Is this the same we get as with the full path?  */
     115    if (stat64 (objdir, &st2) < 0)
     116      {
     117        printf ("cannot stat object directory with full path: %m\n");
     118        exit (1);
     119      }
     120    if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
     121      {
     122        printf ("stat of object directory failed: (%lld,%lld) vs (%lld,%lld)\n",
     123  	      (long long int) st1.st_dev, (long long int) st1.st_ino,
     124  	      (long long int) st2.st_dev, (long long int) st2.st_ino);
     125        exit (1);
     126      }
     127  
     128    objdir_copy1 = getcwd (NULL, 0);
     129    if (objdir_copy1 == NULL)
     130      {
     131        printf ("cannot get current directory name for object directory: %m\n");
     132        result = 1;
     133      }
     134  
     135    /* First test: this directory must include our program.  */
     136    if (stat64 (progpath, &st2) < 0)
     137      {
     138        printf ("cannot stat program: %m\n");
     139        exit (1);
     140      }
     141  
     142    dir1 = opendir (".");
     143    if (dir1 == NULL)
     144      {
     145        printf ("cannot open object directory: %m\n");
     146        exit (1);
     147      }
     148  
     149    while ((d = readdir64 (dir1)) != NULL)
     150      {
     151        if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG)
     152  	continue;
     153  
     154        if (d->d_ino == st2.st_ino)
     155  	{
     156  	  /* Might be it.  Test the device.  We could use the st_dev
     157  	     element from st1 but what the heck, do more testing.  */
     158  	  if (stat64 (d->d_name, &st3) < 0)
     159  	    {
     160  	      printf ("cannot stat entry from readdir: %m\n");
     161  	      result = 1;
     162  	      d = NULL;
     163  	      break;
     164  	    }
     165  
     166  	  if (st3.st_dev == st2.st_dev)
     167  	    break;
     168  	}
     169      }
     170  
     171    if (d == NULL)
     172      {
     173        puts ("haven't found program in object directory");
     174        result = 1;
     175      }
     176  
     177    /* We leave dir1 open.  */
     178  
     179    /* Stat using file descriptor.  */
     180    if (fstat64 (dirfd (dir1), &st2) < 0)
     181      {
     182        printf ("cannot fstat object directory: %m\n");
     183        result = 1;
     184      }
     185    if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
     186      {
     187        printf ("fstat of object directory failed: (%lld,%lld) vs (%lld,%lld)\n",
     188  	      (long long int) st1.st_dev, (long long int) st1.st_ino,
     189  	      (long long int) st2.st_dev, (long long int) st2.st_ino);
     190        exit (1);
     191      }
     192  
     193    if (chdir ("..") < 0)
     194      {
     195        printf ("cannot go to common object directory with \"..\": %m\n");
     196        exit (1);
     197      }
     198  
     199    if (stat64 (".", &st1) < 0)
     200      {
     201        printf ("cannot stat common object directory: %m\n");
     202        exit (1);
     203      }
     204    /* Is this the same we get as with the full path?  */
     205    if (stat64 (common_objdir, &st2) < 0)
     206      {
     207        printf ("cannot stat common object directory with full path: %m\n");
     208        exit (1);
     209      }
     210    if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
     211      {
     212        printf ("stat of object directory failed: (%lld,%lld) vs (%lld,%lld)\n",
     213  	      (long long int) st1.st_dev, (long long int) st1.st_ino,
     214  	      (long long int) st2.st_dev, (long long int) st2.st_ino);
     215        exit (1);
     216      }
     217  
     218    /* Stat using file descriptor.  */
     219    if (fstat64 (dirfd (dir1), &st2) < 0)
     220      {
     221        printf ("cannot fstat object directory: %m\n");
     222        result = 1;
     223      }
     224  
     225    dir2 = opendir (common_objdir);
     226    if (dir2 == NULL)
     227      {
     228        printf ("cannot open common object directory: %m\n");
     229        exit (1);
     230      }
     231  
     232    while ((d = readdir64 (dir2)) != NULL)
     233      {
     234        if (d->d_type != DT_UNKNOWN && d->d_type != DT_DIR)
     235  	continue;
     236  
     237        if (d->d_ino == st2.st_ino)
     238  	{
     239  	  /* Might be it.  Test the device.  We could use the st_dev
     240  	     element from st1 but what the heck, do more testing.  */
     241  	  if (stat64 (d->d_name, &st3) < 0)
     242  	    {
     243  	      printf ("cannot stat entry from readdir: %m\n");
     244  	      result = 1;
     245  	      d = NULL;
     246  	      break;
     247  	    }
     248  
     249  	  if (st3.st_dev == st2.st_dev)
     250  	    break;
     251  	}
     252      }
     253  
     254    /* This better should be the object directory again.  */
     255    if (fchdir (dirfd (dir1)) < 0)
     256      {
     257        printf ("cannot fchdir to object directory: %m\n");
     258        exit (1);
     259      }
     260  
     261    objdir_copy2 = getcwd (NULL, 0);
     262    if (objdir_copy2 == NULL)
     263      {
     264        printf ("cannot get current directory name for object directory: %m\n");
     265        result = 1;
     266      }
     267    if (strcmp (objdir_copy1, objdir_copy2) != 0)
     268      {
     269        puts ("getcwd returned a different string the second time");
     270        result = 1;
     271      }
     272  
     273    /* This better should be the common object directory again.  */
     274    if (fchdir (dirfd (dir2)) < 0)
     275      {
     276        printf ("cannot fchdir to common object directory: %m\n");
     277        exit (1);
     278      }
     279  
     280    if (stat64 (".", &st2) < 0)
     281      {
     282        printf ("cannot stat common object directory: %m\n");
     283        exit (1);
     284      }
     285    if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
     286      {
     287        printf ("stat of object directory failed: (%lld,%lld) vs (%lld,%lld)\n",
     288  	      (long long int) st1.st_dev, (long long int) st1.st_ino,
     289  	      (long long int) st2.st_dev, (long long int) st2.st_ino);
     290        exit (1);
     291      }
     292  
     293    buf = (char *) malloc (strlen (objdir_copy1) + 1 + sizeof "tst-dir.XXXXXX");
     294    if (buf == NULL)
     295      {
     296        printf ("cannot allocate buffer: %m");
     297        exit (1);
     298      }
     299  
     300    stpcpy (stpcpy (stpcpy (buf, objdir_copy1), "/"), "tst-dir.XXXXXX");
     301    if (mkdtemp (buf) == NULL)
     302      {
     303        printf ("cannot create test directory in object directory: %m\n");
     304        exit (1);
     305      }
     306    if (stat64 (buf, &st1) < 0)
     307      {
     308        printf ("cannot stat new directory \"%s\": %m\n", buf);
     309        exit (1);
     310      }
     311    if (chmod (buf, 0700) < 0)
     312      {
     313        printf ("cannot change mode of new directory: %m\n");
     314        exit (1);
     315      }
     316  
     317    /* The test below covers the deprecated readdir64_r function.  */
     318    DIAG_PUSH_NEEDS_COMMENT;
     319    DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
     320  
     321    /* Try to find the new directory.  */
     322    rewinddir (dir1);
     323    while (readdir64_r (dir1, &direntbuf.d, &d) == 0 && d != NULL)
     324      {
     325        if (d->d_type != DT_UNKNOWN && d->d_type != DT_DIR)
     326  	continue;
     327  
     328        if (d->d_ino == st1.st_ino)
     329  	{
     330  	  /* Might be it.  Test the device.  We could use the st_dev
     331  	     element from st1 but what the heck, do more testing.  */
     332  	  size_t len = strlen (objdir) + 1 + _D_EXACT_NAMLEN (d) + 1;
     333  	  char tmpbuf[len];
     334  
     335  	  stpcpy (stpcpy (stpcpy (tmpbuf, objdir), "/"), d->d_name);
     336  
     337  	  if (stat64 (tmpbuf, &st3) < 0)
     338  	    {
     339  	      printf ("cannot stat entry from readdir: %m\n");
     340  	      result = 1;
     341  	      d = NULL;
     342  	      break;
     343  	    }
     344  
     345  	  if (st3.st_dev == st2.st_dev
     346  	      && strcmp (d->d_name, buf + strlen (buf) - 14) == 0)
     347  	    break;
     348  	}
     349      }
     350  
     351    DIAG_POP_NEEDS_COMMENT;
     352  
     353    if (d == NULL)
     354      {
     355        printf ("haven't found new directory \"%s\"\n", buf);
     356        exit (1);
     357      }
     358  
     359    if (closedir (dir2) < 0)
     360      {
     361        printf ("closing dir2 failed: %m\n");
     362        result = 1;
     363      }
     364  
     365    if (chdir (buf) < 0)
     366      {
     367        printf ("cannot change to new directory: %m\n");
     368        exit (1);
     369      }
     370  
     371    dir2 = opendir (buf);
     372    if (dir2 == NULL)
     373      {
     374        printf ("cannot open new directory: %m\n");
     375        exit (1);
     376      }
     377  
     378    if (fstat64 (dirfd (dir2), &st2) < 0)
     379      {
     380        printf ("cannot fstat new directory \"%s\": %m\n", buf);
     381        exit (1);
     382      }
     383    if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
     384      {
     385        printf ("stat of new directory failed: (%lld,%lld) vs (%lld,%lld)\n",
     386  	      (long long int) st1.st_dev, (long long int) st1.st_ino,
     387  	      (long long int) st2.st_dev, (long long int) st2.st_ino);
     388        exit (1);
     389      }
     390  
     391    if (mkdir ("another-dir", 0777) < 0)
     392      {
     393        printf ("cannot create \"another-dir\": %m\n");
     394        exit (1);
     395      }
     396    fd = open ("and-a-file", O_RDWR | O_CREAT | O_EXCL, 0666);
     397    if (fd == -1)
     398      {
     399        printf ("cannot create \"and-a-file\": %m\n");
     400        exit (1);
     401      }
     402    close (fd);
     403  
     404    /* Some tests about error reporting.  */
     405    errno = 0;
     406    if (chdir ("and-a-file") >= 0)
     407      {
     408        printf ("chdir to \"and-a-file\" succeeded\n");
     409        exit (1);
     410      }
     411    if (errno != ENOTDIR)
     412      {
     413        printf ("chdir to \"and-a-file\" didn't set correct error\n");
     414        result = 1;
     415      }
     416  
     417    errno = 0;
     418    if (chdir ("and-a-file/..") >= 0)
     419      {
     420        printf ("chdir to \"and-a-file/..\" succeeded\n");
     421        exit (1);
     422      }
     423    if (errno != ENOTDIR)
     424      {
     425        printf ("chdir to \"and-a-file/..\" didn't set correct error\n");
     426        result = 1;
     427      }
     428  
     429    errno = 0;
     430    if (chdir ("another-dir/../and-a-file") >= 0)
     431      {
     432        printf ("chdir to \"another-dir/../and-a-file\" succeeded\n");
     433        exit (1);
     434      }
     435    if (errno != ENOTDIR)
     436      {
     437        printf ("chdir to \"another-dir/../and-a-file\" didn't set correct error\n");
     438        result = 1;
     439      }
     440  
     441    /* The test below covers the deprecated readdir64_r function.  */
     442    DIAG_PUSH_NEEDS_COMMENT;
     443    DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
     444  
     445    /* We now should have a directory and a file in the new directory.  */
     446    rewinddir (dir2);
     447    while (readdir64_r (dir2, &direntbuf.d, &d) == 0 && d != NULL)
     448      {
     449        if (strcmp (d->d_name, ".") == 0
     450  	  || strcmp (d->d_name, "..") == 0
     451  	  || strcmp (d->d_name, "another-dir") == 0)
     452  	{
     453  	  if (d->d_type != DT_UNKNOWN && d->d_type != DT_DIR)
     454  	    {
     455  	      printf ("d_type for \"%s\" is wrong\n", d->d_name);
     456  	      result = 1;
     457  	    }
     458  	  if (stat64 (d->d_name, &st3) < 0)
     459  	    {
     460  	      printf ("cannot stat \"%s\" is wrong\n", d->d_name);
     461  	      result = 1;
     462  	    }
     463  	  else if (! S_ISDIR (st3.st_mode))
     464  	    {
     465  	      printf ("\"%s\" is no directory\n", d->d_name);
     466  	      result = 1;
     467  	    }
     468  	}
     469        else if (strcmp (d->d_name, "and-a-file") == 0)
     470  	{
     471  	  if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG)
     472  	    {
     473  	      printf ("d_type for \"%s\" is wrong\n", d->d_name);
     474  	      result = 1;
     475  	    }
     476  	  if (stat64 (d->d_name, &st3) < 0)
     477  	    {
     478  	      printf ("cannot stat \"%s\" is wrong\n", d->d_name);
     479  	      result = 1;
     480  	    }
     481  	  else if (! S_ISREG (st3.st_mode))
     482  	    {
     483  	      printf ("\"%s\" is no regular file\n", d->d_name);
     484  	      result = 1;
     485  	    }
     486  	}
     487        else
     488  	{
     489  	  printf ("unexpected directory entry \"%s\"\n", d->d_name);
     490  	  result = 1;
     491  	}
     492      }
     493  
     494    DIAG_POP_NEEDS_COMMENT;
     495  
     496    if (stat64 ("does-not-exist", &st1) >= 0)
     497      {
     498        puts ("stat for unexisting file did not fail");
     499        result = 1;
     500      }
     501  
     502    /* Free all resources.  */
     503  
     504    if (closedir (dir1) < 0)
     505      {
     506        printf ("closing dir1 failed: %m\n");
     507        result = 1;
     508      }
     509    if (closedir (dir2) < 0)
     510      {
     511        printf ("second closing dir2 failed: %m\n");
     512        result = 1;
     513      }
     514  
     515    if (rmdir ("another-dir") < 0)
     516      {
     517        printf ("cannot remove \"another-dir\": %m\n");
     518        result = 1;
     519      }
     520  
     521    if (unlink ("and-a-file") < 0)
     522      {
     523        printf ("cannot remove \"and-a-file\": %m\n");
     524        result = 1;
     525      }
     526  
     527    /* One more test before we leave: mkdir() is supposed to fail with
     528       EEXIST if the named file is a symlink.  */
     529    if (symlink ("a-symlink", "a-symlink") != 0)
     530      {
     531        printf ("cannot create symlink \"a-symlink\": %m\n");
     532        result = 1;
     533      }
     534    else
     535      {
     536        if (mkdir ("a-symlink", 0666) == 0)
     537  	{
     538  	  puts ("can make directory \"a-symlink\"");
     539  	  result = 1;
     540  	}
     541        else if (errno != EEXIST)
     542  	{
     543  	  puts ("mkdir(\"a-symlink\") does not fail with EEXIST\n");
     544  	  result = 1;
     545  	}
     546        if (unlink ("a-symlink") < 0)
     547  	{
     548  	  printf ("cannot unlink \"a-symlink\": %m\n");
     549  	  result = 1;
     550  	}
     551      }
     552  
     553    if (chdir (srcdir) < 0)
     554      {
     555        printf ("cannot change back to source directory: %m\n");
     556        exit (1);
     557      }
     558  
     559    if (rmdir (buf) < 0)
     560      {
     561        printf ("cannot remove \"%s\": %m\n", buf);
     562        result = 1;
     563      }
     564    free (objdir_copy1);
     565    free (objdir_copy2);
     566  
     567    if (result == 0)
     568      puts ("all OK");
     569  
     570    return result;
     571  }