Diff for /xfs-cmds/acl/libmisc/walk_tree.c between versions 1.1 and 1.2

version 1.1, 2007/11/21 05:14:46 version 1.2, 2007/12/04 05:11:38
Line 21 Line 21
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <unistd.h>  #include <unistd.h>
   #include <sys/time.h>
   #include <sys/resource.h>
 #include <dirent.h>  #include <dirent.h>
 #include <stdio.h>  #include <stdio.h>
 #include <string.h>  #include <string.h>
Line 28 Line 30
   
 #include "walk_tree.h"  #include "walk_tree.h"
   
   struct entry_handle {
           struct entry_handle *prev, *next;
           dev_t dev;
           ino_t ino;
           DIR *stream;
           off_t pos;
   };
   
   struct entry_handle head = {
           .next = &head,
           .prev = &head,
           /* The other fields are unused. */
   };
   struct entry_handle *closed = &head;
   unsigned int num_dir_handles;
   
   static int walk_tree_visited(dev_t dev, ino_t ino)
   {
           struct entry_handle *i;
   
           for (i = head.next; i != &head; i = i->next)
                   if (i->dev == dev && i->ino == ino)
                           return 1;
           return 0;
   }
   
 static int walk_tree_rec(const char *path, int walk_flags,  static int walk_tree_rec(const char *path, int walk_flags,
                          int (*func)(const char *, const struct stat *, int, void *),                           int (*func)(const char *, const struct stat *, int,
                          void *arg, int depth)                                       void *), void *arg, int depth)
 {  {
         int (*xstat)(const char *, struct stat *) = lstat;          int follow_symlinks = (walk_flags & WALK_TREE_LOGICAL) ||
                                 (!(walk_flags & WALK_TREE_PHYSICAL) &&
                                  depth == 0);
           int have_dir_stat = 0, flags = walk_flags, err;
           struct entry_handle dir;
         struct stat st;          struct stat st;
         int local_walk_flags = walk_flags, err;  
   
         /* Default to traversing symlinks on the command line, traverse all symlinks          /*
          * with -L, and do not traverse symlinks with -P. (This is similar to chown.)           * If (walk_flags & WALK_TREE_PHYSICAL), do not traverse symlinks.
            * If (walk_flags & WALK_TREE_LOGICAL), traverse all symlinks.
            * Otherwise, traverse only top-level symlinks.
          */           */
           if (depth == 0)
                   flags |= WALK_TREE_TOPLEVEL;
   
 follow_symlink:          if (lstat(path, &st) != 0)
         if (xstat(path, &st) != 0)                  return func(path, NULL, flags | WALK_TREE_FAILED, arg);
                 return func(path, NULL, local_walk_flags | WALK_TREE_FAILED, arg);  
         if (S_ISLNK(st.st_mode)) {          if (S_ISLNK(st.st_mode)) {
                 if ((local_walk_flags & WALK_TREE_PHYSICAL) ||                  flags |= WALK_TREE_SYMLINK;
                     (!(local_walk_flags & WALK_TREE_LOGICAL) && depth > 1))                  if (flags & WALK_TREE_DEREFERENCE) {
                         return 0;                          if (stat(path, &st) != 0)
                 local_walk_flags |= WALK_TREE_SYMLINK;                                  return func(path, NULL,
                 xstat = stat;                                              flags | WALK_TREE_FAILED, arg);
                 if (local_walk_flags & WALK_TREE_DEREFERENCE)                          dir.dev = st.st_dev;
                         goto follow_symlink;                          dir.ino = st.st_ino;
                           have_dir_stat = 1;
                   }
           } else if (S_ISDIR(st.st_mode)) {
                   dir.dev = st.st_dev;
                   dir.ino = st.st_ino;
                   have_dir_stat = 1;
         }          }
         err = func(path, &st, local_walk_flags, arg);          err = func(path, &st, flags, arg);
         if ((local_walk_flags & WALK_TREE_RECURSIVE) &&          if ((flags & WALK_TREE_RECURSIVE) &&
             (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) {              (S_ISDIR(st.st_mode) || (S_ISLNK(st.st_mode) && follow_symlinks))) {
                 char path2[FILENAME_MAX];  
                 DIR *dir;  
                 struct dirent *entry;                  struct dirent *entry;
                 int err2;  
   
                 dir = opendir(path);                  /*
                 if (!dir) {                   * Check if we have already visited this directory to break
                         /* PATH may be a symlink to a regular file or a dead symlink                   * endless loops.
                          * which we didn't follow above.                   *
                    * If we haven't stat()ed the file yet, do an opendir() for
                    * figuring out whether we have a directory, and check whether
                    * the directory has been visited afterwards. This saves a
                    * system call for each non-directory found.
                    */
                   if (have_dir_stat && walk_tree_visited(dir.dev, dir.ino))
                           return err;
   
                   if (num_dir_handles == 0 && closed->prev != &head) {
   close_another_dir:
                           /* Close the topmost directory handle still open. */
                           closed = closed->prev;
                           closed->pos = telldir(closed->stream);
                           closedir(closed->stream);
                           closed->stream = NULL;
                           num_dir_handles++;
                   }
   
                   dir.stream = opendir(path);
                   if (!dir.stream) {
                           if (errno == ENFILE && closed->prev != &head) {
                                   /* Ran out of file descriptors. */
                                   num_dir_handles = 0;
                                   goto close_another_dir;
                           }
   
                           /*
                            * PATH may be a symlink to a regular file, or a dead
                            * symlink which we didn't follow above.
                          */                           */
                         if (errno != ENOTDIR && errno != ENOENT)                          if (errno != ENOTDIR && errno != ENOENT)
                                 err += func(path, &st,                                  err += func(path, NULL, flags |
                                             local_walk_flags | WALK_TREE_FAILED, arg);                                                          WALK_TREE_FAILED, arg);
                         return err;                          return err;
                 }                  }
                 while ((entry = readdir(dir)) != NULL) {  
                         if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))                  /* See walk_tree_visited() comment above... */
                   if (!have_dir_stat) {
                           if (stat(path, &st) != 0)
                                   goto skip_dir;
                           dir.dev = st.st_dev;
                           dir.ino = st.st_ino;
                           if (walk_tree_visited(dir.dev, dir.ino))
                                   goto skip_dir;
                   }
   
                   /* Insert into the list of handles. */
                   dir.next = head.next;
                   dir.prev = &head;
                   dir.prev->next = &dir;
                   dir.next->prev = &dir;
                   num_dir_handles--;
   
                   while ((entry = readdir(dir.stream)) != NULL) {
                           char *path_end;
   
                           if (!strcmp(entry->d_name, ".") ||
                               !strcmp(entry->d_name, ".."))
                                 continue;                                  continue;
                         err2 = snprintf(path2, sizeof(path2), "%s/%s", path,                          path_end = strchr(path, 0);
                                        entry->d_name);                          if ((path_end - path) + strlen(entry->d_name) + 1 >=
                         if (err2 < 0 || err2 > FILENAME_MAX) {                              FILENAME_MAX) {
                                 errno = ENAMETOOLONG;                                  errno = ENAMETOOLONG;
                                 err += func(path, NULL,                                  err += func(path, NULL,
                                             local_walk_flags | WALK_TREE_FAILED, arg);                                              flags | WALK_TREE_FAILED, arg);
                                 continue;                                  continue;
                         }                          }
                         err += walk_tree_rec(path2, walk_flags, func, arg, depth + 1);                          *path_end++ = '/';
                           strcpy(path_end, entry->d_name);
                           err += walk_tree_rec(path, walk_flags, func, arg,
                                                depth + 1);
                           *--path_end = 0;
                           if (!dir.stream) {
                                   /* Reopen the directory handle. */
                                   dir.stream = opendir(path);
                                   if (!dir.stream)
                                           return err + func(path, NULL, flags |
                                                       WALK_TREE_FAILED, arg);
                                   seekdir(dir.stream, dir.pos);
   
                                   closed = closed->next;
                                   num_dir_handles--;
                           }
                 }                  }
                 if (closedir(dir) != 0)  
                         err += func(path, &st, local_walk_flags | WALK_TREE_FAILED, arg);                  /* Remove from the list of handles. */
                   dir.prev->next = dir.next;
                   dir.next->prev = dir.prev;
                   num_dir_handles++;
   
           skip_dir:
                   if (closedir(dir.stream) != 0)
                           err += func(path, NULL, flags | WALK_TREE_FAILED, arg);
         }          }
         return err;          return err;
 }  }
   
 int walk_tree(const char *path, int walk_flags,  int walk_tree(const char *path, int walk_flags, unsigned int num,
               int (*func)(const char *, const struct stat *, int, void *), void *arg)                int (*func)(const char *, const struct stat *, int, void *),
                 void *arg)
 {  {
           char path_copy[FILENAME_MAX];
   
           num_dir_handles = num;
           if (num_dir_handles < 1) {
                   struct rlimit rlimit;
   
                   num_dir_handles = 1;
                   if (getrlimit(RLIMIT_NOFILE, &rlimit) == 0 &&
                       rlimit.rlim_cur >= 2)
                           num_dir_handles = rlimit.rlim_cur / 2;
           }
         if (strlen(path) >= FILENAME_MAX) {          if (strlen(path) >= FILENAME_MAX) {
                 errno = ENAMETOOLONG;                  errno = ENAMETOOLONG;
                 return func(path, NULL, WALK_TREE_FAILED, arg);                  return func(path, NULL, WALK_TREE_FAILED, arg);
         }          }
         return walk_tree_rec(path, walk_flags, func, arg, 1);          strcpy(path_copy, path);
           return walk_tree_rec(path_copy, walk_flags, func, arg, 0);
 }  }

Removed from v.1.1  
changed lines
  Added in v.1.2


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>