1*cf5a6c84SAndroid Build Coastguard Worker /* dirtree.c - Functions for dealing with directory trees.
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2007 Rob Landley <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker */
5*cf5a6c84SAndroid Build Coastguard Worker
6*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
7*cf5a6c84SAndroid Build Coastguard Worker
isdotdot(char * name)8*cf5a6c84SAndroid Build Coastguard Worker int isdotdot(char *name)
9*cf5a6c84SAndroid Build Coastguard Worker {
10*cf5a6c84SAndroid Build Coastguard Worker if (name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2]))) return 1;
11*cf5a6c84SAndroid Build Coastguard Worker
12*cf5a6c84SAndroid Build Coastguard Worker return 0;
13*cf5a6c84SAndroid Build Coastguard Worker }
14*cf5a6c84SAndroid Build Coastguard Worker
15*cf5a6c84SAndroid Build Coastguard Worker // Default callback, filters out "." and ".." except at top level.
16*cf5a6c84SAndroid Build Coastguard Worker
dirtree_notdotdot(struct dirtree * catch)17*cf5a6c84SAndroid Build Coastguard Worker int dirtree_notdotdot(struct dirtree *catch)
18*cf5a6c84SAndroid Build Coastguard Worker {
19*cf5a6c84SAndroid Build Coastguard Worker // Should we skip "." and ".."?
20*cf5a6c84SAndroid Build Coastguard Worker return (!catch->parent||!isdotdot(catch->name))
21*cf5a6c84SAndroid Build Coastguard Worker *(DIRTREE_SAVE|DIRTREE_RECURSE);
22*cf5a6c84SAndroid Build Coastguard Worker }
23*cf5a6c84SAndroid Build Coastguard Worker
24*cf5a6c84SAndroid Build Coastguard Worker // Create a dirtree node from a path, with stat and symlink info.
25*cf5a6c84SAndroid Build Coastguard Worker // (This doesn't open directory filehandles yet so as not to exhaust the
26*cf5a6c84SAndroid Build Coastguard Worker // filehandle space on large trees, dirtree_handle_callback() does that.)
27*cf5a6c84SAndroid Build Coastguard Worker
dirtree_add_node(struct dirtree * parent,char * name,int flags)28*cf5a6c84SAndroid Build Coastguard Worker struct dirtree *dirtree_add_node(struct dirtree *parent, char *name, int flags)
29*cf5a6c84SAndroid Build Coastguard Worker {
30*cf5a6c84SAndroid Build Coastguard Worker struct dirtree *dt = 0;
31*cf5a6c84SAndroid Build Coastguard Worker struct stat st;
32*cf5a6c84SAndroid Build Coastguard Worker int len = 0, linklen = 0, statless = 0;
33*cf5a6c84SAndroid Build Coastguard Worker
34*cf5a6c84SAndroid Build Coastguard Worker if (name) {
35*cf5a6c84SAndroid Build Coastguard Worker // open code fd = because haven't got node to call dirtree_parentfd() on yet
36*cf5a6c84SAndroid Build Coastguard Worker int fd = parent ? parent->dirfd : AT_FDCWD,
37*cf5a6c84SAndroid Build Coastguard Worker sym = AT_SYMLINK_NOFOLLOW*!(flags&DIRTREE_SYMFOLLOW);
38*cf5a6c84SAndroid Build Coastguard Worker
39*cf5a6c84SAndroid Build Coastguard Worker // stat dangling symlinks
40*cf5a6c84SAndroid Build Coastguard Worker if (fstatat(fd, name, &st, sym)) {
41*cf5a6c84SAndroid Build Coastguard Worker // If we got ENOENT without NOFOLLOW, try again with NOFOLLOW.
42*cf5a6c84SAndroid Build Coastguard Worker if (errno!=ENOENT || sym || fstatat(fd, name, &st, AT_SYMLINK_NOFOLLOW)) {
43*cf5a6c84SAndroid Build Coastguard Worker if (flags&DIRTREE_STATLESS) statless++;
44*cf5a6c84SAndroid Build Coastguard Worker else goto error;
45*cf5a6c84SAndroid Build Coastguard Worker }
46*cf5a6c84SAndroid Build Coastguard Worker }
47*cf5a6c84SAndroid Build Coastguard Worker if (!statless && S_ISLNK(st.st_mode)) {
48*cf5a6c84SAndroid Build Coastguard Worker if (0>(linklen = readlinkat(fd, name, libbuf, 4095))) goto error;
49*cf5a6c84SAndroid Build Coastguard Worker libbuf[linklen++]=0;
50*cf5a6c84SAndroid Build Coastguard Worker }
51*cf5a6c84SAndroid Build Coastguard Worker len = strlen(name);
52*cf5a6c84SAndroid Build Coastguard Worker }
53*cf5a6c84SAndroid Build Coastguard Worker
54*cf5a6c84SAndroid Build Coastguard Worker // Allocate/populate return structure
55*cf5a6c84SAndroid Build Coastguard Worker dt = xmalloc((len = sizeof(struct dirtree)+len+1)+linklen);
56*cf5a6c84SAndroid Build Coastguard Worker memset(dt, 0, sizeof(struct dirtree));
57*cf5a6c84SAndroid Build Coastguard Worker dt->parent = parent;
58*cf5a6c84SAndroid Build Coastguard Worker if (statless) dt->again = DIRTREE_STATLESS;
59*cf5a6c84SAndroid Build Coastguard Worker else memcpy(&dt->st, &st, sizeof(struct stat));
60*cf5a6c84SAndroid Build Coastguard Worker if (name) strcpy(dt->name, name);
61*cf5a6c84SAndroid Build Coastguard Worker else *dt->name = 0, dt->st.st_mode = S_IFDIR;
62*cf5a6c84SAndroid Build Coastguard Worker if (linklen) dt->symlink = memcpy(len+(char *)dt, libbuf, linklen);
63*cf5a6c84SAndroid Build Coastguard Worker
64*cf5a6c84SAndroid Build Coastguard Worker return dt;
65*cf5a6c84SAndroid Build Coastguard Worker
66*cf5a6c84SAndroid Build Coastguard Worker error:
67*cf5a6c84SAndroid Build Coastguard Worker if (!(flags&DIRTREE_SHUTUP) && !isdotdot(name)) {
68*cf5a6c84SAndroid Build Coastguard Worker char *path = parent ? dirtree_path(parent, 0) : "";
69*cf5a6c84SAndroid Build Coastguard Worker
70*cf5a6c84SAndroid Build Coastguard Worker perror_msg("%s%s%s", path, parent ? "/" : "", name);
71*cf5a6c84SAndroid Build Coastguard Worker if (parent) free(path);
72*cf5a6c84SAndroid Build Coastguard Worker }
73*cf5a6c84SAndroid Build Coastguard Worker if (parent) parent->symlink = (char *)1;
74*cf5a6c84SAndroid Build Coastguard Worker free(dt);
75*cf5a6c84SAndroid Build Coastguard Worker
76*cf5a6c84SAndroid Build Coastguard Worker return 0;
77*cf5a6c84SAndroid Build Coastguard Worker }
78*cf5a6c84SAndroid Build Coastguard Worker
79*cf5a6c84SAndroid Build Coastguard Worker // Return path to this node.
80*cf5a6c84SAndroid Build Coastguard Worker
81*cf5a6c84SAndroid Build Coastguard Worker // Initial call can pass in NULL to plen, or point to an int initialized to 0
82*cf5a6c84SAndroid Build Coastguard Worker // to return the length of the path, or a value greater than 0 to allocate
83*cf5a6c84SAndroid Build Coastguard Worker // extra space if you want to append your own text to the string.
84*cf5a6c84SAndroid Build Coastguard Worker
dirtree_path(struct dirtree * node,int * plen)85*cf5a6c84SAndroid Build Coastguard Worker char *dirtree_path(struct dirtree *node, int *plen)
86*cf5a6c84SAndroid Build Coastguard Worker {
87*cf5a6c84SAndroid Build Coastguard Worker struct dirtree *nn;
88*cf5a6c84SAndroid Build Coastguard Worker char *path;
89*cf5a6c84SAndroid Build Coastguard Worker int ii, ll, len;
90*cf5a6c84SAndroid Build Coastguard Worker
91*cf5a6c84SAndroid Build Coastguard Worker ll = len = plen ? *plen : 0;
92*cf5a6c84SAndroid Build Coastguard Worker if (!node->parent)
93*cf5a6c84SAndroid Build Coastguard Worker return strcpy(xmalloc(strlen(node->name)+ll+1), node->name);
94*cf5a6c84SAndroid Build Coastguard Worker for (nn = node; nn; nn = nn->parent)
95*cf5a6c84SAndroid Build Coastguard Worker if ((ii = strlen(nn->name))) len += ii+1-(nn->name[ii-1]=='/');
96*cf5a6c84SAndroid Build Coastguard Worker if (plen) *plen = len;
97*cf5a6c84SAndroid Build Coastguard Worker path = xmalloc(len)+len-ll;
98*cf5a6c84SAndroid Build Coastguard Worker for (nn = node; nn; nn = nn->parent) if ((len = strlen(nn->name))) {
99*cf5a6c84SAndroid Build Coastguard Worker *--path = '/'*(nn != node);
100*cf5a6c84SAndroid Build Coastguard Worker if (nn->name[len-1]=='/') len--;
101*cf5a6c84SAndroid Build Coastguard Worker memcpy(path -= len, nn->name, len);
102*cf5a6c84SAndroid Build Coastguard Worker }
103*cf5a6c84SAndroid Build Coastguard Worker
104*cf5a6c84SAndroid Build Coastguard Worker return path;
105*cf5a6c84SAndroid Build Coastguard Worker }
106*cf5a6c84SAndroid Build Coastguard Worker
dirtree_parentfd(struct dirtree * node)107*cf5a6c84SAndroid Build Coastguard Worker int dirtree_parentfd(struct dirtree *node)
108*cf5a6c84SAndroid Build Coastguard Worker {
109*cf5a6c84SAndroid Build Coastguard Worker return node->parent ? node->parent->dirfd : AT_FDCWD;
110*cf5a6c84SAndroid Build Coastguard Worker }
111*cf5a6c84SAndroid Build Coastguard Worker
112*cf5a6c84SAndroid Build Coastguard Worker // Handle callback for a node in the tree. Returns saved node(s) if
113*cf5a6c84SAndroid Build Coastguard Worker // callback returns DIRTREE_SAVE, otherwise frees consumed nodes and
114*cf5a6c84SAndroid Build Coastguard Worker // returns NULL. If !callback return top node unchanged.
115*cf5a6c84SAndroid Build Coastguard Worker // If !new return DIRTREE_ABORTVAL
116*cf5a6c84SAndroid Build Coastguard Worker
dirtree_handle_callback(struct dirtree * new,int (* callback)(struct dirtree * node))117*cf5a6c84SAndroid Build Coastguard Worker static struct dirtree *dirtree_handle_callback(struct dirtree *new,
118*cf5a6c84SAndroid Build Coastguard Worker int (*callback)(struct dirtree *node))
119*cf5a6c84SAndroid Build Coastguard Worker {
120*cf5a6c84SAndroid Build Coastguard Worker int flags, df = DIRTREE_RECURSE|DIRTREE_COMEAGAIN|DIRTREE_BREADTH,
121*cf5a6c84SAndroid Build Coastguard Worker fd = AT_FDCWD;
122*cf5a6c84SAndroid Build Coastguard Worker
123*cf5a6c84SAndroid Build Coastguard Worker if (!new) return DIRTREE_ABORTVAL;
124*cf5a6c84SAndroid Build Coastguard Worker if (!callback) return new;
125*cf5a6c84SAndroid Build Coastguard Worker flags = callback(new);
126*cf5a6c84SAndroid Build Coastguard Worker
127*cf5a6c84SAndroid Build Coastguard Worker if (S_ISDIR(new->st.st_mode) && (flags & df)) {
128*cf5a6c84SAndroid Build Coastguard Worker // TODO: check openat returned fd for errors... and do what about it?
129*cf5a6c84SAndroid Build Coastguard Worker if (*new->name) fd = openat(dirtree_parentfd(new), new->name, O_CLOEXEC);
130*cf5a6c84SAndroid Build Coastguard Worker if (flags&DIRTREE_BREADTH) {
131*cf5a6c84SAndroid Build Coastguard Worker new->again |= DIRTREE_BREADTH;
132*cf5a6c84SAndroid Build Coastguard Worker if ((DIRTREE_ABORT & dirtree_recurse(new, 0, fd, flags)) ||
133*cf5a6c84SAndroid Build Coastguard Worker (DIRTREE_ABORT & (flags = callback(new))))
134*cf5a6c84SAndroid Build Coastguard Worker return DIRTREE_ABORTVAL;
135*cf5a6c84SAndroid Build Coastguard Worker }
136*cf5a6c84SAndroid Build Coastguard Worker flags = dirtree_recurse(new, callback, fd, flags);
137*cf5a6c84SAndroid Build Coastguard Worker close(fd);
138*cf5a6c84SAndroid Build Coastguard Worker }
139*cf5a6c84SAndroid Build Coastguard Worker
140*cf5a6c84SAndroid Build Coastguard Worker // Free node that didn't request saving and has no saved children.
141*cf5a6c84SAndroid Build Coastguard Worker if (!new->child && !(flags & DIRTREE_SAVE)) {
142*cf5a6c84SAndroid Build Coastguard Worker free(new);
143*cf5a6c84SAndroid Build Coastguard Worker new = 0;
144*cf5a6c84SAndroid Build Coastguard Worker }
145*cf5a6c84SAndroid Build Coastguard Worker
146*cf5a6c84SAndroid Build Coastguard Worker return (flags & DIRTREE_ABORT)==DIRTREE_ABORT ? DIRTREE_ABORTVAL : new;
147*cf5a6c84SAndroid Build Coastguard Worker }
148*cf5a6c84SAndroid Build Coastguard Worker
149*cf5a6c84SAndroid Build Coastguard Worker // Recursively read/process children of directory node, filtering through
150*cf5a6c84SAndroid Build Coastguard Worker // callback().
151*cf5a6c84SAndroid Build Coastguard Worker
dirtree_recurse(struct dirtree * node,int (* callback)(struct dirtree * node),int dirfd,int flags)152*cf5a6c84SAndroid Build Coastguard Worker int dirtree_recurse(struct dirtree *node,
153*cf5a6c84SAndroid Build Coastguard Worker int (*callback)(struct dirtree *node), int dirfd, int flags)
154*cf5a6c84SAndroid Build Coastguard Worker {
155*cf5a6c84SAndroid Build Coastguard Worker struct dirtree *new = 0, *next, **ddt = &(node->child);
156*cf5a6c84SAndroid Build Coastguard Worker struct dirent *entry;
157*cf5a6c84SAndroid Build Coastguard Worker DIR *dir = 0;
158*cf5a6c84SAndroid Build Coastguard Worker
159*cf5a6c84SAndroid Build Coastguard Worker // fdopendir() doesn't support AT_FDCWD, closedir() closes fd from opendir()
160*cf5a6c84SAndroid Build Coastguard Worker if (AT_FDCWD == (node->dirfd = dirfd)) dir = opendir(".");
161*cf5a6c84SAndroid Build Coastguard Worker else if (node->dirfd != -1) dir = fdopendir(xdup(node->dirfd));
162*cf5a6c84SAndroid Build Coastguard Worker
163*cf5a6c84SAndroid Build Coastguard Worker if (!dir) {
164*cf5a6c84SAndroid Build Coastguard Worker if (!(flags & DIRTREE_SHUTUP)) {
165*cf5a6c84SAndroid Build Coastguard Worker char *path = dirtree_path(node, 0);
166*cf5a6c84SAndroid Build Coastguard Worker perror_msg_raw(path);
167*cf5a6c84SAndroid Build Coastguard Worker free(path);
168*cf5a6c84SAndroid Build Coastguard Worker }
169*cf5a6c84SAndroid Build Coastguard Worker goto done;
170*cf5a6c84SAndroid Build Coastguard Worker }
171*cf5a6c84SAndroid Build Coastguard Worker
172*cf5a6c84SAndroid Build Coastguard Worker // Iterate through stored entries, if any
173*cf5a6c84SAndroid Build Coastguard Worker if (callback && *ddt) while (*ddt) {
174*cf5a6c84SAndroid Build Coastguard Worker next = (*ddt)->next;
175*cf5a6c84SAndroid Build Coastguard Worker if (!(new = dirtree_handle_callback(*ddt, callback))) *ddt = next;
176*cf5a6c84SAndroid Build Coastguard Worker else if (new == DIRTREE_ABORTVAL) goto done;
177*cf5a6c84SAndroid Build Coastguard Worker else ddt = &new->next;
178*cf5a6c84SAndroid Build Coastguard Worker
179*cf5a6c84SAndroid Build Coastguard Worker // according to the fddir() man page, the filehandle in the DIR * can still
180*cf5a6c84SAndroid Build Coastguard Worker // be externally used by things that don't lseek() it.
181*cf5a6c84SAndroid Build Coastguard Worker } else while ((entry = readdir(dir))) {
182*cf5a6c84SAndroid Build Coastguard Worker if ((flags&DIRTREE_PROC) && !isdigit(*entry->d_name)) continue;
183*cf5a6c84SAndroid Build Coastguard Worker if ((flags&DIRTREE_BREADTH) && isdotdot(entry->d_name)) continue;
184*cf5a6c84SAndroid Build Coastguard Worker if (!(new = dirtree_add_node(node, entry->d_name, flags))) continue;
185*cf5a6c84SAndroid Build Coastguard Worker if ((flags&DIRTREE_SYMFOLLOW) && entry->d_type==DT_LNK
186*cf5a6c84SAndroid Build Coastguard Worker && !S_ISLNK(new->st.st_mode)) new->again |= DIRTREE_SYMFOLLOW;
187*cf5a6c84SAndroid Build Coastguard Worker if (!new->st.st_blksize && !new->st.st_mode)
188*cf5a6c84SAndroid Build Coastguard Worker new->st.st_mode = entry->d_type<<12;
189*cf5a6c84SAndroid Build Coastguard Worker new = dirtree_handle_callback(new, callback);
190*cf5a6c84SAndroid Build Coastguard Worker if (new == DIRTREE_ABORTVAL) goto done;
191*cf5a6c84SAndroid Build Coastguard Worker if (new) {
192*cf5a6c84SAndroid Build Coastguard Worker *ddt = new;
193*cf5a6c84SAndroid Build Coastguard Worker ddt = &((*ddt)->next);
194*cf5a6c84SAndroid Build Coastguard Worker if (flags&DIRTREE_BREADTH) node->extra++;
195*cf5a6c84SAndroid Build Coastguard Worker }
196*cf5a6c84SAndroid Build Coastguard Worker }
197*cf5a6c84SAndroid Build Coastguard Worker
198*cf5a6c84SAndroid Build Coastguard Worker if (callback && (flags & DIRTREE_COMEAGAIN)) {
199*cf5a6c84SAndroid Build Coastguard Worker node->again |= DIRTREE_COMEAGAIN;
200*cf5a6c84SAndroid Build Coastguard Worker flags = callback(node);
201*cf5a6c84SAndroid Build Coastguard Worker }
202*cf5a6c84SAndroid Build Coastguard Worker
203*cf5a6c84SAndroid Build Coastguard Worker done:
204*cf5a6c84SAndroid Build Coastguard Worker closedir(dir);
205*cf5a6c84SAndroid Build Coastguard Worker node->dirfd = -1;
206*cf5a6c84SAndroid Build Coastguard Worker
207*cf5a6c84SAndroid Build Coastguard Worker return (new == DIRTREE_ABORTVAL) ? DIRTREE_ABORT : flags;
208*cf5a6c84SAndroid Build Coastguard Worker }
209*cf5a6c84SAndroid Build Coastguard Worker
210*cf5a6c84SAndroid Build Coastguard Worker // Create dirtree from path, using callback to filter nodes. If !callback
211*cf5a6c84SAndroid Build Coastguard Worker // return just the top node. Use dirtree_notdotdot callback to allocate a
212*cf5a6c84SAndroid Build Coastguard Worker // tree of struct dirtree nodes and return pointer to root node for later
213*cf5a6c84SAndroid Build Coastguard Worker // processing.
214*cf5a6c84SAndroid Build Coastguard Worker // Returns DIRTREE_ABORTVAL if path didn't exist (use DIRTREE_SHUTUP to handle
215*cf5a6c84SAndroid Build Coastguard Worker // error message yourself).
216*cf5a6c84SAndroid Build Coastguard Worker
dirtree_flagread(char * path,int flags,int (* callback)(struct dirtree * node))217*cf5a6c84SAndroid Build Coastguard Worker struct dirtree *dirtree_flagread(char *path, int flags,
218*cf5a6c84SAndroid Build Coastguard Worker int (*callback)(struct dirtree *node))
219*cf5a6c84SAndroid Build Coastguard Worker {
220*cf5a6c84SAndroid Build Coastguard Worker return dirtree_handle_callback(dirtree_add_node(0, path, flags), callback);
221*cf5a6c84SAndroid Build Coastguard Worker }
222*cf5a6c84SAndroid Build Coastguard Worker
223*cf5a6c84SAndroid Build Coastguard Worker // Common case
dirtree_read(char * path,int (* callback)(struct dirtree * node))224*cf5a6c84SAndroid Build Coastguard Worker struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node))
225*cf5a6c84SAndroid Build Coastguard Worker {
226*cf5a6c84SAndroid Build Coastguard Worker return dirtree_flagread(path, 0, callback);
227*cf5a6c84SAndroid Build Coastguard Worker }
228