static void DTAR_write_header(struct archive* ar, uint64_t idx, uint64_t offset) { /* allocate and entry for this item */ struct archive_entry* entry = archive_entry_new(); /* get file name for this item */ /* fill up entry, FIXME: the uglyness of removing leading slash */ const char* fname = mfu_flist_file_get_name(DTAR_flist, idx); archive_entry_copy_pathname(entry, &fname[1]); if (DTAR_user_opts.preserve) { struct archive* source = archive_read_disk_new(); archive_read_disk_set_standard_lookup(source); int fd = open(fname, O_RDONLY); if (archive_read_disk_entry_from_file(source, entry, fd, NULL) != ARCHIVE_OK) { MFU_LOG(MFU_LOG_ERR, "archive_read_disk_entry_from_file(): %s", archive_error_string(ar)); } archive_read_free(source); close(fd); } else { /* TODO: read stat info from mfu_flist */ struct stat stbuf; mfu_lstat(fname, &stbuf); archive_entry_copy_stat(entry, &stbuf); /* set user name of owner */ const char* uname = mfu_flist_file_get_username(DTAR_flist, idx); archive_entry_set_uname(entry, uname); /* set group name */ const char* gname = mfu_flist_file_get_groupname(DTAR_flist, idx); archive_entry_set_gname(entry, gname); } /* TODO: Seems to be a bug here potentially leading to corrupted * archive files. archive_write_free also writes two blocks of * NULL bytes at the end of an archive file, however, each rank * will have a different view of the length of the file, so one * rank may write its NULL blocks over top of the actual data * written by another rank */ /* write entry info to archive */ struct archive* dest = archive_write_new(); archive_write_set_format_pax(dest); if (archive_write_open_fd(dest, DTAR_writer.fd_tar) != ARCHIVE_OK) { MFU_LOG(MFU_LOG_ERR, "archive_write_open_fd(): %s", archive_error_string(ar)); } /* seek to offset in tar archive for this file */ lseek(DTAR_writer.fd_tar, offset, SEEK_SET); /* write header for this item */ if (archive_write_header(dest, entry) != ARCHIVE_OK) { MFU_LOG(MFU_LOG_ERR, "archive_write_header(): %s", archive_error_string(ar)); } archive_entry_free(entry); archive_write_free(dest); }
/* * Sparse test without directory traversals. */ static void verify_sparse_file2(struct archive *a, const char *path, const struct sparse *sparse, int blocks, int preopen) { struct archive_entry *ae; int fd; (void)sparse; /* UNUSED */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_pathname(ae, path); if (preopen) fd = open(path, O_RDONLY | O_BINARY); else fd = -1; assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_entry_from_file(a, ae, fd, NULL)); if (fd >= 0) close(fd); /* Verify the number of holes only, not its offset nor its * length because those alignments are deeply dependence on * its filesystem. */ failure(path); assertEqualInt(blocks, archive_entry_sparse_count(ae)); archive_entry_free(ae); }
int packing_append_file(struct packing *pack, const char *filepath, const char *newpath) { int fd; int len; char linkdest[MAXPATHLEN]; char buf[BUFSIZ]; int retcode = EPKG_OK; struct stat st; archive_entry_clear(pack->entry); archive_entry_copy_sourcepath(pack->entry, filepath); retcode = archive_read_disk_entry_from_file(pack->aread, pack->entry, -1, NULL); if (retcode != ARCHIVE_OK) { pkg_emit_event(PKG_EVENT_ARCHIVE_ERROR, /*argc*/2, filepath, pack->aread); retcode = EPKG_FATAL; goto cleanup; } retcode = EPKG_OK; lstat(filepath, &st); archive_entry_copy_stat(pack->entry, &st); if (S_ISLNK(st.st_mode)) { bzero(linkdest, MAXPATHLEN); readlink(filepath, linkdest, MAXPATHLEN); archive_entry_set_symlink(pack->entry, linkdest); } if (newpath != NULL) archive_entry_set_pathname(pack->entry, newpath); if (archive_entry_filetype(pack->entry) != AE_IFREG) { archive_entry_set_size(pack->entry, 0); } archive_write_header(pack->awrite, pack->entry); if (archive_entry_size(pack->entry) > 0) { if ((fd = open(filepath, O_RDONLY)) < 0) { pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "open", filepath, strerror(errno)); retcode = EPKG_FATAL; goto cleanup; } while ((len = read(fd, buf, sizeof(buf))) > 0 ) archive_write_data(pack->awrite, buf, len); close(fd); } cleanup: archive_entry_clear(pack->entry); return (retcode); }
void DTAR_write_header(struct archive *ar, uint64_t idx, uint64_t offset) { const char * fname = mfu_flist_file_get_name(DTAR_flist, idx); /* fill up entry, FIXME: the uglyness of removing leading slash */ struct archive_entry *entry = archive_entry_new(); archive_entry_copy_pathname(entry, &fname[1]); if (DTAR_user_opts.preserve) { struct archive * source = archive_read_disk_new(); archive_read_disk_set_standard_lookup(source); int fd = open(fname, O_RDONLY); if (archive_read_disk_entry_from_file(source, entry, fd, NULL) != ARCHIVE_OK) { MFU_LOG(MFU_LOG_ERR, "archive_read_disk_entry_from_file(): %s", archive_error_string(ar)); } archive_read_free(source); } else { /* read stat info from mfu_flist */ struct stat stbuf; mfu_lstat(fname, &stbuf); archive_entry_copy_stat(entry, &stbuf); const char* uname = mfu_flist_file_get_username(DTAR_flist, idx); archive_entry_set_uname(entry, uname); const char* gname = mfu_flist_file_get_groupname(DTAR_flist, idx); archive_entry_set_gname(entry, gname); } /* write entry info to archive */ struct archive* dest = archive_write_new(); archive_write_set_format_pax(dest); if (archive_write_open_fd(dest, DTAR_writer.fd_tar) != ARCHIVE_OK) { MFU_LOG(MFU_LOG_ERR, "archive_write_open_fd(): %s", archive_error_string(ar)); } lseek64(DTAR_writer.fd_tar, offset, SEEK_SET); if (archive_write_header(dest, entry) != ARCHIVE_OK) { MFU_LOG(MFU_LOG_ERR, "archive_write_header(): %s", archive_error_string(ar)); } archive_entry_free(entry); archive_write_free(dest); }
void file_to_archive(struct archive *a, const char *name) { char buffer[8192]; size_t bytes_read; struct archive *ard; struct archive_entry *entry; int fd; ard = archive_read_disk_new(); archive_read_disk_set_standard_lookup(ard); entry = archive_entry_new(); fd = open(name, O_RDONLY); if (fd < 0) return; archive_entry_copy_pathname(entry, name); archive_read_disk_entry_from_file(ard, entry, fd, NULL); archive_write_header(a, entry); while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) archive_write_data(a, buffer, bytes_read); archive_write_finish_entry(a); archive_read_close(ard); archive_read_free(ard); archive_entry_free(entry); }
int packing_append_file_attr(struct packing *pack, const char *filepath, const char *newpath, const char *uname, const char *gname, mode_t perm, u_long fflags) { int fd; int retcode = EPKG_OK; int ret; time_t source_time; struct stat st; struct archive_entry *entry, *sparse_entry; bool unset_timestamp; const char *source_date_epoch; char buf[32768]; int len; entry = archive_entry_new(); archive_entry_copy_sourcepath(entry, filepath); pkg_debug(2, "Packing file '%s'", filepath); if (lstat(filepath, &st) != 0) { pkg_emit_errno("lstat", filepath); retcode = EPKG_FATAL; goto cleanup; } ret = archive_read_disk_entry_from_file(pack->aread, entry, -1, &st); if (ret != ARCHIVE_OK) { pkg_emit_error("%s: %s", filepath, archive_error_string(pack->aread)); retcode = EPKG_FATAL; goto cleanup; } if (newpath != NULL) archive_entry_set_pathname(entry, newpath); if (archive_entry_filetype(entry) != AE_IFREG) { archive_entry_set_size(entry, 0); } if (uname != NULL && uname[0] != '\0') { archive_entry_set_uname(entry, uname); } if (gname != NULL && gname[0] != '\0') { archive_entry_set_gname(entry, gname); } if (fflags > 0) archive_entry_set_fflags(entry, fflags, 0); if (perm != 0) archive_entry_set_perm(entry, perm); unset_timestamp = pkg_object_bool(pkg_config_get("UNSET_TIMESTAMP")); if (unset_timestamp) { archive_entry_unset_atime(entry); archive_entry_unset_ctime(entry); archive_entry_unset_mtime(entry); archive_entry_unset_birthtime(entry); } if ((source_date_epoch = getenv("SOURCE_DATE_EPOCH")) != NULL) { if (source_date_epoch[strspn(source_date_epoch, "0123456789")] != '\0') { pkg_emit_error("Bad environment variable " "SOURCE_DATE_EPOCH: %s", source_date_epoch); retcode = EPKG_FATAL; goto cleanup; } source_time = strtoll(source_date_epoch, NULL, 10); archive_entry_set_atime(entry, source_time, 0); archive_entry_set_ctime(entry, source_time, 0); archive_entry_set_mtime(entry, source_time, 0); archive_entry_set_birthtime(entry, source_time, 0); } archive_entry_linkify(pack->resolver, &entry, &sparse_entry); if (sparse_entry != NULL && entry == NULL) entry = sparse_entry; archive_write_header(pack->awrite, entry); if (archive_entry_size(entry) <= 0) goto cleanup; if ((fd = open(filepath, O_RDONLY)) < 0) { pkg_emit_errno("open", filepath); retcode = EPKG_FATAL; goto cleanup; } while ((len = read(fd, buf, sizeof(buf))) > 0) { if (archive_write_data(pack->awrite, buf, len) == -1) { pkg_emit_errno("archive_write_data", "archive write error"); retcode = EPKG_FATAL; break; } } if (len == -1) { pkg_emit_errno("read", "file read error"); retcode = EPKG_FATAL; } close(fd); cleanup: archive_entry_free(entry); return (retcode); }
/* * This is used by both out mode (to copy objects from disk into * an archive) and pass mode (to copy objects from disk to * an archive_write_disk "archive"). */ static int file_to_archive(struct cpio *cpio, const char *srcpath) { const char *destpath; struct archive_entry *entry, *spare; size_t len; const char *p; int r; /* * Create an archive_entry describing the source file. * */ entry = archive_entry_new(); if (entry == NULL) lafe_errc(1, 0, "Couldn't allocate entry"); archive_entry_copy_sourcepath(entry, srcpath); r = archive_read_disk_entry_from_file(cpio->archive_read_disk, entry, -1, NULL); if (r < ARCHIVE_FAILED) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive_read_disk)); if (r < ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(cpio->archive_read_disk)); if (r <= ARCHIVE_FAILED) { cpio->return_value = 1; return (r); } if (cpio->uid_override >= 0) archive_entry_set_uid(entry, cpio->uid_override); if (cpio->gid_override >= 0) archive_entry_set_gid(entry, cpio->gid_override); /* * Generate a destination path for this entry. * "destination path" is the name to which it will be copied in * pass mode or the name that will go into the archive in * output mode. */ destpath = srcpath; if (cpio->destdir) { len = strlen(cpio->destdir) + strlen(srcpath) + 8; if (len >= cpio->pass_destpath_alloc) { while (len >= cpio->pass_destpath_alloc) { cpio->pass_destpath_alloc += 512; cpio->pass_destpath_alloc *= 2; } free(cpio->pass_destpath); cpio->pass_destpath = malloc(cpio->pass_destpath_alloc); if (cpio->pass_destpath == NULL) lafe_errc(1, ENOMEM, "Can't allocate path buffer"); } strcpy(cpio->pass_destpath, cpio->destdir); p = srcpath; while (p[0] == '/') ++p; strcat(cpio->pass_destpath, p); destpath = cpio->pass_destpath; } if (cpio->option_rename) destpath = cpio_rename(destpath); if (destpath == NULL) return (0); archive_entry_copy_pathname(entry, destpath); /* * If we're trying to preserve hardlinks, match them here. */ spare = NULL; if (cpio->linkresolver != NULL && archive_entry_filetype(entry) != AE_IFDIR) { archive_entry_linkify(cpio->linkresolver, &entry, &spare); } if (entry != NULL) { r = entry_to_archive(cpio, entry); archive_entry_free(entry); if (spare != NULL) { if (r == 0) r = entry_to_archive(cpio, spare); archive_entry_free(spare); } } return (r); }
int packing_append_file_attr(struct packing *pack, const char *filepath, const char *newpath, const char *uname, const char *gname, mode_t perm) { int fd; int len; char buf[BUFSIZ]; int retcode = EPKG_OK; int ret; struct stat st; struct archive_entry *entry, *sparse_entry; /* ugly hack for python and emacs */ /*char *p;*/ /*bool unset_timestamp = true;*/ entry = archive_entry_new(); archive_entry_copy_sourcepath(entry, filepath); if (lstat(filepath, &st) != 0) { pkg_emit_errno("lstat", filepath); retcode = EPKG_FATAL; goto cleanup; } ret = archive_read_disk_entry_from_file(pack->aread, entry, -1, &st); if (ret != ARCHIVE_OK) { pkg_emit_error("%s: %s", filepath, archive_error_string(pack->aread)); retcode = EPKG_FATAL; goto cleanup; } if (newpath != NULL) archive_entry_set_pathname(entry, newpath); if (archive_entry_filetype(entry) != AE_IFREG) { archive_entry_set_size(entry, 0); } if (uname != NULL && uname[0] != '\0') archive_entry_set_uname(entry, uname); if (gname != NULL && gname[0] != '\0') archive_entry_set_gname(entry, gname); if (perm != 0) archive_entry_set_perm(entry, perm); /* XXX ugly hack for python and emacs */ /* p = strrchr(filepath, '.'); if (p != NULL && (strcmp(p, ".pyc") == 0 || strcmp(p, ".py") == 0 || strcmp(p, ".pyo") == 0 || strcmp(p, ".elc") == 0 || strcmp(p, ".el") == 0 )) unset_timestamp = false; if (unset_timestamp) { archive_entry_unset_atime(entry); archive_entry_unset_ctime(entry); archive_entry_unset_mtime(entry); archive_entry_unset_birthtime(entry); }*/ archive_entry_linkify(pack->resolver, &entry, &sparse_entry); if (sparse_entry != NULL && entry == NULL) entry = sparse_entry; archive_write_header(pack->awrite, entry); if (archive_entry_size(entry) > 0) { if ((fd = open(filepath, O_RDONLY)) < 0) { pkg_emit_errno("open", filepath); retcode = EPKG_FATAL; goto cleanup; } while ((len = read(fd, buf, sizeof(buf))) > 0) archive_write_data(pack->awrite, buf, len); close(fd); } cleanup: archive_entry_free(entry); return (retcode); }
/* * Add the file or dir hierarchy named by 'path' to the archive */ static void write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) { struct archive_entry *entry = NULL, *spare_entry = NULL; struct tree *tree; char symlink_mode = bsdtar->symlink_mode; dev_t first_dev = 0; int dev_recorded = 0; int tree_ret; tree = tree_open(path); if (!tree) { lafe_warnc(errno, "%s: Cannot open", path); bsdtar->return_value = 1; return; } while ((tree_ret = tree_next(tree)) != 0) { int r; const char *name = tree_current_path(tree); const struct stat *st = NULL; /* info to use for this entry */ const struct stat *lst = NULL; /* lstat() information */ int descend; if (tree_ret == TREE_ERROR_FATAL) lafe_errc(1, tree_errno(tree), "%s: Unable to continue traversing directory tree", name); if (tree_ret == TREE_ERROR_DIR) { lafe_warnc(errno, "%s: Couldn't visit directory", name); bsdtar->return_value = 1; } if (tree_ret != TREE_REGULAR) continue; /* * If this file/dir is excluded by a filename * pattern, skip it. */ if (lafe_excluded(bsdtar->matching, name)) continue; /* * Get lstat() info from the tree library. */ lst = tree_current_lstat(tree); if (lst == NULL) { /* Couldn't lstat(); must not exist. */ lafe_warnc(errno, "%s: Cannot stat", name); /* Return error if files disappear during traverse. */ bsdtar->return_value = 1; continue; } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(tree); /* 'L': Follow symlinks to files. */ archive_read_disk_set_symlink_logical(bsdtar->diskreader); /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(tree); if (st != NULL) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(tree); /* 'P': Don't follow symlinks to files. */ archive_read_disk_set_symlink_physical(bsdtar->diskreader); /* 'P': Archive symlinks as symlinks. */ st = lst; break; } if (bsdtar->option_no_subdirs) descend = 0; /* * Are we about to cross to a new filesystem? */ if (!dev_recorded) { /* This is the initial file system. */ first_dev = lst->st_dev; dev_recorded = 1; } else if (lst->st_dev == first_dev) { /* The starting file system is always acceptable. */ } else if (descend == 0) { /* We're not descending, so no need to check. */ } else if (bsdtar->option_dont_traverse_mounts) { descend = 0; } else { /* We're prepared to cross a mount point. */ /* XXX TODO: check whether this filesystem is * synthetic and/or local. Add a new * --local-only option to skip non-local * filesystems. Skip synthetic filesystems * regardless. * * The results should be cached, since * tree.c doesn't usually visit a directory * and the directory contents together. A simple * move-to-front list should perform quite well. * * This is going to be heavily OS dependent: * FreeBSD's statfs() in conjunction with getvfsbyname() * provides all of this; NetBSD's statvfs() does * most of it; other systems will vary. */ } /* * In -u mode, check that the file is newer than what's * already in the archive; in all modes, obey --newerXXX flags. */ if (!new_enough(bsdtar, name, st)) { if (!descend) continue; if (bsdtar->option_interactive && !yes("add '%s'", name)) continue; tree_descend(tree); continue; } archive_entry_free(entry); entry = archive_entry_new(); archive_entry_set_pathname(entry, name); archive_entry_copy_sourcepath(entry, tree_current_access_path(tree)); /* Populate the archive_entry with metadata from the disk. */ /* XXX TODO: Arrange to open a regular file before * calling this so we can pass in an fd and shorten * the race to query metadata. The linkify dance * makes this more complex than it might sound. */ #if defined(_WIN32) && !defined(__CYGWIN__) /* TODO: tree.c uses stat(), which is badly broken * on Windows. To fix this, we should * deprecate tree_current_stat() and provide a new * call tree_populate_entry(t, entry). This call * would use stat() internally on POSIX and * GetInfoByFileHandle() internally on Windows. * This would be another step towards a tree-walker * that can be integrated deep into libarchive. * For now, just set st to NULL on Windows; * archive_read_disk_entry_from_file() should * be smart enough to use platform-appropriate * ways to probe file information. */ st = NULL; #endif r = archive_read_disk_entry_from_file(bsdtar->diskreader, entry, -1, st); if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); if (!bsdtar->uname) archive_entry_set_uname(entry, archive_read_disk_uname(bsdtar->diskreader, bsdtar->uid)); } if (bsdtar->gid >= 0) { archive_entry_set_gid(entry, bsdtar->gid); if (!bsdtar->gname) archive_entry_set_gname(entry, archive_read_disk_gname(bsdtar->diskreader, bsdtar->gid)); } if (bsdtar->uname) archive_entry_set_uname(entry, bsdtar->uname); if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); if (r != ARCHIVE_OK) lafe_warnc(archive_errno(bsdtar->diskreader), "%s", archive_error_string(bsdtar->diskreader)); if (r < ARCHIVE_WARN) continue; /* XXX TODO: Just use flag data from entry; avoid the * duplicate check here. */ /* * If this file/dir is flagged "nodump" and we're * honoring such flags, skip this file/dir. */ #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) /* BSD systems store flags in struct stat */ if (bsdtar->option_honor_nodump && (lst->st_flags & UF_NODUMP)) continue; #endif #if defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) /* Linux uses ioctl to read flags. */ if (bsdtar->option_honor_nodump) { int fd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY); if (fd >= 0) { unsigned long fflags; int r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags); close(fd); if (r >= 0 && (fflags & EXT2_NODUMP_FL)) continue; } } #endif #ifdef __APPLE__ if (bsdtar->enable_copyfile) { /* If we're using copyfile(), ignore "._XXX" files. */ const char *bname = strrchr(name, '/'); if (bname == NULL) bname = name; else ++bname; if (bname[0] == '.' && bname[1] == '_') continue; } else { /* If not, drop the copyfile() data. */ archive_entry_copy_mac_metadata(entry, NULL, 0); } #endif /* * If the user vetoes this file/directory, skip it. * We want this to be fairly late; if some other * check would veto this file, we shouldn't bother * the user with it. */ if (bsdtar->option_interactive && !yes("add '%s'", name)) continue; if (descend) tree_descend(tree); /* * Rewrite the pathname to be archived. If rewrite * fails, skip the entry. */ if (edit_pathname(bsdtar, entry)) continue; /* Display entry as we process it. * This format is required by SUSv2. */ if (bsdtar->verbose) safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); /* Non-regular files get archived with zero size. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); while (entry != NULL) { write_file(bsdtar, a, entry); archive_entry_free(entry); entry = spare_entry; spare_entry = NULL; } if (bsdtar->verbose) fprintf(stderr, "\n"); } archive_entry_free(entry); tree_close(tree); }
/* * Add the file or dir hierarchy named by 'path' to the archive */ static void write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) { struct archive_entry *entry = NULL, *spare_entry = NULL; struct tree *tree; char symlink_mode = bsdtar->symlink_mode; dev_t first_dev = 0; int dev_recorded = 0; int tree_ret; dev_t last_dev = 0; char * fstype; tree = tree_open(path); if (!tree) { bsdtar_warnc(bsdtar, errno, "%s: Cannot open", path); bsdtar->return_value = 1; return; } while ((tree_ret = tree_next(tree))) { int r; const char *name = tree_current_path(tree); const struct stat *st = NULL; /* info to use for this entry */ const struct stat *lst = NULL; /* lstat() information */ int descend; if (truncate_archive(bsdtar)) break; if (checkpoint_archive(bsdtar, 0)) exit(1); disk_pause(bsdtar); if (network_select(0)) exit(1); if (tree_ret == TREE_ERROR_FATAL) bsdtar_errc(bsdtar, 1, tree_errno(tree), "%s: Unable to continue traversing directory tree", name); if (tree_ret == TREE_ERROR_DIR) { bsdtar_warnc(bsdtar, errno, "%s: Couldn't visit directory", name); bsdtar->return_value = 1; } if (tree_ret != TREE_REGULAR) continue; /* * If this file/dir is excluded by a filename * pattern, skip it. */ if (excluded(bsdtar, name)) continue; /* * Get lstat() info from the tree library. */ lst = tree_current_lstat(tree); if (lst == NULL) { /* Couldn't lstat(); must not exist. */ bsdtar_warnc(bsdtar, errno, "%s: Cannot stat", name); /* Return error if files disappear during traverse. */ bsdtar->return_value = 1; continue; } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(tree); /* 'L': Follow symlinks to files. */ archive_read_disk_set_symlink_logical(bsdtar->diskreader); /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(tree); if (st != NULL) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(tree); /* 'P': Don't follow symlinks to files. */ archive_read_disk_set_symlink_physical(bsdtar->diskreader); /* 'P': Archive symlinks as symlinks. */ st = lst; break; } if (bsdtar->option_no_subdirs) descend = 0; /* * If user has asked us not to cross mount points, * then don't descend into a dir on a different * device. */ if (!dev_recorded) { last_dev = first_dev = lst->st_dev; dev_recorded = 1; } if (bsdtar->option_dont_traverse_mounts) { if (lst->st_dev != first_dev) descend = 0; } /* * If the user did not specify --insane-filesystems, do not * cross into a new filesystem which is known to be synthetic. * Note that we will archive synthetic filesystems if we are * explicitly told to do so. */ if ((bsdtar->option_insane_filesystems == 0) && (descend != 0) && (lst->st_dev != last_dev)) { fstype = getfstype(tree_current_access_path(tree)); if (fstype == NULL) bsdtar_errc(bsdtar, 1, errno, "%s: Error getting filesystem type", name); if (getfstype_issynthetic(fstype)) { if (!bsdtar->option_quiet) bsdtar_warnc(bsdtar, 0, "Not descending into filesystem of type %s: %s", fstype, name); descend = 0; } else { /* This device is ok to archive. */ last_dev = lst->st_dev; } free(fstype); } /* * In -u mode, check that the file is newer than what's * already in the archive; in all modes, obey --newerXXX flags. */ if (!new_enough(bsdtar, name, st)) { if (!descend) continue; if (bsdtar->option_interactive && !yes("add '%s'", name)) continue; tree_descend(tree); continue; } archive_entry_free(entry); entry = archive_entry_new(); archive_entry_set_pathname(entry, name); archive_entry_copy_sourcepath(entry, tree_current_access_path(tree)); /* Populate the archive_entry with metadata from the disk. */ /* XXX TODO: Arrange to open a regular file before * calling this so we can pass in an fd and shorten * the race to query metadata. The linkify dance * makes this more complex than it might sound. */ r = archive_read_disk_entry_from_file(bsdtar->diskreader, entry, -1, st); if (r != ARCHIVE_OK) bsdtar_warnc(bsdtar, archive_errno(bsdtar->diskreader), "%s", archive_error_string(bsdtar->diskreader)); if (r < ARCHIVE_WARN) continue; /* XXX TODO: Just use flag data from entry; avoid the * duplicate check here. */ /* * If this file/dir is flagged "nodump" and we're * honoring such flags, skip this file/dir. */ #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) /* BSD systems store flags in struct stat */ if (bsdtar->option_honor_nodump && (lst->st_flags & UF_NODUMP)) continue; #endif #if defined(EXT2_NODUMP_FL) /* Linux uses ioctl to read flags. */ if (bsdtar->option_honor_nodump) { unsigned long fflags, dummy; archive_entry_fflags(entry, &fflags, &dummy); if (fflags & EXT2_NODUMP_FL) continue; } #endif /* * Don't back up the cache directory or any files inside it. */ if ((lst->st_ino == bsdtar->cachedir_ino) && (lst->st_dev == bsdtar->cachedir_dev)) { if (!bsdtar->option_quiet) bsdtar_warnc(bsdtar, 0, "Not adding cache directory to archive: %s", name); continue; } /* * If the user vetoes this file/directory, skip it. * We want this to be fairly late; if some other * check would veto this file, we shouldn't bother * the user with it. */ if (bsdtar->option_interactive && !yes("add '%s'", name)) continue; /* Note: if user vetoes, we won't descend. */ if (descend) tree_descend(tree); /* * Rewrite the pathname to be archived. If rewrite * fails, skip the entry. */ if (edit_pathname(bsdtar, entry)) continue; /* * If this is a socket, skip the entry: POSIX requires that * pax(1) emit a "diagnostic message" (i.e., warning) that * sockets cannot be archived, but this can make backups of * running systems very noisy. */ if (S_ISSOCK(st->st_mode)) continue; /* Display entry as we process it. * This format is required by SUSv2. */ if (bsdtar->verbose) safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); /* * If the user hasn't specifically asked to have the access * time stored, zero it. At the moment this usually only * matters for files which have flags set, since the "posix * restricted" format doesn't store access times for most * other files. */ if (bsdtar->option_store_atime == 0) archive_entry_set_atime(entry, 0, 0); /* Non-regular files get archived with zero size. */ if (!S_ISREG(st->st_mode)) archive_entry_set_size(entry, 0); /* Record what we're doing, for SIGINFO / SIGUSR1. */ siginfo_setinfo(bsdtar, "adding", archive_entry_pathname(entry), archive_entry_size(entry)); archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); /* Handle SIGINFO / SIGUSR1 request if one was made. */ siginfo_printinfo(bsdtar, 0); while (entry != NULL) { write_entry_backend(bsdtar, a, entry, st, tree_current_realpath(tree)); archive_entry_free(entry); entry = spare_entry; spare_entry = NULL; } if (bsdtar->verbose) fprintf(stderr, "\n"); } archive_entry_free(entry); if (tree_close(tree)) bsdtar_errc(bsdtar, 1, 0, "Error traversing directory tree"); }
static void test_macpolicy(const char *polname, const char *trylabel) { char fn[64]; char mac1[512], mac2[512]; struct archive *ar, *aw; struct archive_entry *ae; int i, found; /* Create a file with the given label */ strcpy(fn, "test_"); strncat(fn, polname, 54); fn[59] = '\0'; if (!create_labeled_file(fn, trylabel)) return; /* Get the actual label. Note that it may be different than * our original argument if multiple policies are loaded. */ failure("Cannot get MAC label from file"); if (!assert(get_file_maclabel(fn, mac1, 512))) return; /* Read the file data into archive */ assert((ar = archive_read_disk_new()) != NULL); ae = archive_entry_new(); assert(ae != NULL); archive_entry_copy_pathname(ae, fn); failure("Reading disk entry failed."); assertEqualIntA(ar, ARCHIVE_OK, archive_read_disk_entry_from_file(ar, ae, -1, NULL)); /* Check that there is a matching xattr */ i = archive_entry_xattr_reset(ae); found = 0; while (i--) { const char *name; const void *value; size_t size; archive_entry_xattr_next(ae, &name, &value, &size); if (name != NULL) { if (strncmp(name, "system.mac", 10) == 0) { if (strncmp(value, mac1, (size > 512 ? 512 : size)) == 0) { found = 1; break; } } } } failure("No matching label in archive"); assert(found); /* Write the file back to disk. Note that the file contained * no data so we're only concerned with the header. */ assert((aw = archive_write_disk_new()) != NULL); archive_write_disk_set_options(aw, ARCHIVE_EXTRACT_XATTR); strncat(fn, "_out", 4); /* New name for extraction */ fn[63] = '\0'; archive_entry_copy_pathname(ae, fn); assertEqualIntA(aw, ARCHIVE_OK, archive_write_header(aw, ae)); assertEqualIntA(aw, ARCHIVE_OK, archive_write_finish_entry(aw)); /* Clean up */ assertEqualInt(ARCHIVE_OK, archive_write_free(aw)); assertEqualInt(ARCHIVE_OK, archive_read_free(ar)); archive_entry_free(ae); /* Finally, verify the label of the created file */ failure("Cannot get MAC label from file"); if (!assert(get_file_maclabel(fn, mac2, 512))) return; failure("Original and extracted MAC labels do not match"); assert(strncmp(mac1, mac2, 512) == 0); }
static void create(const char *filename, int compress, const char **argv) { struct archive *a; struct archive *disk; struct archive_entry *entry; ssize_t len; int fd; a = archive_write_new(); switch (compress) { #ifndef NO_BZIP2_CREATE case 'j': case 'y': archive_write_set_compression_bzip2(a); break; #endif #ifndef NO_COMPRESS_CREATE case 'Z': archive_write_set_compression_compress(a); break; #endif #ifndef NO_GZIP_CREATE case 'z': archive_write_set_compression_gzip(a); break; #endif default: archive_write_set_compression_none(a); break; } archive_write_set_format_ustar(a); if (strcmp(filename, "-") == 0) filename = NULL; archive_write_open_file(a, filename); disk = archive_read_disk_new(); #ifndef NO_LOOKUP archive_read_disk_set_standard_lookup(disk); #endif while (*argv != NULL) { struct tree *t = tree_open(*argv); while (tree_next(t)) { entry = archive_entry_new(); archive_entry_set_pathname(entry, tree_current_path(t)); archive_read_disk_entry_from_file(disk, entry, -1, tree_current_stat(t)); if (verbose) { msg("a "); msg(tree_current_path(t)); } archive_write_header(a, entry); fd = open(tree_current_access_path(t), O_RDONLY); len = read(fd, buff, sizeof(buff)); while (len > 0) { archive_write_data(a, buff, len); len = read(fd, buff, sizeof(buff)); } close(fd); archive_entry_free(entry); if (verbose) msg("\n"); } argv++; } archive_write_close(a); archive_write_finish(a); }
/* * Add the file or dir hierarchy named by 'path' to the archive */ static void write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) { struct archive_entry *entry = NULL, *spare_entry = NULL; struct tree *tree; char symlink_mode = bsdtar->symlink_mode; dev_t first_dev = 0; int dev_recorded = 0; int tree_ret; tree = tree_open(path); if (!tree) { bsdtar_warnc(bsdtar, errno, "%s: Cannot open", path); bsdtar->return_value = 1; return; } while ((tree_ret = tree_next(tree))) { int r; const char *name = tree_current_path(tree); const struct stat *st = NULL; /* info to use for this entry */ const struct stat *lst = NULL; /* lstat() information */ int descend; if (tree_ret == TREE_ERROR_FATAL) bsdtar_errc(bsdtar, 1, tree_errno(tree), "%s: Unable to continue traversing directory tree", name); if (tree_ret == TREE_ERROR_DIR) { bsdtar_warnc(bsdtar, errno, "%s: Couldn't visit directory", name); bsdtar->return_value = 1; } if (tree_ret != TREE_REGULAR) continue; /* * If this file/dir is excluded by a filename * pattern, skip it. */ if (excluded(bsdtar, name)) continue; /* * Get lstat() info from the tree library. */ lst = tree_current_lstat(tree); if (lst == NULL) { /* Couldn't lstat(); must not exist. */ bsdtar_warnc(bsdtar, errno, "%s: Cannot stat", name); /* Return error if files disappear during traverse. */ bsdtar->return_value = 1; continue; } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(tree); /* 'L': Follow symlinks to files. */ archive_read_disk_set_symlink_logical(bsdtar->diskreader); /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(tree); if (st != NULL) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(tree); /* 'P': Don't follow symlinks to files. */ archive_read_disk_set_symlink_physical(bsdtar->diskreader); /* 'P': Archive symlinks as symlinks. */ st = lst; break; } /* * 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->st_dev != first_dev) descend = 0; } /* * In -u mode, check that the file is newer than what's * already in the archive; in all modes, obey --newerXXX flags. */ if (!new_enough(bsdtar, name, st)) continue; archive_entry_free(entry); entry = archive_entry_new(); archive_entry_set_pathname(entry, name); archive_entry_copy_sourcepath(entry, tree_current_access_path(tree)); /* Populate the archive_entry with metadata from the disk. */ /* XXX TODO: Arrange to open a regular file before * calling this so we can pass in an fd and shorten * the race to query metadata. The linkify dance * makes this more complex than it might sound. */ r = archive_read_disk_entry_from_file(bsdtar->diskreader, entry, -1, st); if (r != ARCHIVE_OK) bsdtar_warnc(bsdtar, archive_errno(bsdtar->diskreader), archive_error_string(bsdtar->diskreader)); if (r < ARCHIVE_WARN) continue; /* XXX TODO: Just use flag data from entry; avoid the * duplicate check here. */ /* * If this file/dir is flagged "nodump" and we're * honoring such flags, skip this file/dir. */ #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* BSD systems store flags in struct stat */ if (bsdtar->option_honor_nodump && (lst->st_flags & UF_NODUMP)) continue; #endif #if defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) /* Linux uses ioctl to read flags. */ if (bsdtar->option_honor_nodump) { int fd = open(name, O_RDONLY | O_NONBLOCK); if (fd >= 0) { unsigned long fflags; int r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags); close(fd); if (r >= 0 && (fflags & EXT2_NODUMP_FL)) continue; } } #endif /* * If the user vetoes this file/directory, skip it. * We want this to be fairly late; if some other * check would veto this file, we shouldn't bother * the user with it. */ if (bsdtar->option_interactive && !yes("add '%s'", name)) continue; /* Note: if user vetoes, we won't descend. */ if (descend && !bsdtar->option_no_subdirs) tree_descend(tree); /* * Rewrite the pathname to be archived. If rewrite * fails, skip the entry. */ if (edit_pathname(bsdtar, entry)) continue; /* Display entry as we process it. * This format is required by SUSv2. */ if (bsdtar->verbose) safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); /* Non-regular files get archived with zero size. */ if (!S_ISREG(st->st_mode)) archive_entry_set_size(entry, 0); /* Record what we're doing, for SIGINFO / SIGUSR1. */ siginfo_setinfo(bsdtar, "adding", archive_entry_pathname(entry), archive_entry_size(entry)); archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); /* Handle SIGINFO / SIGUSR1 request if one was made. */ siginfo_printinfo(bsdtar, 0); while (entry != NULL) { write_entry_backend(bsdtar, a, entry); archive_entry_free(entry); entry = spare_entry; spare_entry = NULL; } if (bsdtar->verbose) fprintf(stderr, "\n"); } archive_entry_free(entry); tree_close(tree); }
int packing_append_file_attr(struct packing *pack, const char *filepath, const char *newpath, const char *uname, const char *gname, mode_t perm) { int fd; char *map; int retcode = EPKG_OK; int ret; struct stat st; struct archive_entry *entry, *sparse_entry; bool unset_timestamp; entry = archive_entry_new(); archive_entry_copy_sourcepath(entry, filepath); pkg_debug(2, "Packing file '%s'", filepath); if (lstat(filepath, &st) != 0) { pkg_emit_errno("lstat", filepath); retcode = EPKG_FATAL; goto cleanup; } ret = archive_read_disk_entry_from_file(pack->aread, entry, -1, &st); if (ret != ARCHIVE_OK) { pkg_emit_error("%s: %s", filepath, archive_error_string(pack->aread)); retcode = EPKG_FATAL; goto cleanup; } if (newpath != NULL) archive_entry_set_pathname(entry, newpath); if (archive_entry_filetype(entry) != AE_IFREG) { archive_entry_set_size(entry, 0); } if (uname != NULL && uname[0] != '\0') { if (pack->pass) { struct passwd* pw = getpwnam(uname); if (pw == NULL) { pkg_emit_error("Unknown user: '******'", uname); retcode = EPKG_FATAL; goto cleanup; } archive_entry_set_uid(entry, pw->pw_uid); } archive_entry_set_uname(entry, uname); } if (gname != NULL && gname[0] != '\0') { if (pack->pass) { struct group *gr = (getgrnam(gname)); if (gr == NULL) { pkg_emit_error("Unknown group: '%s'", gname); retcode = EPKG_FATAL; goto cleanup; } archive_entry_set_gid(entry, gr->gr_gid); } archive_entry_set_gname(entry, gname); } if (perm != 0) archive_entry_set_perm(entry, perm); pkg_config_bool(PKG_CONFIG_UNSET_TIMESTAMP, &unset_timestamp); if (unset_timestamp) { archive_entry_unset_atime(entry); archive_entry_unset_ctime(entry); archive_entry_unset_mtime(entry); archive_entry_unset_birthtime(entry); } archive_entry_linkify(pack->resolver, &entry, &sparse_entry); if (sparse_entry != NULL && entry == NULL) entry = sparse_entry; archive_write_header(pack->awrite, entry); if (archive_entry_size(entry) > 0) { if ((fd = open(filepath, O_RDONLY)) < 0) { pkg_emit_errno("open", filepath); retcode = EPKG_FATAL; goto cleanup; } if (st.st_size > SSIZE_MAX) { char buf[BUFSIZ]; int len; while ((len = read(fd, buf, sizeof(buf))) > 0) if (archive_write_data(pack->awrite, buf, len) == -1) { pkg_emit_errno("archive_write_data", "archive write error"); retcode = EPKG_FATAL; break; } if (len == -1) { pkg_emit_errno("read", "file read error"); retcode = EPKG_FATAL; } close(fd); } else { if ((map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) != MAP_FAILED) { close(fd); if (archive_write_data(pack->awrite, map, st.st_size) == -1) { pkg_emit_errno("archive_write_data", "archive write error"); retcode = EPKG_FATAL; } munmap(map, st.st_size); } else { close(fd); pkg_emit_errno("open", filepath); retcode = EPKG_FATAL; goto cleanup; } } } cleanup: archive_entry_free(entry); return (retcode); }