Mercurial > hg > toybox
view lib/dirtree.c @ 1572:da1bf31ed322 draft
Tweak the "ignoring return value" fortify workaround for readlinkat.
We zero the buffer and if the link read fails that's left alone, so
it's ok for the symlink not to be there. Unfortunately, typecasting the
return value to (void) doesn't shut up gcc, and having an if(); with the
semicolon on the same line doesn't shut up llvm. (The semicolon on a new
line would, but C does not have significant whitespace and I'm not going
to humor llvm if it plans to start.)
So far, empty curly brackets consistently get the warning to shut up.
author | Rob Landley <rob@landley.net> |
---|---|
date | Mon, 24 Nov 2014 17:23:23 -0600 |
parents | a2796b4cef49 |
children |
line wrap: on
line source
/* dirtree.c - Functions for dealing with directory trees. * * Copyright 2007 Rob Landley <rob@landley.net> */ #include "toys.h" static int notdotdot(char *name) { if (name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2]))) return 0; return 1; } // Default callback, filters out "." and "..". int dirtree_notdotdot(struct dirtree *catch) { // Should we skip "." and ".."? return notdotdot(catch->name) ? DIRTREE_SAVE|DIRTREE_RECURSE : 0; } // Create a dirtree node from a path, with stat and symlink info. // (This doesn't open directory filehandles yet so as not to exhaust the // filehandle space on large trees, dirtree_handle_callback() does that.) struct dirtree *dirtree_add_node(struct dirtree *parent, char *name, int symfollow) { struct dirtree *dt = NULL; struct stat st; char buf[4096]; int len = 0, linklen = 0; if (name) { // open code this because haven't got node to call dirtree_parentfd() on yet int fd = parent ? parent->data : AT_FDCWD; if (fstatat(fd, name, &st, symfollow ? 0 : AT_SYMLINK_NOFOLLOW)) goto error; if (S_ISLNK(st.st_mode)) { if (0>(linklen = readlinkat(fd, name, buf, 4095))) goto error; buf[linklen++]=0; } len = strlen(name); } dt = xzalloc((len = sizeof(struct dirtree)+len+1)+linklen); dt->parent = parent; if (name) { memcpy(&(dt->st), &st, sizeof(struct stat)); strcpy(dt->name, name); if (linklen) { dt->symlink = memcpy(len+(char *)dt, buf, linklen); dt->data = --linklen; } } return dt; error: if (notdotdot(name)) { char *path = parent ? dirtree_path(parent, 0) : ""; perror_msg("%s%s%s", path, parent ? "/" : "", name); if (parent) free(path); } if (parent) parent->symlink = (char *)1; free(dt); return 0; } // Return path to this node, assembled recursively. // Initial call can pass in NULL to plen, or point to an int initialized to 0 // to return the length of the path, or a value greater than 0 to allocate // extra space if you want to append your own text to the string. char *dirtree_path(struct dirtree *node, int *plen) { char *path; int len; if (!node) { path = xmalloc(*plen); *plen = 0; return path; } len = (plen ? *plen : 0)+strlen(node->name)+1; path = dirtree_path(node->parent, &len); if (len && path[len-1] != '/') path[len++]='/'; len = (stpcpy(path+len, node->name) - path); if (plen) *plen = len; return path; } int dirtree_parentfd(struct dirtree *node) { return node->parent ? node->parent->data : AT_FDCWD; } // Handle callback for a node in the tree. Returns saved node(s) or NULL. // // By default, allocates a tree of struct dirtree, not following symlinks // If callback==NULL, or callback always returns 0, allocate tree of struct // dirtree and return root of tree. Otherwise call callback(node) on each // hit, free structures after use, and return NULL. // struct dirtree *dirtree_handle_callback(struct dirtree *new, int (*callback)(struct dirtree *node)) { int flags, dir = S_ISDIR(new->st.st_mode); if (!callback) callback = dirtree_notdotdot; flags = callback(new); if (dir) { if (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN)) { new->data = openat(dirtree_parentfd(new), new->name, O_CLOEXEC); flags = dirtree_recurse(new, callback, flags); } } // If this had children, it was callback's job to free them already. if (!(flags & DIRTREE_SAVE)) { free(new); new = NULL; } return (flags & DIRTREE_ABORT)==DIRTREE_ABORT ? DIRTREE_ABORTVAL : new; } // Recursively read/process children of directory node (with dirfd in data), // filtering through callback(). int dirtree_recurse(struct dirtree *node, int (*callback)(struct dirtree *node), int flags) { struct dirtree *new, **ddt = &(node->child); struct dirent *entry; DIR *dir; if (node->data == -1 || !(dir = fdopendir(node->data))) { char *path = dirtree_path(node, 0); perror_msg("No %s", path); free(path); close(node->data); return flags; } // according to the fddir() man page, the filehandle in the DIR * can still // be externally used by things that don't lseek() it. // The extra parentheses are to shut the stupid compiler up. while ((entry = readdir(dir))) { if (!(new = dirtree_add_node(node, entry->d_name, flags&DIRTREE_SYMFOLLOW))) continue; new = dirtree_handle_callback(new, callback); if (new == DIRTREE_ABORTVAL) break; if (new) { *ddt = new; ddt = &((*ddt)->next); } } if (flags & DIRTREE_COMEAGAIN) { node->again++; flags = callback(node); } // This closes filehandle as well, so note it closedir(dir); node->data = -1; return flags; } // Create dirtree from path, using callback to filter nodes. // If callback == NULL allocate a tree of struct dirtree nodes and return // pointer to root node. // symfollow is just for the top of tree, callback return code controls children struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node)) { struct dirtree *root = dirtree_add_node(0, path, 0); return root ? dirtree_handle_callback(root, callback) : DIRTREE_ABORTVAL; }