/* Test for ftw function related to symbolic links for BZ #23501 Copyright (C) 2019-2020 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #include #include #include #include #include #include #include #include #include #include #define TSTDIR "tst-ftw-lnk.d" static void un (const char *file) { struct stat st; /* Does the file exist? */ if (lstat (file, &st) < 0 && errno == ENOENT) return; /* If so, try to remove it. */ if (unlink (file) < 0) FAIL_EXIT1 ("Unable to unlink %s", file); } static void debug_cb (const char *which, const char *fpath, const struct stat *sb, int typeflags) { const char *sb_type = "???"; const char *ftw_type = "???"; /* Coding style here is intentionally "wrong" to increase readability. */ if (S_ISREG (sb->st_mode)) sb_type = "REG"; if (S_ISDIR (sb->st_mode)) sb_type = "DIR"; if (S_ISLNK (sb->st_mode)) sb_type = "LNK"; if (typeflags == FTW_F) ftw_type = "F"; if (typeflags == FTW_D) ftw_type = "D"; if (typeflags == FTW_DNR) ftw_type = "DNR"; if (typeflags == FTW_DP) ftw_type = "DP"; if (typeflags == FTW_NS) ftw_type = "NS"; if (typeflags == FTW_SL) ftw_type = "SL"; if (typeflags == FTW_SLN) ftw_type = "SLN"; printf ("%s %5d %-3s %-3s %s\n", which, (int)(sb->st_ino % 100000), sb_type, ftw_type, fpath); } int good_cb = 0; #define EXPECTED_GOOD 12 /* See if the stat buffer SB refers to the file AS_FNAME. */ static void check_same_stats (const struct stat *sb, const char *as_fname) { struct stat as; if (lstat (as_fname, &as) < 0) FAIL_EXIT1 ("unable to stat %s for comparison", as_fname); if (as.st_mode == sb->st_mode && as.st_ino == sb->st_ino && as.st_size == sb->st_size) good_cb ++; else printf ("statbuf data doesn't match %s\n", as_fname); } static int callback_phys (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf) { debug_cb ("P", fpath, sb, typeflags); /* This callback is for when the FTW_PHYS flag is set. The results should reflect the physical filesystem entry, not what it might point to. */ /* link1-bad is a dangling symlink, but we're reporting on the link anyway (ala lstat ()). */ if (strcmp (fpath, "./link1-bad") == 0) { if (S_ISLNK (sb->st_mode) && typeflags == FTW_SL) good_cb ++; else printf ("link1-bad had wrong phys stats\n"); check_same_stats (sb, "link1-bad"); } /* link2-ok is a regular non-dangling symlink. */ if (strcmp (fpath, "./link2-ok") == 0) { if (S_ISLNK (sb->st_mode) && typeflags == FTW_SL) good_cb ++; else printf ("link2-ok had wrong phys stats\n"); check_same_stats (sb, "link2-ok"); } /* This is the file link2-ok points to. */ if (strcmp (fpath, "./link2-tgt") == 0) { if (S_ISREG (sb->st_mode) && typeflags == FTW_F) good_cb ++; else printf ("link2-tgt had wrong phys stats\n"); check_same_stats (sb, "link2-tgt"); } return 0; } static int callback_log (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf) { debug_cb ("L", fpath, sb, typeflags); /* This callback is for when the FTW_PHYS flags is NOT set. The results should reflect the logical file, i.e. symlinks should be followed. */ /* We would normally report what link1-bad links to, but link1-bad is a dangling symlink. This is an exception to FTW_PHYS in that we report FTW_SLN (dangling symlink) but the stat data is correctly set to the link itself (ala lstat ()). */ if (strcmp (fpath, "./link1-bad") == 0) { if (S_ISLNK (sb->st_mode) && typeflags == FTW_SLN) good_cb ++; else printf ("link1-bad had wrong logical stats\n"); check_same_stats (sb, "link1-bad"); } /* link2-ok points to link2-tgt, so we expect data reflecting link2-tgt (ala stat ()). */ if (strcmp (fpath, "./link2-ok") == 0) { if (S_ISREG (sb->st_mode) && typeflags == FTW_F) good_cb ++; else printf ("link2-ok had wrong logical stats\n"); check_same_stats (sb, "link2-tgt"); } /* This is the file link2-ok points to. */ if (strcmp (fpath, "./link2-tgt") == 0) { if (S_ISREG (sb->st_mode) && typeflags == FTW_F) good_cb ++; else printf ("link2-tgt had wrong logical stats\n"); check_same_stats (sb, "link2-tgt"); } return 0; } static int do_test (void) { struct stat st; if (chdir (support_objdir_root) < 0) FAIL_EXIT1 ("cannot chdir to objdir root"); if (chdir ("io") < 0) FAIL_EXIT1 ("cannot chdir to objdir/io subdir"); if (stat (TSTDIR, &st) >= 0) { /* Directory does exist, delete any potential conflicts. */ if (chdir (TSTDIR) < 0) FAIL_EXIT1 ("cannot chdir to %s\n", TSTDIR); un ("link1-bad"); un ("link1-tgt"); un ("link2-ok"); un ("link2-tgt"); } else { /* Directory does not exist, create it. */ mkdir (TSTDIR, 0777); if (chdir (TSTDIR) < 0) FAIL_EXIT1 ("cannot chdir to %s\n", TSTDIR); } /* At this point, we're inside our test directory, and need to prepare it. */ if (symlink ("link1-tgt", "link1-bad") < 0) FAIL_EXIT1 ("symlink link1-bad failed"); if (symlink ("link2-tgt", "link2-ok") < 0) FAIL_EXIT1 ("symlink link2-ok failed"); if (open ("link2-tgt", O_RDWR|O_CREAT, 0777) < 0) FAIL_EXIT1 ("create of link2-tgt failed"); /* Now we run the tests. */ nftw (".", callback_phys, 10, FTW_PHYS); nftw (".", callback_log, 10, 0); /* Did we see the expected number of correct callbacks? */ if (good_cb != EXPECTED_GOOD) { FAIL_EXIT1 ("Saw %d good callbacks, expected %d\n", good_cb, EXPECTED_GOOD); } return 0; } #include