From fb2443eda97b881818ced16b7885288752bdb8b4 Mon Sep 17 00:00:00 2001 From: Alexey Sheplyakov Date: Mon, 6 Jul 2020 13:41:42 +0400 Subject: [PATCH] mksquashfs can read uid/gid/file type from fakeroot(1) database With this patch I can easily pack chroots built in a fakeroot environment without running mksquashfs under fakeroot. Instead I can use `-fakerootdb .fakedata` to get ownership/permissions/device nodes right. It's possible to convert fakeroot database into mksquashfs' pseudo files definitions but using the fakeroot database directly is more efficient (fakeroot uses device/inode number to identify the object) and requires less code. --- squashfs-tools/Makefile | 3 +- squashfs-tools/fakerootdb.c | 89 +++++++++++++++++++++++++++++++++++++ squashfs-tools/fakerootdb.h | 16 +++++++ squashfs-tools/mksquashfs.c | 50 +++++++++++++++++++-- 4 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 squashfs-tools/fakerootdb.c create mode 100644 squashfs-tools/fakerootdb.h diff --git a/squashfs-tools/Makefile b/squashfs-tools/Makefile index 00b593bd..0719aeda 100644 --- a/squashfs-tools/Makefile +++ b/squashfs-tools/Makefile @@ -153,13 +153,14 @@ INSTALL_DIR = /usr/local/bin MKSQUASHFS_OBJS = mksquashfs.o read_fs.o action.o swap.o pseudo.o compressor.o \ sort.o progressbar.o info.o restore.o process_fragments.o \ + fakerootdb.o \ caches-queues-lists.o reader.o UNSQUASHFS_OBJS = unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o \ unsquash-4.o unsquash-123.o unsquash-34.o unsquash-1234.o swap.o \ compressor.o unsquashfs_info.o -CFLAGS ?= -O2 +CFLAGS ?= -O0 -g CFLAGS += $(EXTRA_CFLAGS) $(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 \ -D_LARGEFILE_SOURCE -D_GNU_SOURCE -DCOMP_DEFAULT=\"$(COMP_DEFAULT)\" \ -Wall diff --git a/squashfs-tools/fakerootdb.c b/squashfs-tools/fakerootdb.c new file mode 100644 index 00000000..5b319931 --- /dev/null +++ b/squashfs-tools/fakerootdb.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include "fakerootdb.h" + +#define FAKEROOT_ENTRY_FIELDS 7 +#define FAKEROOT_ENTRY_FMT "dev=%lx,ino=%lu,mode=%o,uid=%u,gid=%u,nlink=%lu,rdev=%lu" + +static int compare_by_dev_ino(const void *px, const void *py) +{ + struct stat const* const x = px; + struct stat const* const y = py; + + if (x->st_dev < y->st_dev) + return -1; + else if (x->st_dev > y->st_dev) + return 1; + else if (x->st_ino < y->st_ino) + return -1; + else if (x->st_ino > y->st_ino) + return 1; + else + return 0; +} + +int fakeroot_read_db(FILE *fakedata, struct fakerootdb *db) +{ + struct stat elt, *d; + int n; + if (!db) + return -EINVAL; + if (db->db) { + free(db->db); + db->db = NULL; + db->count = 0; + } + while (!feof(fakedata)) { + if (ferror(fakedata)) + return -EIO; + memset(&elt, 0, sizeof(elt)); + n = fscanf(fakedata, + FAKEROOT_ENTRY_FMT "\n", + &elt.st_dev, + &elt.st_ino, + &elt.st_mode, + &elt.st_uid, + &elt.st_gid, + &elt.st_nlink, + &elt.st_rdev); + if (n != FAKEROOT_ENTRY_FIELDS) + return -EINVAL; + + /* skip uid = gid = 0 entries, unless they are device nodes. + * fakeroot assumes uid = gid = 0 by default */ + if (elt.st_uid == 0 && elt.st_gid == 0 && elt.st_rdev == 0) + continue; + + d = realloc(db->db, (db->count + 1)*sizeof(elt)); + if (!d) + return -ENOMEM; + memcpy(&d[db->count], &elt, sizeof(elt)); + db->db = d; + db->count += 1; + } + qsort(db->db, db->count, sizeof(elt), compare_by_dev_ino); + return 0; +} + +void fakeroot_override_stat(struct stat *st, const struct fakerootdb *db) +{ + struct stat key; + struct stat const* o = NULL; + if (!db|| !db->db || db->count == 0) + return; + memset(&key, 0, sizeof(key)); + key.st_dev = st->st_dev; + key.st_ino = st->st_ino; + o = bsearch(&key, db->db, db->count, sizeof(key), compare_by_dev_ino); + if (o) { + st->st_mode = o->st_mode; + st->st_uid = o->st_uid; + st->st_gid = o->st_gid; + st->st_rdev = o->st_rdev; + } else { + /* fakeroot sets uid=gid=0 if the object is not in the DB */ + st->st_uid = 0; + st->st_gid = 0; + } +} diff --git a/squashfs-tools/fakerootdb.h b/squashfs-tools/fakerootdb.h new file mode 100644 index 00000000..1939e7b2 --- /dev/null +++ b/squashfs-tools/fakerootdb.h @@ -0,0 +1,16 @@ +#ifndef SQUASHFS_FAKEROOTDB_H +#define SQUASHFS_FAKEROOTDB_H +#include +#include +#include + +struct fakerootdb { + struct stat *db; + size_t count; +}; + +int fakeroot_read_db(FILE *fakedata, struct fakerootdb *db); + +void fakeroot_override_stat(struct stat *st, const struct fakerootdb *fakerootdb); + +#endif /* SQUASHFS_FAKEROOTDB_H */ diff --git a/squashfs-tools/mksquashfs.c b/squashfs-tools/mksquashfs.c index 46da0a8a..c933ff9d 100644 --- a/squashfs-tools/mksquashfs.c +++ b/squashfs-tools/mksquashfs.c @@ -72,6 +72,7 @@ #include "restore.h" #include "process_fragments.h" #include "fnmatch_compat.h" +#include "fakerootdb.h" int delete = FALSE; int quiet = FALSE; @@ -275,6 +276,9 @@ int no_hardlinks = FALSE; int one_file_system = FALSE; dev_t *source_dev; dev_t cur_dev; +/* Override owner/group/permissions/file type from fakeroot(1) database */ +struct fakerootdb fakerootdb; +char const *fakerootdb_filename = NULL; static char *read_from_disk(long long start, unsigned int avail_bytes); static void add_old_root_entry(char *name, squashfs_inode inode, @@ -308,6 +312,18 @@ static void check_usable_phys_mem(int total_mem); static void print_summary(); void write_destination(int fd, long long byte, long long bytes, void *buff); +static int lstat_with_fakeroot(const char *path, struct stat *stbuf) +{ + int err; + err = lstat(path, stbuf); + if (err < 0) + goto out; + if (!fakerootdb.db || fakerootdb.count == 0) + goto out; + fakeroot_override_stat(stbuf, &fakerootdb); +out: + return err; +} void prep_exit() { @@ -3350,7 +3366,7 @@ static squashfs_inode scan_single(char *pathname, int progress) * it to the root directory dir_info structure */ dir_ent = create_dir_entry("", NULL, pathname, scan1_opendir("", "", 0)); - if(lstat(pathname, &buf) == -1) + if(lstat_with_fakeroot(pathname, &buf) == -1) /* source directory has disappeared? */ BAD_ERROR("Cannot stat source directory %s because %s\n", pathname, strerror(errno)); @@ -3392,6 +3408,7 @@ static squashfs_inode scan_encomp(int progress) buf.st_mtime = time(NULL); buf.st_dev = 0; buf.st_ino = 0; + fakeroot_override_stat(&buf, &fakerootdb); dir_ent->inode = lookup_inode(&buf); dir_ent->inode->dummy_root_dir = TRUE; dir_ent->dir = root_dir; @@ -3593,7 +3610,7 @@ static struct dir_info *dir_scan1(char *filename, char *subpath, continue; } - if(lstat(filename, &buf) == -1) { + if(lstat_with_fakeroot(filename, &buf) == -1) { ERROR_START("Cannot stat dir/file %s because %s", filename, strerror(errno)); ERROR_EXIT(", ignoring\n"); @@ -4361,7 +4378,7 @@ static struct dir_info *add_source(struct dir_info *sdir, char *source, goto failed_early; } - res = lstat(file, &buf); + res = lstat_with_fakeroot(file, &buf); if (res == -1) { ERROR("Error: Can't stat %s because %s\n", file, strerror(errno)); goto failed_early; @@ -4708,13 +4725,14 @@ static squashfs_inode process_source(int progress) buf.st_uid = getuid(); buf.st_gid = getgid(); buf.st_mtime = time(NULL); + fakeroot_override_stat(&buf, &fakerootdb); entry = create_dir_entry("", NULL, "", new); entry->inode = lookup_inode(&buf); entry->inode->dummy_root_dir = TRUE; } else { char *pathname = absolute ? "/" : "."; - if(lstat(pathname, &buf) == -1) + if(lstat_with_fakeroot(pathname, &buf) == -1) BAD_ERROR("Cannot stat %s because %s\n", pathname, strerror(errno)); @@ -6576,6 +6594,12 @@ int main(int argc, char *argv[]) exit(1); } } + } else if(strcmp(argv[i], "-fakerootdb") == 0) { + if(++i == argc) { + ERROR("%s: -fakerootdb: missing filename\n", argv[0]); + exit(1); + } + fakerootdb_filename = argv[i]; } else if(strcmp(argv[i], "-noI") == 0 || strcmp(argv[i], "-noInodeCompression") == 0) noI = TRUE; @@ -6807,6 +6831,24 @@ int main(int argc, char *argv[]) strcmp(argv[i], "-log") == 0) i++; + if (fakerootdb_filename) { + int err; + FILE *fakedata = NULL; + fakedata = fopen(fakerootdb_filename, "r"); + if (!fakedata) { + ERROR("%s: -fakerootdb: failed to open fakeroot database %s\n", + argv[0], fakerootdb_filename); + EXIT_MKSQUASHFS(); + } + err = fakeroot_read_db(fakedata, &fakerootdb); + fclose(fakedata); + if (err) { + ERROR("%s: -fakerootdb: failed to read fakeroot database %s\n", + argv[0], fakerootdb_filename); + EXIT_MKSQUASHFS(); + } + } + if(!delete) { comp = read_super(fd, &sBlk, destination_file); if(comp == NULL) {