"Fossies" - the Fresh Open Source Software Archive

Member "modutils-2.4.27/util/xftw.c" (27 Oct 2003, 12539 Bytes) of package /linux/misc/old/modutils-2.4.27.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "xftw.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * modutils specific implementation of ftw().
    3  *
    4  * Copyright 2000:
    5  *  Keith Owens <kaos@ocs.com.au> August 2000
    6  *
    7  * This file is part of the Linux modutils.
    8  *
    9  * This program is free software; you can redistribute it and/or modify it
   10  * under the terms of the GNU General Public License as published by the
   11  * Free Software Foundation; either version 2 of the License, or (at your
   12  * option) any later version.
   13  *
   14  * This program is distributed in the hope that it will be useful, but
   15  * WITHOUT ANY WARRANTY; without even the implied warranty of
   16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   17  * General Public License for more details.
   18  *
   19  * You should have received a copy of the GNU General Public License
   20  * along with this program; if not, write to the Free Software Foundation,
   21  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   22  */
   23 
   24 /*
   25     modutils requires special processing during the file tree walk
   26     of /lib/modules/<version> and any paths that the user specifies.
   27     The standard ftw() does a blind walk of all paths and can end
   28     up following the build symlink down the kernel source tree.
   29     Although nftw() has the option to get more control such as not
   30     automatically following symbolic links, even that is not enough
   31     for modutils.  The requirements are:
   32 
   33     Paths must be directories or symlinks to directories.
   34 
   35     Each directory is read and sorted into alphabetical order
   36     before processing.
   37 
   38     A directory is type 1 iff it was specified on a path statement
   39     (either explicit or default) and the directory contains a
   40     subdirectory with one of the known names and the directory name
   41     does not end with "/kernel".  Otherwise it is type 2.
   42 
   43     In a type 1 directory, walk the kernel subdirectory if it exists,
   44     then the old known names in their historical order then any
   45     remaining directory entries in alphabetical order and finally any
   46     non-directory entries in alphabetical order.
   47 
   48     Entries in a type 1 directory are filtered against the "prune"
   49     list.  A type 1 directory can contain additional files which
   50     are not modules nor symlinks to modules.  The prune list skips
   51     known additional files, if a distribution wants to store
   52     additional text files in the top level directory they should be
   53     added to the prune list.
   54 
   55     A type 2 directory must contain only modules or symlinks to
   56     modules.  They are processed in alphabetical order, without
   57     pruning.  Symlinks to directories are an error in type 2
   58     directories.
   59 
   60     The user function is not called for type 1 directories, nor for
   61     pruned entries.  It is called for type 2 directories and their
   62     contents.  It is also called for any files left in a type 1
   63     directory after pruning and processing type 2 subdirectories.
   64     The user function never sees symlinks, they are resolved before
   65     calling the function.
   66 
   67     Why have different directory types?  The original file tree
   68     walk was not well defined.  Some users specified each directory
   69     individually, others just pointed at the top level directory.
   70     Either version worked until the "build" symlink was added.  Now
   71     users who specify the top level directory end up running the
   72     entire kernel source tree looking for modules, not nice.  We
   73     cannot just ignore symlinks because pcmcia uses symlinks to
   74     modules for backwards compatibility.
   75 
   76     Type 1 is when a user specifies the top level directory which needs
   77     special processing, type 2 is individual subdirectories.  But the
   78     only way to tell the difference is by looking at the contents.  The
   79     "/kernel" directory introduced in 2.3.12 either contains nothing
   80     (old make modules_install) or contains all the kernel modules using
   81     the same tree structure as the source.  Because "/kernel" can
   82     contain old names but is really a type 2 directory, it is detected
   83     as a special case.
   84  */
   85 
   86 #include <dirent.h>
   87 #include <errno.h>
   88 #include <limits.h>
   89 #include <malloc.h>
   90 #include <stdlib.h>
   91 #include <string.h>
   92 #include <sys/stat.h>
   93 
   94 #include "util.h"
   95 #include "config.h"
   96 
   97 extern char *tbpath[];
   98 
   99 extern OPT_LIST *prune_list;
  100 extern int n_prune_list;
  101 
  102 extern char *tbtype[];
  103 
  104 struct xftw_dirent {
  105     struct stat statbuf;
  106     char *name;
  107     char *fullname;
  108 };
  109 
  110 #define XFTW_MAXDEPTH 64    /* Maximum directory depth handled */
  111 
  112 typedef struct {
  113     struct xftw_dirent *contents;
  114     int size;
  115     int used;
  116 } xftw_tree_t;
  117 
  118 static xftw_tree_t tree[XFTW_MAXDEPTH];
  119 
  120 /* Free all data for one tree level */
  121 static void xftw_free_tree(int depth)
  122 {
  123     int i;
  124     xftw_tree_t *t = tree+depth;
  125     for (i = 0; i < t->size; ++i) {
  126     free(t->contents[i].name);
  127     free(t->contents[i].fullname);
  128     }
  129     free(t->contents);
  130     t->contents = NULL;
  131     t->size = 0;
  132     t->used = 0;
  133 }
  134 
  135 /* Increment dirents used at this depth, resizing if necessary */
  136 static void xftw_add_dirent(int depth)
  137 {
  138     xftw_tree_t *t = tree+depth;
  139     int i, size = t->size;
  140     if (++t->used < size)
  141     return;
  142     size += 10; /* arbitrary increment */
  143     t->contents = xrealloc(t->contents, size*sizeof(*(t->contents)));
  144     for (i = t->size; i < size; ++i) {
  145     memset(&(t->contents[i].statbuf), 0, sizeof(t->contents[i].statbuf));
  146     t->contents[i].name = NULL;
  147     t->contents[i].fullname = NULL;
  148     }
  149     t->size = size;
  150 }
  151 
  152 /* Concatenate directory name and entry name into one string.
  153  * Note: caller must free result or leak.
  154  */
  155 static char *xftw_dir_name(const char *directory, const char *entry)
  156 {
  157     int i = strlen(directory);
  158     char *name;
  159     if (entry)
  160     i += strlen(entry);
  161     i += 2;
  162     name = xmalloc(i);
  163     strcpy(name, directory);    /* safe, xmalloc */
  164     if (*directory && entry)
  165     strcat(name, "/");  /* safe, xmalloc */
  166     if (entry)
  167     strcat(name, entry);    /* safe, xmalloc */
  168     return(name);
  169 }
  170 
  171 /* Call the user function for a directory entry */
  172 static int xftw_do_name(const char *directory, const char *entry, struct stat *sb, xftw_func_t funcptr)
  173 {
  174     int ret = 0;
  175     char *name = xftw_dir_name(directory, entry);
  176 
  177     if (S_ISLNK(sb->st_mode)) {
  178     char real[PATH_MAX], *newname;
  179     verbose("resolving %s symlink to ", name);
  180     if (!(newname = realpath(name, real))) {
  181         if (errno == ENOENT) {
  182         verbose("%s: does not exist, dangling symlink ignored\n", real);
  183         goto cleanup;
  184         }
  185         perror("... failed");
  186         goto cleanup;
  187     }
  188     verbose("%s ", newname);
  189     if (lstat(newname, sb)) {
  190         error("lstat on %s failed ", newname);
  191         perror("");
  192         goto cleanup;
  193     }
  194     free(name);
  195     name = xstrdup(newname);
  196     }
  197 
  198     if (!S_ISREG(sb->st_mode) &&
  199     !S_ISDIR(sb->st_mode)) {
  200     error("%s is not plain file nor directory\n", name);
  201     goto cleanup;
  202     }
  203     
  204     verbose("user function %s\n", name);
  205     ret = (*funcptr)(name, sb);
  206 cleanup:
  207     free(name);
  208     return(ret);
  209 }
  210 
  211 /* Sort directory entries into alphabetical order */
  212 static int xftw_sortdir(const void *a, const void *b)
  213 {
  214     return(strcmp(((struct xftw_dirent *)a)->name, ((struct xftw_dirent *)b)->name));
  215 }
  216 
  217 /* Read a directory and sort it, ignoring "." and ".." */
  218 static int xftw_readdir(const char *directory, int depth)
  219 {
  220     DIR *d;
  221     struct dirent *ent;
  222     verbose("xftw_readdir %s\n", directory);
  223     if (!(d = opendir(directory))) {
  224     perror(directory);
  225     return(1);
  226     }
  227     while ((ent = readdir(d))) {
  228     char *name;
  229     struct xftw_dirent *f;
  230     if (strcmp(ent->d_name, ".") == 0 ||
  231         strcmp(ent->d_name, "..") == 0)
  232         continue;
  233     name = xftw_dir_name(directory, ent->d_name);
  234     xftw_add_dirent(depth);
  235     f = tree[depth].contents+tree[depth].used-1;
  236     f->name = xstrdup(ent->d_name);
  237     f->fullname = name;     /* do not free name, it is in use */
  238     if (lstat(name, &(f->statbuf))) {
  239         perror(name);
  240         return(1);
  241     }
  242     }
  243     closedir(d);
  244     qsort(tree[depth].contents, tree[depth].used, sizeof(*(tree[0].contents)), &xftw_sortdir);
  245     return(0);
  246 }
  247 
  248 /* Process a type 2 directory */
  249 int xftw_type2(const char *directory, const char *entry, int depth, xftw_func_t funcptr)
  250 {
  251     int ret, i;
  252     xftw_tree_t *t = tree+depth;
  253     struct stat statbuf;
  254     char *dirname = xftw_dir_name(directory, entry);
  255 
  256     verbose("type 2 %s\n", dirname);
  257     if (depth > XFTW_MAXDEPTH) {
  258     error("xftw_type2 exceeded maxdepth\n");
  259     ret = 1;
  260     goto cleanup;
  261     }
  262     if ((ret = xftw_readdir(dirname, depth)))
  263     goto cleanup;
  264 
  265     t = tree+depth;
  266     /* user function sees type 2 directories */
  267     if ((ret = lstat(dirname, &statbuf)) ||
  268     (ret = xftw_do_name("", dirname, &statbuf, funcptr)))
  269     goto cleanup;
  270 
  271     /* user sees all contents of type 2 directory, no pruning */
  272     for (i = 0; i < t->used; ++i) {
  273     struct xftw_dirent *c = t->contents+i;
  274     if (S_ISLNK(c->statbuf.st_mode)) {
  275         if (!stat(c->name, &(c->statbuf))) {
  276         if (S_ISDIR(c->statbuf.st_mode)) {
  277             error("symlink to directory is not allowed, %s ignored\n", c->name);
  278             *(c->name) = '\0';  /* ignore it */
  279         }
  280         }
  281     }
  282     if (!*(c->name))
  283         continue;
  284     if (S_ISDIR(c->statbuf.st_mode)) {
  285         /* recursion is the curse of the programming classes */
  286         ret = xftw_type2(dirname, c->name, depth+1, funcptr);
  287         if (ret)
  288         goto cleanup;
  289     }
  290     else if ((ret = xftw_do_name(dirname, c->name, &(c->statbuf), funcptr)))
  291         goto cleanup;
  292     *(c->name) = '\0';  /* processed */
  293     }
  294 
  295     ret = 0;
  296 cleanup:
  297     free(dirname);
  298     return(ret);
  299 }
  300 
  301 /* Only external visible function.  Decide on the type of directory and
  302  * process accordingly.
  303  */
  304 int xftw(const char *directory, xftw_func_t funcptr)
  305 {
  306     struct stat statbuf;
  307     int ret, i, j, type;
  308     xftw_tree_t *t;
  309     struct xftw_dirent *c;
  310 
  311     verbose("xftw starting at %s ", directory);
  312     if (lstat(directory, &statbuf)) {
  313     verbose("lstat on %s failed\n", directory);
  314     return(0);
  315     }
  316     if (S_ISLNK(statbuf.st_mode)) {
  317     char real[PATH_MAX];
  318     verbose("resolving symlink to ");
  319     if (!(directory = realpath(directory, real))) {
  320         if (errno == ENOENT) {
  321         verbose("%s: does not exist, dangling symlink ignored\n", real);
  322         return(0);
  323         }
  324         perror("... failed");
  325         return(-1);
  326     }
  327     verbose("%s ", directory);
  328     if (lstat(directory, &statbuf)) {
  329         error("lstat on %s failed ", directory);
  330         perror("");
  331         return(-1);
  332     }
  333     }
  334     if (!S_ISDIR(statbuf.st_mode)) {
  335     error("%s is not a directory\n", directory);
  336     return(-1);
  337     }
  338     verbose("\n");
  339 
  340     /* All returns after this point must be via cleanup */
  341 
  342     if ((ret = xftw_readdir(directory, 0)))
  343     goto cleanup;
  344 
  345     t = tree;   /* depth 0 */
  346     type = 2;
  347     for (i = 0 ; type == 2 && i < t->used; ++i) {
  348     c = t->contents+i;
  349     for (j = 0; tbtype[j]; ++j) {
  350         if (strcmp(c->name, tbtype[j]) == 0 &&
  351         S_ISDIR(c->statbuf.st_mode)) {
  352         const char *p = directory + strlen(directory) - 1;
  353         if (*p == '/')
  354             --p;
  355         if (p - directory >= 6 && strncmp(p-6, "/kernel", 7) == 0)
  356             continue;   /* "/kernel" path is a special case, type 2 */
  357         type = 1;   /* known subdirectory */
  358         break;
  359         }
  360     }
  361     }
  362 
  363     if (type == 1) {
  364     OPT_LIST *p;
  365     /* prune entries in type 1 directories only */
  366     for (i = 0 ; i < t->used; ++i) {
  367         for (p = prunelist; p->name; ++p) {
  368         c = t->contents+i;
  369         if (strcmp(p->name, c->name) == 0) {
  370             verbose("pruned %s\n", c->name);
  371             *(c->name) = '\0';  /* ignore */
  372         }
  373         }
  374     }
  375     /* run known subdirectories first in historical order, "kernel" is now top of list */
  376         for (j = 0; tbtype[j]; ++j) {
  377         for (i = 0 ; i < t->used; ++i) {
  378             c = t->contents+i;
  379         if (*(c->name) &&
  380             strcmp(c->name, tbtype[j]) == 0 &&
  381             S_ISDIR(c->statbuf.st_mode)) {
  382             if ((ret = xftw_type2(directory, c->name, 1, funcptr)))
  383             goto cleanup;
  384             *(c->name) = '\0';  /* processed */
  385         }
  386         }
  387     }
  388     /* any other directories left, in alphabetical order */
  389     for (i = 0 ; i < t->used; ++i) {
  390         c = t->contents+i;
  391         if (*(c->name) &&
  392             S_ISDIR(c->statbuf.st_mode)) {
  393         if ((ret = xftw_type2(directory, c->name, 1, funcptr)))
  394             goto cleanup;
  395         *(c->name) = '\0';  /* processed */
  396         }
  397     }
  398     /* anything else is passed to the user function */
  399     for (i = 0 ; i < t->used; ++i) {
  400         c = t->contents+i;
  401         if (*(c->name)) {
  402         verbose("%s found in type 1 directory %s\n", c->name, directory);
  403         if ((ret = xftw_do_name(directory, c->name, &(c->statbuf), funcptr)))
  404             goto cleanup;
  405         *(c->name) = '\0';  /* processed */
  406         }
  407     }
  408     }
  409     else {
  410     /* type 2 */
  411     xftw_free_tree(0);
  412     if ((ret = xftw_type2(directory, NULL, 0, funcptr)))
  413         goto cleanup;
  414     }
  415 
  416     /* amazing, it all worked */
  417     ret = 0;
  418 cleanup:
  419     for (i = 0; i < XFTW_MAXDEPTH; ++i)
  420     xftw_free_tree(i);
  421     return(ret);
  422 }