173 lines
4.4 KiB
C
173 lines
4.4 KiB
C
/* Test the fts function.
|
|
Copyright 2017-2024 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 <https://www.gnu.org/licenses/>. */
|
|
|
|
#include <config.h>
|
|
|
|
#include <fts_.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#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, NULL };
|
|
|
|
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, NULL };
|
|
FTS *ftsp = fts_open (curr_dir, FTS_NOSTAT | FTS_PHYSICAL | FTS_CWDFD, NULL);
|
|
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, NULL);
|
|
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,
|
|
NULL);
|
|
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;
|
|
}
|