xref: /aosp_15_r20/external/toybox/lib/dirtree.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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