diff --git a/acl.changes b/acl.changes index 615f1d9..452d956 100644 --- a/acl.changes +++ b/acl.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Sat Oct 27 18:17:58 CEST 2007 - agruen@suse.de + +- Don't exhaust the number of file descriptors in the path walking + code, and make sure each directory is only visited once. + ------------------------------------------------------------------- Fri Oct 26 02:34:02 CEST 2007 - agruen@suse.de diff --git a/acl.spec b/acl.spec index 1f32172..2127768 100644 --- a/acl.spec +++ b/acl.spec @@ -16,7 +16,7 @@ Group: System/Filesystems AutoReqProv: on Summary: Commands for Manipulating POSIX Access Control Lists Version: 2.2.45 -Release: 1 +Release: 3 Source: %name-%version.src.tar.bz2 Patch0: builddefs.in.diff Patch1: walk-acl.diff @@ -179,6 +179,9 @@ rm -f $RPM_BUILD_ROOT/%{_lib}/libacl.{a,la,so} %defattr(755,root,root,755) /%{_lib}/libacl.so.1* %changelog +* Sat Oct 27 2007 - agruen@suse.de +- Don't exhaust the number of file descriptors in the path walking + code, and make sure each directory is only visited once. * Fri Oct 26 2007 - agruen@suse.de - A large jump to the current upstream version 2.2.45. - Fix the upstream path walking code. diff --git a/walk-acl.diff b/walk-acl.diff index 055c07e..9474fa4 100644 --- a/walk-acl.diff +++ b/walk-acl.diff @@ -80,7 +80,7 @@ Index: acl-2.2.45/getfacl/getfacl.c static const char *xquote(const char *str) -@@ -425,12 +423,18 @@ acl_get_file_mode(const char *path_p) +@@ -425,12 +423,23 @@ acl_get_file_mode(const char *path_p) return acl_from_mode(st.st_mode); } @@ -96,11 +96,16 @@ Index: acl-2.2.45/getfacl/getfacl.c + 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 +553,7 @@ void help(void) +@@ -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" @@ -109,7 +114,7 @@ Index: acl-2.2.45/getfacl/getfacl.c " --tabular use tabular output format\n" " --numeric print numeric user/group identifiers\n" " --absolute-names don't strip leading '/' in pathnames\n")); -@@ -560,75 +564,6 @@ void help(void) +@@ -560,75 +569,6 @@ void help(void) " --help this help text\n")); } @@ -185,7 +190,7 @@ Index: acl-2.2.45/getfacl/getfacl.c int main(int argc, char *argv[]) { int opt; -@@ -691,21 +626,21 @@ int main(int argc, char *argv[]) +@@ -691,21 +631,21 @@ int main(int argc, char *argv[]) case 'R': /* recursive */ if (posixly_correct) goto synopsis; @@ -212,21 +217,23 @@ Index: acl-2.2.45/getfacl/getfacl.c break; case 's': /* skip files with only base entries */ -@@ -762,7 +697,7 @@ int main(int argc, char *argv[]) +@@ -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, do_print, NULL); ++ had_errors += walk_tree(line, walk_flags, 0, ++ do_print, NULL); } if (!feof(stdin)) { fprintf(stderr, _("%s: Standard input: %s\n"), -@@ -770,7 +705,7 @@ int main(int argc, char *argv[]) +@@ -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, do_print, NULL); ++ had_errors += walk_tree(argv[optind], walk_flags, 0, ++ do_print, NULL); optind++; } while (optind < argc); @@ -286,7 +293,7 @@ Index: acl-2.2.45/setfacl/do_set.c acl_t old_acl = NULL, old_default_acl = NULL; acl_t acl = NULL, default_acl = NULL; acl_t *xacl, *old_xacl; -@@ -272,6 +274,11 @@ do_set( +@@ -272,6 +274,16 @@ do_set( int acl_modified = 0, default_acl_modified = 0; int acl_mask_provided = 0, default_acl_mask_provided = 0; @@ -294,11 +301,16 @@ Index: acl-2.2.45/setfacl/do_set.c + 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 +433,7 @@ do_set( +@@ -426,7 +438,7 @@ do_set( } /* Only directores can have default ACLs */ @@ -437,7 +449,7 @@ Index: acl-2.2.45/setfacl/setfacl.c if (strcmp(arg, "-") == 0) { while ((line = next_line(stdin))) - errors = walk_tree(line, seq); -+ errors = walk_tree(line, walk_flags, do_set, seq); ++ errors = walk_tree(line, walk_flags, 0, do_set, seq); if (!feof(stdin)) { fprintf(stderr, _("%s: Standard input: %s\n"), progname, strerror(errno)); @@ -445,7 +457,7 @@ Index: acl-2.2.45/setfacl/setfacl.c } } else { - errors = walk_tree(arg, seq); -+ errors = walk_tree(arg, walk_flags, do_set, seq); ++ errors = walk_tree(arg, walk_flags, 0, do_set, seq); } return errors ? 1 : 0; } @@ -476,7 +488,7 @@ Index: acl-2.2.45/include/walk_tree.h =================================================================== --- /dev/null +++ acl-2.2.45/include/walk_tree.h -@@ -0,0 +1,18 @@ +@@ -0,0 +1,19 @@ +#ifndef __WALK_TREE_H +#define __WALK_TREE_H + @@ -485,21 +497,22 @@ Index: acl-2.2.45/include/walk_tree.h +#define WALK_TREE_LOGICAL 0x4 +#define WALK_TREE_DEREFERENCE 0x8 + -+#define WALK_TREE_SYMLINK 0x10 -+#define WALK_TREE_FAILED 0x20 ++#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, -+ int (*func)(const char *, const struct stat *, int, void *), -+ void *arg); ++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,100 @@ +@@ -0,0 +1,188 @@ +/* + File: walk_tree.c + @@ -523,6 +536,8 @@ Index: acl-2.2.45/libmisc/walk_tree.c +#include +#include +#include ++#include ++#include +#include +#include +#include @@ -530,73 +545,159 @@ Index: acl-2.2.45/libmisc/walk_tree.c + +#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 (*func)(const char *, const struct stat *, int, ++ void *), void *arg, int depth) +{ + int (*xstat)(const char *, struct stat *) = lstat; -+ struct stat st; -+ int local_walk_flags = walk_flags, err; ++ int flags = walk_flags, err; ++ struct entry_handle dir; + -+ /* 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 (xstat(path, &st) != 0) -+ return func(path, NULL, local_walk_flags | WALK_TREE_FAILED, arg); -+ if (S_ISLNK(st.st_mode)) { -+ if ((local_walk_flags & WALK_TREE_PHYSICAL) || -+ (!(local_walk_flags & WALK_TREE_LOGICAL) && depth > 1)) -+ return 0; -+ local_walk_flags |= WALK_TREE_SYMLINK; -+ xstat = stat; -+ if (local_walk_flags & WALK_TREE_DEREFERENCE) ++ 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, &st, local_walk_flags, arg); -+ if ((local_walk_flags & WALK_TREE_RECURSIVE) && -+ (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) { -+ char path2[FILENAME_MAX]; -+ DIR *dir; ++ 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; -+ int err2; + -+ dir = opendir(path); -+ if (!dir) { -+ /* PATH may be a symlink to a regular file or a dead symlink -+ * which we didn't follow above. ++ /* 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, &st, -+ local_walk_flags | WALK_TREE_FAILED, arg); ++ err += func(path, &dir.st, ++ flags | WALK_TREE_FAILED, arg); + return err; + } -+ while ((entry = readdir(dir)) != NULL) { -+ if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) ++ ++ /* 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; -+ err2 = snprintf(path2, sizeof(path2), "%s/%s", path, -+ entry->d_name); -+ if (err2 < 0 || err2 > FILENAME_MAX) { ++ path_end = strchr(path, 0); ++ if ((path_end - path) + strlen(entry->d_name) + 1 >= ++ FILENAME_MAX) { + errno = ENAMETOOLONG; + err += func(path, NULL, -+ local_walk_flags | WALK_TREE_FAILED, arg); ++ flags | WALK_TREE_FAILED, arg); + 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, &dir.st, 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); ++ ++ 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, -+ int (*func)(const char *, const struct stat *, int, void *), void *arg) ++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); + } -+ 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); +}