(root)/
glibc-2.38/
io/
tst-ftw-lnk.c
       1  /* Test for ftw function related to symbolic links for BZ #23501
       2     Copyright (C) 2019-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 <ftw.h>
      20  #include <stdio.h>
      21  #include <string.h>
      22  #include <sys/types.h>
      23  #include <sys/stat.h>
      24  #include <unistd.h>
      25  #include <fcntl.h>
      26  #include <errno.h>
      27  
      28  #include <support/support.h>
      29  #include <support/check.h>
      30  
      31  #define TSTDIR "tst-ftw-lnk.d"
      32  
      33  static void
      34  un (const char *file)
      35  {
      36    struct stat st;
      37    /* Does the file exist?  */
      38    if (lstat (file, &st) < 0
      39        && errno == ENOENT)
      40      return;
      41  
      42    /* If so, try to remove it.  */
      43    if (unlink (file) < 0)
      44      FAIL_EXIT1 ("Unable to unlink %s", file);
      45  }
      46  
      47  static void
      48  debug_cb (const char *which, const char *fpath,
      49  	  const struct stat *sb, int typeflags)
      50  {
      51    const char *sb_type = "???";
      52    const char *ftw_type = "???";
      53  
      54    /* Coding style here is intentionally "wrong" to increase readability.  */
      55    if (S_ISREG (sb->st_mode))  sb_type = "REG";
      56    if (S_ISDIR (sb->st_mode))  sb_type = "DIR";
      57    if (S_ISLNK (sb->st_mode))  sb_type = "LNK";
      58  
      59    if (typeflags == FTW_F)   ftw_type = "F";
      60    if (typeflags == FTW_D)   ftw_type = "D";
      61    if (typeflags == FTW_DNR) ftw_type = "DNR";
      62    if (typeflags == FTW_DP)  ftw_type = "DP";
      63    if (typeflags == FTW_NS)  ftw_type = "NS";
      64    if (typeflags == FTW_SL)  ftw_type = "SL";
      65    if (typeflags == FTW_SLN) ftw_type = "SLN";
      66  
      67    printf ("%s %5d %-3s %-3s %s\n", which, (int)(sb->st_ino % 100000), sb_type, ftw_type, fpath);
      68  }
      69  
      70  int good_cb = 0;
      71  #define EXPECTED_GOOD 12
      72  
      73  /* See if the stat buffer SB refers to the file AS_FNAME.  */
      74  static void
      75  check_same_stats (const struct stat *sb, const char *as_fname)
      76  {
      77    struct stat as;
      78    if (lstat (as_fname, &as) < 0)
      79      FAIL_EXIT1 ("unable to stat %s for comparison", as_fname);
      80  
      81    if (as.st_mode == sb->st_mode
      82        && as.st_ino == sb->st_ino
      83        && as.st_size == sb->st_size)
      84      good_cb ++;
      85    else
      86      printf ("statbuf data doesn't match %s\n", as_fname);
      87  }
      88  
      89  static int
      90  callback_phys (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf)
      91  {
      92    debug_cb ("P", fpath, sb, typeflags);
      93  
      94    /* This callback is for when the FTW_PHYS flag is set.  The results
      95       should reflect the physical filesystem entry, not what it might
      96       point to.  */
      97  
      98    /* link1-bad is a dangling symlink, but we're reporting on the link
      99       anyway (ala lstat ()).  */
     100    if (strcmp (fpath, "./link1-bad") == 0)
     101      {
     102        if (S_ISLNK (sb->st_mode) && typeflags == FTW_SL)
     103  	good_cb ++;
     104        else
     105  	printf ("link1-bad had wrong phys stats\n");
     106  
     107        check_same_stats (sb, "link1-bad");
     108      }
     109  
     110    /* link2-ok is a regular non-dangling symlink.  */
     111    if (strcmp (fpath, "./link2-ok") == 0)
     112      {
     113        if (S_ISLNK (sb->st_mode) && typeflags == FTW_SL)
     114  	good_cb ++;
     115        else
     116  	printf ("link2-ok had wrong phys stats\n");
     117  
     118        check_same_stats (sb, "link2-ok");
     119      }
     120  
     121    /* This is the file link2-ok points to.  */
     122    if (strcmp (fpath, "./link2-tgt") == 0)
     123      {
     124        if (S_ISREG (sb->st_mode) && typeflags == FTW_F)
     125  	good_cb ++;
     126        else
     127  	printf ("link2-tgt had wrong phys stats\n");
     128  
     129        check_same_stats (sb, "link2-tgt");
     130      }
     131  
     132    return 0;
     133  }
     134  
     135  static int
     136  callback_log (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf)
     137  {
     138    debug_cb ("L", fpath, sb, typeflags);
     139  
     140    /* This callback is for when the FTW_PHYS flags is NOT set.  The
     141       results should reflect the logical file, i.e. symlinks should be
     142       followed.  */
     143  
     144    /* We would normally report what link1-bad links to, but link1-bad
     145       is a dangling symlink.  This is an exception to FTW_PHYS in that
     146       we report FTW_SLN (dangling symlink) but the stat data is
     147       correctly set to the link itself (ala lstat ()).  */
     148    if (strcmp (fpath, "./link1-bad") == 0)
     149      {
     150        if (S_ISLNK (sb->st_mode) && typeflags == FTW_SLN)
     151  	good_cb ++;
     152        else
     153  	printf ("link1-bad had wrong logical stats\n");
     154  
     155        check_same_stats (sb, "link1-bad");
     156      }
     157  
     158    /* link2-ok points to link2-tgt, so we expect data reflecting
     159       link2-tgt (ala stat ()).  */
     160    if (strcmp (fpath, "./link2-ok") == 0)
     161      {
     162        if (S_ISREG (sb->st_mode) && typeflags == FTW_F)
     163  	good_cb ++;
     164        else
     165  	printf ("link2-ok had wrong logical stats\n");
     166  
     167        check_same_stats (sb, "link2-tgt");
     168      }
     169  
     170    /* This is the file link2-ok points to.  */
     171    if (strcmp (fpath, "./link2-tgt") == 0)
     172      {
     173        if (S_ISREG (sb->st_mode) && typeflags == FTW_F)
     174  	good_cb ++;
     175        else
     176  	printf ("link2-tgt had wrong logical stats\n");
     177  
     178        check_same_stats (sb, "link2-tgt");
     179      }
     180  
     181    return 0;
     182  }
     183  
     184  static int
     185  do_test (void)
     186  {
     187    struct stat st;
     188  
     189    if (chdir (support_objdir_root) < 0)
     190      FAIL_EXIT1 ("cannot chdir to objdir root");
     191  
     192    if (chdir ("io") < 0)
     193      FAIL_EXIT1 ("cannot chdir to objdir/io subdir");
     194  
     195    if (stat (TSTDIR, &st) >= 0)
     196      {
     197        /* Directory does exist, delete any potential conflicts. */
     198        if (chdir (TSTDIR) < 0)
     199  	FAIL_EXIT1 ("cannot chdir to %s\n", TSTDIR);
     200        un ("link1-bad");
     201        un ("link1-tgt");
     202        un ("link2-ok");
     203        un ("link2-tgt");
     204      }
     205    else
     206      {
     207        /* Directory does not exist, create it.  */
     208        mkdir (TSTDIR, 0777);
     209        if (chdir (TSTDIR) < 0)
     210  	FAIL_EXIT1 ("cannot chdir to %s\n", TSTDIR);
     211      }
     212  
     213    /* At this point, we're inside our test directory, and need to
     214       prepare it.  */
     215  
     216    if (symlink ("link1-tgt", "link1-bad") < 0)
     217      FAIL_EXIT1 ("symlink link1-bad failed");
     218    if (symlink ("link2-tgt", "link2-ok") < 0)
     219      FAIL_EXIT1 ("symlink link2-ok failed");
     220    if (open ("link2-tgt", O_RDWR|O_CREAT, 0777) < 0)
     221      FAIL_EXIT1 ("create of link2-tgt failed");
     222  
     223    /* Now we run the tests.  */
     224  
     225    nftw (".", callback_phys, 10, FTW_PHYS);
     226    nftw (".", callback_log, 10, 0);
     227  
     228    /* Did we see the expected number of correct callbacks? */
     229  
     230    if (good_cb != EXPECTED_GOOD)
     231      {
     232        FAIL_EXIT1 ("Saw %d good callbacks, expected %d\n",
     233  		  good_cb, EXPECTED_GOOD);
     234      }
     235  
     236    return 0;
     237  }
     238  
     239  #include <support/test-driver.c>