/* Test the fts function. Copyright 2017-2023 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #define BASE "t-fts.tmp" static char base[] = BASE; /* Not const, since argv needs non-const. */ static char const base_d[] = BASE "/d"; static char *const argv[2] = { base, 0 }; static void perror_exit (char const *message, int status) { perror (message); exit (status); } /* alloc/dealloc to ensure structures initialized appropriately. */ static void fts_dealloc (void) { static char dir[] = "./"; static char *const curr_dir[2] = { dir, 0 }; FTS *ftsp = fts_open (curr_dir, FTS_NOSTAT | FTS_PHYSICAL | FTS_CWDFD, 0); if (ftsp) { if (fts_close (ftsp) != 0) perror_exit ("fts_close", 9); } else perror_exit (base, 10); } /* Remove BASE and all files under it. */ static void remove_tree (void) { FTSENT *e; FTS *ftsp = fts_open (argv, FTS_NOSTAT | FTS_PHYSICAL | FTS_CWDFD, 0); if (ftsp) { while ((e = fts_read (ftsp))) { int status = 0; switch (e->fts_info) { case FTS_DP: status = unlinkat (ftsp->fts_cwd_fd, e->fts_accpath, AT_REMOVEDIR); break; case FTS_F: case FTS_NSOK: status = unlinkat (ftsp->fts_cwd_fd, e->fts_accpath, 0); break; } if (status != 0) perror_exit (e->fts_path, 1); } if (fts_close (ftsp) != 0) perror_exit ("fts_close", 2); } else if (errno != ENOENT) perror_exit (base, 3); } int main (void) { FTS *ftsp; FTSENT *e; char buf[sizeof BASE + 100]; int i; enum { needles = 4 }; int needles_seen = 0; struct stat st; remove_tree (); /* Create directories BASE, BASE/d, BASE/d/1, BASE/d/2, ..., BASE/d/65536, to stress-test fts. Stop if directory creation fails due to EMFILE or EMLINK problems, or if BASE/d's link count no longer matches the Unix tradition. See: https://bugzilla.kernel.org/show_bug.cgi?id=196405 for more info. */ if (mkdir (BASE, 0777) != 0) perror_exit (base, 4); if (mkdir (base_d, 0777) != 0) perror_exit (base_d, 5); for (i = 1; i <= 65536; i++) { sprintf (buf, "%s/d/%i", base, i); if (mkdir (buf, 0777) != 0) { if (errno == EMFILE || errno == EMLINK) break; if (i <= needles) perror_exit (buf, 77); break; } if (needles < i && stat (base_d, &st) == 0 && st.st_nlink != i + 2) break; } /* Create empty files BASE/d/1/needle etc. */ for (i = 1; i <= needles; i++) { int fd; sprintf (buf, "%s/d/%d/needle", base, i); fd = open (buf, O_WRONLY | O_CREAT, 0666); if (fd < 0 || close (fd) != 0) perror_exit (buf, 77); } /* Use fts to look for the needles. */ ftsp = fts_open (argv, FTS_SEEDOT | FTS_NOSTAT | FTS_PHYSICAL | FTS_CWDFD, 0); if (!ftsp) perror_exit (base, 6); while ((e = fts_read (ftsp))) needles_seen += strcmp (e->fts_name, "needle") == 0; int fts_read_errno = errno; fflush (stdout); if (fts_read_errno) { errno = fts_read_errno; perror_exit ("fts_read", 7); } if (fts_close (ftsp) != 0) perror_exit (base, 8); /* Report an error if we did not find the needles. */ if (needles_seen != needles) { fprintf (stderr, "%d needles found (should be %d)\n", needles_seen, needles); return 1; } remove_tree (); if (stat (base, &st) == 0) { fprintf (stderr, "fts could not remove directory\n"); return 1; } fts_dealloc (); return 0; }