Viewing: sorted_readdir.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2026, DataDirect Networks, Inc.
*
* Author: Xiyang Wang <xiwang@ddn.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#define OPEN_DIR_FLAGS (O_RDONLY | O_DIRECTORY)
static int exit_status;
static long long
extract_num(const char *s)
{
const char *p = s + strlen(s);
while (p > s && '0' <= p[-1] && p[-1] <= '9')
p--;
return strtoll(p, NULL, 10);
}
static int
cmp_name_numeric(const void *a, const void *b)
{
long long x = extract_num(*(const char **)a);
long long y = extract_num(*(const char **)b);
/* numeric field wins; tie-break with strcmp to keep the order
* independent of LC_COLLATE
*/
if (x < y)
return -1;
if (x > y)
return 1;
return strcmp(*(const char **)a, *(const char **)b);
}
static void
walk_dir(const char *dirpath)
{
struct stat root_st;
struct stat st;
struct dirent *d;
DIR *dir;
char **names;
size_t n_alloc;
size_t n_used;
size_t i;
int fd;
int dfd;
char subpath[PATH_MAX];
if (fstatat(AT_FDCWD, dirpath, &root_st, AT_SYMLINK_NOFOLLOW) < 0) {
fprintf(stderr, "sorted_readdir: cannot stat '%s': %s\n",
dirpath, strerror(errno));
exit_status = 1;
return;
}
fd = open(dirpath, OPEN_DIR_FLAGS);
if (fd < 0) {
fprintf(stderr, "sorted_readdir: cannot open directory '%s': %s\n",
dirpath, strerror(errno));
exit_status = 1;
return;
}
dir = fdopendir(fd);
if (!dir) {
fprintf(stderr, "sorted_readdir: fdopendir '%s': %s\n",
dirpath, strerror(errno));
close(fd);
exit_status = 1;
return;
}
/*
* Keep the dup'd fd (dfd) alive past closedir(): closedir() drops
* one reference, but dfd keeps the same struct file alive, so on
* llite ll_dir_release() -> ll_deauthorize_statahead() does not run
* until close(dfd) after the stat loop below. Without this, statahead
* would be torn down before the first fstatat() and test_123h would
* silently stop exercising it.
*/
dfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (dfd < 0) {
fprintf(stderr, "sorted_readdir: fcntl '%s': %s\n",
dirpath, strerror(errno));
closedir(dir);
exit_status = 1;
return;
}
n_used = 0;
n_alloc = (size_t)(root_st.st_size / 40);
if (n_alloc < 64)
n_alloc = 64;
names = malloc(n_alloc * sizeof(names[0]));
if (!names) {
fprintf(stderr, "sorted_readdir: memory allocation failed\n");
exit(1);
}
errno = 0;
while ((d = readdir(dir))) {
if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
continue;
if (n_used == n_alloc) {
n_alloc = n_alloc * 2;
names = realloc(names, n_alloc * sizeof(names[0]));
if (!names) {
fprintf(stderr, "sorted_readdir: memory allocation failed\n");
exit(1);
}
}
names[n_used] = malloc(strlen(d->d_name) + 1);
if (!names[n_used]) {
fprintf(stderr, "sorted_readdir: memory allocation failed\n");
exit(1);
}
strcpy(names[n_used], d->d_name);
n_used++;
}
if (errno) {
fprintf(stderr, "sorted_readdir: readdir '%s': %s\n",
dirpath, strerror(errno));
exit_status = 1;
}
qsort(names, n_used, sizeof(names[0]), cmp_name_numeric);
if (closedir(dir) < 0) {
fprintf(stderr, "sorted_readdir: closedir '%s': %s\n",
dirpath, strerror(errno));
exit_status = 1;
}
for (i = 0; i < n_used; i++) {
if (fstatat(dfd, names[i], &st, AT_SYMLINK_NOFOLLOW) < 0) {
fprintf(stderr, "sorted_readdir: cannot stat '%s' in '%s': %s\n",
names[i], dirpath, strerror(errno));
exit_status = 1;
continue;
}
if (S_ISDIR(st.st_mode)) {
snprintf(subpath, sizeof(subpath), "%s/%s",
dirpath, names[i]);
walk_dir(subpath);
continue;
}
printf("%s/%s\n", dirpath, names[i]);
}
close(dfd);
for (i = 0; i < n_used; i++)
free(names[i]);
free(names);
printf("%s\n", dirpath);
}
int
main(int argc, char *argv[])
{
int i;
if (argc < 2)
walk_dir(".");
else
for (i = 1; i < argc; i++)
walk_dir(argv[i]);
return exit_status;
}