| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2023 SUSE Linux Products GmbH. All Rights Reserved. |
| */ |
| #include <dirent.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| |
| /* |
| * Number of files we add to the test directory after calling opendir(3) and |
| * before calling rewinddir(3). |
| */ |
| #define NUM_FILES 10000 |
| |
| int main(int argc, char *argv[]) |
| { |
| int file_counters[NUM_FILES] = { 0 }; |
| int dot_count = 0; |
| int dot_dot_count = 0; |
| struct dirent *entry; |
| DIR *dir = NULL; |
| char *dir_path = NULL; |
| char *file_path = NULL; |
| int ret = 0; |
| int i; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <directory>\n", argv[0]); |
| ret = 1; |
| goto out; |
| } |
| |
| dir_path = malloc(strlen(argv[1]) + strlen("/testdir") + 1); |
| if (!dir_path) { |
| fprintf(stderr, "malloc failure\n"); |
| ret = ENOMEM; |
| goto out; |
| } |
| i = strlen(argv[1]); |
| memcpy(dir_path, argv[1], i); |
| memcpy(dir_path + i, "/testdir", strlen("/testdir")); |
| dir_path[i + strlen("/testdir")] = '\0'; |
| |
| /* More than enough to contain any full file path. */ |
| file_path = malloc(strlen(dir_path) + 12); |
| if (!file_path) { |
| fprintf(stderr, "malloc failure\n"); |
| ret = ENOMEM; |
| goto out; |
| } |
| |
| ret = mkdir(dir_path, 0700); |
| if (ret == -1) { |
| fprintf(stderr, "Failed to create test directory: %d\n", errno); |
| ret = errno; |
| goto out; |
| } |
| |
| /* Open the directory first. */ |
| dir = opendir(dir_path); |
| if (dir == NULL) { |
| fprintf(stderr, "Failed to open directory: %d\n", errno); |
| ret = errno; |
| goto out; |
| } |
| |
| /* |
| * Now create all files inside the directory. |
| * File names go from 1 to NUM_FILES, 0 is unused as it's the return |
| * value for atoi(3) when an error happens. |
| */ |
| for (i = 1; i <= NUM_FILES; i++) { |
| FILE *f; |
| |
| sprintf(file_path, "%s/%d", dir_path, i); |
| f = fopen(file_path, "w"); |
| if (f == NULL) { |
| fprintf(stderr, "Failed to create file number %d: %d\n", |
| i, errno); |
| ret = errno; |
| goto out; |
| } |
| fclose(f); |
| } |
| |
| /* |
| * Rewind the directory and read it. |
| * POSIX requires that after a rewind, any new names added to the |
| * directory after the openddir(3) call and before the rewinddir(3) |
| * call, must be returned by readdir(3) calls |
| */ |
| rewinddir(dir); |
| |
| /* |
| * readdir(3) returns NULL when it reaches the end of the directory or |
| * when an error happens, so reset errno to 0 to distinguish between |
| * both cases later. |
| */ |
| errno = 0; |
| while ((entry = readdir(dir)) != NULL) { |
| if (strcmp(entry->d_name, ".") == 0) { |
| dot_count++; |
| continue; |
| } |
| if (strcmp(entry->d_name, "..") == 0) { |
| dot_dot_count++; |
| continue; |
| } |
| i = atoi(entry->d_name); |
| if (i == 0) { |
| fprintf(stderr, |
| "Failed to parse name '%s' to integer: %d\n", |
| entry->d_name, errno); |
| ret = errno; |
| goto out; |
| } |
| /* File names go from 1 to NUM_FILES, so subtract 1. */ |
| file_counters[i - 1]++; |
| } |
| |
| if (errno) { |
| fprintf(stderr, "Failed to read directory: %d\n", errno); |
| ret = errno; |
| goto out; |
| } |
| |
| /* |
| * Now check that the readdir() calls return every single file name |
| * and without repeating any of them. If any name is missing or |
| * repeated, don't exit immediatelly, so that we print a message for |
| * all missing or repeated names. |
| */ |
| for (i = 0; i < NUM_FILES; i++) { |
| if (file_counters[i] != 1) { |
| fprintf(stderr, "File name %d appeared %d times\n", |
| i + 1, file_counters[i]); |
| ret = EINVAL; |
| } |
| } |
| if (dot_count != 1) { |
| fprintf(stderr, "File name . appeared %d times\n", dot_count); |
| ret = EINVAL; |
| } |
| if (dot_dot_count != 1) { |
| fprintf(stderr, "File name .. appeared %d times\n", dot_dot_count); |
| ret = EINVAL; |
| } |
| out: |
| free(dir_path); |
| free(file_path); |
| if (dir != NULL) |
| closedir(dir); |
| |
| return ret; |
| } |