static int ar_entry_ctime(lua_State *L) { struct archive_entry* self = *ar_entry_check(L, 1); int is_set; int num_results; if ( NULL == self ) return 0; is_set = ( lua_gettop(L) >= 2 ); num_results = 0; if ( archive_entry_ctime_is_set(self) ) { num_results = 2; lua_pushnumber(L, archive_entry_ctime(self)); lua_pushnumber(L, archive_entry_ctime_nsec(self)); } if ( is_set ) { if ( lua_isnil(L, 2) ) { archive_entry_unset_ctime(self); } else if ( lua_istable(L, 2) ) { lua_rawgeti(L, 2, 1); lua_rawgeti(L, 2, 2); archive_entry_set_ctime(self, lua_tonumber(L, -2), lua_tonumber(L, -1)); } else { archive_entry_set_ctime(self, lua_tonumber(L, 2), lua_tonumber(L, 3)); } } return num_results; }
static int set_timefilter_stat(struct archive_match *a, int timetype, struct stat *st) { struct archive_entry *ae; time_t ctime_sec, mtime_sec; long ctime_ns, mtime_ns; ae = archive_entry_new(); if (ae == NULL) return (error_nomem(a)); archive_entry_copy_stat(ae, st); ctime_sec = archive_entry_ctime(ae); ctime_ns = archive_entry_ctime_nsec(ae); mtime_sec = archive_entry_mtime(ae); mtime_ns = archive_entry_mtime_nsec(ae); archive_entry_free(ae); return set_timefilter(a, timetype, mtime_sec, mtime_ns, ctime_sec, ctime_ns); }
const struct stat * archive_entry_stat(struct archive_entry *entry) { struct stat *st; if (entry->stat == NULL) { entry->stat = calloc(1, sizeof(*st)); if (entry->stat == NULL) return (NULL); entry->stat_valid = 0; } /* * If none of the underlying fields have been changed, we * don't need to regenerate. In theory, we could use a bitmap * here to flag only those items that have changed, but the * extra complexity probably isn't worth it. It will be very * rare for anyone to change just one field then request a new * stat structure. */ if (entry->stat_valid) return (entry->stat); st = entry->stat; /* * Use the public interfaces to extract items, so that * the appropriate conversions get invoked. */ st->st_atime = archive_entry_atime(entry); #if HAVE_STRUCT_STAT_ST_BIRTHTIME st->st_birthtime = archive_entry_birthtime(entry); #endif st->st_ctime = archive_entry_ctime(entry); st->st_mtime = archive_entry_mtime(entry); st->st_dev = archive_entry_dev(entry); st->st_gid = archive_entry_gid(entry); st->st_uid = archive_entry_uid(entry); st->st_ino = archive_entry_ino64(entry); st->st_nlink = archive_entry_nlink(entry); st->st_rdev = archive_entry_rdev(entry); st->st_size = archive_entry_size(entry); st->st_mode = archive_entry_mode(entry); /* * On systems that support high-res timestamps, copy that * information into struct stat. */ #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry); st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry); st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC st->st_atim.tv_nsec = archive_entry_atime_nsec(entry); st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry); st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry); #elif HAVE_STRUCT_STAT_ST_MTIME_N st->st_atime_n = archive_entry_atime_nsec(entry); st->st_ctime_n = archive_entry_ctime_nsec(entry); st->st_mtime_n = archive_entry_mtime_nsec(entry); #elif HAVE_STRUCT_STAT_ST_UMTIME st->st_uatime = archive_entry_atime_nsec(entry) / 1000; st->st_uctime = archive_entry_ctime_nsec(entry) / 1000; st->st_umtime = archive_entry_mtime_nsec(entry) / 1000; #elif HAVE_STRUCT_STAT_ST_MTIME_USEC st->st_atime_usec = archive_entry_atime_nsec(entry) / 1000; st->st_ctime_usec = archive_entry_ctime_nsec(entry) / 1000; st->st_mtime_usec = archive_entry_mtime_nsec(entry) / 1000; #endif #if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC st->st_birthtimespec.tv_nsec = archive_entry_birthtime_nsec(entry); #endif /* * TODO: On Linux, store 32 or 64 here depending on whether * the cached stat structure is a stat32 or a stat64. This * will allow us to support both variants interchangeably. */ entry->stat_valid = 1; return (st); }
/* * Test if entry is excluded by its timestamp. */ static int time_excluded(struct archive_match *a, struct archive_entry *entry) { struct match_file *f; const void *pathname; time_t sec; long nsec; /* * If this file/dir is excluded by a time comparison, skip it. */ if (a->newer_ctime_filter) { /* If ctime is not set, use mtime instead. */ if (archive_entry_ctime_is_set(entry)) sec = archive_entry_ctime(entry); else sec = archive_entry_mtime(entry); if (sec < a->newer_ctime_sec) return (1); /* Too old, skip it. */ if (sec == a->newer_ctime_sec) { if (archive_entry_ctime_is_set(entry)) nsec = archive_entry_ctime_nsec(entry); else nsec = archive_entry_mtime_nsec(entry); if (nsec < a->newer_ctime_nsec) return (1); /* Too old, skip it. */ if (nsec == a->newer_ctime_nsec && (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL) == 0) return (1); /* Equal, skip it. */ } } if (a->older_ctime_filter) { /* If ctime is not set, use mtime instead. */ if (archive_entry_ctime_is_set(entry)) sec = archive_entry_ctime(entry); else sec = archive_entry_mtime(entry); if (sec > a->older_ctime_sec) return (1); /* Too new, skip it. */ if (sec == a->older_ctime_sec) { if (archive_entry_ctime_is_set(entry)) nsec = archive_entry_ctime_nsec(entry); else nsec = archive_entry_mtime_nsec(entry); if (nsec > a->older_ctime_nsec) return (1); /* Too new, skip it. */ if (nsec == a->older_ctime_nsec && (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL) == 0) return (1); /* Equal, skip it. */ } } if (a->newer_mtime_filter) { sec = archive_entry_mtime(entry); if (sec < a->newer_mtime_sec) return (1); /* Too old, skip it. */ if (sec == a->newer_mtime_sec) { nsec = archive_entry_mtime_nsec(entry); if (nsec < a->newer_mtime_nsec) return (1); /* Too old, skip it. */ if (nsec == a->newer_mtime_nsec && (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL) == 0) return (1); /* Equal, skip it. */ } } if (a->older_mtime_filter) { sec = archive_entry_mtime(entry); if (sec > a->older_mtime_sec) return (1); /* Too new, skip it. */ nsec = archive_entry_mtime_nsec(entry); if (sec == a->older_mtime_sec) { if (nsec > a->older_mtime_nsec) return (1); /* Too new, skip it. */ if (nsec == a->older_mtime_nsec && (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL) == 0) return (1); /* Equal, skip it. */ } } /* If there is no exclusion list, include the file. */ if (a->exclusion_entry_list.count == 0) return (0); #if defined(_WIN32) && !defined(__CYGWIN__) pathname = archive_entry_pathname_w(entry); a->exclusion_tree.rbt_ops = &rb_ops_wcs; #else (void)rb_ops_wcs; pathname = archive_entry_pathname(entry); a->exclusion_tree.rbt_ops = &rb_ops_mbs; #endif if (pathname == NULL) return (0); f = (struct match_file *)__archive_rb_tree_find_node( &(a->exclusion_tree), pathname); /* If the file wasn't rejected, include it. */ if (f == NULL) return (0); if (f->flag & ARCHIVE_MATCH_CTIME) { sec = archive_entry_ctime(entry); if (f->ctime_sec > sec) { if (f->flag & ARCHIVE_MATCH_OLDER) return (1); } else if (f->ctime_sec < sec) { if (f->flag & ARCHIVE_MATCH_NEWER) return (1); } else { nsec = archive_entry_ctime_nsec(entry); if (f->ctime_nsec > nsec) { if (f->flag & ARCHIVE_MATCH_OLDER) return (1); } else if (f->ctime_nsec < nsec) { if (f->flag & ARCHIVE_MATCH_NEWER) return (1); } else if (f->flag & ARCHIVE_MATCH_EQUAL) return (1); } } if (f->flag & ARCHIVE_MATCH_MTIME) { sec = archive_entry_mtime(entry); if (f->mtime_sec > sec) { if (f->flag & ARCHIVE_MATCH_OLDER) return (1); } else if (f->mtime_sec < sec) { if (f->flag & ARCHIVE_MATCH_NEWER) return (1); } else { nsec = archive_entry_mtime_nsec(entry); if (f->mtime_nsec > nsec) { if (f->flag & ARCHIVE_MATCH_OLDER) return (1); } else if (f->mtime_nsec < nsec) { if (f->flag & ARCHIVE_MATCH_NEWER) return (1); } else if (f->flag & ARCHIVE_MATCH_EQUAL) return (1); } } return (0); }
static int add_entry(struct archive_match *a, int flag, struct archive_entry *entry) { struct match_file *f; const void *pathname; int r; f = calloc(1, sizeof(*f)); if (f == NULL) return (error_nomem(a)); #if defined(_WIN32) && !defined(__CYGWIN__) pathname = archive_entry_pathname_w(entry); if (pathname == NULL) { free(f); archive_set_error(&(a->archive), EINVAL, "pathname is NULL"); return (ARCHIVE_FAILED); } archive_mstring_copy_wcs(&(f->pathname), pathname); a->exclusion_tree.rbt_ops = &rb_ops_wcs; #else (void)rb_ops_wcs; pathname = archive_entry_pathname(entry); if (pathname == NULL) { free(f); archive_set_error(&(a->archive), EINVAL, "pathname is NULL"); return (ARCHIVE_FAILED); } archive_mstring_copy_mbs(&(f->pathname), pathname); a->exclusion_tree.rbt_ops = &rb_ops_mbs; #endif f->flag = flag; f->mtime_sec = archive_entry_mtime(entry); f->mtime_nsec = archive_entry_mtime_nsec(entry); f->ctime_sec = archive_entry_ctime(entry); f->ctime_nsec = archive_entry_ctime_nsec(entry); r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node)); if (!r) { struct match_file *f2; /* Get the duplicated file. */ f2 = (struct match_file *)__archive_rb_tree_find_node( &(a->exclusion_tree), pathname); /* * We always overwrite comparison condition. * If you do not want to overwrite it, you should not * call archive_match_exclude_entry(). We cannot know * what behavior you really expect since overwriting * condition might be different with the flag. */ if (f2 != NULL) { f2->flag = f->flag; f2->mtime_sec = f->mtime_sec; f2->mtime_nsec = f->mtime_nsec; f2->ctime_sec = f->ctime_sec; f2->ctime_nsec = f->ctime_nsec; } /* Release the duplicated file. */ archive_mstring_clean(&(f->pathname)); free(f); return (ARCHIVE_OK); } entry_list_add(&(a->exclusion_entry_list), f); a->setflag |= TIME_IS_SET; return (ARCHIVE_OK); }
/* * Test writing an empty file. */ static void test_only_empty_file(void) { struct archive *a; struct archive_entry *ae; size_t buffsize = 1000; char *buff; size_t used; buff = malloc(buffsize); /* Create a new archive in memory. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_7zip(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used)); /* * Write an empty file to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_mtime(ae, 1, 10); assertEqualInt(1, archive_entry_mtime(ae)); assertEqualInt(10, archive_entry_mtime_nsec(ae)); archive_entry_set_atime(ae, 2, 20); assertEqualInt(2, archive_entry_atime(ae)); assertEqualInt(20, archive_entry_atime_nsec(ae)); archive_entry_set_ctime(ae, 0, 100); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualInt(100, archive_entry_ctime_nsec(ae)); archive_entry_copy_pathname(ae, "empty"); assertEqualString("empty", archive_entry_pathname(ae)); archive_entry_set_mode(ae, AE_IFREG | 0755); assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae)); assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Close out the archive. */ assertEqualInt(ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* Verify the archive file size. */ assertEqualInt(102, used); /* Verify the initial header. */ assertEqualMem(buff, "\x37\x7a\xbc\xaf\x27\x1c\x00\x03" "\x00\x5b\x58\x25\x00\x00\x00\x00" "\x00\x00\x00\x00\x46\x00\x00\x00" "\x00\x00\x00\x00\x8f\xce\x1d\xf3", 32); /* * Now, read the data back. */ /* With the test memory reader -- seeking mode. */ 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, read_open_memory_seek(a, buff, used, 7)); /* * Read and verify an empty file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(2, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_atime_nsec(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualInt(100, archive_entry_ctime_nsec(ae)); assertEqualString("empty", archive_entry_pathname(ae)); assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); assertEqualInt(0, archive_entry_size(ae)); /* Verify the end of the archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); free(buff); }
/* * Handle 'x' and 't' modes. */ static void read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) { struct progress_data progress_data; FILE *out; struct archive *a; struct archive_entry *entry; int r; time_t sec; long nsec; while (*bsdtar->argv) { lafe_include(&bsdtar->matching, *bsdtar->argv); bsdtar->argv++; } if (bsdtar->names_from_file != NULL) lafe_include_from_file(&bsdtar->matching, bsdtar->names_from_file, bsdtar->option_null); a = archive_read_new(); if (bsdtar->compress_program != NULL) archive_read_support_filter_program(a, bsdtar->compress_program); else archive_read_support_filter_all(a); archive_read_support_format_all(a); if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options)) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_file(a, bsdtar->filename, bsdtar->bytes_per_block)) lafe_errc(1, 0, "Error opening archive: %s", archive_error_string(a)); do_chdir(bsdtar); if (mode == 'x') { /* Set an extract callback so that we can handle SIGINFO. */ progress_data.bsdtar = bsdtar; progress_data.archive = a; archive_read_extract_set_progress_callback(a, progress_func, &progress_data); } if (mode == 'x' && bsdtar->option_chroot) { #if HAVE_CHROOT if (chroot(".") != 0) lafe_errc(1, errno, "Can't chroot to \".\""); #else lafe_errc(1, 0, "chroot isn't supported on this platform"); #endif } for (;;) { /* Support --fast-read option */ if (bsdtar->option_fast_read && lafe_unmatched_inclusions(bsdtar->matching) == 0) break; r = archive_read_next_header(a, &entry); progress_data.entry = entry; if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (r == ARCHIVE_RETRY) { /* Retryable error: try again */ lafe_warnc(0, "Retrying..."); continue; } if (r == ARCHIVE_FATAL) break; if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); archive_entry_set_uname(entry, NULL); } if (bsdtar->gid >= 0) { archive_entry_set_gid(entry, bsdtar->gid); archive_entry_set_gname(entry, NULL); } if (bsdtar->uname) archive_entry_set_uname(entry, bsdtar->uname); if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); /* * Exclude entries that are too old. */ if (bsdtar->newer_ctime_filter) { /* Use ctime if format provides, else mtime. */ if (archive_entry_ctime_is_set(entry)) { sec = archive_entry_ctime(entry); nsec = archive_entry_ctime_nsec(entry); } else if (archive_entry_mtime_is_set(entry)) { sec = archive_entry_mtime(entry); nsec = archive_entry_mtime_nsec(entry); } else { sec = 0; nsec = 0; } if (sec < bsdtar->newer_ctime_sec) continue; /* Too old, skip it. */ if (sec == bsdtar->newer_ctime_sec && nsec <= bsdtar->newer_ctime_nsec) continue; /* Too old, skip it. */ } if (bsdtar->newer_mtime_filter) { if (archive_entry_mtime_is_set(entry)) { sec = archive_entry_mtime(entry); nsec = archive_entry_mtime_nsec(entry); } else { sec = 0; nsec = 0; } if (sec < bsdtar->newer_mtime_sec) continue; /* Too old, skip it. */ if (sec == bsdtar->newer_mtime_sec && nsec <= bsdtar->newer_mtime_nsec) continue; /* Too old, skip it. */ } /* * Note that pattern exclusions are checked before * pathname rewrites are handled. This gives more * control over exclusions, since rewrites always lose * information. (For example, consider a rewrite * s/foo[0-9]/foo/. If we check exclusions after the * rewrite, there would be no way to exclude foo1/bar * while allowing foo2/bar.) */ if (lafe_excluded(bsdtar->matching, archive_entry_pathname(entry))) continue; /* Excluded by a pattern test. */ if (mode == 't') { /* Perversely, gtar uses -O to mean "send to stderr" * when used with -t. */ out = bsdtar->option_stdout ? stderr : stdout; /* * TODO: Provide some reasonable way to * preview rewrites. gtar always displays * the unedited path in -t output, which means * you cannot easily preview rewrites. */ if (bsdtar->verbose < 2) safe_fprintf(out, "%s", archive_entry_pathname(entry)); else list_item_verbose(bsdtar, out, entry); fflush(out); r = archive_read_data_skip(a); if (r == ARCHIVE_WARN) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_RETRY) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; break; } fprintf(out, "\n"); } else { /* Note: some rewrite failures prevent extraction. */ if (edit_pathname(bsdtar, entry)) continue; /* Excluded by a rewrite failure. */ if (bsdtar->option_interactive && !yes("extract '%s'", archive_entry_pathname(entry))) continue; /* * Format here is from SUSv2, including the * deferred '\n'. */ if (bsdtar->verbose) { safe_fprintf(stderr, "x %s", archive_entry_pathname(entry)); fflush(stderr); } /* TODO siginfo_printinfo(bsdtar, 0); */ if (bsdtar->option_stdout) r = archive_read_data_into_fd(a, 1); else r = archive_read_extract2(a, entry, writer); if (r != ARCHIVE_OK) { if (!bsdtar->verbose) safe_fprintf(stderr, "%s", archive_entry_pathname(entry)); safe_fprintf(stderr, ": %s", archive_error_string(a)); if (!bsdtar->verbose) fprintf(stderr, "\n"); bsdtar->return_value = 1; } if (bsdtar->verbose) fprintf(stderr, "\n"); if (r == ARCHIVE_FATAL) break; } } r = archive_read_close(a); if (r != ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (bsdtar->verbose > 2) fprintf(stdout, "Archive Format: %s, Compression: %s\n", archive_format_name(a), archive_compression_name(a)); archive_read_free(a); }