10f66f451Sopenharmony_ci/* dirtree.c - Functions for dealing with directory trees. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2007 Rob Landley <rob@landley.net> 40f66f451Sopenharmony_ci */ 50f66f451Sopenharmony_ci 60f66f451Sopenharmony_ci#include "toys.h" 70f66f451Sopenharmony_ci 80f66f451Sopenharmony_ci 90f66f451Sopenharmony_ciint isdot(char *name) 100f66f451Sopenharmony_ci{ 110f66f451Sopenharmony_ci if (name[0]=='.' && (!name[1] || (name[1]=='/' && !name[2]))) return 1; 120f66f451Sopenharmony_ci 130f66f451Sopenharmony_ci return 0; 140f66f451Sopenharmony_ci} 150f66f451Sopenharmony_ci 160f66f451Sopenharmony_ciint isdotdot(char *name) 170f66f451Sopenharmony_ci{ 180f66f451Sopenharmony_ci if (name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2]))) return 1; 190f66f451Sopenharmony_ci 200f66f451Sopenharmony_ci return 0; 210f66f451Sopenharmony_ci} 220f66f451Sopenharmony_ci 230f66f451Sopenharmony_ci// Default callback, filters out "." and ".." except at top level. 240f66f451Sopenharmony_ci 250f66f451Sopenharmony_ciint dirtree_notdotdot(struct dirtree *catch) 260f66f451Sopenharmony_ci{ 270f66f451Sopenharmony_ci // Should we skip "." and ".."? 280f66f451Sopenharmony_ci return (!catch->parent||!isdotdot(catch->name)) 290f66f451Sopenharmony_ci *(DIRTREE_SAVE|DIRTREE_RECURSE); 300f66f451Sopenharmony_ci} 310f66f451Sopenharmony_ci 320f66f451Sopenharmony_ci// Create a dirtree node from a path, with stat and symlink info. 330f66f451Sopenharmony_ci// (This doesn't open directory filehandles yet so as not to exhaust the 340f66f451Sopenharmony_ci// filehandle space on large trees, dirtree_handle_callback() does that.) 350f66f451Sopenharmony_ci 360f66f451Sopenharmony_cistruct dirtree *dirtree_add_node(struct dirtree *parent, char *name, int flags) 370f66f451Sopenharmony_ci{ 380f66f451Sopenharmony_ci struct dirtree *dt = 0; 390f66f451Sopenharmony_ci struct stat st; 400f66f451Sopenharmony_ci int len = 0, linklen = 0, statless = 0; 410f66f451Sopenharmony_ci 420f66f451Sopenharmony_ci if (name) { 430f66f451Sopenharmony_ci // open code this because haven't got node to call dirtree_parentfd() on yet 440f66f451Sopenharmony_ci int fd = parent ? parent->dirfd : AT_FDCWD; 450f66f451Sopenharmony_ci 460f66f451Sopenharmony_ci if (fstatat(fd, name, &st,AT_SYMLINK_NOFOLLOW*!(flags&DIRTREE_SYMFOLLOW))) { 470f66f451Sopenharmony_ci if (flags&DIRTREE_STATLESS) statless++; 480f66f451Sopenharmony_ci else goto error; 490f66f451Sopenharmony_ci } 500f66f451Sopenharmony_ci if (S_ISLNK(st.st_mode)) { 510f66f451Sopenharmony_ci if (0>(linklen = readlinkat(fd, name, libbuf, 4095))) goto error; 520f66f451Sopenharmony_ci libbuf[linklen++]=0; 530f66f451Sopenharmony_ci } 540f66f451Sopenharmony_ci len = strlen(name); 550f66f451Sopenharmony_ci } 560f66f451Sopenharmony_ci 570f66f451Sopenharmony_ci // Allocate/populate return structure 580f66f451Sopenharmony_ci dt = xmalloc((len = sizeof(struct dirtree)+len+1)+linklen); 590f66f451Sopenharmony_ci memset(dt, 0, statless ? offsetof(struct dirtree, again) 600f66f451Sopenharmony_ci : offsetof(struct dirtree, st)); 610f66f451Sopenharmony_ci dt->parent = parent; 620f66f451Sopenharmony_ci dt->again = statless ? 2 : 0; 630f66f451Sopenharmony_ci if (!statless) memcpy(&dt->st, &st, sizeof(struct stat)); 640f66f451Sopenharmony_ci strcpy(dt->name, name ? name : ""); 650f66f451Sopenharmony_ci if (linklen) dt->symlink = memcpy(len+(char *)dt, libbuf, linklen); 660f66f451Sopenharmony_ci 670f66f451Sopenharmony_ci return dt; 680f66f451Sopenharmony_ci 690f66f451Sopenharmony_cierror: 700f66f451Sopenharmony_ci if (!(flags&DIRTREE_SHUTUP) && !isdotdot(name)) { 710f66f451Sopenharmony_ci char *path = parent ? dirtree_path(parent, 0) : ""; 720f66f451Sopenharmony_ci 730f66f451Sopenharmony_ci perror_msg("%s%s%s", path, parent ? "/" : "", name); 740f66f451Sopenharmony_ci if (parent) free(path); 750f66f451Sopenharmony_ci } 760f66f451Sopenharmony_ci if (parent) parent->symlink = (char *)1; 770f66f451Sopenharmony_ci free(dt); 780f66f451Sopenharmony_ci return 0; 790f66f451Sopenharmony_ci} 800f66f451Sopenharmony_ci 810f66f451Sopenharmony_ci// Return path to this node, assembled recursively. 820f66f451Sopenharmony_ci 830f66f451Sopenharmony_ci// Initial call can pass in NULL to plen, or point to an int initialized to 0 840f66f451Sopenharmony_ci// to return the length of the path, or a value greater than 0 to allocate 850f66f451Sopenharmony_ci// extra space if you want to append your own text to the string. 860f66f451Sopenharmony_ci 870f66f451Sopenharmony_cichar *dirtree_path(struct dirtree *node, int *plen) 880f66f451Sopenharmony_ci{ 890f66f451Sopenharmony_ci char *path; 900f66f451Sopenharmony_ci int len; 910f66f451Sopenharmony_ci 920f66f451Sopenharmony_ci if (!node) { 930f66f451Sopenharmony_ci path = xmalloc(*plen); 940f66f451Sopenharmony_ci *plen = 0; 950f66f451Sopenharmony_ci return path; 960f66f451Sopenharmony_ci } 970f66f451Sopenharmony_ci 980f66f451Sopenharmony_ci len = (plen ? *plen : 0)+strlen(node->name)+1; 990f66f451Sopenharmony_ci path = dirtree_path(node->parent, &len); 1000f66f451Sopenharmony_ci if (len && path[len-1] != '/') path[len++]='/'; 1010f66f451Sopenharmony_ci len = stpcpy(path+len, node->name) - path; 1020f66f451Sopenharmony_ci if (plen) *plen = len; 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_ci return path; 1050f66f451Sopenharmony_ci} 1060f66f451Sopenharmony_ci 1070f66f451Sopenharmony_ciint dirtree_parentfd(struct dirtree *node) 1080f66f451Sopenharmony_ci{ 1090f66f451Sopenharmony_ci return node->parent ? node->parent->dirfd : AT_FDCWD; 1100f66f451Sopenharmony_ci} 1110f66f451Sopenharmony_ci 1120f66f451Sopenharmony_ci// Handle callback for a node in the tree. Returns saved node(s) if 1130f66f451Sopenharmony_ci// callback returns DIRTREE_SAVE, otherwise frees consumed nodes and 1140f66f451Sopenharmony_ci// returns NULL. If !callback return top node unchanged. 1150f66f451Sopenharmony_ci// If !new return DIRTREE_ABORTVAL 1160f66f451Sopenharmony_ci 1170f66f451Sopenharmony_cistruct dirtree *dirtree_handle_callback(struct dirtree *new, 1180f66f451Sopenharmony_ci int (*callback)(struct dirtree *node)) 1190f66f451Sopenharmony_ci{ 1200f66f451Sopenharmony_ci int flags; 1210f66f451Sopenharmony_ci 1220f66f451Sopenharmony_ci if (!new) return DIRTREE_ABORTVAL; 1230f66f451Sopenharmony_ci if (!callback) return new; 1240f66f451Sopenharmony_ci flags = callback(new); 1250f66f451Sopenharmony_ci 1260f66f451Sopenharmony_ci if (S_ISDIR(new->st.st_mode) && (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN))) 1270f66f451Sopenharmony_ci flags = dirtree_recurse(new, callback, 1280f66f451Sopenharmony_ci openat(dirtree_parentfd(new), new->name, O_CLOEXEC|O_DIRECTORY), flags); 1290f66f451Sopenharmony_ci // If this had children, it was callback's job to free them already. 1300f66f451Sopenharmony_ci if (!(flags & DIRTREE_SAVE)) { 1310f66f451Sopenharmony_ci free(new); 1320f66f451Sopenharmony_ci new = 0; 1330f66f451Sopenharmony_ci } 1340f66f451Sopenharmony_ci 1350f66f451Sopenharmony_ci return (flags & DIRTREE_ABORT)==DIRTREE_ABORT ? DIRTREE_ABORTVAL : new; 1360f66f451Sopenharmony_ci} 1370f66f451Sopenharmony_ci 1380f66f451Sopenharmony_ci// Recursively read/process children of directory node, filtering through 1390f66f451Sopenharmony_ci// callback(). Uses and closes supplied ->dirfd. 1400f66f451Sopenharmony_ci 1410f66f451Sopenharmony_ciint dirtree_recurse(struct dirtree *node, 1420f66f451Sopenharmony_ci int (*callback)(struct dirtree *node), int dirfd, int flags) 1430f66f451Sopenharmony_ci{ 1440f66f451Sopenharmony_ci struct dirtree *new, **ddt = &(node->child); 1450f66f451Sopenharmony_ci struct dirent *entry; 1460f66f451Sopenharmony_ci DIR *dir; 1470f66f451Sopenharmony_ci 1480f66f451Sopenharmony_ci node->dirfd = dirfd; 1490f66f451Sopenharmony_ci if (node->dirfd == -1 || !(dir = fdopendir(node->dirfd))) { 1500f66f451Sopenharmony_ci if (!(flags & DIRTREE_SHUTUP)) { 1510f66f451Sopenharmony_ci char *path = dirtree_path(node, 0); 1520f66f451Sopenharmony_ci perror_msg_raw(path); 1530f66f451Sopenharmony_ci free(path); 1540f66f451Sopenharmony_ci } 1550f66f451Sopenharmony_ci close(node->dirfd); 1560f66f451Sopenharmony_ci 1570f66f451Sopenharmony_ci return flags; 1580f66f451Sopenharmony_ci } 1590f66f451Sopenharmony_ci 1600f66f451Sopenharmony_ci // according to the fddir() man page, the filehandle in the DIR * can still 1610f66f451Sopenharmony_ci // be externally used by things that don't lseek() it. 1620f66f451Sopenharmony_ci 1630f66f451Sopenharmony_ci // The extra parentheses are to shut the stupid compiler up. 1640f66f451Sopenharmony_ci while ((entry = readdir(dir))) { 1650f66f451Sopenharmony_ci if ((flags&DIRTREE_PROC) && !isdigit(*entry->d_name)) continue; 1660f66f451Sopenharmony_ci if (!(new = dirtree_add_node(node, entry->d_name, flags))) continue; 1670f66f451Sopenharmony_ci if (!new->st.st_blksize && !new->st.st_mode) 1680f66f451Sopenharmony_ci new->st.st_mode = entry->d_type<<12; 1690f66f451Sopenharmony_ci new = dirtree_handle_callback(new, callback); 1700f66f451Sopenharmony_ci if (new == DIRTREE_ABORTVAL) break; 1710f66f451Sopenharmony_ci if (new) { 1720f66f451Sopenharmony_ci *ddt = new; 1730f66f451Sopenharmony_ci ddt = &((*ddt)->next); 1740f66f451Sopenharmony_ci } 1750f66f451Sopenharmony_ci } 1760f66f451Sopenharmony_ci 1770f66f451Sopenharmony_ci if (flags & DIRTREE_COMEAGAIN) { 1780f66f451Sopenharmony_ci node->again |= 1; 1790f66f451Sopenharmony_ci flags = callback(node); 1800f66f451Sopenharmony_ci } 1810f66f451Sopenharmony_ci 1820f66f451Sopenharmony_ci // This closes filehandle as well, so note it 1830f66f451Sopenharmony_ci closedir(dir); 1840f66f451Sopenharmony_ci node->dirfd = -1; 1850f66f451Sopenharmony_ci 1860f66f451Sopenharmony_ci return flags; 1870f66f451Sopenharmony_ci} 1880f66f451Sopenharmony_ci 1890f66f451Sopenharmony_ci// Create dirtree from path, using callback to filter nodes. If !callback 1900f66f451Sopenharmony_ci// return just the top node. Use dirtree_notdotdot callback to allocate a 1910f66f451Sopenharmony_ci// tree of struct dirtree nodes and return pointer to root node for later 1920f66f451Sopenharmony_ci// processing. 1930f66f451Sopenharmony_ci// Returns DIRTREE_ABORTVAL if path didn't exist (use DIRTREE_SHUTUP to handle 1940f66f451Sopenharmony_ci// error message yourself). 1950f66f451Sopenharmony_ci 1960f66f451Sopenharmony_cistruct dirtree *dirtree_flagread(char *path, int flags, 1970f66f451Sopenharmony_ci int (*callback)(struct dirtree *node)) 1980f66f451Sopenharmony_ci{ 1990f66f451Sopenharmony_ci return dirtree_handle_callback(dirtree_add_node(0, path, flags), callback); 2000f66f451Sopenharmony_ci} 2010f66f451Sopenharmony_ci 2020f66f451Sopenharmony_ci// Common case 2030f66f451Sopenharmony_cistruct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node)) 2040f66f451Sopenharmony_ci{ 2050f66f451Sopenharmony_ci return dirtree_flagread(path, 0, callback); 2060f66f451Sopenharmony_ci} 207