1/* vi: set sw=4 ts=4: */ 2/* 3 * Utility routines. 4 * 5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> 6 * 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 8 */ 9#include "libbb.h" 10 11#undef DEBUG_RECURS_ACTION 12 13/* 14 * Walk down all the directories under the specified 15 * location, and do something (something specified 16 * by the fileAction and dirAction function pointers). 17 * 18 * Unfortunately, while nftw(3) could replace this and reduce 19 * code size a bit, nftw() wasn't supported before GNU libc 2.1, 20 * and so isn't sufficiently portable to take over since glibc2.1 21 * is so stinking huge. 22 */ 23 24static int FAST_FUNC true_action(struct recursive_state *state UNUSED_PARAM, 25 const char *fileName UNUSED_PARAM, 26 struct stat *statbuf UNUSED_PARAM) 27{ 28 return TRUE; 29} 30 31/* fileName is (l)stat'ed (depending on ACTION_FOLLOWLINKS[_L0]). 32 * 33 * If it is a file: fileAction in run on it, its return value is returned. 34 * 35 * In case we are in a recursive invocation (see below): 36 * normally, fileAction should return 1 (TRUE) to indicate that 37 * everything is okay and processing should continue. 38 * fileAction return value of 0 (FALSE) on any file in directory will make 39 * recursive_action() also return 0, but it doesn't stop directory traversal 40 * (fileAction/dirAction will be called on each file). 41 * 42 * [TODO: maybe introduce -1 to mean "stop traversal NOW and return"] 43 * 44 * If it is a directory: 45 * 46 * If !ACTION_RECURSE, dirAction is called and its 47 * return value is returned from recursive_action(). No recursion. 48 * 49 * If ACTION_RECURSE, directory is opened, and recursive_action() is called 50 * on each file/subdirectory. 51 * If any one of these calls returns 0, current recursive_action() returns 0. 52 * 53 * If !ACTION_DEPTHFIRST, dirAction is called before recurse. 54 * Return value of 0 (FALSE) is an error: prevents recursion, 55 * the warning is printed (unless ACTION_QUIET) and recursive_action() returns 0. 56 * Return value of 2 (SKIP) prevents recursion, instead recursive_action() 57 * returns 1 (TRUE, no error). 58 * 59 * If ACTION_DEPTHFIRST, dirAction is called after recurse. 60 * If it returns 0, the warning is printed and recursive_action() returns 0. 61 * 62 * ACTION_FOLLOWLINKS mainly controls handling of links to dirs. 63 * 0: lstat(statbuf). Calls fileAction on link name even if points to dir. 64 * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir. 65 */ 66 67static int recursive_action1(recursive_state_t *state, const char *fileName) 68{ 69 struct stat statbuf; 70 unsigned follow; 71 int status; 72 DIR *dir; 73 struct dirent *next; 74 75 follow = ACTION_FOLLOWLINKS; 76 if (state->depth == 0) 77 follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0; 78 follow &= state->flags; 79 status = (follow ? stat : lstat)(fileName, &statbuf); 80 if (status < 0) { 81#ifdef DEBUG_RECURS_ACTION 82 bb_error_msg("status=%d flags=%x", status, state->flags); 83#endif 84 if ((state->flags & ACTION_DANGLING_OK) 85 && errno == ENOENT 86 && lstat(fileName, &statbuf) == 0 87 ) { 88 /* Dangling link */ 89 return state->fileAction(state, fileName, &statbuf); 90 } 91 goto done_nak_warn; 92 } 93 94 /* If S_ISLNK(m), then we know that !S_ISDIR(m). 95 * Then we can skip checking first part: if it is true, then 96 * (!dir) is also true! */ 97 if ( /* (!(state->flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */ 98 !S_ISDIR(statbuf.st_mode) 99 ) { 100 return state->fileAction(state, fileName, &statbuf); 101 } 102 103 /* It's a directory (or a link to one, and followLinks is set) */ 104 105 if (!(state->flags & ACTION_RECURSE)) { 106 return state->dirAction(state, fileName, &statbuf); 107 } 108 109 if (!(state->flags & ACTION_DEPTHFIRST)) { 110 status = state->dirAction(state, fileName, &statbuf); 111 if (status == FALSE) 112 goto done_nak_warn; 113 if (status == SKIP) 114 return TRUE; 115 } 116 117 dir = opendir(fileName); 118 if (!dir) { 119 /* findutils-4.1.20 reports this */ 120 /* (i.e. it doesn't silently return with exit code 1) */ 121 /* To trigger: "find -exec rm -rf {} \;" */ 122 goto done_nak_warn; 123 } 124 status = TRUE; 125 while ((next = readdir(dir)) != NULL) { 126 char *nextFile; 127 int s; 128 129 nextFile = concat_subpath_file(fileName, next->d_name); 130 if (nextFile == NULL) 131 continue; 132 133 /* process every file (NB: ACTION_RECURSE is set in flags) */ 134 state->depth++; 135 s = recursive_action1(state, nextFile); 136 if (s == FALSE) 137 status = FALSE; 138 free(nextFile); 139 state->depth--; 140 141//#define RECURSE_RESULT_ABORT -1 142// if (s == RECURSE_RESULT_ABORT) { 143// closedir(dir); 144// return s; 145// } 146 } 147 closedir(dir); 148 149 if (state->flags & ACTION_DEPTHFIRST) { 150 if (!state->dirAction(state, fileName, &statbuf)) 151 goto done_nak_warn; 152 } 153 154 return status; 155 156 done_nak_warn: 157 if (!(state->flags & ACTION_QUIET)) 158 bb_simple_perror_msg(fileName); 159 return FALSE; 160} 161 162int FAST_FUNC recursive_action(const char *fileName, 163 unsigned flags, 164 int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf), 165 int FAST_FUNC (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf), 166 void *userData) 167{ 168 /* Keeping a part of variables of recusive descent in a "state structure" 169 * instead of passing ALL of them down as parameters of recursive_action1() 170 * relieves register pressure, both in recursive_action1() 171 * and in every file/dirAction(). 172 */ 173 recursive_state_t state; 174 state.flags = flags; 175 state.depth = 0; 176 state.userData = userData; 177 state.fileAction = fileAction ? fileAction : true_action; 178 state.dirAction = dirAction ? dirAction : true_action; 179 180 return recursive_action1(&state, fileName); 181} 182