busybox/libbb/recursive_action.c
<<
>>
Prefs
   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