int packing_append_file_attr(struct packing *pack, const char *filepath, const char *newpath, const char *uname, const char *gname, mode_t perm, u_long fflags) { int fd; int retcode = EPKG_OK; int ret; time_t source_time; struct stat st; struct archive_entry *entry, *sparse_entry; bool unset_timestamp; const char *source_date_epoch; char buf[32768]; int len; entry = archive_entry_new(); archive_entry_copy_sourcepath(entry, filepath); pkg_debug(2, "Packing file '%s'", filepath); if (lstat(filepath, &st) != 0) { pkg_emit_errno("lstat", filepath); retcode = EPKG_FATAL; goto cleanup; } ret = archive_read_disk_entry_from_file(pack->aread, entry, -1, &st); if (ret != ARCHIVE_OK) { pkg_emit_error("%s: %s", filepath, archive_error_string(pack->aread)); retcode = EPKG_FATAL; goto cleanup; } if (newpath != NULL) archive_entry_set_pathname(entry, newpath); if (archive_entry_filetype(entry) != AE_IFREG) { archive_entry_set_size(entry, 0); } if (uname != NULL && uname[0] != '\0') { archive_entry_set_uname(entry, uname); } if (gname != NULL && gname[0] != '\0') { archive_entry_set_gname(entry, gname); } if (fflags > 0) archive_entry_set_fflags(entry, fflags, 0); if (perm != 0) archive_entry_set_perm(entry, perm); unset_timestamp = pkg_object_bool(pkg_config_get("UNSET_TIMESTAMP")); if (unset_timestamp) { archive_entry_unset_atime(entry); archive_entry_unset_ctime(entry); archive_entry_unset_mtime(entry); archive_entry_unset_birthtime(entry); } if ((source_date_epoch = getenv("SOURCE_DATE_EPOCH")) != NULL) { if (source_date_epoch[strspn(source_date_epoch, "0123456789")] != '\0') { pkg_emit_error("Bad environment variable " "SOURCE_DATE_EPOCH: %s", source_date_epoch); retcode = EPKG_FATAL; goto cleanup; } source_time = strtoll(source_date_epoch, NULL, 10); archive_entry_set_atime(entry, source_time, 0); archive_entry_set_ctime(entry, source_time, 0); archive_entry_set_mtime(entry, source_time, 0); archive_entry_set_birthtime(entry, source_time, 0); } archive_entry_linkify(pack->resolver, &entry, &sparse_entry); if (sparse_entry != NULL && entry == NULL) entry = sparse_entry; archive_write_header(pack->awrite, entry); if (archive_entry_size(entry) <= 0) goto cleanup; if ((fd = open(filepath, O_RDONLY)) < 0) { pkg_emit_errno("open", filepath); retcode = EPKG_FATAL; goto cleanup; } while ((len = read(fd, buf, sizeof(buf))) > 0) { if (archive_write_data(pack->awrite, buf, len) == -1) { pkg_emit_errno("archive_write_data", "archive write error"); retcode = EPKG_FATAL; break; } } if (len == -1) { pkg_emit_errno("read", "file read error"); retcode = EPKG_FATAL; } close(fd); cleanup: archive_entry_free(entry); return (retcode); }
int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const char *path, *name; struct stat s; int initial_fd = fd; int r, r1; archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (a->tree == NULL) { if (st == NULL) { #if HAVE_FSTAT if (fd >= 0) { if (fstat(fd, &s) != 0) { archive_set_error(&a->archive, errno, "Can't fstat"); return (ARCHIVE_FAILED); } } else #endif #if HAVE_LSTAT if (!a->follow_symlinks) { if (lstat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't lstat %s", path); return (ARCHIVE_FAILED); } } else #endif if (stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); } st = &s; } archive_entry_copy_stat(entry, st); } /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ if (st->st_flags != 0) archive_entry_set_fflags(entry, st->st_flags, 0); #endif #if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) /* Linux requires an extra ioctl to pull the flags. Although * this is an extra step, it has a nice side-effect: We get an * open file descriptor which we can use in the subsequent lookups. */ if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { if (fd < 0) { if (a->tree != NULL) fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); } if (fd >= 0) { int stflags; r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); } } #endif #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) if (S_ISLNK(st->st_mode)) { size_t linkbuffer_len = st->st_size + 1; char *linkbuffer; int lnklen; linkbuffer = malloc(linkbuffer_len); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't read link data"); return (ARCHIVE_FAILED); } if (a->tree != NULL) { #ifdef HAVE_READLINKAT lnklen = readlinkat(a->tree_current_dir_fd(a->tree), path, linkbuffer, linkbuffer_len); #else if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } lnklen = readlink(path, linkbuffer, linkbuffer_len); #endif /* HAVE_READLINKAT */ } else lnklen = readlink(path, linkbuffer, linkbuffer_len); if (lnklen < 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } linkbuffer[lnklen] = 0; archive_entry_set_symlink(entry, linkbuffer); free(linkbuffer); } #endif /* HAVE_READLINK || HAVE_READLINKAT */ r = setup_acls(a, entry, &fd); r1 = setup_xattrs(a, entry, &fd); if (r1 < r) r = r1; if (a->enable_copyfile) { r1 = setup_mac_metadata(a, entry, &fd); if (r1 < r) r = r1; } r1 = setup_sparse(a, entry, &fd); if (r1 < r) r = r1; /* If we opened the file earlier in this function, close it. */ if (initial_fd != fd) close(fd); return (r); }
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)); }
int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const char *path, *name; struct stat s; int initial_fd = fd; int r, r1; archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); #ifdef EXT2_IOC_GETFLAGS /* Linux requires an extra ioctl to pull the flags. Although * this is an extra step, it has a nice side-effect: We get an * open file descriptor which we can use in the subsequent lookups. */ if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { if (fd < 0) fd = open(pathname, O_RDONLY | O_NONBLOCK | O_BINARY); if (fd >= 0) { unsigned long stflags; int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); } } #endif if (st == NULL) { /* TODO: On Windows, use GetFileInfoByHandle() here. * Using Windows stat() call is badly broken, but * even the stat() wrapper has problems because * 'struct stat' is broken on Windows. */ #if HAVE_FSTAT if (fd >= 0) { if (fstat(fd, &s) != 0) { archive_set_error(&a->archive, errno, "Can't fstat"); return (ARCHIVE_FAILED); } } else #endif #if HAVE_LSTAT if (!a->follow_symlinks) { if (lstat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't lstat %s", path); return (ARCHIVE_FAILED); } } else #endif if (stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); } st = &s; } archive_entry_copy_stat(entry, st); /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ if (st->st_flags != 0) archive_entry_set_fflags(entry, st->st_flags, 0); #endif #ifdef HAVE_READLINK if (S_ISLNK(st->st_mode)) { char linkbuffer[PATH_MAX + 1]; int lnklen = readlink(path, linkbuffer, PATH_MAX); if (lnklen < 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); return (ARCHIVE_FAILED); } linkbuffer[lnklen] = 0; archive_entry_set_symlink(entry, linkbuffer); } #endif r = setup_acls_posix1e(a, entry, fd); r1 = setup_xattrs(a, entry, fd); if (r1 < r) r = r1; /* If we opened the file earlier in this function, close it. */ if (initial_fd != fd) close(fd); return (r); }
int packing_append_file_attr(struct packing *pack, const char *filepath, const char *newpath, const char *uname, const char *gname, mode_t perm, u_long fflags) { int fd; char *map; int retcode = EPKG_OK; int ret; struct stat st; struct archive_entry *entry, *sparse_entry; bool unset_timestamp; entry = archive_entry_new(); archive_entry_copy_sourcepath(entry, filepath); pkg_debug(2, "Packing file '%s'", filepath); if (lstat(filepath, &st) != 0) { pkg_emit_errno("lstat", filepath); retcode = EPKG_FATAL; goto cleanup; } ret = archive_read_disk_entry_from_file(pack->aread, entry, -1, &st); if (ret != ARCHIVE_OK) { pkg_emit_error("%s: %s", filepath, archive_error_string(pack->aread)); retcode = EPKG_FATAL; goto cleanup; } if (newpath != NULL) archive_entry_set_pathname(entry, newpath); if (archive_entry_filetype(entry) != AE_IFREG) { archive_entry_set_size(entry, 0); } if (uname != NULL && uname[0] != '\0') { if (pack->pass) { struct passwd* pw = getpwnam(uname); if (pw == NULL) { pkg_emit_error("Unknown user: '******'", uname); retcode = EPKG_FATAL; goto cleanup; } archive_entry_set_uid(entry, pw->pw_uid); } archive_entry_set_uname(entry, uname); } if (gname != NULL && gname[0] != '\0') { if (pack->pass) { struct group *gr = (getgrnam(gname)); if (gr == NULL) { pkg_emit_error("Unknown group: '%s'", gname); retcode = EPKG_FATAL; goto cleanup; } archive_entry_set_gid(entry, gr->gr_gid); } archive_entry_set_gname(entry, gname); } if (fflags > 0) archive_entry_set_fflags(entry, fflags, 0); if (perm != 0) archive_entry_set_perm(entry, perm); unset_timestamp = pkg_object_bool(pkg_config_get("UNSET_TIMESTAMP")); if (unset_timestamp) { archive_entry_unset_atime(entry); archive_entry_unset_ctime(entry); archive_entry_unset_mtime(entry); archive_entry_unset_birthtime(entry); } archive_entry_linkify(pack->resolver, &entry, &sparse_entry); if (sparse_entry != NULL && entry == NULL) entry = sparse_entry; archive_write_header(pack->awrite, entry); if (archive_entry_size(entry) > 0) { if ((fd = open(filepath, O_RDONLY)) < 0) { pkg_emit_errno("open", filepath); retcode = EPKG_FATAL; goto cleanup; } if (st.st_size > SSIZE_MAX) { char buf[BUFSIZ]; int len; while ((len = read(fd, buf, sizeof(buf))) > 0) if (archive_write_data(pack->awrite, buf, len) == -1) { pkg_emit_errno("archive_write_data", "archive write error"); retcode = EPKG_FATAL; break; } if (len == -1) { pkg_emit_errno("read", "file read error"); retcode = EPKG_FATAL; } close(fd); } else { if ((map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) != MAP_FAILED) { close(fd); if (archive_write_data(pack->awrite, map, st.st_size) == -1) { pkg_emit_errno("archive_write_data", "archive write error"); retcode = EPKG_FATAL; } munmap(map, st.st_size); } else { close(fd); pkg_emit_errno("open", filepath); retcode = EPKG_FATAL; goto cleanup; } } } cleanup: archive_entry_free(entry); return (retcode); }