Index: acl-2.2.45/getfacl/getfacl.c =================================================================== --- acl-2.2.45.orig/getfacl/getfacl.c +++ acl-2.2.45/getfacl/getfacl.c @@ -34,10 +34,10 @@ #include #include #include -#include #include #include "config.h" #include "user_group.h" +#include "walk_tree.h" #include "misc.h" #define POSIXLY_CORRECT_STR "POSIXLY_CORRECT" @@ -70,24 +70,22 @@ struct option long_options[] = { const char *progname; const char *cmd_line_options; -int opt_recursive; /* recurse into sub-directories? */ -int opt_walk_logical; /* always follow symbolic links */ -int opt_walk_physical; /* never follow symbolic links */ -int opt_print_acl = 0; -int opt_print_default_acl = 0; +int walk_flags = WALK_TREE_DEREFERENCE; +int opt_print_acl; +int opt_print_default_acl; int opt_strip_leading_slash = 1; int opt_comments = 1; /* include comments */ -int opt_skip_base = 0; /* skip files that only have the base entries */ -int opt_tabular = 0; /* tabular output format (alias `showacl') */ +int opt_skip_base; /* skip files that only have the base entries */ +int opt_tabular; /* tabular output format (alias `showacl') */ #if POSIXLY_CORRECT const int posixly_correct = 1; /* Posix compatible behavior! */ #else -int posixly_correct = 0; /* Posix compatible behavior? */ +int posixly_correct; /* Posix compatible behavior? */ #endif -int had_errors = 0; -int absolute_warning = 0; /* Absolute path warning was issued */ +int had_errors; +int absolute_warning; /* Absolute path warning was issued */ int print_options = TEXT_SOME_EFFECTIVE; -int opt_numeric = 0; /* don't convert id's to symbolic names */ +int opt_numeric; /* don't convert id's to symbolic names */ static const char *xquote(const char *str) @@ -425,12 +423,23 @@ acl_get_file_mode(const char *path_p) return acl_from_mode(st.st_mode); } -int do_print(const char *path_p, const struct stat *st) +int do_print(const char *path_p, const struct stat *st, int walk_flags, void *unused) { const char *default_prefix = NULL; acl_t acl = NULL, default_acl = NULL; int error = 0; + if (walk_flags & WALK_TREE_FAILED) { + fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p), + strerror(errno)); + return 1; + } + + if ((walk_flags & WALK_TREE_SYMLINK) && + ((walk_flags & WALK_TREE_PHYSICAL) || + !(walk_flags & (WALK_TREE_TOPLEVEL | WALK_TREE_LOGICAL)))) + return 0; + if (opt_print_acl) { acl = acl_get_file(path_p, ACL_TYPE_ACCESS); if (acl == NULL && (errno == ENOSYS || errno == ENOTSUP)) @@ -549,7 +558,7 @@ void help(void) " --skip-base skip files that only have the base entries\n" " -R, --recursive recurse into subdirectories\n" " -L, --logical logical walk, follow symbolic links\n" -" -P --physical physical walk, do not follow symbolic links\n" +" -P, --physical physical walk, do not follow symbolic links\n" " --tabular use tabular output format\n" " --numeric print numeric user/group identifiers\n" " --absolute-names don't strip leading '/' in pathnames\n")); @@ -560,75 +569,6 @@ void help(void) " --help this help text\n")); } - -static int __errors; -int __do_print(const char *file, const struct stat *stat, - int flag, struct FTW *ftw) -{ - int saved_errno = errno; - - /* Process the target of a symbolic link, and traverse the link, - only if doing a logical walk, or if the symbolic link was - specified on the command line. Always skip symbolic links if - doing a physical walk. */ - - if (S_ISLNK(stat->st_mode) && - (opt_walk_physical || (ftw->level > 0 && !opt_walk_logical))) - return 0; - - if (do_print(file, stat)) - __errors++; - - if (flag == FTW_DNR && opt_recursive) { - /* Item is a directory which can't be read. */ - fprintf(stderr, "%s: %s: %s\n", - progname, file, strerror(saved_errno)); - return 0; - } - - /* We also get here in non-recursive mode. In that case, - return something != 0 to abort nftw. */ - - if (!opt_recursive) - return 1; - - return 0; -} - -char *resolve_symlinks(const char *file) -{ - static char buffer[4096]; - struct stat stat; - char *path = NULL; - - if (lstat(file, &stat) == -1) - return path; - - if (S_ISLNK(stat.st_mode) && !opt_walk_physical) - path = realpath(file, buffer); - else - path = (char *)file; /* not a symlink, use given path */ - - return path; -} - -int walk_tree(const char *file) -{ - const char *p; - - __errors = 0; - if ((p = resolve_symlinks(file)) == NULL) { - fprintf(stderr, "%s: %s: %s\n", progname, - xquote(file), strerror(errno)); - __errors++; - } else if (nftw(p, __do_print, 0, opt_walk_logical? 0 : FTW_PHYS) < 0) { - fprintf(stderr, "%s: %s: %s\n", progname, xquote(file), - strerror(errno)); - __errors++; - } - return __errors; -} - int main(int argc, char *argv[]) { int opt; @@ -691,21 +631,21 @@ int main(int argc, char *argv[]) case 'R': /* recursive */ if (posixly_correct) goto synopsis; - opt_recursive = 1; + walk_flags |= WALK_TREE_RECURSIVE; break; case 'L': /* follow all symlinks */ if (posixly_correct) goto synopsis; - opt_walk_logical = 1; - opt_walk_physical = 0; + walk_flags |= WALK_TREE_LOGICAL; + walk_flags &= ~WALK_TREE_PHYSICAL; break; case 'P': /* skip all symlinks */ if (posixly_correct) goto synopsis; - opt_walk_logical = 0; - opt_walk_physical = 1; + walk_flags |= WALK_TREE_PHYSICAL; + walk_flags &= ~WALK_TREE_LOGICAL; break; case 's': /* skip files with only base entries */ @@ -762,7 +702,8 @@ int main(int argc, char *argv[]) if (*line == '\0') continue; - had_errors += walk_tree(line); + had_errors += walk_tree(line, walk_flags, 0, + do_print, NULL); } if (!feof(stdin)) { fprintf(stderr, _("%s: Standard input: %s\n"), @@ -770,7 +711,8 @@ int main(int argc, char *argv[]) had_errors++; } } else - had_errors += walk_tree(argv[optind]); + had_errors += walk_tree(argv[optind], walk_flags, 0, + do_print, NULL); optind++; } while (optind < argc); Index: acl-2.2.45/libmisc/Makefile =================================================================== --- acl-2.2.45.orig/libmisc/Makefile +++ acl-2.2.45/libmisc/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/include/builddefs LTLIBRARY = libmisc.la LTLDFLAGS = -CFILES = quote.c unquote.c high_water_alloc.c next_line.c +CFILES = quote.c unquote.c high_water_alloc.c next_line.c walk_tree.c default: $(LTLIBRARY) install install-dev install-lib: Index: acl-2.2.45/setfacl/do_set.c =================================================================== --- acl-2.2.45.orig/setfacl/do_set.c +++ acl-2.2.45/setfacl/do_set.c @@ -36,10 +36,10 @@ #include "sequence.h" #include "parse.h" #include "config.h" +#include "walk_tree.h" extern const char *progname; -extern int opt_recursive; extern int opt_recalculate; extern int opt_test; extern int print_options; @@ -259,8 +259,10 @@ int do_set( const char *path_p, const struct stat *st, - const seq_t seq) + int walk_flags, + void *arg) { + const seq_t seq = (const seq_t)arg; acl_t old_acl = NULL, old_default_acl = NULL; acl_t acl = NULL, default_acl = NULL; acl_t *xacl, *old_xacl; @@ -272,6 +274,16 @@ do_set( int acl_modified = 0, default_acl_modified = 0; int acl_mask_provided = 0, default_acl_mask_provided = 0; + if (walk_flags & WALK_TREE_FAILED) { + fprintf(stderr, "%s: %s: %s\n", progname, path_p, strerror(errno)); + return 1; + } + + if ((walk_flags & WALK_TREE_SYMLINK) && + ((walk_flags & WALK_TREE_PHYSICAL) || + !(walk_flags & (WALK_TREE_TOPLEVEL | WALK_TREE_LOGICAL)))) + return 0; + /* Execute the commands in seq (read ACLs on demand) */ error = seq_get_cmd(seq, SEQ_FIRST_CMD, &cmd); if (error == 0) @@ -426,7 +438,7 @@ do_set( } /* Only directores can have default ACLs */ - if (default_acl && !S_ISDIR(st->st_mode) && opt_recursive) { + if (default_acl && !S_ISDIR(st->st_mode) && (walk_flags & WALK_TREE_RECURSIVE)) { /* In recursive mode, ignore default ACLs for files */ acl_free(default_acl); default_acl = NULL; Index: acl-2.2.45/setfacl/setfacl.c =================================================================== --- acl-2.2.45.orig/setfacl/setfacl.c +++ acl-2.2.45/setfacl/setfacl.c @@ -28,20 +28,15 @@ #include #include #include -#include #include #include #include "config.h" #include "sequence.h" #include "parse.h" +#include "walk_tree.h" #include "misc.h" -extern int -do_set( - const char *path_p, - const struct stat *stat_p, - const seq_t seq); - +extern int do_set(const char *path_p, const struct stat *stat_p, int flags, void *arg); #define POSIXLY_CORRECT_STR "POSIXLY_CORRECT" @@ -82,9 +77,7 @@ struct option long_options[] = { const char *progname; const char *cmd_line_options, *cmd_line_spec; -int opt_recursive; /* recurse into sub-directories? */ -int opt_walk_logical; /* always follow symbolic links */ -int opt_walk_physical; /* never follow symbolic links */ +int walk_flags = WALK_TREE_DEREFERENCE; int opt_recalculate; /* recalculate mask entry (0=default, 1=yes, -1=no) */ int opt_promote; /* promote access ACL to default ACL */ int opt_test; /* do not write to the file system. @@ -188,7 +181,7 @@ restore( stat.st_uid = uid; stat.st_gid = gid; - error = do_set(path_p, &stat, seq); + error = do_set(path_p, &stat, 0, seq); if (error != 0) { status = 1; goto resume; @@ -275,77 +268,6 @@ void help(void) } -static int __errors; -static seq_t __seq; - -int __do_set(const char *file, const struct stat *stat, - int flag, struct FTW *ftw) -{ - int saved_errno = errno; - - /* Process the target of a symbolic link, and traverse the link, - only if doing a logical walk, or if the symbolic link was - specified on the command line. Always skip symbolic links if - doing a physical walk. */ - - if (S_ISLNK(stat->st_mode) && - (opt_walk_physical || (ftw->level > 0 && !opt_walk_logical))) - return 0; - - if (do_set(file, stat, __seq)) - __errors++; - - if (flag == FTW_DNR && opt_recursive) { - /* Item is a directory which can't be read. */ - fprintf(stderr, "%s: %s: %s\n", - progname, file, strerror(saved_errno)); - return 0; - } - - /* We also get here in non-recursive mode. In that case, - return something != 0 to abort nftw. */ - - if (!opt_recursive) - return 1; - - return 0; -} - -char *resolve_symlinks(const char *file) -{ - static char buffer[4096]; - struct stat stat; - char *path = NULL; - - if (lstat(file, &stat) == -1) - return path; - - if (S_ISLNK(stat.st_mode) && !opt_walk_physical) - path = realpath(file, buffer); - else - path = (char *)file; /* not a symlink, use given path */ - - return path; -} - -int walk_tree(const char *file, seq_t seq) -{ - const char *p; - - __errors = 0; - __seq = seq; - if ((p = resolve_symlinks(file)) == NULL) { - fprintf(stderr, "%s: %s: %s\n", progname, - xquote(file), strerror(errno)); - __errors++; - } else if (nftw(p, __do_set, 0, opt_walk_logical ? 0 : FTW_PHYS) < 0) { - fprintf(stderr, "%s: %s: %s\n", progname, - xquote(file), strerror(errno)); - __errors++; - } - return __errors; -} - int next_file(const char *arg, seq_t seq) { char *line; @@ -353,14 +275,14 @@ int next_file(const char *arg, seq_t seq if (strcmp(arg, "-") == 0) { while ((line = next_line(stdin))) - errors = walk_tree(line, seq); + errors = walk_tree(line, walk_flags, 0, do_set, seq); if (!feof(stdin)) { fprintf(stderr, _("%s: Standard input: %s\n"), progname, strerror(errno)); errors = 1; } } else { - errors = walk_tree(arg, seq); + errors = walk_tree(arg, walk_flags, 0, do_set, seq); } return errors ? 1 : 0; } @@ -627,17 +549,17 @@ int main(int argc, char *argv[]) break; case 'R': /* recursive */ - opt_recursive = 1; + walk_flags |= WALK_TREE_RECURSIVE; break; case 'L': /* follow symlinks */ - opt_walk_logical = 1; - opt_walk_physical = 0; + walk_flags |= WALK_TREE_LOGICAL; + walk_flags &= ~WALK_TREE_PHYSICAL; break; case 'P': /* do not follow symlinks */ - opt_walk_logical = 0; - opt_walk_physical = 1; + walk_flags |= WALK_TREE_PHYSICAL; + walk_flags &= ~WALK_TREE_LOGICAL; break; case 't': /* test mode */ Index: acl-2.2.45/include/walk_tree.h =================================================================== --- /dev/null +++ acl-2.2.45/include/walk_tree.h @@ -0,0 +1,19 @@ +#ifndef __WALK_TREE_H +#define __WALK_TREE_H + +#define WALK_TREE_RECURSIVE 0x1 +#define WALK_TREE_PHYSICAL 0x2 +#define WALK_TREE_LOGICAL 0x4 +#define WALK_TREE_DEREFERENCE 0x8 + +#define WALK_TREE_TOPLEVEL 0x100 +#define WALK_TREE_SYMLINK 0x200 +#define WALK_TREE_FAILED 0x400 + +struct stat; + +extern int walk_tree(const char *path, int walk_flags, unsigned int num, + int (*func)(const char *, const struct stat *, int, + void *), void *arg); + +#endif Index: acl-2.2.45/libmisc/walk_tree.c =================================================================== --- /dev/null +++ acl-2.2.45/libmisc/walk_tree.c @@ -0,0 +1,188 @@ +/* + File: walk_tree.c + + Copyright (C) 2007 Andreas Gruenbacher + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "walk_tree.h" + +struct entry_handle { + struct entry_handle *prev, *next; + struct stat st; + 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_rec(const char *path, int walk_flags, + int (*func)(const char *, const struct stat *, int, + void *), void *arg, int depth) +{ + int (*xstat)(const char *, struct stat *) = lstat; + int flags = walk_flags, err; + struct entry_handle dir; + + /* + * 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 (xstat(path, &dir.st) != 0) + return func(path, NULL, flags | WALK_TREE_FAILED, arg); + if (S_ISLNK(dir.st.st_mode)) { + flags |= WALK_TREE_SYMLINK; + if (flags & WALK_TREE_DEREFERENCE) { + xstat = stat; + goto follow_symlink; + } + } + err = func(path, &dir.st, flags, arg); + if ((flags & WALK_TREE_RECURSIVE) && + (S_ISDIR(dir.st.st_mode) || (S_ISLNK(dir.st.st_mode))) && + (!(flags & WALK_TREE_PHYSICAL) || !(flags & WALK_TREE_SYMLINK)) && + (flags & (WALK_TREE_LOGICAL | WALK_TREE_TOPLEVEL))) { + struct entry_handle *i; + struct dirent *entry; + + /* Check if we have already visited this directory. */ + for (i = head.next; i != &head; i = i->next) + if (i->st.st_dev == dir.st.st_dev && + i->st.st_ino == dir.st.st_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) + err += func(path, &dir.st, + flags | WALK_TREE_FAILED, arg); + return err; + } + + /* 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; + path_end = strchr(path, 0); + if ((path_end - path) + strlen(entry->d_name) + 1 >= + FILENAME_MAX) { + errno = ENAMETOOLONG; + err += func(path, NULL, + flags | WALK_TREE_FAILED, arg); + continue; + } + *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, &dir.st, flags | + WALK_TREE_FAILED, arg); + seekdir(dir.stream, dir.pos); + + closed = closed->next; + num_dir_handles--; + } + } + + if (closedir(dir.stream) != 0) + err += func(path, &dir.st, flags | WALK_TREE_FAILED, + arg); + + /* Remove from the list of handles. */ + dir.prev->next = dir.next; + dir.next->prev = dir.prev; + num_dir_handles++; + } + return err; +} + +int walk_tree(const char *path, int walk_flags, unsigned int num, + 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) { + errno = ENAMETOOLONG; + return func(path, NULL, WALK_TREE_FAILED, arg); + } + strcpy(path_copy, path); + return walk_tree_rec(path_copy, walk_flags, func, arg, 0); +}