-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdirents.c
149 lines (125 loc) · 3.65 KB
/
dirents.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
* SPDX-License-Identifier: ISC
* SPDX-URL: https://spdx.org/licenses/ISC.html
*
* Copyright (C) 2023-2024 Aaron M. D. Jones <[email protected]>
*/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <unistd.h>
#include "filemap.h"
bool FM_NONNULL(2, 3) FM_WARN_UNUSED
fm_scan_directory(const int fd, const struct stat *const restrict sb, const char *const restrict abspath)
{
DIR *dp;
if (! fm_run_quietly)
(void) fm_print_message("%s: scanning %s ...", argvzero, abspath);
if (fm_sync_files && fsync(fd) < 0)
{
(void) fm_print_message("%s: while scanning '%s': fsync(2): %s\n",
argvzero, abspath, strerror(errno));
return false;
}
if ((dp = fdopendir(fd)) == NULL)
{
(void) fm_print_message("%s: while scanning '%s': fdopendir(3): %s\n",
argvzero, abspath, strerror(errno));
return false;
}
for (;;)
{
char entpath[PATH_MAX];
struct dirent *ede;
struct stat esb;
int efd;
if (! fm_run_quietly)
(void) fm_print_message("%s: walking %s ...", argvzero, abspath);
errno = 0;
if ((ede = readdir(dp)) == NULL)
{
if (errno == 0)
// End of directory entries
break;
(void) fm_print_message("%s: while walking '%s': readdir(3): %s\n",
argvzero, abspath, strerror(errno));
return false;
}
if (strcmp(ede->d_name, ".") == 0 || strcmp(ede->d_name, "..") == 0)
// Avoid infinite loops
continue;
(void) memset(entpath, 0x00, sizeof entpath);
(void) memset(&esb, 0x00, sizeof esb);
// Prepend a slash to the directory entry name only if the directory is not /
(void) snprintf(entpath, sizeof entpath, "%s%s%s",
abspath, ((strcmp(abspath, "/") != 0) ? "/" : ""), ede->d_name);
if (fstatat(fd, ede->d_name, &esb, AT_SYMLINK_NOFOLLOW) < 0)
{
(void) fm_print_message("%s: while scanning '%s': fstatat(2): %s\n",
argvzero, entpath, strerror(errno));
return false;
}
if (esb.st_dev != sb->st_dev)
// Not on the same filesystem
continue;
if (! (((esb.st_mode & S_IFMT) == S_IFDIR) || ((esb.st_mode & S_IFMT) == S_IFREG)))
// Not a file or directory
continue;
(void) memset(&esb, 0x00, sizeof esb);
if ((efd = openat(fd, ede->d_name, O_NOCTTY | O_RDONLY | O_NOFOLLOW, 0)) < 0)
{
(void) fm_print_message("%s: while scanning '%s': openat(2): %s\n",
argvzero, entpath, strerror(errno));
return false;
}
if (fstat(efd, &esb) < 0)
{
(void) fm_print_message("%s: while scanning '%s': fstat(2): %s\n",
argvzero, entpath, strerror(errno));
return false;
}
if (esb.st_dev != sb->st_dev)
{
// Not on the same filesystem
(void) close(efd);
continue;
}
if ((esb.st_mode & S_IFMT) == S_IFDIR)
{
if (! fm_scan_directory(efd, &esb, entpath))
// This function prints messages on error
return false;
// The function called above will close the fd
continue;
}
if ((esb.st_mode & S_IFMT) == S_IFREG)
{
if (! fm_scan_extents(efd, &esb, entpath))
// This function prints messages on error
return false;
// The function called above will close the fd
continue;
}
// This directory entry wasn't a file or directory; close the fd
(void) close(efd);
}
if (fm_scan_directories)
{
if (! fm_scan_extents(fd, sb, abspath))
// This function prints messages on error
return false;
}
// This will also close the fd
(void) closedir(dp);
return true;
}