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(const char *fileName UNUSED_PARAM,
  25                struct stat *statbuf UNUSED_PARAM,
  26                void* userData UNUSED_PARAM,
  27                int depth UNUSED_PARAM)
  28{
  29        return TRUE;
  30}
  31
  32/* fileName is (l)stat'ed (depending on ACTION_FOLLOWLINKS[_L0]).
  33 *
  34 * If it is a file: fileAction in run on it, its return value is returned.
  35 *
  36 * In case we are in a recursive invocation (see below):
  37 * normally, fileAction should return 1 (TRUE) to indicate that
  38 * everything is okay and processing should continue.
  39 * fileAction return value of 0 (FALSE) on any file in directory will make
  40 * recursive_action() also return 0, but it doesn't stop directory traversal
  41 * (fileAction/dirAction will be called on each file).
  42 *
  43 * [TODO: maybe introduce -1 to mean "stop traversal NOW and return"]
  44 *
  45 * If it is a directory:
  46 *
  47 * If !ACTION_RECURSE, dirAction is called and its
  48 * return value is returned from recursive_action(). No recursion.
  49 *
  50 * If ACTION_RECURSE, directory is opened, and recursive_action() is called
  51 * on each file/subdirectory.
  52 * If any one of these calls returns 0, current recursive_action() returns 0.
  53 *
  54 * If !ACTION_DEPTHFIRST, dirAction is called before recurse.
  55 * Return value of 0 (FALSE) is an error: prevents recursion,
  56 * the warning is printed (unless ACTION_QUIET) and recursive_action() returns 0.
  57 * Return value of 2 (SKIP) prevents recursion, instead recursive_action()
  58 * returns 1 (TRUE, no error).
  59 *
  60 * If ACTION_DEPTHFIRST, dirAction is called after recurse.
  61 * If it returns 0, the warning is printed and recursive_action() returns 0.
  62 *
  63 * ACTION_FOLLOWLINKS mainly controls handling of links to dirs.
  64 * 0: lstat(statbuf). Calls fileAction on link name even if points to dir.
  65 * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir.
  66 */
  67
  68int FAST_FUNC recursive_action(const char *fileName,
  69                unsigned flags,
  70                int FAST_FUNC (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
  71                int FAST_FUNC (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
  72                void* userData,
  73                unsigned depth)
  74{
  75        struct stat statbuf;
  76        unsigned follow;
  77        int status;
  78        DIR *dir;
  79        struct dirent *next;
  80
  81        if (!fileAction) fileAction = true_action;
  82        if (!dirAction) dirAction = true_action;
  83
  84        follow = ACTION_FOLLOWLINKS;
  85        if (depth == 0)
  86                follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0;
  87        follow &= flags;
  88        status = (follow ? stat : lstat)(fileName, &statbuf);
  89        if (status < 0) {
  90#ifdef DEBUG_RECURS_ACTION
  91                bb_error_msg("status=%d flags=%x", status, flags);
  92#endif
  93                if ((flags & ACTION_DANGLING_OK)
  94                 && errno == ENOENT
  95                 && lstat(fileName, &statbuf) == 0
  96                ) {
  97                        /* Dangling link */
  98                        return fileAction(fileName, &statbuf, userData, depth);
  99                }
 100                goto done_nak_warn;
 101        }
 102
 103        /* If S_ISLNK(m), then we know that !S_ISDIR(m).
 104         * Then we can skip checking first part: if it is true, then
 105         * (!dir) is also true! */
 106        if ( /* (!(flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */
 107         !S_ISDIR(statbuf.st_mode)
 108        ) {
 109                return fileAction(fileName, &statbuf, userData, depth);
 110        }
 111
 112        /* It's a directory (or a link to one, and followLinks is set) */
 113
 114        if (!(flags & ACTION_RECURSE)) {
 115                return dirAction(fileName, &statbuf, userData, depth);
 116        }
 117
 118        if (!(flags & ACTION_DEPTHFIRST)) {
 119                status = dirAction(fileName, &statbuf, userData, depth);
 120                if (status == FALSE)
 121                        goto done_nak_warn;
 122                if (status == SKIP)
 123                        return TRUE;
 124        }
 125
 126        dir = opendir(fileName);
 127        if (!dir) {
 128                /* findutils-4.1.20 reports this */
 129                /* (i.e. it doesn't silently return with exit code 1) */
 130                /* To trigger: "find -exec rm -rf {} \;" */
 131                goto done_nak_warn;
 132        }
 133        status = TRUE;
 134        while ((next = readdir(dir)) != NULL) {
 135                char *nextFile;
 136                int s;
 137
 138                nextFile = concat_subpath_file(fileName, next->d_name);
 139                if (nextFile == NULL)
 140                        continue;
 141
 142                /* process every file (NB: ACTION_RECURSE is set in flags) */
 143                s = recursive_action(nextFile, flags, fileAction, dirAction,
 144                                                userData, depth + 1);
 145                if (s == FALSE)
 146                        status = FALSE;
 147                free(nextFile);
 148//#define RECURSE_RESULT_ABORT -1
 149//              if (s == RECURSE_RESULT_ABORT) {
 150//                      closedir(dir);
 151//                      return s;
 152//              }
 153        }
 154        closedir(dir);
 155
 156        if (flags & ACTION_DEPTHFIRST) {
 157                if (!dirAction(fileName, &statbuf, userData, depth))
 158                        goto done_nak_warn;
 159        }
 160
 161        return status;
 162
 163 done_nak_warn:
 164        if (!(flags & ACTION_QUIET))
 165                bb_simple_perror_msg(fileName);
 166        return FALSE;
 167}
 168