int main(int argc, char **argv) { struct archive_entry *entry = archive_entry_new(); unsigned long set, clear; const wchar_t *remainder; remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,"); archive_entry_fflags(entry, &set, &clear); wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder); wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry)); return (0); }
static int get_keys(struct mtree_writer *mtree, struct archive_entry *entry) { int keys; keys = mtree->keys; if (mtree->set.keys == 0) return (keys); if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 && mtree->set.gid == archive_entry_gid(entry)) keys &= ~(F_GNAME | F_GID); if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 && mtree->set.uid == archive_entry_uid(entry)) keys &= ~(F_UNAME | F_UID); if (mtree->set.keys & F_FLAGS) { unsigned long set, clear; archive_entry_fflags(entry, &set, &clear); if (mtree->set.fflags_set == set && mtree->set.fflags_clear == clear) keys &= ~F_FLAGS; } if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == (archive_entry_mode(entry) & 07777)) keys &= ~F_MODE; switch (archive_entry_filetype(entry)) { case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR: case AE_IFBLK: case AE_IFIFO: break; case AE_IFDIR: if ((mtree->set.keys & F_TYPE) != 0 && mtree->set.type == AE_IFDIR) keys &= ~F_TYPE; break; case AE_IFREG: default: /* Handle unknown file types as regular files. */ if ((mtree->set.keys & F_TYPE) != 0 && mtree->set.type == AE_IFREG) keys &= ~F_TYPE; break; } return (keys); }
static int do_extract(struct archive *a, struct archive_entry *ae, const char *location, int nfiles, struct pkg *pkg, struct pkg *local) { int retcode = EPKG_OK; int ret = 0, cur_file = 0; char path[MAXPATHLEN], pathname[MAXPATHLEN], rpath[MAXPATHLEN]; struct stat st; const struct stat *aest; bool renamed = false; const struct pkg_file *rf; struct pkg_config_file *rcf; struct sbuf *newconf; bool automerge = pkg_object_bool(pkg_config_get("AUTOMERGE")); unsigned long set, clear; #ifndef HAVE_ARC4RANDOM srand(time(NULL)); #endif if (nfiles == 0) return (EPKG_OK); pkg_emit_extract_begin(pkg); pkg_emit_progress_start(NULL); newconf = sbuf_new_auto(); do { ret = ARCHIVE_OK; sbuf_clear(newconf); rf = NULL; rcf = NULL; pkg_absolutepath(archive_entry_pathname(ae), path, sizeof(path)); snprintf(pathname, sizeof(pathname), "%s%s%s", location ? location : "", *path == '/' ? "" : "/", path ); strlcpy(rpath, pathname, sizeof(rpath)); aest = archive_entry_stat(ae); archive_entry_fflags(ae, &set, &clear); if (lstat(rpath, &st) != -1) { /* * We have an existing file on the path, so handle it */ if (!S_ISDIR(aest->st_mode)) { pkg_debug(2, "Old version found, renaming"); pkg_add_file_random_suffix(rpath, sizeof(rpath), 12); renamed = true; } if (!S_ISDIR(st.st_mode) && S_ISDIR(aest->st_mode)) { if (S_ISLNK(st.st_mode)) { if (stat(rpath, &st) == -1) { pkg_emit_error("Dead symlink %s", rpath); } else { pkg_debug(2, "Directory is a symlink, use it"); pkg_emit_progress_tick(cur_file++, nfiles); continue; } } } } archive_entry_set_pathname(ae, rpath); /* load in memory the content of config files */ if (pkg_is_config_file(pkg, path, &rf, &rcf)) { pkg_debug(1, "Populating config_file %s", pathname); size_t len = archive_entry_size(ae); rcf->content = malloc(len); archive_read_data(a, rcf->content, len); if (renamed && (!automerge || local == NULL)) strlcat(pathname, ".pkgnew", sizeof(pathname)); } /* * check if the file is already provided by previous package */ if (!automerge) attempt_to_merge(renamed, rcf, local, pathname, path, newconf); if (sbuf_len(newconf) == 0 && (rcf == NULL || rcf->content == NULL)) { pkg_debug(1, "Extracting: %s", archive_entry_pathname(ae)); int install_as_user = (getenv("INSTALL_AS_USER") != NULL); int extract_flags = EXTRACT_ARCHIVE_FLAGS; if (install_as_user) { /* when installing as user don't try to set file ownership */ extract_flags &= ~ARCHIVE_EXTRACT_OWNER; } ret = archive_read_extract(a, ae, extract_flags); } else { if (sbuf_len(newconf) == 0) { sbuf_cat(newconf, rcf->content); sbuf_finish(newconf); } pkg_debug(2, "Writing conf in %s", pathname); unlink(rpath); FILE *f = fopen(rpath, "w+"); fprintf(f, "%s", sbuf_data(newconf)); fclose(f); } if (ret != ARCHIVE_OK) { /* * show error except when the failure is during * extracting a directory and that the directory already * exists. * this allow to install packages linux_base from * package for example */ if (archive_entry_filetype(ae) != AE_IFDIR || !is_dir(pathname)) { pkg_emit_error("archive_read_extract(): %s", archive_error_string(a)); retcode = EPKG_FATAL; goto cleanup; } } /* Reapply modes to the directories to work around a problem on FreeBSD 9 */ if (archive_entry_filetype(ae) == AE_IFDIR) chmod(pathname, aest->st_mode); pkg_emit_progress_tick(cur_file++, nfiles); /* Rename old file */ if (renamed) { pkg_debug(1, "Renaming %s -> %s", rpath, pathname); #ifdef HAVE_CHFLAGS bool old = false; if (set & NOCHANGESFLAGS) chflags(rpath, 0); if (lstat(pathname, &st) != -1) { old = true; if (st.st_flags & NOCHANGESFLAGS) chflags(pathname, 0); } #endif if (rename(rpath, pathname) == -1) { #ifdef HAVE_CHFLAGS /* restore flags */ if (old) chflags(pathname, st.st_flags); #endif pkg_emit_error("cannot rename %s to %s: %s", rpath, pathname, strerror(errno)); retcode = EPKG_FATAL; goto cleanup; } #ifdef HAVE_CHFLAGS /* Restore flags */ chflags(pathname, set); #endif } if (string_end_with(pathname, ".pkgnew")) pkg_emit_notice("New configuration file: %s", pathname); renamed = false; } while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK); if (ret != ARCHIVE_EOF) { pkg_emit_error("archive_read_next_header(): %s", archive_error_string(a)); retcode = EPKG_FATAL; } cleanup: pkg_emit_progress_tick(nfiles, nfiles); pkg_emit_extract_finished(pkg); if (renamed && retcode == EPKG_FATAL) { #ifdef HAVE_CHFLAGS if (set & NOCHANGESFLAGS) chflags(rpath, set & ~NOCHANGESFLAGS); #endif unlink(rpath); } return (retcode); }
/* * Write /set keyword. It means set global datas. * [directory-only mode] * - It is only once to write /set keyword. It is using values of the * first entry. * [normal mode] * - Write /set keyword. It is using values of the first entry whose * filetype is a regular file. * - When a parent directory of the entry whose filetype is the regular * file is changed, check the global datas and write it again if its * values are different from the entry's. */ static void set_global(struct mtree_writer *mtree, struct archive_entry *entry) { struct archive_string setstr; struct archive_string unsetstr; const char *name; int keys, oldkeys, effkeys; mode_t set_type = 0; switch (archive_entry_filetype(entry)) { case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR: case AE_IFBLK: case AE_IFIFO: break; case AE_IFDIR: if (mtree->dironly) set_type = AE_IFDIR; break; case AE_IFREG: default: /* Handle unknown file types as regular files. */ if (!mtree->dironly) set_type = AE_IFREG; break; } if (set_type == 0) return; if (mtree->set.processed && !parent_dir_changed(&mtree->set.parent, entry)) return; /* At first, save a parent directory of the entry for following * entries. */ if (!mtree->set.processed && set_type == AE_IFREG) parent_dir_changed(&mtree->set.parent, entry); archive_string_init(&setstr); archive_string_init(&unsetstr); keys = mtree->keys & (F_FLAGS | F_GID | F_GNAME | F_NLINK | F_MODE | F_TYPE | F_UID | F_UNAME); oldkeys = mtree->set.keys; effkeys = keys; if (mtree->set.processed) { /* * Check the global datas for whether it needs updating. */ effkeys &= ~F_TYPE; if ((oldkeys & (F_UNAME | F_UID)) != 0 && mtree->set.uid == archive_entry_uid(entry)) effkeys &= ~(F_UNAME | F_UID); if ((oldkeys & (F_GNAME | F_GID)) != 0 && mtree->set.gid == archive_entry_gid(entry)) effkeys &= ~(F_GNAME | F_GID); if ((oldkeys & F_MODE) != 0 && mtree->set.mode == (archive_entry_mode(entry) & 07777)) effkeys &= ~F_MODE; if ((oldkeys & F_FLAGS) != 0) { unsigned long fflags_set; unsigned long fflags_clear; archive_entry_fflags(entry, &fflags_set, &fflags_clear); if (fflags_set == mtree->set.fflags_set && fflags_clear == mtree->set.fflags_clear) effkeys &= ~F_FLAGS; } } if ((keys & effkeys & F_TYPE) != 0) { mtree->set.type = set_type; if (set_type == AE_IFDIR) archive_strcat(&setstr, " type=dir"); else archive_strcat(&setstr, " type=file"); } if ((keys & effkeys & F_UNAME) != 0) { if ((name = archive_entry_uname(entry)) != NULL) { archive_strcat(&setstr, " uname="); mtree_quote(&setstr, name); } else if ((oldkeys & F_UNAME) != 0) archive_strcat(&unsetstr, " uname"); else keys &= ~F_UNAME; } if ((keys & effkeys & F_UID) != 0) { mtree->set.uid = archive_entry_uid(entry); archive_string_sprintf(&setstr, " uid=%jd", (intmax_t)mtree->set.uid); } if ((keys & effkeys & F_GNAME) != 0) { if ((name = archive_entry_gname(entry)) != NULL) { archive_strcat(&setstr, " gname="); mtree_quote(&setstr, name); } else if ((oldkeys & F_GNAME) != 0) archive_strcat(&unsetstr, " gname"); else keys &= ~F_GNAME; } if ((keys & effkeys & F_GID) != 0) { mtree->set.gid = archive_entry_gid(entry); archive_string_sprintf(&setstr, " gid=%jd", (intmax_t)mtree->set.gid); } if ((keys & effkeys & F_MODE) != 0) { mtree->set.mode = archive_entry_mode(entry) & 07777; archive_string_sprintf(&setstr, " mode=%o", mtree->set.mode); } if ((keys & effkeys & F_FLAGS) != 0) { if ((name = archive_entry_fflags_text(entry)) != NULL) { archive_strcat(&setstr, " flags="); mtree_quote(&setstr, name); archive_entry_fflags(entry, &mtree->set.fflags_set, &mtree->set.fflags_clear); } else if ((oldkeys & F_FLAGS) != 0) archive_strcat(&unsetstr, " flags"); else keys &= ~F_FLAGS; } if (unsetstr.length > 0) archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s); archive_string_free(&unsetstr); if (setstr.length > 0) archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s); archive_string_free(&setstr); mtree->set.keys = keys; mtree->set.processed = 1; /* On directory-only mode, it is only once to write /set keyword. */ if (mtree->dironly) mtree->set.output = 0; }
/* * 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_write_format_mtree_sub(int use_set) { struct archive_entry *ae; struct archive* a; size_t used; int i; /* Create a mtree format archive. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_mtree(a)); if (use_set) assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "use-set,!all,flags,type")); else assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "!all,flags,type")); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, sizeof(buff)-1, &used)); /* Write entries */ for (i = 0; entries[i].path != NULL; i++) { assert((ae = archive_entry_new()) != NULL); archive_entry_set_fflags(ae, entries[i].fflags, 0); archive_entry_copy_pathname(ae, entries[i].path); archive_entry_set_size(ae, 0); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); } assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); if (use_set) { const char *p; buff[used] = '\0'; assert(NULL != (p = strstr(buff, "\n/set "))); if (p != NULL) { char *r; const char *o; p++; r = strchr(p, '\n'); if (r != NULL) *r = '\0'; o = "/set type=file flags=uchg,nodump"; assertEqualString(o, p); if (r != NULL) *r = '\n'; } } /* * Read the data and check it. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used)); /* Read entries */ for (i = 0; entries[i].path != NULL; i++) { unsigned long fset, fclr; assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); archive_entry_fflags(ae, &fset, &fclr); assertEqualInt((int)entries[i].fflags, (int)fset); assertEqualInt(0, (int)fclr); assertEqualString(entries[i].path, archive_entry_pathname(ae)); } assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }