diff options
Diffstat (limited to 'usr.bin/tar/write.c')
| -rw-r--r-- | usr.bin/tar/write.c | 291 |
1 files changed, 116 insertions, 175 deletions
diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c index dfcbcba274a4..3d833e93f8d3 100644 --- a/usr.bin/tar/write.c +++ b/usr.bin/tar/write.c @@ -37,7 +37,6 @@ __FBSDID("$FreeBSD$"); #include <errno.h> #include <fcntl.h> #include <fnmatch.h> -#include <fts.h> #include <grp.h> #include <limits.h> #include <pwd.h> @@ -51,6 +50,7 @@ __FBSDID("$FreeBSD$"); #endif #include "bsdtar.h" +#include "tree.h" /* Fixed size of uname/gname caches. */ #define name_cache_size 101 @@ -514,205 +514,146 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, const char *filename) static void write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) { - FTS *fts; - FTSENT *ftsent; - int ftsoptions; - char *fts_argv[2]; + struct tree *tree; + char symlink_mode = bsdtar->symlink_mode; + dev_t first_dev = 0; + int dev_recorded = 0; + int tree_ret; #ifdef __linux int fd, r; unsigned long fflags; #endif - /* - * Sigh: fts_open modifies it's first parameter, so we have to - * copy 'path' to mutable storage. - */ - fts_argv[0] = strdup(path); - if (fts_argv[0] == NULL) - bsdtar_errc(bsdtar, 1, ENOMEM, "Can't open %s", path); - fts_argv[1] = NULL; - ftsoptions = FTS_PHYSICAL; - switch (bsdtar->symlink_mode) { - case 'H': - ftsoptions |= FTS_COMFOLLOW; - break; - case 'L': - ftsoptions = FTS_COMFOLLOW | FTS_LOGICAL; - break; - } - if (bsdtar->option_dont_traverse_mounts) - ftsoptions |= FTS_XDEV; - - fts = fts_open(fts_argv, ftsoptions, NULL); + tree = tree_open(path); - - if (!fts) { + if (!tree) { bsdtar_warnc(bsdtar, errno, "%s: Cannot open", path); bsdtar->return_value = 1; return; } - while ((ftsent = fts_read(fts))) { - switch (ftsent->fts_info) { - case FTS_NS: - bsdtar_warnc(bsdtar, ftsent->fts_errno, - "%s: Could not stat", ftsent->fts_path); - bsdtar->return_value = 1; - break; - case FTS_ERR: - bsdtar_warnc(bsdtar, ftsent->fts_errno, "%s", - ftsent->fts_path); - bsdtar->return_value = 1; - break; - case FTS_DNR: - bsdtar_warnc(bsdtar, ftsent->fts_errno, - "%s: Cannot read directory contents", - ftsent->fts_path); - bsdtar->return_value = 1; - break; - case FTS_W: /* Skip Whiteout entries */ - break; - case FTS_DC: /* Directory that causes cycle */ - /* XXX Does this need special handling ? */ - break; - case FTS_D: - /* - * If this dir is flagged "nodump" and we're - * honoring such flags, tell FTS to skip the - * entire tree and don't write the entry for the - * directory itself. - */ -#ifdef HAVE_CHFLAGS - if (bsdtar->option_honor_nodump && - (ftsent->fts_statp->st_flags & UF_NODUMP)) { - fts_set(fts, ftsent, FTS_SKIP); - break; - } -#endif - -#ifdef __linux - /* - * Linux has a nodump flag too but to read it - * we have to open() the dir and do an ioctl on it... - */ - if (bsdtar->option_honor_nodump && - ((fd = open(ftsent->fts_name, O_RDONLY|O_NONBLOCK)) >= 0) && - ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags)), - close(fd), r) >= 0 && - (fflags & EXT2_NODUMP_FL)) { - fts_set(fts, ftsent, FTS_SKIP); - break; - } -#endif - - /* - * In -u mode, we need to check whether this - * is newer than what's already in the archive. - * In all modes, we need to obey --newerXXX flags. - */ - if (!new_enough(bsdtar, ftsent->fts_path, - ftsent->fts_statp)) - break; - /* - * If this dir is excluded by a filename - * pattern, tell FTS to skip the entire tree - * and don't write the entry for the directory - * itself. - */ - if (excluded(bsdtar, ftsent->fts_path)) { - fts_set(fts, ftsent, FTS_SKIP); - break; - } + while ((tree_ret = tree_next(tree))) { + const char *name = tree_current_path(tree); + const struct stat *st = NULL, *lst = NULL; + int descend; - /* - * If the user vetoes the directory, skip - * the whole thing. - */ - if (bsdtar->option_interactive && - !yes("add '%s'", ftsent->fts_path)) { - fts_set(fts, ftsent, FTS_SKIP); - break; - } + if (tree_ret == TREE_ERROR) + bsdtar_warnc(bsdtar, errno, "%s", name); + if (tree_ret != TREE_REGULAR) + continue; + lst = tree_current_lstat(tree); + if (lst == NULL) { + /* Couldn't lstat(); must not exist. */ + bsdtar_warnc(bsdtar, errno, "%s: Cannot open", path); + bsdtar->return_value = 1; + continue; + } + if (S_ISLNK(lst->st_mode)) + st = tree_current_stat(tree); + /* Default: descend into any dir or symlink to dir. */ + /* We'll adjust this later on. */ + descend = 0; + if ((st != NULL) && S_ISDIR(st->st_mode)) + descend = 1; + if ((lst != NULL) && S_ISDIR(lst->st_mode)) + descend = 1; + + /* + * If user has asked us not to cross mount points, + * then don't descend into into a dir on a different + * device. + */ + if (!dev_recorded) { + first_dev = lst->st_dev; + dev_recorded = 1; + } + if (bsdtar->option_dont_traverse_mounts) { + if (lst != NULL && lst->st_dev != first_dev) + descend = 0; + } - /* - * If we're not recursing, tell FTS to skip the - * tree but do fall through and write the entry - * for the dir itself. - */ - if (bsdtar->option_no_subdirs) - fts_set(fts, ftsent, FTS_SKIP); - write_entry(bsdtar, a, ftsent->fts_statp, - ftsent->fts_path, ftsent->fts_pathlen, - ftsent->fts_accpath); - break; - case FTS_F: - case FTS_SL: - case FTS_SLNONE: - case FTS_DEFAULT: - /* - * Skip this file if it's flagged "nodump" and we're - * honoring that flag. - */ -#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) - if (bsdtar->option_honor_nodump && - (ftsent->fts_statp->st_flags & UF_NODUMP)) - break; + /* + * If this file/dir is flagged "nodump" and we're + * honoring such flags, skip this file/dir. + */ +#ifdef HAVE_CHFLAGS + if (bsdtar->option_honor_nodump && + (lst->st_flags & UF_NODUMP)) + continue; #endif #ifdef __linux - /* - * Linux has a nodump flag too but to read it - * we have to open() the file and do an ioctl on it... - */ - if (bsdtar->option_honor_nodump && - S_ISREG(ftsent->fts_statp->st_mode) && - ((fd = open(ftsent->fts_name, O_RDONLY|O_NONBLOCK)) >= 0) && - ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags)), - close(fd), r) >= 0 && - (fflags & EXT2_NODUMP_FL)) - break; + /* + * Linux has a nodump flag too but to read it + * we have to open() the file/dir and do an ioctl on it... + */ + if (bsdtar->option_honor_nodump && + ((fd = open(name, O_RDONLY|O_NONBLOCK)) >= 0) && + ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags)), + close(fd), r) >= 0 && + (fflags & EXT2_NODUMP_FL)) + continue; #endif - /* - * Skip this file if it's excluded by a - * filename pattern. - */ - if (excluded(bsdtar, ftsent->fts_path)) - break; - - /* - * In -u mode, we need to check whether this - * is newer than what's already in the archive. - */ - if (!new_enough(bsdtar, ftsent->fts_path, - ftsent->fts_statp)) - break; + /* + * If this file/dir is excluded by a filename + * pattern, skip it. + */ + if (excluded(bsdtar, name)) + continue; - if (bsdtar->option_interactive && - !yes("add '%s'", ftsent->fts_path)) { - break; - } + /* + * If the user vetoes this file/directory, skip it. + */ + if (bsdtar->option_interactive && + !yes("add '%s'", name)) + continue; - write_entry(bsdtar, a, ftsent->fts_statp, - ftsent->fts_path, ftsent->fts_pathlen, - ftsent->fts_accpath); + /* + * If this is a dir, decide whether or not to recurse. + */ + if (bsdtar->option_no_subdirs) + descend = 0; + + /* + * Distinguish 'L'/'P'/'H' symlink following. + */ + switch(symlink_mode) { + case 'H': + /* 'H': First item (from command line) like 'L'. */ + lst = tree_current_stat(tree); + /* 'H': After the first item, rest like 'P'. */ + symlink_mode = 'P'; break; - case FTS_DP: + case 'L': + /* 'L': Do descend through a symlink to dir. */ + /* 'L': Archive symlink to file as file. */ + lst = tree_current_stat(tree); break; default: - bsdtar_warnc(bsdtar, 0, - "%s: Hierarchy traversal error %d\n", - ftsent->fts_path, - ftsent->fts_info); + /* 'P': Don't descend through a symlink to dir. */ + if (!S_ISDIR(lst->st_mode)) + descend = 0; + /* 'P': Archive symlink to file as symlink. */ + /* lst = tree_current_lstat(tree); */ break; } + if (descend) + tree_descend(tree); + + /* + * In -u mode, we need to check whether this + * is newer than what's already in the archive. + * In all modes, we need to obey --newerXXX flags. + */ + if (new_enough(bsdtar, name, lst)) { + write_entry(bsdtar, a, lst, name, + tree_current_pathlen(tree), + tree_current_access_path(tree)); + } } - if (errno) - bsdtar_warnc(bsdtar, errno, "%s", path); - if (fts_close(fts)) - bsdtar_warnc(bsdtar, errno, "fts_close failed"); - free(fts_argv[0]); + tree_close(tree); } /* @@ -750,7 +691,7 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st, /* Display entry as we process it. This format is required by SUSv2. */ if (bsdtar->verbose) - safe_fprintf(stderr, "a %s", pathname); + safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); /* Read symbolic link information. */ if ((st->st_mode & S_IFMT) == S_IFLNK) { @@ -784,7 +725,7 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st, #ifdef __linux if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) && ((fd = open(accpath, O_RDONLY|O_NONBLOCK)) >= 0) && - ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags)), close(fd), r) >= 0 && + ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags)), close(fd), (fd = -1), r) >= 0 && stflags) { archive_entry_set_fflags(entry, stflags, 0); } |
