From 16bf7172fe11f7b77346f19b47a2df65468e4daa Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Mon, 4 Feb 2019 17:43:24 +0100 Subject: [PATCH] Add support for device special files in packages Signed-off-by: David Oberhollenzer --- include/filelist/image_entry.h | 4 +++ include/pkgformat.h | 4 +++ include/pkgio.h | 1 + lib/filelist/dump_toc.c | 53 +++++++++++++++++++++++++++++++--- main/cmd/install/install.c | 6 +++- main/cmd/pack/filelist.c | 44 ++++++++++++++++++++++++++++ main/cmd/pack/pack.h | 1 + main/cmd/pack/write_toc.c | 10 +++++++ main/cmd/unpack.c | 6 +++- main/pkg_unpack.c | 45 ++++++++++++++++++++++++----- main/pkgio_rd_image_entry.c | 14 +++++++++ 11 files changed, 174 insertions(+), 14 deletions(-) diff --git a/include/filelist/image_entry.h b/include/filelist/image_entry.h index e2d4e67..d8ab6c2 100644 --- a/include/filelist/image_entry.h +++ b/include/filelist/image_entry.h @@ -21,6 +21,10 @@ typedef struct image_entry_t { struct { char *target; } symlink; + + struct { + dev_t devno; + } device; } data; } image_entry_t; diff --git a/include/pkgformat.h b/include/pkgformat.h index 416ef18..1da153a 100644 --- a/include/pkgformat.h +++ b/include/pkgformat.h @@ -53,6 +53,10 @@ typedef struct { uint32_t id; } toc_file_extra_t; +typedef struct { + uint64_t devno; +} toc_device_extra_t; + typedef struct { uint32_t id; /* uint8_t data[]; */ diff --git a/include/pkgio.h b/include/pkgio.h index 6c00cb8..2f5a455 100644 --- a/include/pkgio.h +++ b/include/pkgio.h @@ -8,6 +8,7 @@ enum { UNPACK_NO_CHOWN = 0x01, UNPACK_NO_CHMOD = 0x02, UNPACK_NO_SYMLINKS = 0x04, + UNPACK_NO_DEVICES = 0x08, }; int pkg_unpack(int rootfd, int flags, pkg_reader_t *rd); diff --git a/lib/filelist/dump_toc.c b/lib/filelist/dump_toc.c index cfa5bb4..88600ba 100644 --- a/lib/filelist/dump_toc.c +++ b/lib/filelist/dump_toc.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -31,6 +32,18 @@ static int print_pretty(image_entry_t *ent, const char *root) case S_IFDIR: fputs("directory\n\n", stdout); break; + case S_IFCHR: + fputs("character device\n", stdout); + printf("\tmajor: %u\n\tminor: %u\n\n", + major(ent->data.device.devno), + minor(ent->data.device.devno)); + break; + case S_IFBLK: + fputs("block device\n", stdout); + printf("\tmajor: %u\n\tminor: %u\n\n", + major(ent->data.device.devno), + minor(ent->data.device.devno)); + break; default: fputs("unknown\n\n", stdout); goto fail_type; @@ -46,12 +59,32 @@ static int print_sqfs(image_entry_t *ent, const char *root) mode_t mode = ent->mode & (~S_IFMT); (void)root; - if ((ent->mode & S_IFMT) == S_IFLNK) + switch (ent->mode & S_IFMT) { + case S_IFCHR: + printf("%s c %o %u %u %u %u\n", ent->name, mode, + (unsigned int)ent->uid, (unsigned int)ent->gid, + major(ent->data.device.devno), + minor(ent->data.device.devno)); + break; + case S_IFBLK: + printf("%s b %o %u %u %u %u\n", ent->name, mode, + (unsigned int)ent->uid, (unsigned int)ent->gid, + major(ent->data.device.devno), + minor(ent->data.device.devno)); + break; + case S_IFLNK: mode = 0777; + /* fall-through */ + case S_IFDIR: + case S_IFREG: + printf("%s m %o %u %u\n", ent->name, mode, + (unsigned int)ent->uid, (unsigned int)ent->gid); + break; + default: + fputs("unknown file type in table of contents\n", stderr); + return -1; + } - printf("%s m %o %u %u\n", ent->name, mode, - (unsigned int)ent->uid, - (unsigned int)ent->gid); return 0; } @@ -60,6 +93,18 @@ static int print_initrd(image_entry_t *ent, const char *root) mode_t mode = ent->mode & (~S_IFMT); switch (ent->mode & S_IFMT) { + case S_IFCHR: + printf("nod /%s 0%o %u %u c %u %u\n", ent->name, mode, + (unsigned int)ent->uid, (unsigned int)ent->gid, + major(ent->data.device.devno), + minor(ent->data.device.devno)); + return 0; + case S_IFBLK: + printf("nod /%s 0%o %u %u b %u %u\n", ent->name, mode, + (unsigned int)ent->uid, (unsigned int)ent->gid, + major(ent->data.device.devno), + minor(ent->data.device.devno)); + return 0; case S_IFLNK: printf("slink /%s %s", ent->name, ent->data.symlink.target); mode = 0777; diff --git a/main/cmd/install/install.c b/main/cmd/install/install.c index 350dfc8..16eac7b 100644 --- a/main/cmd/install/install.c +++ b/main/cmd/install/install.c @@ -10,10 +10,11 @@ static const struct option long_opts[] = { { "list-files", no_argument, NULL, 'l' }, { "format", required_argument, NULL, 'F' }, { "no-symlinks", no_argument, NULL, 'L' }, + { "no-devices", no_argument, NULL, 'D' }, { NULL, 0, NULL, 0 }, }; -static const char *short_opts = "r:omdR:plF:L"; +static const char *short_opts = "r:omdR:plF:LD"; static int unpack_packages(int repofd, int rootfd, int flags, struct pkg_dep_list *list) @@ -154,6 +155,9 @@ static int cmd_install(int argc, char **argv) case 'L': flags |= UNPACK_NO_SYMLINKS; break; + case 'D': + flags |= UNPACK_NO_DEVICES; + break; default: tell_read_help(argv[0]); goto out; diff --git a/main/cmd/pack/filelist.c b/main/cmd/pack/filelist.c index 619320c..e4dbb8d 100644 --- a/main/cmd/pack/filelist.c +++ b/main/cmd/pack/filelist.c @@ -206,10 +206,54 @@ fail: return -1; } +static int filelist_mknod(input_file_t *f, void *obj) +{ + image_entry_t **listptr = obj, *ent; + unsigned int maj, min; + char *ptr; + + ent = filelist_mkentry(f, S_IFCHR); + if (ent == NULL) + return -1; + + switch (f->line[0]) { + case 'c': + case 'C': + break; + case 'b': + case 'B': + ent->mode = (ent->mode & (~S_IFMT)) | S_IFBLK; + break; + default: + goto fail; + } + + if (!isspace(f->line[1])) + goto fail; + + ptr = f->line + 1; + while (isspace(*ptr)) + ++ptr; + + if (sscanf(ptr, "%u %u", &maj, &min) != 2) + goto fail; + + ent->data.device.devno = makedev(maj, min); + + ent->next = *listptr; + *listptr = ent; + return 0; +fail: + image_entry_free(ent); + input_file_complain(f, "error in device specification"); + return -1; +} + static const keyword_handler_t line_hooks[] = { { "file", filelist_mkfile }, { "dir", filelist_mkdir }, { "slink", filelist_mkslink }, + { "nod", filelist_mknod }, }; #define NUM_LINE_HOOKS (sizeof(line_hooks) / sizeof(line_hooks[0])) diff --git a/main/cmd/pack/pack.h b/main/cmd/pack/pack.h index fc64881..ea7854f 100644 --- a/main/cmd/pack/pack.h +++ b/main/cmd/pack/pack.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/main/cmd/pack/write_toc.c b/main/cmd/pack/write_toc.c index 924e846..8a1cfcc 100644 --- a/main/cmd/pack/write_toc.c +++ b/main/cmd/pack/write_toc.c @@ -48,6 +48,16 @@ static int write_entry(pkg_writer_t *wr, image_entry_t *it) } case S_IFDIR: break; + case S_IFBLK: + case S_IFCHR: { + toc_device_extra_t dev; + + dev.devno = htole64(it->data.device.devno); + + if (pkg_writer_write_payload(wr, &dev, sizeof(dev))) + return -1; + break; + } default: assert(0); } diff --git a/main/cmd/unpack.c b/main/cmd/unpack.c index d579a9d..00172b5 100644 --- a/main/cmd/unpack.c +++ b/main/cmd/unpack.c @@ -15,10 +15,11 @@ static const struct option long_opts[] = { { "no-chown", no_argument, NULL, 'o' }, { "no-chmod", no_argument, NULL, 'm' }, { "no-symlinks", no_argument, NULL, 'L' }, + { "no-devices", no_argument, NULL, 'D' }, { NULL, 0, NULL, 0 }, }; -static const char *short_opts = "r:omL"; +static const char *short_opts = "r:omLD"; static int cmd_unpack(int argc, char **argv) { @@ -38,6 +39,9 @@ static int cmd_unpack(int argc, char **argv) case 'L': flags |= UNPACK_NO_SYMLINKS; break; + case 'D': + flags |= UNPACK_NO_DEVICES; + break; case 'o': flags |= UNPACK_NO_CHOWN; break; diff --git a/main/pkg_unpack.c b/main/pkg_unpack.c index c4238ac..cbfba55 100644 --- a/main/pkg_unpack.c +++ b/main/pkg_unpack.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -40,6 +41,20 @@ static int create_hierarchy(int dirfd, image_entry_t *list, int flags) } } + for (ent = list; ent != NULL; ent = ent->next) { + if (S_ISBLK(ent->mode) || S_ISCHR(ent->mode)) { + if (flags & UNPACK_NO_DEVICES) + continue; + + if (mknodat(dirfd, ent->name, ent->mode, + ent->data.device.devno)) { + fprintf(stderr, "mknod %s: %s\n", + ent->name, strerror(errno)); + return -1; + } + } + } + return 0; } @@ -133,13 +148,29 @@ fail_trunc: static int change_permissions(int dirfd, image_entry_t *list, int flags) { - while (list != NULL) { - if (S_ISLNK(list->mode)) { - list = list->next; - continue; + bool do_chmod, do_chown; + + for (; list != NULL; list = list->next) { + do_chmod = (flags & UNPACK_NO_CHMOD) == 0; + do_chown = (flags & UNPACK_NO_CHOWN) == 0; + + switch (list->mode & S_IFMT) { + case S_IFLNK: + if (flags & UNPACK_NO_SYMLINKS) + continue; + do_chmod = false; + break; + case S_IFBLK: + case S_IFCHR: + if (flags & UNPACK_NO_DEVICES) + continue; + do_chmod = false; + break; + default: + break; } - if (!(flags & UNPACK_NO_CHMOD) && !S_ISLNK(list->mode)) { + if (do_chmod) { if (fchmodat(dirfd, list->name, list->mode & 07777, 0)) { fprintf(stderr, "%s: chmod: %s\n", list->name, @@ -148,7 +179,7 @@ static int change_permissions(int dirfd, image_entry_t *list, int flags) } } - if (!(flags & UNPACK_NO_CHOWN)) { + if (do_chown) { if (fchownat(dirfd, list->name, list->uid, list->gid, AT_SYMLINK_NOFOLLOW)) { fprintf(stderr, "%s: chown: %s\n", list->name, @@ -156,8 +187,6 @@ static int change_permissions(int dirfd, image_entry_t *list, int flags) return -1; } } - - list = list->next; } return 0; diff --git a/main/pkgio_rd_image_entry.c b/main/pkgio_rd_image_entry.c index 6c94b12..76e063d 100644 --- a/main/pkgio_rd_image_entry.c +++ b/main/pkgio_rd_image_entry.c @@ -52,6 +52,20 @@ static int read_extra(pkg_reader_t *pkg, image_entry_t *ent) } case S_IFDIR: break; + case S_IFBLK: + case S_IFCHR: { + toc_device_extra_t extra; + int ret; + + ret = pkg_reader_read_payload(pkg, &extra, sizeof(extra)); + if (ret < 0) + return -1; + if ((size_t)ret < sizeof(extra)) + goto fail_trunc; + + ent->data.device.devno = le64toh(extra.devno); + break; + } default: goto fail_unknown; }