Move package unpacking functionality to generic pkg_unpack function

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
This commit is contained in:
David Oberhollenzer 2019-01-30 12:59:20 +01:00
parent be9bfed9b0
commit 35fabedb49
5 changed files with 195 additions and 185 deletions

View File

@ -35,7 +35,7 @@ pkg_SOURCES += main/cmd/dump/dump_header.c
# unpack command
pkg_SOURCES += main/cmd/unpack/unpack.c main/cmd/unpack/unpack.h
pkg_SOURCES += main/cmd/unpack/create_hierarchy.c
pkg_SOURCES += main/cmd/unpack/pkg_unpack.c
# help command
pkg_SOURCES += main/cmd/help.c

View File

@ -1,27 +0,0 @@
#include "unpack.h"
int create_hierarchy(int dirfd, image_entry_t *list)
{
image_entry_t *ent;
for (ent = list; ent != NULL; ent = ent->next) {
if (S_ISDIR(ent->mode)) {
if (mkdirat(dirfd, ent->name, 0755)) {
perror(ent->name);
return -1;
}
}
}
for (ent = list; ent != NULL; ent = ent->next) {
if (S_ISLNK(ent->mode)) {
if (symlinkat(ent->data.symlink.target,
dirfd, ent->name)) {
perror(ent->name);
return -1;
}
}
}
return 0;
}

View File

@ -0,0 +1,190 @@
#include "unpack.h"
static int create_hierarchy(int dirfd, image_entry_t *list)
{
image_entry_t *ent;
for (ent = list; ent != NULL; ent = ent->next) {
if (S_ISDIR(ent->mode)) {
if (mkdirat(dirfd, ent->name, 0755)) {
perror(ent->name);
return -1;
}
}
}
for (ent = list; ent != NULL; ent = ent->next) {
if (S_ISLNK(ent->mode)) {
if (symlinkat(ent->data.symlink.target,
dirfd, ent->name)) {
perror(ent->name);
return -1;
}
}
}
return 0;
}
static image_entry_t *get_file_entry(image_entry_t *list, uint32_t id)
{
while (list != NULL) {
if (S_ISREG(list->mode) && list->data.file.id == id)
return list;
list = list->next;
}
return NULL;
}
static int unpack_files(int dirfd, image_entry_t *list, pkg_reader_t *rd)
{
uint8_t buffer[2048];
image_entry_t *meta;
file_data_t frec;
ssize_t ret;
size_t diff;
uint64_t i;
int fd;
for (;;) {
ret = pkg_reader_read_payload(rd, &frec, sizeof(frec));
if (ret == 0)
break;
if (ret < 0)
return -1;
if ((size_t)ret < sizeof(frec))
goto fail_trunc;
frec.id = le32toh(frec.id);
meta = get_file_entry(list, frec.id);
if (meta == NULL) {
fprintf(stderr, "%s: missing meta information for "
"file %u\n", pkg_reader_get_filename(rd),
(unsigned int)frec.id);
return -1;
}
fd = openat(dirfd, meta->name, O_WRONLY | O_CREAT | O_EXCL,
0644);
if (fd < 0) {
perror(meta->name);
return -1;
}
for (i = 0; i < meta->data.file.size; i += ret) {
if ((meta->data.file.size - i) <
(uint64_t)sizeof(buffer)) {
diff = meta->data.file.size - i;
} else {
diff = sizeof(buffer);
}
ret = pkg_reader_read_payload(rd, buffer, diff);
if (ret < 0)
goto fail_fd;
if ((size_t)ret < diff)
goto fail_trunc;
ret = write_retry(fd, buffer, diff);
if (ret < 0) {
perror(meta->name);
goto fail_fd;
}
if ((size_t)ret < diff) {
fprintf(stderr, "%s: truncated write\n",
pkg_reader_get_filename(rd));
goto fail_fd;
}
}
close(fd);
}
return 0;
fail_fd:
close(fd);
return -1;
fail_trunc:
fprintf(stderr, "%s: truncated file data record\n",
pkg_reader_get_filename(rd));
return -1;
}
static int change_permissions(int dirfd, image_entry_t *list, int flags)
{
while (list != NULL) {
if (S_ISLNK(list->mode)) {
list = list->next;
continue;
}
if (!(flags & FLAG_NO_CHMOD) && !S_ISLNK(list->mode)) {
if (fchmodat(dirfd, list->name,
list->mode & 07777, 0)) {
fprintf(stderr, "%s: chmod: %s\n", list->name,
strerror(errno));
return -1;
}
}
if (!(flags & FLAG_NO_CHOWN)) {
if (fchownat(dirfd, list->name, list->uid, list->gid,
AT_SYMLINK_NOFOLLOW)) {
fprintf(stderr, "%s: chown: %s\n", list->name,
strerror(errno));
return -1;
}
}
list = list->next;
}
return 0;
}
int pkg_unpack(int rootfd, int flags, pkg_reader_t *rd)
{
image_entry_t *list = NULL;
record_t *hdr;
int ret;
list = image_entry_list_from_package(rd);
if (list == NULL)
return -1;
list = image_entry_sort(list);
if (pkg_reader_rewind(rd))
goto fail;
if (create_hierarchy(rootfd, list))
goto fail;
for (;;) {
ret = pkg_reader_get_next_record(rd);
if (ret == 0)
break;
if (ret < 0)
goto fail;
hdr = pkg_reader_current_record_header(rd);
if (hdr->magic == PKG_MAGIC_DATA) {
if (unpack_files(rootfd, list, rd))
goto fail;
}
}
if (change_permissions(rootfd, list, flags))
goto fail;
image_entry_free_list(list);
return 0;
fail:
image_entry_free_list(list);
return -1;
}

