static void verify5(struct archive_entry *ae) { assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(archive_entry_mtime(ae), 1131430878); assertEqualInt(archive_entry_mode(ae) & 0777, 0755); assertEqualInt(archive_entry_uid(ae), 1000); assertEqualInt(archive_entry_gid(ae), 1000); assertEqualString(archive_entry_uname(ae), "tim"); assertEqualString(archive_entry_gname(ae), "tim"); }
static void verify5(struct archive *a, struct archive_entry *ae) { (void)a; /* UNUSED */ assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); assertEqualInt(archive_entry_mtime(ae), 86401); assertEqualInt(archive_entry_mode(ae) & 0777, 0755); assertEqualInt(archive_entry_uid(ae), UID); assertEqualInt(archive_entry_gid(ae), GID); assertEqualString(archive_entry_uname(ae), UNAME); assertEqualString(archive_entry_gname(ae), GNAME); }
static int ar_entry_uname(lua_State *L) { struct archive_entry* self = *ar_entry_check(L, 1); int is_set; if ( NULL == self ) return 0; is_set = ( lua_gettop(L) == 2 ); lua_pushstring(L, archive_entry_uname(self)); if ( is_set ) { archive_entry_copy_uname(self, lua_tostring(L, 2)); } return 1; }
static void verify4(struct archive_entry *ae) { assert(S_ISBLK(archive_entry_mode(ae))); assertEqualInt(archive_entry_mode(ae) & 0777, 0755); assertEqualInt(archive_entry_uid(ae), 1000); assertEqualInt(archive_entry_gid(ae), 1000); assertEqualString(archive_entry_uname(ae), "tim"); assertEqualString(archive_entry_gname(ae), "tim"); assertEqualString(archive_entry_pathname(ae), "devblock"); assert(archive_entry_symlink(ae) == NULL); assert(archive_entry_hardlink(ae) == NULL); assertEqualInt(archive_entry_mtime(ae), 1184389185); }
static uid_t get_uid_from_archive(struct archive_entry *ae) { char buffer[128]; struct passwd pwent, *result; if ((getpwnam_r(archive_entry_uname(ae), &pwent, buffer, sizeof(buffer), &result)) < 0) return (0); if (result == NULL) return (0); return (pwent.pw_uid); }
static void verify6(struct archive_entry *ae) { assertEqualInt(archive_entry_filetype(ae), AE_IFIFO); assertEqualInt(archive_entry_mode(ae) & 0777, 0755); assertEqualInt(archive_entry_uid(ae), 1000); assertEqualInt(archive_entry_gid(ae), 1000); assertEqualString(archive_entry_uname(ae), "tim"); assertEqualString(archive_entry_gname(ae), "tim"); assertEqualString(archive_entry_pathname(ae), "fifo"); assert(archive_entry_symlink(ae) == NULL); assert(archive_entry_hardlink(ae) == NULL); assertEqualInt(archive_entry_mtime(ae), 1184389185); }
static void verify2(struct archive *a, struct archive_entry *ae) { (void)a; /* UNUSED */ assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); assertEqualInt(archive_entry_mode(ae) & 0777, 0755); assertEqualInt(archive_entry_uid(ae), UID); assertEqualInt(archive_entry_gid(ae), GID); assertEqualString(archive_entry_uname(ae), UNAME); assertEqualString(archive_entry_gname(ae), GNAME); assertEqualString(archive_entry_pathname(ae), "symlink"); assertEqualString(archive_entry_symlink(ae), "f1"); assert(archive_entry_hardlink(ae) == NULL); }
static void verify7(struct archive *a, struct archive_entry *ae) { (void)a; /* UNUSED */ assert(archive_entry_filetype(ae) == AE_IFREG); assertEqualInt(archive_entry_mode(ae) & 0777, 0644); assertEqualInt(archive_entry_uid(ae), UID); assertEqualInt(archive_entry_gid(ae), GID); assertEqualString(archive_entry_uname(ae), UNAME); assertEqualString(archive_entry_gname(ae), GNAME); assertEqualString(archive_entry_pathname(ae), "dir1/f1"); assert(archive_entry_hardlink(ae) == NULL); assert(archive_entry_symlink(ae) == NULL); assertEqualInt(archive_entry_mtime(ae), 86401); }
static void verify1(struct archive_entry *ae) { /* A hardlink is not a symlink. */ assert(archive_entry_filetype(ae) != AE_IFLNK); /* Nor is it a directory. */ assert(archive_entry_filetype(ae) != AE_IFDIR); assertEqualInt(archive_entry_mode(ae) & 0777, 0644); assertEqualInt(archive_entry_uid(ae), 1000); assertEqualInt(archive_entry_gid(ae), 1000); assertEqualString(archive_entry_uname(ae), "tim"); assertEqualString(archive_entry_gname(ae), "tim"); assertEqualString(archive_entry_pathname(ae), "hardlink"); assertEqualString(archive_entry_hardlink(ae), "file"); assert(archive_entry_symlink(ae) == NULL); assertEqualInt(archive_entry_mtime(ae), 1184388530); }
/* * Test if entry is excluded by uid, gid, uname or gname. */ static int owner_excluded(struct archive_match *a, struct archive_entry *entry) { int r; if (a->inclusion_uids.count) { if (!match_owner_id(&(a->inclusion_uids), archive_entry_uid(entry))) return (1); } if (a->inclusion_gids.count) { if (!match_owner_id(&(a->inclusion_gids), archive_entry_gid(entry))) return (1); } if (a->inclusion_unames.count) { #if defined(_WIN32) && !defined(__CYGWIN__) r = match_owner_name_wcs(a, &(a->inclusion_unames), archive_entry_uname_w(entry)); #else r = match_owner_name_mbs(a, &(a->inclusion_unames), archive_entry_uname(entry)); #endif if (!r) return (1); else if (r < 0) return (r); } if (a->inclusion_gnames.count) { #if defined(_WIN32) && !defined(__CYGWIN__) r = match_owner_name_wcs(a, &(a->inclusion_gnames), archive_entry_gname_w(entry)); #else r = match_owner_name_mbs(a, &(a->inclusion_gnames), archive_entry_gname(entry)); #endif if (!r) return (1); else if (r < 0) return (r); } return (0); }
static void verify1(struct archive *a, struct archive_entry *ae) { (void)a; /* UNUSED */ /* A hardlink is not a symlink. */ assert(archive_entry_filetype(ae) != AE_IFLNK); /* Nor is it a directory. */ assert(archive_entry_filetype(ae) != AE_IFDIR); assertEqualInt(archive_entry_mode(ae) & 0777, 0644); assertEqualInt(archive_entry_uid(ae), UID); assertEqualInt(archive_entry_gid(ae), GID); assertEqualString(archive_entry_uname(ae), UNAME); assertEqualString(archive_entry_gname(ae), GNAME); assertEqualString(archive_entry_pathname(ae), "hardlink"); assertEqualString(archive_entry_hardlink(ae), "f1"); assert(archive_entry_symlink(ae) == NULL); assertEqualInt(archive_entry_mtime(ae), 86401); assertEqualInt(archive_entry_nlink(ae), 2); }
static uid_t get_uid_from_archive(struct archive_entry *ae) { static char user_buffer[128]; const char *user; static struct passwd pwent; struct passwd *result; user = archive_entry_uname(ae); if (pwent.pw_name != NULL && strcmp(user, pwent.pw_name) == 0) goto out; pwent.pw_name = NULL; if ((getpwnam_r(user, &pwent, user_buffer, sizeof(user_buffer), &result)) < 0) return (0); if (result == NULL) return (0); out: return (pwent.pw_uid); }
static void verifyxL(struct archive_entry *ae) { assertEqualInt(archive_entry_filetype(ae), AE_IFLNK); assertEqualInt(archive_entry_mode(ae) & 0777, 0755); assertEqualInt(archive_entry_uid(ae), 1000); assertEqualInt(archive_entry_gid(ae), 1000); assertEqualString(archive_entry_uname(ae), "tim"); assertEqualString(archive_entry_gname(ae), "tim"); assertEqualString(archive_entry_pathname(ae), "symlink"); assertEqualString(archive_entry_symlink(ae), "this_is_a_very_long_symlink_body_abcdefghijklmnopqrstuvwxyz_" "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_" "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_" "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_" "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_" "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_" "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_" "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz"); assert(archive_entry_hardlink(ae) == NULL); assertEqualInt(archive_entry_mtime(ae), 1184390648); }
static void verify0(struct archive *a, struct archive_entry *ae) { const void *p; size_t size; int64_t offset; assert(archive_entry_filetype(ae) == AE_IFREG); assertEqualInt(archive_entry_mode(ae) & 0777, 0644); assertEqualInt(archive_entry_uid(ae), UID); assertEqualInt(archive_entry_gid(ae), GID); assertEqualString(archive_entry_uname(ae), UNAME); assertEqualString(archive_entry_gname(ae), GNAME); assertEqualString(archive_entry_pathname(ae), "f1"); assert(archive_entry_hardlink(ae) == NULL); assert(archive_entry_symlink(ae) == NULL); assertEqualInt(archive_entry_mtime(ae), 86401); assertEqualInt(archive_entry_size(ae), 16); assertEqualInt(archive_read_data_block(a, &p, &size, &offset), 0); assertEqualInt((int)size, 16); assertEqualInt((int)offset, 0); assertEqualMem(p, "hellohellohello\n", 16); }
const char *Entry::uname() { return archive_entry_uname(_entry); }
/*- * The logic here for -C <dir> attempts to avoid * chdir() as long as possible. For example: * "-C /foo -C /bar file" needs chdir("/bar") but not chdir("/foo") * "-C /foo -C bar file" needs chdir("/foo/bar") * "-C /foo -C bar /file1" does not need chdir() * "-C /foo -C bar /file1 file2" needs chdir("/foo/bar") before file2 * * The only correct way to handle this is to record a "pending" chdir * request and combine multiple requests intelligently until we * need to process a non-absolute file. set_chdir() adds the new dir * to the pending list; do_chdir() actually executes any pending chdir. * * This way, programs that build tar command lines don't have to worry * about -C with non-existent directories; such requests will only * fail if the directory must be accessed. * */ void set_chdir(struct bsdtar *bsdtar, const char *newdir) { #if defined(_WIN32) && !defined(__CYGWIN__) if (newdir[0] == '/' || newdir[0] == '\\' || /* Detect this type, for example, "C:\" or "C:/" */ (((newdir[0] >= 'a' && newdir[0] <= 'z') || (newdir[0] >= 'A' && newdir[0] <= 'Z')) && newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) { #else if (newdir[0] == '/') { #endif /* The -C /foo -C /bar case; dump first one. */ free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } if (bsdtar->pending_chdir == NULL) /* Easy case: no previously-saved dir. */ bsdtar->pending_chdir = strdup(newdir); else { /* The -C /foo -C bar case; concatenate */ char *old_pending = bsdtar->pending_chdir; size_t old_len = strlen(old_pending); bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2); if (old_pending[old_len - 1] == '/') old_pending[old_len - 1] = '\0'; if (bsdtar->pending_chdir != NULL) sprintf(bsdtar->pending_chdir, "%s/%s", old_pending, newdir); free(old_pending); } if (bsdtar->pending_chdir == NULL) lafe_errc(1, errno, "No memory"); } void do_chdir(struct bsdtar *bsdtar) { if (bsdtar->pending_chdir == NULL) return; if (chdir(bsdtar->pending_chdir) != 0) { lafe_errc(1, 0, "could not chdir to '%s'\n", bsdtar->pending_chdir); } free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } static const char * strip_components(const char *p, int elements) { /* Skip as many elements as necessary. */ while (elements > 0) { switch (*p++) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif elements--; break; case '\0': /* Path is too short, skip it. */ return (NULL); } } /* Skip any / characters. This handles short paths that have * additional / termination. This also handles the case where * the logic above stops in the middle of a duplicate // * sequence (which would otherwise get converted to an * absolute path). */ for (;;) { switch (*p) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif ++p; break; case '\0': return (NULL); default: return (p); } } } static void warn_strip_leading_char(struct bsdtar *bsdtar, const char *c) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading '%c' from member names", c[0]); bsdtar->warned_lead_slash = 1; } } static void warn_strip_drive_letter(struct bsdtar *bsdtar) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading drive letter from " "member names"); bsdtar->warned_lead_slash = 1; } } /* * Convert absolute path to non-absolute path by skipping leading * absolute path prefixes. */ static const char* strip_absolute_path(struct bsdtar *bsdtar, const char *p) { const char *rp; /* Remove leading "//./" or "//?/" or "//?/UNC/" * (absolute path prefixes used by Windows API) */ if ((p[0] == '/' || p[0] == '\\') && (p[1] == '/' || p[1] == '\\') && (p[2] == '.' || p[2] == '?') && (p[3] == '/' || p[3] == '\\')) { if (p[2] == '?' && (p[4] == 'U' || p[4] == 'u') && (p[5] == 'N' || p[5] == 'n') && (p[6] == 'C' || p[6] == 'c') && (p[7] == '/' || p[7] == '\\')) p += 8; else p += 4; warn_strip_drive_letter(bsdtar); } /* Remove multiple leading slashes and Windows drive letters. */ do { rp = p; if (((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z')) && p[1] == ':') { p += 2; warn_strip_drive_letter(bsdtar); } /* Remove leading "/../", "/./", "//", etc. */ while (p[0] == '/' || p[0] == '\\') { if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\\')) { p += 3; /* Remove "/..", leave "/" for next pass. */ } else if (p[1] == '.' && (p[2] == '/' || p[2] == '\\')) { p += 2; /* Remove "/.", leave "/" for next pass. */ } else p += 1; /* Remove "/". */ warn_strip_leading_char(bsdtar, rp); } } while (rp != p); return (p); } /* * Handle --strip-components and any future path-rewriting options. * Returns non-zero if the pathname should not be extracted. * * Note: The rewrites are applied uniformly to pathnames and hardlink * names but not to symlink bodies. This is deliberate: Symlink * bodies are not necessarily filenames. Even when they are, they * need to be interpreted relative to the directory containing them, * so simple rewrites like this are rarely appropriate. * * TODO: Support pax-style regex path rewrites. */ int edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) { const char *name = archive_entry_pathname(entry); const char *original_name = name; const char *hardlinkname = archive_entry_hardlink(entry); const char *original_hardlinkname = hardlinkname; #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) char *subst_name; int r; /* Apply user-specified substitution to pathname. */ r = apply_substitution(bsdtar, name, &subst_name, 0, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_pathname(entry, subst_name); if (*subst_name == '\0') { free(subst_name); return -1; } else free(subst_name); name = archive_entry_pathname(entry); original_name = name; } /* Apply user-specified substitution to hardlink target. */ if (hardlinkname != NULL) { r = apply_substitution(bsdtar, hardlinkname, &subst_name, 0, 1); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_hardlink(entry, subst_name); free(subst_name); } hardlinkname = archive_entry_hardlink(entry); original_hardlinkname = hardlinkname; } /* Apply user-specified substitution to symlink body. */ if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_symlink(entry, subst_name); free(subst_name); } } #endif /* Strip leading dir names as per --strip-components option. */ if (bsdtar->strip_components > 0) { name = strip_components(name, bsdtar->strip_components); if (name == NULL) return (1); if (hardlinkname != NULL) { hardlinkname = strip_components(hardlinkname, bsdtar->strip_components); if (hardlinkname == NULL) return (1); } } if (!bsdtar->option_absolute_paths) { /* By default, don't write or restore absolute pathnames. */ name = strip_absolute_path(bsdtar, name); if (*name == '\0') name = "."; if (hardlinkname != NULL) { hardlinkname = strip_absolute_path(bsdtar, hardlinkname); if (*hardlinkname == '\0') return (1); } } else { /* Strip redundant leading '/' characters. */ while (name[0] == '/' && name[1] == '/') name++; } /* Replace name in archive_entry. */ if (name != original_name) { archive_entry_copy_pathname(entry, name); } if (hardlinkname != original_hardlinkname) { archive_entry_copy_hardlink(entry, hardlinkname); } return (0); } /* * It would be nice to just use printf() for formatting large numbers, * but the compatibility problems are quite a headache. Hence the * following simple utility function. */ const char * tar_i64toa(int64_t n0) { static char buff[24]; uint64_t n = n0 < 0 ? -n0 : n0; char *p = buff + sizeof(buff); *--p = '\0'; do { *--p = '0' + (int)(n % 10); } while (n /= 10); if (n0 < 0) *--p = '-'; return p; } /* * Like strcmp(), but try to be a little more aware of the fact that * we're comparing two paths. Right now, it just handles leading * "./" and trailing '/' specially, so that "a/b/" == "./a/b" * * TODO: Make this better, so that "./a//b/./c/" == "a/b/c" * TODO: After this works, push it down into libarchive. * TODO: Publish the path normalization routines in libarchive so * that bsdtar can normalize paths and use fast strcmp() instead * of this. * * Note: This is currently only used within write.c, so should * not handle \ path separators. */ int pathcmp(const char *a, const char *b) { /* Skip leading './' */ if (a[0] == '.' && a[1] == '/' && a[2] != '\0') a += 2; if (b[0] == '.' && b[1] == '/' && b[2] != '\0') b += 2; /* Find the first difference, or return (0) if none. */ while (*a == *b) { if (*a == '\0') return (0); a++; b++; } /* * If one ends in '/' and the other one doesn't, * they're the same. */ if (a[0] == '/' && a[1] == '\0' && b[0] == '\0') return (0); if (a[0] == '\0' && b[0] == '/' && b[1] == '\0') return (0); /* They're really different, return the correct sign. */ return (*(const unsigned char *)a - *(const unsigned char *)b); } #define PPBUFF_SIZE 1024 const char * passphrase_callback(struct archive *a, void *_client_data) { struct bsdtar *bsdtar = (struct bsdtar *)_client_data; (void)a; /* UNUSED */ if (bsdtar->ppbuff == NULL) { bsdtar->ppbuff = malloc(PPBUFF_SIZE); if (bsdtar->ppbuff == NULL) lafe_errc(1, errno, "Out of memory"); } return lafe_readpassphrase("Enter passphrase:", bsdtar->ppbuff, PPBUFF_SIZE); } void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { memset(ppbuff, 0, PPBUFF_SIZE); free(ppbuff); } } /* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ void list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) { char tmp[100]; size_t w; const char *p; const char *fmt; time_t tim; static time_t now; /* * We avoid collecting the entire list in memory at once by * listing things as we see them. However, that also means we can't * just pre-compute the field widths. Instead, we start with guesses * and just widen them as necessary. These numbers are completely * arbitrary. */ if (!bsdtar->u_width) { bsdtar->u_width = 6; bsdtar->gs_width = 13; } if (!now) time(&now); fprintf(out, "%s %d ", archive_entry_strmode(entry), archive_entry_nlink(entry)); /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); if ((p == NULL) || (*p == '\0')) { sprintf(tmp, "%lu ", (unsigned long)archive_entry_uid(entry)); p = tmp; } w = strlen(p); if (w > bsdtar->u_width) bsdtar->u_width = w; fprintf(out, "%-*s ", (int)bsdtar->u_width, p); /* Use gname if it's present, else gid. */ p = archive_entry_gname(entry); if (p != NULL && p[0] != '\0') { fprintf(out, "%s", p); w = strlen(p); } else { sprintf(tmp, "%lu", (unsigned long)archive_entry_gid(entry)); w = strlen(tmp); fprintf(out, "%s", tmp); } /* * Print device number or file size, right-aligned so as to make * total width of group and devnum/filesize fields be gs_width. * If gs_width is too small, grow it. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { sprintf(tmp, "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(tmp, tar_i64toa(archive_entry_size(entry))); } if (w + strlen(tmp) >= bsdtar->gs_width) bsdtar->gs_width = w+strlen(tmp)+1; fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); /* Format the time using 'ls -l' conventions. */ tim = archive_entry_mtime(entry); #define HALF_YEAR (time_t)365 * 86400 / 2 #if defined(_WIN32) && !defined(__CYGWIN__) #define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ #else #define DAY_FMT "%e" /* Day number without leading zeros */ #endif if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; else fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ safe_fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); }
/* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ static void list_item_verbose(struct cpio *cpio, struct archive_entry *entry) { char size[32]; char date[32]; char uids[16], gids[16]; const char *uname, *gname; FILE *out = stdout; const char *fmt; time_t mtime; static time_t now; struct tm *tm; if (!now) time(&now); if (cpio->option_numeric_uid_gid) { /* Format numeric uid/gid for display. */ strcpy(uids, cpio_i64toa(archive_entry_uid(entry))); uname = uids; strcpy(gids, cpio_i64toa(archive_entry_gid(entry))); gname = gids; } else { /* Use uname if it's present, else lookup name from uid. */ uname = archive_entry_uname(entry); if (uname == NULL) uname = lookup_uname(cpio, archive_entry_uid(entry)); /* Use gname if it's present, else lookup name from gid. */ gname = archive_entry_gname(entry); if (gname == NULL) gname = lookup_gname(cpio, archive_entry_gid(entry)); } /* Print device number or file size. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { snprintf(size, sizeof(size), "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(size, cpio_i64toa(archive_entry_size(entry))); } /* Format the time using 'ls -l' conventions. */ mtime = archive_entry_mtime(entry); #if defined(_WIN32) && !defined(__CYGWIN__) /* Windows' strftime function does not support %e format. */ if (mtime - now > 365*86400/2 || mtime - now < -365*86400/2) fmt = cpio->day_first ? "%d %b %Y" : "%b %d %Y"; else fmt = cpio->day_first ? "%d %b %H:%M" : "%b %d %H:%M"; #else if (abs(mtime - now) > (365/2)*86400) fmt = cpio->day_first ? "%e %b %Y" : "%b %e %Y"; else fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; #endif tm = localtime(&mtime); if (tm != NULL) { strftime(date, sizeof(date), fmt, tm); } else { *date = '\0'; } fprintf(out, "%s%3d %-8s %-8s %8s %12s %s", archive_entry_strmode(entry), archive_entry_nlink(entry), uname, gname, size, date, archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ fprintf(out, " -> %s", archive_entry_symlink(entry)); fprintf(out, "\n"); }
/* * 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; }
/* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ static void list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) { const struct stat *st; char tmp[100]; size_t w; const char *p; const char *fmt; time_t tim; static time_t now; st = archive_entry_stat(entry); /* * We avoid collecting the entire list in memory at once by * listing things as we see them. However, that also means we can't * just pre-compute the field widths. Instead, we start with guesses * and just widen them as necessary. These numbers are completely * arbitrary. */ if (!bsdtar->u_width) { bsdtar->u_width = 6; bsdtar->gs_width = 13; } if (!now) time(&now); fprintf(out, "%s %d ", archive_entry_strmode(entry), (int)(st->st_nlink)); /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); if ((p == NULL) || (*p == '\0')) { sprintf(tmp, "%lu ", (unsigned long)st->st_uid); p = tmp; } w = strlen(p); if (w > bsdtar->u_width) bsdtar->u_width = w; fprintf(out, "%-*s ", (int)bsdtar->u_width, p); /* Use gname if it's present, else gid. */ p = archive_entry_gname(entry); if (p != NULL && p[0] != '\0') { fprintf(out, "%s", p); w = strlen(p); } else { sprintf(tmp, "%lu", (unsigned long)st->st_gid); w = strlen(tmp); fprintf(out, "%s", tmp); } /* * Print device number or file size, right-aligned so as to make * total width of group and devnum/filesize fields be gs_width. * If gs_width is too small, grow it. */ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { sprintf(tmp, "%lu,%lu", (unsigned long)major(st->st_rdev), (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */ } else { /* * Note the use of platform-dependent macros to format * the filesize here. We need the format string and the * corresponding type for the cast. */ sprintf(tmp, BSDTAR_FILESIZE_PRINTF, (BSDTAR_FILESIZE_TYPE)st->st_size); } if (w + strlen(tmp) >= bsdtar->gs_width) bsdtar->gs_width = w+strlen(tmp)+1; fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); /* Format the time using 'ls -l' conventions. */ tim = (time_t)st->st_mtime; if (abs(tim - now) > (365/2)*86400) fmt = bsdtar->day_first ? "%e %b %Y" : "%b %e %Y"; else fmt = bsdtar->day_first ? "%e %b %R" : "%b %e %R"; strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ safe_fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (S_ISLNK(st->st_mode)) /* Symbolic link */ safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); }
static int archive_write_gnutar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; int r, ret, ret2 = ARCHIVE_OK; int tartype; struct gnutar *gnutar; struct archive_string_conv *sconv; gnutar = (struct gnutar *)a->format_data; /* Setup default string conversion. */ if (gnutar->opt_sconv == NULL) { if (!gnutar->init_default_conversion) { gnutar->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); gnutar->init_default_conversion = 1; } sconv = gnutar->sconv_default; } else sconv = gnutar->opt_sconv; /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || !(archive_entry_filetype(entry) == AE_IFREG)) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { const char *p; char *t; /* * Ensure a trailing '/'. Modify the entry so * the client sees the change. */ p = archive_entry_pathname(entry); if (p[strlen(p) - 1] != '/') { t = (char *)malloc(strlen(p) + 2); if (t == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate gnutar data"); return(ARCHIVE_FATAL); } strcpy(t, p); strcat(t, "/"); archive_entry_copy_pathname(entry, t); free(t); } } r = archive_entry_pathname_l(entry, &(gnutar->pathname), &(gnutar->pathname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathame"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_uname_l(entry, &(gnutar->uname), &(gnutar->uname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to %s", archive_entry_uname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_gname_l(entry, &(gnutar->gname), &(gnutar->gname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to %s", archive_entry_gname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } /* If linkname is longer than 100 chars we need to add a 'K' header. */ r = archive_entry_hardlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (gnutar->linkname_length == 0) { r = archive_entry_symlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } } if (gnutar->linkname_length > GNUTAR_linkname_size) { size_t todo = gnutar->linkname_length; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, gnutar->linkname_length + 1); ret = archive_format_gnutar_header(a, buff, temp, 'K'); if (ret < ARCHIVE_WARN) return (ret); ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) return (ret); archive_entry_free(temp); /* Write as many 512 bytes blocks as needed to write full name. */ ret = __archive_write_output(a, gnutar->linkname, todo); if(ret < ARCHIVE_WARN) return (ret); ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo)); if (ret < ARCHIVE_WARN) return (ret); } /* If pathname is longer than 100 chars we need to add an 'L' header. */ if (gnutar->pathname_length > GNUTAR_name_size) { const char *pathname = gnutar->pathname; size_t todo = gnutar->pathname_length; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, gnutar->pathname_length + 1); ret = archive_format_gnutar_header(a, buff, temp, 'L'); if (ret < ARCHIVE_WARN) return (ret); ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) return (ret); archive_entry_free(temp); /* Write as many 512 bytes blocks as needed to write full name. */ ret = __archive_write_output(a, pathname, todo); if(ret < ARCHIVE_WARN) return (ret); ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo)); if (ret < ARCHIVE_WARN) return (ret); } if (archive_entry_hardlink(entry) != NULL) { tartype = '1'; } else switch (archive_entry_filetype(entry)) { case AE_IFREG: tartype = '0' ; break; case AE_IFLNK: tartype = '2' ; break; case AE_IFCHR: tartype = '3' ; break; case AE_IFBLK: tartype = '4' ; break; case AE_IFDIR: tartype = '5' ; break; case AE_IFIFO: tartype = '6' ; break; case AE_IFSOCK: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (mode=0%lo)", (unsigned long)archive_entry_mode(entry)); return (ARCHIVE_FAILED); } ret = archive_format_gnutar_header(a, buff, entry, tartype); if (ret < ARCHIVE_WARN) return (ret); if (ret2 < ret) ret = ret2; ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) return (ret2); if (ret2 < ret) ret = ret2; gnutar->entry_bytes_remaining = archive_entry_size(entry); gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining); return (ret); }
static int archive_write_shar_finish_entry(struct archive_write *a) { const char *g, *p, *u; struct shar *shar; int ret; shar = (struct shar *)a->format_data; if (shar->entry == NULL) return (0); if (shar->dump) { /* Finish uuencoded data. */ if (shar->has_data) { if (shar->outpos > 0) uuencode_line(a, shar, shar->outbuff, shar->outpos); archive_strcat(&shar->work, "`\nend\n"); archive_strcat(&shar->work, "SHAR_END\n"); } /* Restore file mode, owner, flags. */ /* * TODO: Don't immediately restore mode for * directories; defer that to end of script. */ archive_string_sprintf(&shar->work, "chmod %o ", (unsigned int)(archive_entry_mode(shar->entry) & 07777)); shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1); archive_strcat(&shar->work, "\n"); u = archive_entry_uname(shar->entry); g = archive_entry_gname(shar->entry); if (u != NULL || g != NULL) { archive_strcat(&shar->work, "chown "); if (u != NULL) shar_quote(&shar->work, u, 1); if (g != NULL) { archive_strcat(&shar->work, ":"); shar_quote(&shar->work, g, 1); } archive_strcat(&shar->work, " "); shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1); archive_strcat(&shar->work, "\n"); } if ((p = archive_entry_fflags_text(shar->entry)) != NULL) { archive_string_sprintf(&shar->work, "chflags %s ", p); shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1); archive_strcat(&shar->work, "\n"); } /* TODO: restore ACLs */ } else { if (shar->has_data) { /* Finish sed-encoded data: ensure last line ends. */ if (!shar->end_of_line) archive_strappend_char(&shar->work, '\n'); archive_strcat(&shar->work, "SHAR_END\n"); } } archive_entry_free(shar->entry); shar->entry = NULL; if (shar->work.length < 65536) return (ARCHIVE_OK); ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); archive_string_empty(&shar->work); return (ARCHIVE_OK); }
/* * Apple shipped an extended version of GNU tar with Mac OS X 10.5 * and earlier. */ static void test_compat_mac_1(void) { char name[] = "test_compat_mac-1.tar.Z"; struct archive_entry *ae; struct archive *a; const void *attr; size_t attrSize; assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); extract_reference_file(name); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(TESTPATH, archive_entry_pathname(ae)); assertEqualInt(1275688109, archive_entry_mtime(ae)); assertEqualInt(95594, archive_entry_uid(ae)); assertEqualString("kientzle", archive_entry_uname(ae)); assertEqualInt(5000, archive_entry_gid(ae)); assertEqualString("", archive_entry_gname(ae)); assertEqualInt(040755, archive_entry_mode(ae)); attr = archive_entry_mac_metadata(ae, &attrSize); assert(attr == NULL); assertEqualInt(0, attrSize); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(TESTPATH "dir/", archive_entry_pathname(ae)); assertEqualInt(1275687611, archive_entry_mtime(ae)); assertEqualInt(95594, archive_entry_uid(ae)); assertEqualString("kientzle", archive_entry_uname(ae)); assertEqualInt(5000, archive_entry_gid(ae)); assertEqualString("", archive_entry_gname(ae)); assertEqualInt(040755, archive_entry_mode(ae)); attr = archive_entry_mac_metadata(ae, &attrSize); assert(attr != NULL); assertEqualInt(225, attrSize); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(TESTPATH "file", archive_entry_pathname(ae)); assertEqualInt(1275687588, archive_entry_mtime(ae)); assertEqualInt(95594, archive_entry_uid(ae)); assertEqualString("kientzle", archive_entry_uname(ae)); assertEqualInt(5000, archive_entry_gid(ae)); assertEqualString("", archive_entry_gname(ae)); assertEqualInt(0100644, archive_entry_mode(ae)); attr = archive_entry_mac_metadata(ae, &attrSize); assert(attr != NULL); assertEqualInt(225, attrSize); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("dir/", archive_entry_pathname(ae)); assertEqualInt(1275688064, archive_entry_mtime(ae)); assertEqualInt(95594, archive_entry_uid(ae)); assertEqualString("kientzle", archive_entry_uname(ae)); assertEqualInt(5000, archive_entry_gid(ae)); assertEqualString("", archive_entry_gname(ae)); assertEqualInt(040755, archive_entry_mode(ae)); attr = archive_entry_mac_metadata(ae, &attrSize); assert(attr != NULL); assertEqualInt(225, attrSize); assertEqualMem("\x00\x05\x16\x07\x00\x02\x00\x00Mac OS X", attr, 16); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("file", archive_entry_pathname(ae)); assertEqualInt(1275625860, archive_entry_mtime(ae)); assertEqualInt(95594, archive_entry_uid(ae)); assertEqualString("kientzle", archive_entry_uname(ae)); assertEqualInt(5000, archive_entry_gid(ae)); assertEqualString("", archive_entry_gname(ae)); assertEqualInt(0100644, archive_entry_mode(ae)); attr = archive_entry_mac_metadata(ae, &attrSize); assert(attr != NULL); assertEqualInt(225, attrSize); assertEqualMem("\x00\x05\x16\x07\x00\x02\x00\x00Mac OS X", attr, 16); /* Verify the end-of-archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify that the format detection worked. */ assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_COMPRESS); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_GNUTAR); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
/* * Apple shipped a customized version of bsdtar starting with MacOS 10.6. */ static void test_compat_mac_2(void) { char name[] = "test_compat_mac-2.tar.Z"; struct archive_entry *ae; struct archive *a; const void *attr; size_t attrSize; assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); extract_reference_file(name); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("./", archive_entry_pathname(ae)); assertEqualInt(1303628303, archive_entry_mtime(ae)); assertEqualInt(501, archive_entry_uid(ae)); assertEqualString("tim", archive_entry_uname(ae)); assertEqualInt(20, archive_entry_gid(ae)); assertEqualString("staff", archive_entry_gname(ae)); assertEqualInt(040755, archive_entry_mode(ae)); attr = archive_entry_mac_metadata(ae, &attrSize); assert(attr == NULL); assertEqualInt(0, attrSize); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("./mydir/", archive_entry_pathname(ae)); assertEqualInt(1303628303, archive_entry_mtime(ae)); assertEqualInt(501, archive_entry_uid(ae)); assertEqualString("tim", archive_entry_uname(ae)); assertEqualInt(20, archive_entry_gid(ae)); assertEqualString("staff", archive_entry_gname(ae)); assertEqualInt(040755, archive_entry_mode(ae)); attr = archive_entry_mac_metadata(ae, &attrSize); assert(attr != NULL); assertEqualInt(267, attrSize); assertEqualMem("\x00\x05\x16\x07\x00\x02\x00\x00Mac OS X", attr, 16); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("./myfile", archive_entry_pathname(ae)); assertEqualInt(1303628303, archive_entry_mtime(ae)); assertEqualInt(501, archive_entry_uid(ae)); assertEqualString("tim", archive_entry_uname(ae)); assertEqualInt(20, archive_entry_gid(ae)); assertEqualString("staff", archive_entry_gname(ae)); assertEqualInt(0100644, archive_entry_mode(ae)); attr = archive_entry_mac_metadata(ae, &attrSize); assert(attr != NULL); assertEqualInt(267, attrSize); assertEqualMem("\x00\x05\x16\x07\x00\x02\x00\x00Mac OS X", attr, 16); /* Verify the end-of-archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify that the format detection worked. */ assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_COMPRESS); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
static int archive_write_mtree_finish_entry(struct archive_write *a) { struct mtree_writer *mtree = a->format_data; struct archive_entry *entry; struct archive_string *str; const char *name; int keys, ret; entry = mtree->entry; if (entry == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Finished entry without being open first."); return (ARCHIVE_FATAL); } mtree->entry = NULL; if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR) { archive_entry_free(entry); return (ARCHIVE_OK); } str = (mtree->indent)? &mtree->ebuf : &mtree->buf; keys = get_keys(mtree, entry); if ((keys & F_NLINK) != 0 && archive_entry_nlink(entry) != 1 && archive_entry_filetype(entry) != AE_IFDIR) archive_string_sprintf(str, " nlink=%u", archive_entry_nlink(entry)); if ((keys & F_GNAME) != 0 && (name = archive_entry_gname(entry)) != NULL) { archive_strcat(str, " gname="); mtree_quote(str, name); } if ((keys & F_UNAME) != 0 && (name = archive_entry_uname(entry)) != NULL) { archive_strcat(str, " uname="); mtree_quote(str, name); } if ((keys & F_FLAGS) != 0 && (name = archive_entry_fflags_text(entry)) != NULL) { archive_strcat(str, " flags="); mtree_quote(str, name); } if ((keys & F_TIME) != 0) archive_string_sprintf(str, " time=%jd.%jd", (intmax_t)archive_entry_mtime(entry), (intmax_t)archive_entry_mtime_nsec(entry)); if ((keys & F_MODE) != 0) archive_string_sprintf(str, " mode=%o", archive_entry_mode(entry) & 07777); if ((keys & F_GID) != 0) archive_string_sprintf(str, " gid=%jd", (intmax_t)archive_entry_gid(entry)); if ((keys & F_UID) != 0) archive_string_sprintf(str, " uid=%jd", (intmax_t)archive_entry_uid(entry)); switch (archive_entry_filetype(entry)) { case AE_IFLNK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=link"); if ((keys & F_SLINK) != 0) { archive_strcat(str, " link="); mtree_quote(str, archive_entry_symlink(entry)); } break; case AE_IFSOCK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=socket"); break; case AE_IFCHR: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=char"); if ((keys & F_DEV) != 0) { archive_string_sprintf(str, " device=native,%d,%d", archive_entry_rdevmajor(entry), archive_entry_rdevminor(entry)); } break; case AE_IFBLK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=block"); if ((keys & F_DEV) != 0) { archive_string_sprintf(str, " device=native,%d,%d", archive_entry_rdevmajor(entry), archive_entry_rdevminor(entry)); } break; case AE_IFDIR: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=dir"); break; case AE_IFIFO: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=fifo"); break; case AE_IFREG: default: /* Handle unknown file types as regular files. */ if ((keys & F_TYPE) != 0) archive_strcat(str, " type=file"); if ((keys & F_SIZE) != 0) archive_string_sprintf(str, " size=%jd", (intmax_t)archive_entry_size(entry)); break; } if (mtree->compute_sum & F_CKSUM) { uint64_t len; /* Include the length of the file. */ for (len = mtree->crc_len; len != 0; len >>= 8) COMPUTE_CRC(mtree->crc, len & 0xff); mtree->crc = ~mtree->crc; archive_string_sprintf(str, " cksum=%ju", (uintmax_t)mtree->crc); }
static int archive_format_gnutar_header(struct archive_write *a, char h[512], struct archive_entry *entry, int tartype) { unsigned int checksum; int i, ret; size_t copy_length; const char *p; struct gnutar *gnutar; gnutar = (struct gnutar *)a->format_data; ret = 0; /* * The "template header" already includes the signature, * various end-of-field markers, and other required elements. */ memcpy(h, &template_header, 512); /* * Because the block is already null-filled, and strings * are allowed to exactly fill their destination (without null), * I use memcpy(dest, src, strlen()) here a lot to copy strings. */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_pathname(entry); copy_length = strlen(p); } else { p = gnutar->pathname; copy_length = gnutar->pathname_length; } if (copy_length > GNUTAR_name_size) copy_length = GNUTAR_name_size; memcpy(h + GNUTAR_name_offset, p, copy_length); if ((copy_length = gnutar->linkname_length) > 0) { if (copy_length > GNUTAR_linkname_size) copy_length = GNUTAR_linkname_size; memcpy(h + GNUTAR_linkname_offset, gnutar->linkname, copy_length); } /* TODO: How does GNU tar handle unames longer than GNUTAR_uname_size? */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_uname(entry); copy_length = strlen(p); } else { p = gnutar->uname; copy_length = gnutar->uname_length; } if (copy_length > 0) { if (copy_length > GNUTAR_uname_size) copy_length = GNUTAR_uname_size; memcpy(h + GNUTAR_uname_offset, p, copy_length); } /* TODO: How does GNU tar handle gnames longer than GNUTAR_gname_size? */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_gname(entry); copy_length = strlen(p); } else { p = gnutar->gname; copy_length = gnutar->gname_length; } if (copy_length > 0) { if (strlen(p) > GNUTAR_gname_size) copy_length = GNUTAR_gname_size; memcpy(h + GNUTAR_gname_offset, p, copy_length); } /* By truncating the mode here, we ensure it always fits. */ format_octal(archive_entry_mode(entry) & 07777, h + GNUTAR_mode_offset, GNUTAR_mode_size); /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_uid(entry), h + GNUTAR_uid_offset, GNUTAR_uid_size, GNUTAR_uid_max_size)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID %jd too large", (intmax_t)archive_entry_uid(entry)); ret = ARCHIVE_FAILED; } /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_gid(entry), h + GNUTAR_gid_offset, GNUTAR_gid_size, GNUTAR_gid_max_size)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID %jd too large", (intmax_t)archive_entry_gid(entry)); ret = ARCHIVE_FAILED; } /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_size(entry), h + GNUTAR_size_offset, GNUTAR_size_size, GNUTAR_size_max_size)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); ret = ARCHIVE_FAILED; } /* Shouldn't overflow before 2106, since mtime field is 33 bits. */ format_octal(archive_entry_mtime(entry), h + GNUTAR_mtime_offset, GNUTAR_mtime_size); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { if (format_octal(archive_entry_rdevmajor(entry), h + GNUTAR_rdevmajor_offset, GNUTAR_rdevmajor_size)) { archive_set_error(&a->archive, ERANGE, "Major device number too large"); ret = ARCHIVE_FAILED; } if (format_octal(archive_entry_rdevminor(entry), h + GNUTAR_rdevminor_offset, GNUTAR_rdevminor_size)) { archive_set_error(&a->archive, ERANGE, "Minor device number too large"); ret = ARCHIVE_FAILED; } } h[GNUTAR_typeflag_offset] = tartype; checksum = 0; for (i = 0; i < 512; i++) checksum += 255 & (unsigned int)h[i]; h[GNUTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */ /* h[GNUTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */ format_octal(checksum, h + GNUTAR_checksum_offset, 6); return (ret); }
static int archive_write_gnutar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; int r, ret, ret2 = ARCHIVE_OK; int tartype; struct gnutar *gnutar; struct archive_string_conv *sconv; struct archive_entry *entry_main; gnutar = (struct gnutar *)a->format_data; /* Setup default string conversion. */ if (gnutar->opt_sconv == NULL) { if (!gnutar->init_default_conversion) { gnutar->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); gnutar->init_default_conversion = 1; } sconv = gnutar->sconv_default; } else sconv = gnutar->opt_sconv; /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || !(archive_entry_filetype(entry) == AE_IFREG)) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { const char *p; size_t path_length; /* * Ensure a trailing '/'. Modify the entry so * the client sees the change. */ #if defined(_WIN32) && !defined(__CYGWIN__) const wchar_t *wp; wp = archive_entry_pathname_w(entry); if (wp != NULL && wp[wcslen(wp) -1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); path_length = wcslen(wp); if (archive_wstring_ensure(&ws, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_wstring_free(&ws); return(ARCHIVE_FATAL); } /* Should we keep '\' ? */ if (wp[path_length -1] == L'\\') path_length--; archive_wstrncpy(&ws, wp, path_length); archive_wstrappend_wchar(&ws, L'/'); archive_entry_copy_pathname_w(entry, ws.s); archive_wstring_free(&ws); p = NULL; } else #endif p = archive_entry_pathname(entry); /* * On Windows, this is a backup operation just in * case getting WCS failed. On POSIX, this is a * normal operation. */ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); path_length = strlen(p); if (archive_string_ensure(&as, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_string_free(&as); return(ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) /* NOTE: This might break the pathname * if the current code page is CP932 and * the pathname includes a character '\' * as a part of its multibyte pathname. */ if (p[strlen(p) -1] == '\\') path_length--; else #endif archive_strncpy(&as, p, path_length); archive_strappend_char(&as, '/'); archive_entry_copy_pathname(entry, as.s); archive_string_free(&as); } } #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif r = archive_entry_pathname_l(entry, &(gnutar->pathname), &(gnutar->pathname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathame"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_uname_l(entry, &(gnutar->uname), &(gnutar->uname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to %s", archive_entry_uname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_gname_l(entry, &(gnutar->gname), &(gnutar->gname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to %s", archive_entry_gname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } /* If linkname is longer than 100 chars we need to add a 'K' header. */ r = archive_entry_hardlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (gnutar->linkname_length == 0) { r = archive_entry_symlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } } if (gnutar->linkname_length > GNUTAR_linkname_size) { size_t length = gnutar->linkname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'K'); archive_entry_free(temp); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); if (ret < ARCHIVE_WARN) goto exit_write_header; /* Write name and trailing null byte. */ ret = __archive_write_output(a, gnutar->linkname, length); if (ret < ARCHIVE_WARN) goto exit_write_header; /* Pad to 512 bytes */ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length)); if (ret < ARCHIVE_WARN) goto exit_write_header; } /* If pathname is longer than 100 chars we need to add an 'L' header. */ if (gnutar->pathname_length > GNUTAR_name_size) { const char *pathname = gnutar->pathname; size_t length = gnutar->pathname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'L'); archive_entry_free(temp); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) goto exit_write_header; /* Write pathname + trailing null byte. */ ret = __archive_write_output(a, pathname, length); if(ret < ARCHIVE_WARN) goto exit_write_header; /* Pad to multiple of 512 bytes. */ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length)); if (ret < ARCHIVE_WARN) goto exit_write_header; } if (archive_entry_hardlink(entry) != NULL) { tartype = '1'; } else switch (archive_entry_filetype(entry)) { case AE_IFREG: tartype = '0' ; break; case AE_IFLNK: tartype = '2' ; break; case AE_IFCHR: tartype = '3' ; break; case AE_IFBLK: tartype = '4' ; break; case AE_IFDIR: tartype = '5' ; break; case AE_IFIFO: tartype = '6' ; break; case AE_IFSOCK: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); ret = ARCHIVE_FAILED; goto exit_write_header; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (mode=0%lo)", (unsigned long)archive_entry_mode(entry)); ret = ARCHIVE_FAILED; goto exit_write_header; } ret = archive_format_gnutar_header(a, buff, entry, tartype); if (ret < ARCHIVE_WARN) goto exit_write_header; if (ret2 < ret) ret = ret2; ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { ret = ret2; goto exit_write_header; } if (ret2 < ret) ret = ret2; gnutar->entry_bytes_remaining = archive_entry_size(entry); gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining); exit_write_header: archive_entry_free(entry_main); return (ret); }
/* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ static void list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) { char tmp[100]; size_t w; const char *p; const char *fmt; time_t tim; static time_t now; /* * We avoid collecting the entire list in memory at once by * listing things as we see them. However, that also means we can't * just pre-compute the field widths. Instead, we start with guesses * and just widen them as necessary. These numbers are completely * arbitrary. */ if (!bsdtar->u_width) { bsdtar->u_width = 6; bsdtar->gs_width = 13; } if (!now) time(&now); fprintf(out, "%s %d ", archive_entry_strmode(entry), archive_entry_nlink(entry)); /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); if ((p == NULL) || (*p == '\0')) { sprintf(tmp, "%lu ", (unsigned long)archive_entry_uid(entry)); p = tmp; } w = strlen(p); if (w > bsdtar->u_width) bsdtar->u_width = w; fprintf(out, "%-*s ", (int)bsdtar->u_width, p); /* Use gname if it's present, else gid. */ p = archive_entry_gname(entry); if (p != NULL && p[0] != '\0') { fprintf(out, "%s", p); w = strlen(p); } else { sprintf(tmp, "%lu", (unsigned long)archive_entry_gid(entry)); w = strlen(tmp); fprintf(out, "%s", tmp); } /* * Print device number or file size, right-aligned so as to make * total width of group and devnum/filesize fields be gs_width. * If gs_width is too small, grow it. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { sprintf(tmp, "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(tmp, tar_i64toa(archive_entry_size(entry))); } if (w + strlen(tmp) >= bsdtar->gs_width) bsdtar->gs_width = w+strlen(tmp)+1; fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); /* Format the time using 'ls -l' conventions. */ tim = archive_entry_mtime(entry); #define HALF_YEAR (time_t)365 * 86400 / 2 #if defined(_WIN32) && !defined(__CYGWIN__) #define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ #else #define DAY_FMT "%e" /* Day number without leading zeros */ #endif if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; else fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ safe_fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); }
/* * test_compat_gtar_1.tgz exercises reading long filenames and * symlink targets stored in the GNU tar format. */ static void test_compat_gtar_1(void) { char name[] = "test_compat_gtar_1.tar"; struct archive_entry *ae; struct archive *a; int r; assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); extract_reference_file(name); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240)); /* Read first entry. */ assertEqualIntA(a, ARCHIVE_OK, r = archive_read_next_header(a, &ae)); if (r != ARCHIVE_OK) { archive_read_free(a); return; } assertEqualString( "12345678901234567890123456789012345678901234567890" "12345678901234567890123456789012345678901234567890" "12345678901234567890123456789012345678901234567890" "12345678901234567890123456789012345678901234567890", archive_entry_pathname(ae)); assertEqualInt(1197179003, archive_entry_mtime(ae)); assertEqualInt(1000, archive_entry_uid(ae)); assertEqualString("tim", archive_entry_uname(ae)); assertEqualInt(1000, archive_entry_gid(ae)); assertEqualString("tim", archive_entry_gname(ae)); assertEqualInt(0100644, archive_entry_mode(ae)); /* Read second entry. */ assertEqualIntA(a, ARCHIVE_OK, r = archive_read_next_header(a, &ae)); if (r != ARCHIVE_OK) { archive_read_free(a); return; } assertEqualString( "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij" "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij" "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij" "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij", archive_entry_pathname(ae)); assertEqualString( "12345678901234567890123456789012345678901234567890" "12345678901234567890123456789012345678901234567890" "12345678901234567890123456789012345678901234567890" "12345678901234567890123456789012345678901234567890", archive_entry_symlink(ae)); assertEqualInt(1197179043, archive_entry_mtime(ae)); assertEqualInt(1000, archive_entry_uid(ae)); assertEqualString("tim", archive_entry_uname(ae)); assertEqualInt(1000, archive_entry_gid(ae)); assertEqualString("tim", archive_entry_gname(ae)); assertEqualInt(0120755, archive_entry_mode(ae)); /* Verify the end-of-archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify that the format detection worked. */ assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_NONE); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_GNUTAR); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
int main(int argc, char* argv[]){ struct archive* ar; struct archive_entry* aren; int arnum; if(argc < 3){ fprintf(stderr, "Usage: %s infile outfile\n", argv[0]); return 255; } #ifdef ENABLE_STAGE_1 puts("==> Stage 1: Listing"); ar = archive_read_new(); archive_read_support_filter_all(ar); archive_read_support_format_all(ar); arnum = archive_read_open_filename(ar, argv[1], 16384); if(arnum != ARCHIVE_OK){ fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1], archive_error_string(ar)); return 1; } while(archive_read_next_header(ar, &aren) == ARCHIVE_OK){ const char *hardlink, *symlink; printf("%s format: %s, pathname: %s, size: %"PRId64", links: %d," "username: %s, uid: %d", argv[1], archive_format_name(ar), archive_entry_pathname(aren), archive_entry_size(aren), archive_entry_nlink(aren), archive_entry_uname(aren), archive_entry_uid(aren)); hardlink = archive_entry_hardlink(aren); symlink = archive_entry_symlink(aren); if(hardlink != NULL){ printf(", hardlink: %s", hardlink); } if(symlink != NULL){ printf(", symlink: %s", symlink); } putchar('\n'); } archive_read_close(ar); archive_read_free(ar); #endif #ifdef ENABLE_STAGE_2 puts("==> Stage 2: Displaying"); ar = archive_read_new(); archive_read_support_filter_all(ar); archive_read_support_format_all(ar); arnum = archive_read_open_filename(ar, argv[1], 16384); if(arnum != ARCHIVE_OK){ fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1], archive_error_string(ar)); return 2; } while(archive_read_next_header(ar, &aren) == ARCHIVE_OK){ printf("<<< %s >>>\n", archive_entry_pathname(aren)); for(;;){ size_t size; off_t offset; const void* buffer; switch(archive_read_data_block(ar, &buffer, &size, &offset)){ case ARCHIVE_OK: puts(":: Block reading succeeded"); fwrite(buffer, size, 1, stdout); break; case ARCHIVE_WARN: puts(":: Block reading succeeded, warning exists"); fwrite(buffer, size, 1, stdout); break; case ARCHIVE_EOF: goto loop_outside; case ARCHIVE_RETRY: puts(":: Block reading failed, retrying"); break; case ARCHIVE_FATAL: puts(":: Fatal error! STOP!"); return 2; } } loop_outside: puts("@@ Extract OK @@"); } archive_read_close(ar); archive_read_free(ar); #endif #ifdef ENABLE_STAGE_3 puts("==> Stage 3: Extracting"); struct archive* arext; ar = archive_read_new(); archive_read_support_format_all(ar); archive_read_support_filter_all(ar); arext = archive_write_disk_new(); archive_write_disk_set_options(arext, ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR ); archive_write_disk_set_standard_lookup(arext); if(archive_read_open_filename(ar, argv[1], 16384)){ fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1], archive_error_string(ar)); return 3; } while((arnum = archive_read_next_header(ar, &aren)) == ARCHIVE_OK){ int filesize, accsize; printf("<<< %s >>>\n", archive_entry_pathname(aren)); if(archive_write_header(arext, aren) != ARCHIVE_OK){ puts(":: Write header not OK ..."); }else if((filesize = archive_entry_size(aren)) > 0){ accsize = 0; for(;;){ size_t size; off_t offset; const void* buffer; arnum = archive_read_data_block(ar, &buffer, &size, &offset); if(arnum != ARCHIVE_OK){ break; } arnum = archive_write_data(arext, buffer, size); if(arnum >= 0){ accsize += arnum; printf(":: %d of %d bytes written\n", accsize, filesize); } } } if(archive_write_finish_entry(arext) != ARCHIVE_OK){ return 3; } } archive_read_close(ar); archive_read_free(ar); archive_write_close(arext); archive_write_free(arext); #endif return 0; }
/* * Verify our ability to read sample files created by Solaris pax for * a sparse file. */ static void test_compat_solaris_pax_sparse_1(void) { char name[] = "test_compat_solaris_pax_sparse_1.pax.Z"; struct archive_entry *ae; struct archive *a; int64_t offset, length; const void *buff; size_t bytes_read; char data[1024*8]; int r; assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); extract_reference_file(name); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240)); /* Read first entry. */ assertEqualIntA(a, ARCHIVE_OK, r = archive_read_next_header(a, &ae)); if (r != ARCHIVE_OK) { archive_read_free(a); return; } assertEqualString("hole", archive_entry_pathname(ae)); assertEqualInt(1310411683, archive_entry_mtime(ae)); assertEqualInt(101, archive_entry_uid(ae)); assertEqualString("cue", archive_entry_uname(ae)); assertEqualInt(10, archive_entry_gid(ae)); assertEqualString("staff", archive_entry_gname(ae)); assertEqualInt(0100644, archive_entry_mode(ae)); /* Verify the sparse information. */ failure("This sparse file should have tree data blocks"); assertEqualInt(3, archive_entry_sparse_reset(ae)); assertEqualInt(ARCHIVE_OK, archive_entry_sparse_next(ae, &offset, &length)); assertEqualInt(0, offset); assertEqualInt(131072, length); assertEqualInt(ARCHIVE_OK, archive_entry_sparse_next(ae, &offset, &length)); assertEqualInt(393216, offset); assertEqualInt(131072, length); assertEqualInt(ARCHIVE_OK, archive_entry_sparse_next(ae, &offset, &length)); assertEqualInt(786432, offset); assertEqualInt(32775, length); while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read, &offset)) { failure("The data blocks should not include the hole"); assert((offset >= 0 && offset + bytes_read <= 131072) || (offset >= 393216 && offset + bytes_read <= 393216+131072) || (offset >= 786432 && offset + bytes_read <= 786432+32775)); if (offset == 0 && bytes_read >= 1024*8) { memset(data, 'a', sizeof(data)); failure("First data block should be 8K bytes of 'a'"); assertEqualMem(buff, data, sizeof(data)); } else if (offset + bytes_read == 819207 && bytes_read >= 7) { const char *last = buff; last += bytes_read - 7; memset(data, 'c', 7); failure("Last seven bytes should be all 'c'"); assertEqualMem(last, data, 7); } } /* Verify the end-of-archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify that the format detection worked. */ assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_COMPRESS); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }