|
rpm-build |
0a0c83 |
/*
|
|
rpm-build |
0a0c83 |
File: walk_tree.c
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
Copyright (C) 2007 Andreas Gruenbacher <a.gruenbacher@computer.org>
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
This program is free software; you can redistribute it and/or modify it under
|
|
rpm-build |
0a0c83 |
the terms of the GNU Lesser General Public License as published by the
|
|
rpm-build |
0a0c83 |
Free Software Foundation; either version 2.1 of the License, or (at
|
|
rpm-build |
0a0c83 |
your option) any later version.
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
rpm-build |
0a0c83 |
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
rpm-build |
0a0c83 |
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
rpm-build |
0a0c83 |
License for more details.
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
You should have received a copy of the GNU Lesser General Public
|
|
rpm-build |
0a0c83 |
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
rpm-build |
0a0c83 |
*/
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
#include "config.h"
|
|
rpm-build |
0a0c83 |
#include <sys/types.h>
|
|
rpm-build |
0a0c83 |
#include <sys/stat.h>
|
|
rpm-build |
0a0c83 |
#include <unistd.h>
|
|
rpm-build |
0a0c83 |
#include <sys/time.h>
|
|
rpm-build |
0a0c83 |
#include <sys/resource.h>
|
|
rpm-build |
0a0c83 |
#include <dirent.h>
|
|
rpm-build |
0a0c83 |
#include <stdio.h>
|
|
rpm-build |
0a0c83 |
#include <string.h>
|
|
rpm-build |
0a0c83 |
#include <errno.h>
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
#include "walk_tree.h"
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
struct entry_handle {
|
|
rpm-build |
0a0c83 |
struct entry_handle *prev, *next;
|
|
rpm-build |
0a0c83 |
dev_t dev;
|
|
rpm-build |
0a0c83 |
ino_t ino;
|
|
rpm-build |
0a0c83 |
DIR *stream;
|
|
rpm-build |
0a0c83 |
long pos;
|
|
rpm-build |
0a0c83 |
};
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
static struct entry_handle head = {
|
|
rpm-build |
0a0c83 |
.next = &head,
|
|
rpm-build |
0a0c83 |
.prev = &head,
|
|
rpm-build |
0a0c83 |
/* The other fields are unused. */
|
|
rpm-build |
0a0c83 |
};
|
|
rpm-build |
0a0c83 |
static struct entry_handle *closed = &head;
|
|
rpm-build |
0a0c83 |
static unsigned int num_dir_handles;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
static int walk_tree_visited(dev_t dev, ino_t ino)
|
|
rpm-build |
0a0c83 |
{
|
|
rpm-build |
0a0c83 |
struct entry_handle *i;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
for (i = head.next; i != &head; i = i->next)
|
|
rpm-build |
0a0c83 |
if (i->dev == dev && i->ino == ino)
|
|
rpm-build |
0a0c83 |
return 1;
|
|
rpm-build |
0a0c83 |
return 0;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
static int walk_tree_rec(const char *path, int walk_flags,
|
|
rpm-build |
0a0c83 |
int (*func)(const char *, const struct stat *, int,
|
|
rpm-build |
0a0c83 |
void *), void *arg, int depth)
|
|
rpm-build |
0a0c83 |
{
|
|
rpm-build |
0a0c83 |
int follow_symlinks = (walk_flags & WALK_TREE_LOGICAL) ||
|
|
rpm-build |
0a0c83 |
((walk_flags & WALK_TREE_DEREFERENCE) &&
|
|
rpm-build |
0a0c83 |
!(walk_flags & WALK_TREE_PHYSICAL) &&
|
|
rpm-build |
0a0c83 |
depth == 0);
|
|
rpm-build |
0a0c83 |
int have_dir_stat = 0, flags = walk_flags, err;
|
|
rpm-build |
0a0c83 |
struct entry_handle dir;
|
|
rpm-build |
0a0c83 |
struct stat st;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
/*
|
|
rpm-build |
0a0c83 |
* If (walk_flags & WALK_TREE_PHYSICAL), do not traverse symlinks.
|
|
rpm-build |
0a0c83 |
* If (walk_flags & WALK_TREE_LOGICAL), traverse all symlinks.
|
|
rpm-build |
0a0c83 |
* Otherwise, traverse only top-level symlinks.
|
|
rpm-build |
0a0c83 |
*/
|
|
rpm-build |
0a0c83 |
if (depth == 0)
|
|
rpm-build |
0a0c83 |
flags |= WALK_TREE_TOPLEVEL;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
if (lstat(path, &st) != 0)
|
|
rpm-build |
0a0c83 |
return func(path, NULL, flags | WALK_TREE_FAILED, arg);
|
|
rpm-build |
0a0c83 |
if (S_ISLNK(st.st_mode)) {
|
|
rpm-build |
0a0c83 |
flags |= WALK_TREE_SYMLINK;
|
|
rpm-build |
0a0c83 |
if ((flags & WALK_TREE_DEREFERENCE) ||
|
|
rpm-build |
0a0c83 |
((flags & WALK_TREE_TOPLEVEL) &&
|
|
rpm-build |
0a0c83 |
(flags & WALK_TREE_DEREFERENCE_TOPLEVEL))) {
|
|
rpm-build |
0a0c83 |
if (stat(path, &st) != 0)
|
|
rpm-build |
0a0c83 |
return func(path, NULL,
|
|
rpm-build |
0a0c83 |
flags | WALK_TREE_FAILED, arg);
|
|
rpm-build |
0a0c83 |
dir.dev = st.st_dev;
|
|
rpm-build |
0a0c83 |
dir.ino = st.st_ino;
|
|
rpm-build |
0a0c83 |
have_dir_stat = 1;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
} else if (S_ISDIR(st.st_mode)) {
|
|
rpm-build |
0a0c83 |
dir.dev = st.st_dev;
|
|
rpm-build |
0a0c83 |
dir.ino = st.st_ino;
|
|
rpm-build |
0a0c83 |
have_dir_stat = 1;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
err = func(path, &st, flags, arg);
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
/*
|
|
rpm-build |
0a0c83 |
* Recurse if WALK_TREE_RECURSIVE and the path is:
|
|
rpm-build |
0a0c83 |
* a dir not from a symlink
|
|
rpm-build |
0a0c83 |
* a link and follow_symlinks
|
|
rpm-build |
0a0c83 |
*/
|
|
rpm-build |
0a0c83 |
if ((flags & WALK_TREE_RECURSIVE) &&
|
|
rpm-build |
0a0c83 |
((!(flags & WALK_TREE_SYMLINK) && S_ISDIR(st.st_mode)) ||
|
|
rpm-build |
0a0c83 |
((flags & WALK_TREE_SYMLINK) && follow_symlinks))) {
|
|
rpm-build |
0a0c83 |
struct dirent *entry;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
/*
|
|
rpm-build |
0a0c83 |
* Check if we have already visited this directory to break
|
|
rpm-build |
0a0c83 |
* endless loops.
|
|
rpm-build |
0a0c83 |
*
|
|
rpm-build |
0a0c83 |
* If we haven't stat()ed the file yet, do an opendir() for
|
|
rpm-build |
0a0c83 |
* figuring out whether we have a directory, and check whether
|
|
rpm-build |
0a0c83 |
* the directory has been visited afterwards. This saves a
|
|
rpm-build |
0a0c83 |
* system call for each non-directory found.
|
|
rpm-build |
0a0c83 |
*/
|
|
rpm-build |
0a0c83 |
if (have_dir_stat && walk_tree_visited(dir.dev, dir.ino))
|
|
rpm-build |
0a0c83 |
return err;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
if (num_dir_handles == 0 && closed->prev != &head) {
|
|
rpm-build |
0a0c83 |
close_another_dir:
|
|
rpm-build |
0a0c83 |
/* Close the topmost directory handle still open. */
|
|
rpm-build |
0a0c83 |
closed = closed->prev;
|
|
rpm-build |
0a0c83 |
closed->pos = telldir(closed->stream);
|
|
rpm-build |
0a0c83 |
closedir(closed->stream);
|
|
rpm-build |
0a0c83 |
closed->stream = NULL;
|
|
rpm-build |
0a0c83 |
num_dir_handles++;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
dir.stream = opendir(path);
|
|
rpm-build |
0a0c83 |
if (!dir.stream) {
|
|
rpm-build |
0a0c83 |
if (errno == ENFILE && closed->prev != &head) {
|
|
rpm-build |
0a0c83 |
/* Ran out of file descriptors. */
|
|
rpm-build |
0a0c83 |
num_dir_handles = 0;
|
|
rpm-build |
0a0c83 |
goto close_another_dir;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
/*
|
|
rpm-build |
0a0c83 |
* PATH may be a symlink to a regular file, or a dead
|
|
rpm-build |
0a0c83 |
* symlink which we didn't follow above.
|
|
rpm-build |
0a0c83 |
*/
|
|
rpm-build |
0a0c83 |
if (errno != ENOTDIR && errno != ENOENT)
|
|
rpm-build |
0a0c83 |
err += func(path, NULL, flags |
|
|
rpm-build |
0a0c83 |
WALK_TREE_FAILED, arg);
|
|
rpm-build |
0a0c83 |
return err;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
/* See walk_tree_visited() comment above... */
|
|
rpm-build |
0a0c83 |
if (!have_dir_stat) {
|
|
rpm-build |
0a0c83 |
if (stat(path, &st) != 0)
|
|
rpm-build |
0a0c83 |
goto skip_dir;
|
|
rpm-build |
0a0c83 |
dir.dev = st.st_dev;
|
|
rpm-build |
0a0c83 |
dir.ino = st.st_ino;
|
|
rpm-build |
0a0c83 |
if (walk_tree_visited(dir.dev, dir.ino))
|
|
rpm-build |
0a0c83 |
goto skip_dir;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
/* Insert into the list of handles. */
|
|
rpm-build |
0a0c83 |
dir.next = head.next;
|
|
rpm-build |
0a0c83 |
dir.prev = &head;
|
|
rpm-build |
0a0c83 |
dir.prev->next = &dir;
|
|
rpm-build |
0a0c83 |
dir.next->prev = &dir;
|
|
rpm-build |
0a0c83 |
num_dir_handles--;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
while ((entry = readdir(dir.stream)) != NULL) {
|
|
rpm-build |
0a0c83 |
char *path_end;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
if (!strcmp(entry->d_name, ".") ||
|
|
rpm-build |
0a0c83 |
!strcmp(entry->d_name, ".."))
|
|
rpm-build |
0a0c83 |
continue;
|
|
rpm-build |
0a0c83 |
path_end = strchr(path, 0);
|
|
rpm-build |
0a0c83 |
if ((path_end - path) + strlen(entry->d_name) + 1 >=
|
|
rpm-build |
0a0c83 |
FILENAME_MAX) {
|
|
rpm-build |
0a0c83 |
errno = ENAMETOOLONG;
|
|
rpm-build |
0a0c83 |
err += func(path, NULL,
|
|
rpm-build |
0a0c83 |
flags | WALK_TREE_FAILED, arg);
|
|
rpm-build |
0a0c83 |
continue;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
*path_end++ = '/';
|
|
rpm-build |
0a0c83 |
strcpy(path_end, entry->d_name);
|
|
rpm-build |
0a0c83 |
err += walk_tree_rec(path, walk_flags, func, arg,
|
|
rpm-build |
0a0c83 |
depth + 1);
|
|
rpm-build |
0a0c83 |
*--path_end = 0;
|
|
rpm-build |
0a0c83 |
if (!dir.stream) {
|
|
rpm-build |
0a0c83 |
/* Reopen the directory handle. */
|
|
rpm-build |
0a0c83 |
dir.stream = opendir(path);
|
|
rpm-build |
0a0c83 |
if (!dir.stream)
|
|
rpm-build |
0a0c83 |
return err + func(path, NULL, flags |
|
|
rpm-build |
0a0c83 |
WALK_TREE_FAILED, arg);
|
|
rpm-build |
0a0c83 |
seekdir(dir.stream, dir.pos);
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
closed = closed->next;
|
|
rpm-build |
0a0c83 |
num_dir_handles--;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
/* Remove from the list of handles. */
|
|
rpm-build |
0a0c83 |
dir.prev->next = dir.next;
|
|
rpm-build |
0a0c83 |
dir.next->prev = dir.prev;
|
|
rpm-build |
0a0c83 |
num_dir_handles++;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
skip_dir:
|
|
rpm-build |
0a0c83 |
if (closedir(dir.stream) != 0)
|
|
rpm-build |
0a0c83 |
err += func(path, NULL, flags | WALK_TREE_FAILED, arg);
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
return err;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
int walk_tree(const char *path, int walk_flags, unsigned int num,
|
|
rpm-build |
0a0c83 |
int (*func)(const char *, const struct stat *, int, void *),
|
|
rpm-build |
0a0c83 |
void *arg)
|
|
rpm-build |
0a0c83 |
{
|
|
rpm-build |
0a0c83 |
char path_copy[FILENAME_MAX];
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
num_dir_handles = num;
|
|
rpm-build |
0a0c83 |
if (num_dir_handles < 1) {
|
|
rpm-build |
0a0c83 |
struct rlimit rlimit;
|
|
rpm-build |
0a0c83 |
|
|
rpm-build |
0a0c83 |
num_dir_handles = 1;
|
|
rpm-build |
0a0c83 |
if (getrlimit(RLIMIT_NOFILE, &rlimit) == 0 &&
|
|
rpm-build |
0a0c83 |
rlimit.rlim_cur >= 2)
|
|
rpm-build |
0a0c83 |
num_dir_handles = rlimit.rlim_cur / 2;
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
if (strlen(path) >= FILENAME_MAX) {
|
|
rpm-build |
0a0c83 |
errno = ENAMETOOLONG;
|
|
rpm-build |
0a0c83 |
return func(path, NULL, WALK_TREE_FAILED, arg);
|
|
rpm-build |
0a0c83 |
}
|
|
rpm-build |
0a0c83 |
strcpy(path_copy, path);
|
|
rpm-build |
0a0c83 |
return walk_tree_rec(path_copy, walk_flags, func, arg, 0);
|
|
rpm-build |
0a0c83 |
}
|