View File

@ -10,133 +10,11 @@ static const struct option long_opts[] = {
static const char *short_opts = "r:om";
static image_entry_t *get_file_entry(image_entry_t *list, uint32_t id)
{
while (list != NULL) {
if (S_ISREG(list->mode) && list->data.file.id == id)
return list;
list = list->next;
}
return NULL;
}
static int unpack_files(int dirfd, image_entry_t *list, pkg_reader_t *rd)
{
uint8_t buffer[2048];
image_entry_t *meta;
file_data_t frec;
ssize_t ret;
size_t diff;
uint64_t i;
int fd;
for (;;) {
ret = pkg_reader_read_payload(rd, &frec, sizeof(frec));
if (ret == 0)
break;
if (ret < 0)
return -1;
if ((size_t)ret < sizeof(frec))
goto fail_trunc;
frec.id = le32toh(frec.id);
meta = get_file_entry(list, frec.id);
if (meta == NULL) {
fprintf(stderr, "%s: missing meta information for "
"file %u\n", pkg_reader_get_filename(rd),
(unsigned int)frec.id);
return -1;
}
fd = openat(dirfd, meta->name, O_WRONLY | O_CREAT | O_EXCL,
0644);
if (fd < 0) {
perror(meta->name);
return -1;
}
for (i = 0; i < meta->data.file.size; i += ret) {
if ((meta->data.file.size - i) <
(uint64_t)sizeof(buffer)) {
diff = meta->data.file.size - i;
} else {
diff = sizeof(buffer);
}
ret = pkg_reader_read_payload(rd, buffer, diff);
if (ret < 0)
goto fail_fd;
if ((size_t)ret < diff)
goto fail_trunc;
ret = write_retry(fd, buffer, diff);
if (ret < 0) {
perror(meta->name);
goto fail_fd;
}
if ((size_t)ret < diff) {
fprintf(stderr, "%s: truncated write\n",
pkg_reader_get_filename(rd));
goto fail_fd;
}
}
close(fd);
}
return 0;
fail_fd:
close(fd);
return -1;
fail_trunc:
fprintf(stderr, "%s: truncated file data record\n",
pkg_reader_get_filename(rd));
return -1;
}
static int change_permissions(int dirfd, image_entry_t *list, int flags)
{
while (list != NULL) {
if (S_ISLNK(list->mode)) {
list = list->next;
continue;
}
if (!(flags & FLAG_NO_CHMOD) && !S_ISLNK(list->mode)) {
if (fchmodat(dirfd, list->name,
list->mode & 07777, 0)) {
fprintf(stderr, "%s: chmod: %s\n", list->name,
strerror(errno));
return -1;
}
}
if (!(flags & FLAG_NO_CHOWN)) {
if (fchownat(dirfd, list->name, list->uid, list->gid,
AT_SYMLINK_NOFOLLOW)) {
fprintf(stderr, "%s: chown: %s\n", list->name,
strerror(errno));
return -1;
}
}
list = list->next;
}
return 0;
}
static int cmd_unpack(int argc, char **argv)
{
const char *root = NULL, *filename;
int i, rootfd, ret, flags = 0;
image_entry_t *list = NULL;
int i, rootfd, flags = 0;
pkg_reader_t *rd;
record_t *hdr;
for (;;) {
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
@ -186,45 +64,14 @@ static int cmd_unpack(int argc, char **argv)
if (rd == NULL)
goto fail_rootfd;
list = image_entry_list_from_package(rd);
if (list == NULL) {
pkg_reader_close(rd);
goto fail_rootfd;
}
list = image_entry_sort(list);
if (pkg_reader_rewind(rd))
if (pkg_unpack(rootfd, flags, rd))
goto fail;
if (create_hierarchy(rootfd, list))
goto fail;
for (;;) {
ret = pkg_reader_get_next_record(rd);
if (ret == 0)
break;
if (ret < 0)
goto fail;
hdr = pkg_reader_current_record_header(rd);
if (hdr->magic == PKG_MAGIC_DATA) {
if (unpack_files(rootfd, list, rd))
goto fail;
}
}
if (change_permissions(rootfd, list, flags))
goto fail;
image_entry_free_list(list);
pkg_reader_close(rd);
if (rootfd != AT_FDCWD)
close(rootfd);
return EXIT_SUCCESS;
fail:
image_entry_free_list(list);
pkg_reader_close(rd);
fail_rootfd:
if (rootfd != AT_FDCWD)

View File

@ -22,8 +22,8 @@ enum {
FLAG_NO_CHMOD = 0x02,
};
int create_hierarchy(int dirfd, image_entry_t *list);
int mkdir_p(const char *path);
int pkg_unpack(int rootfd, int flags, pkg_reader_t *rd);
#endif /* UNPACK_H */