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
|
#include "internal.h"
#define hash(k) ((int32)k.ino ^ (int32)k.dev)
#define equal(k1, k2) (k1.ino == k2.ino && k1.dev == k2.dev)
static
int
morehistory(fs·History *h, int n)
{
SET_GROW(h, struct Key, n, hash, sys·Memory, nil);
}
static
int
addentry(fs·History *h, struct Key key, int *err)
{
SET_PUT(h, key, hash, equal, morehistory, err);
}
static
void
forget(fs·History *h)
{
if (!h)
return;
SET_RESET(h);
}
void
fs·walk(fs·Walker *fs)
{
char *e, *b;
DIR *dir;
int new, fd, ofd, flags;
fs·History *h;
struct dirent *d;
io·Stat cwd;
struct fs·Entry *it;
flags = 0;
if(fs->flags & fs·nolinks)
flags |= AT_SYMLINK_NOFOLLOW;
/* get info for base relative to current fd */
if(fstatat(fs->fd, fs->base, &cwd, flags) < 0){
if(fs->flags & fs·verbose)
errorf("stat: %s", fs->path);
return;
}
/* if we hit a file, finish! */
if(!S_ISDIR(cwd.st_mode)) {
fs->func(fs->data, fs->base, fs->path, &cwd);
return;
}
/* have we been here before? (cycle detection) */
/* if not, add to our path history */
if(!(fs->flags & fs·nolinks)){
addentry(fs->hist, (struct Key){.dev=cwd.st_dev, .ino=cwd.st_ino}, &new);
if (!new)
return;
}
/*
* operate on directory first if preorder traversal
* truncate recursion if callback returns an error code
*/
if(fs->flags & fs·preorder){
if(fs->func(fs->data, fs->base, fs->path, &cwd))
return;
}
/* open directory */
if(!fs->max || fs->lev + 1 < fs->max) {
fd = openat(fs->fd, fs->base, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
if(fd < 0)
errorf("open %s:", fs->path);
if (!(dir=fdopendir(fd))) {
if(fs->flags & fs·verbose)
errorf("fdopendir: %s", fs->path);
return;
}
ofd = fs->fd, fs->fd = fd;
/* traverse children */
e = fs->end, b = fs->base;
if (fs->end[-1] != '/')
*fs->end++ = '/';
fs->base = fs->end;
while((d = readdir(dir))) {
if(*d->d_name == '.')
if(d->d_name[1] == 0 || /* . */
(d->d_name[1] == '.' && d->d_name[2] == 0)) /* .. */
continue;
fs->end = str·copyn(fs->base, d->d_name, arrend(fs->path) - fs->base);
fs->lev++;
fs·walk(fs);
fs->lev--;
}
*e = 0;
fs->fd = ofd;
fs->end = e, fs->base = b;
closedir(dir);
}
/* operate on directory if postorder (default) traversal */
if (!(fs->flags & fs·preorder))
fs->func(fs->data, fs->base, fs->path, &cwd);
if (!fs->lev)
forget(fs->hist);
}
|