Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965001AbbKCUZy (ORCPT ); Tue, 3 Nov 2015 15:25:54 -0500 Received: from mga03.intel.com ([134.134.136.65]:54031 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S964952AbbKCUXU (ORCPT ); Tue, 3 Nov 2015 15:23:20 -0500 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.20,239,1444719600"; d="scan'208";a="677670498" From: Octavian Purdila To: linux-arch@vger.kernel.org Cc: linux-kernel@vger.kernel.org, thehajime@gmail.com, Octavian Purdila Subject: [RFC PATCH 23/28] lkl tools: tool that converts a filesystem image to tar Date: Tue, 3 Nov 2015 22:20:54 +0200 Message-Id: <1446582059-17355-24-git-send-email-octavian.purdila@intel.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1446582059-17355-1-git-send-email-octavian.purdila@intel.com> References: <1446582059-17355-1-git-send-email-octavian.purdila@intel.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 10395 Lines: 447 Simple utility that converts a filesystem image to a tar file, preserving file rights and extended attributes. Signed-off-by: Octavian Purdila --- tools/lkl/.gitignore | 1 + tools/lkl/Makefile | 3 + tools/lkl/fs2tar.c | 397 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 401 insertions(+) create mode 100644 tools/lkl/fs2tar.c diff --git a/tools/lkl/.gitignore b/tools/lkl/.gitignore index 7c456f2..a345a79 100644 --- a/tools/lkl/.gitignore +++ b/tools/lkl/.gitignore @@ -1 +1,2 @@ test/boot +fs2tar diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 1ae4481..9aeab49 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -9,6 +9,7 @@ endif lib_source = $(filter-out %-host.c,$(wildcard lib/*.c)) source = $(wildcard tests/*.c) ifneq (,$(filter $(shell $(LD) -r -print-output-format),elf64-x86-64 elf32-i386)) +source += $(wildcard *.c) lib_source += lib/posix-host.c LDFLAGS += -lpthread -lrt endif @@ -35,3 +36,5 @@ $(execs): lib/liblkl.a clean: -rm -rf include/lkl/ lib/liblkl.a $(lib_objs) $(objs) $(execs) + +fs2tar: LDFLAGS += -larchive diff --git a/tools/lkl/fs2tar.c b/tools/lkl/fs2tar.c new file mode 100644 index 0000000..b74da66 --- /dev/null +++ b/tools/lkl/fs2tar.c @@ -0,0 +1,397 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef st_atime +#undef st_mtime +#undef st_ctime +#include +#include + +char doc[] = ""; +char args_doc[] = "-t fstype fsimage_path tar_path"; +static struct argp_option options[] = { + {"enable-printk", 'p', 0, 0, "show Linux printks"}, + {"filesystem-type", 't', "string", 0, + "select filesystem type - mandatory"}, + {"selinux-contexts", 's', "file", 0, + "export sexlinux contexts to file"}, + {0}, +}; + +static struct cl_args { + int printk; + const char *fsimg_type; + const char *fsimg_path; + const char *tar_path; + FILE *selinux; +} cla; + +static error_t parse_opt(int key, char *arg, struct argp_state *state) +{ + struct cl_args *cla = state->input; + + switch (key) { + case 'p': + cla->printk = 1; + break; + case 't': + cla->fsimg_type = arg; + break; + case 's': + cla->selinux = fopen(arg, "w"); + if (!cla->selinux) { + fprintf(stderr, "failed to open selinux contexts file: %s\n", + strerror(errno)); + return -1; + } + break; + case ARGP_KEY_ARG: + if (!cla->fsimg_path) + cla->fsimg_path = arg; + else if (!cla->tar_path) + cla->tar_path = arg; + else + return -1; + break; + case ARGP_KEY_END: + if (state->arg_num < 2 || !cla->fsimg_type) + argp_usage(state); + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, doc }; + +static struct archive *tar; + +static int searchdir(const char *fsimg_path, const char *path); + +static int copy_file(const char *fsimg_path, const char *path) +{ + long fsimg_fd; + char buff[4096]; + long len, wrote; + int ret = 0; + + fsimg_fd = lkl_sys_open(fsimg_path, LKL_O_RDONLY, 0); + if (fsimg_fd < 0) { + fprintf(stderr, "fsimg error opening %s: %s\n", fsimg_path, + lkl_strerror(fsimg_fd)); + return fsimg_fd; + } + + do { + len = lkl_sys_read(fsimg_fd, buff, sizeof(buff)); + if (len > 0) { + wrote = archive_write_data(tar, buff, len); + if (wrote != len) { + fprintf(stderr, "error writing file %s to archive: %s [%d %ld]\n", + path, archive_error_string(tar), ret, + len); + ret = -archive_errno(tar); + break; + } + } + + if (len < 0) { + fprintf(stderr, "error reading fsimg file %s: %s\n", + fsimg_path, lkl_strerror(len)); + ret = len; + } + + } while (len > 0); + + lkl_sys_close(fsimg_fd); + + return ret; +} + +static int add_link(const char *fsimg_path, const char *path, + struct archive_entry *entry) +{ + char buf[4096] = { 0, }; + long len; + + len = lkl_sys_readlink(fsimg_path, buf, sizeof(buf)); + if (len < 0) { + fprintf(stderr, "fsimg readlink error %s: %s\n", + fsimg_path, lkl_strerror(len)); + return len; + } + + archive_entry_set_symlink(entry, buf); + + return 0; +} + +static inline void fsimg_copy_stat(struct stat *st, struct lkl_stat64 *fst) +{ + st->st_dev = fst->st_dev; + st->st_ino = fst->st_ino; + st->st_mode = fst->st_mode; + st->st_nlink = fst->st_nlink; + st->st_uid = fst->st_uid; + st->st_gid = fst->st_gid; + st->st_rdev = fst->st_rdev; + st->st_size = fst->st_size; + st->st_blksize = fst->st_blksize; + st->st_blocks = fst->st_blocks; + st->st_atim.tv_sec = fst->st_atime; + st->st_atim.tv_nsec = fst->st_atime_nsec; + st->st_mtim.tv_sec = fst->st_mtime; + st->st_mtim.tv_nsec = fst->st_mtime_nsec; + st->st_ctim.tv_sec = fst->st_ctime; + st->st_ctim.tv_nsec = fst->st_ctime_nsec; +} + +static int copy_xattr(const char *fsimg_path, const char *path, + struct archive_entry *entry) +{ + long ret; + char *xattr_list, *i; + long xattr_list_size; + + ret = lkl_sys_llistxattr(fsimg_path, NULL, 0); + if (ret < 0) { + fprintf(stderr, "fsimg llistxattr(%s) error: %s\n", + path, lkl_strerror(ret)); + return ret; + } + + if (!ret) + return 0; + + xattr_list = malloc(ret); + + ret = lkl_sys_llistxattr(fsimg_path, xattr_list, ret); + if (ret < 0) { + fprintf(stderr, "fsimg llistxattr(%s) error: %s\n", path, + lkl_strerror(ret)); + free(xattr_list); + return ret; + } + + xattr_list_size = ret; + + for (i = xattr_list; i - xattr_list < xattr_list_size; + i += strlen(i) + 1) { + void *xattr_buf; + + ret = lkl_sys_lgetxattr(fsimg_path, i, NULL, 0); + if (ret < 0) { + fprintf(stderr, "fsimg lgetxattr(%s) error: %s\n", path, + lkl_strerror(ret)); + free(xattr_list); + return ret; + } + + xattr_buf = malloc(ret); + + ret = lkl_sys_lgetxattr(fsimg_path, i, xattr_buf, ret); + if (ret < 0) { + fprintf(stderr, "fsimg lgetxattr2(%s) error: %s\n", + path, lkl_strerror(ret)); + free(xattr_list); + free(xattr_buf); + return ret; + } + + if (cla.selinux && strcmp(i, "security.selinux") == 0) + fprintf(cla.selinux, "%s %s\n", path, + (char *)xattr_buf); + + archive_entry_xattr_clear(entry); + archive_entry_xattr_add_entry(entry, i, xattr_buf, ret); + + free(xattr_buf); + } + + free(xattr_list); + + return 0; +} + +static int do_entry(const char *fsimg_path, const char *path, + const struct lkl_dirent64 *de) +{ + char fsimg_new_path[PATH_MAX], new_path[PATH_MAX]; + struct lkl_stat64 fsimg_stat; + struct stat stat; + struct archive_entry *entry; + int ftype; + long ret; + + snprintf(new_path, sizeof(new_path), "%s/%s", path, de->d_name); + snprintf(fsimg_new_path, sizeof(fsimg_new_path), "%s/%s", fsimg_path, + de->d_name); + + ret = lkl_sys_lstat64(fsimg_new_path, &fsimg_stat); + if (ret) { + fprintf(stderr, "fsimg fstat64(%s) error: %s\n", + path, lkl_strerror(ret)); + return ret; + } + + entry = archive_entry_new(); + + archive_entry_set_pathname(entry, new_path); + fsimg_copy_stat(&stat, &fsimg_stat); + archive_entry_copy_stat(entry, &stat); + ret = copy_xattr(fsimg_new_path, new_path, entry); + if (ret) + return ret; + /* TODO: ACLs */ + + ftype = stat.st_mode & S_IFMT; + + switch (ftype) { + case S_IFREG: + archive_write_header(tar, entry); + ret = copy_file(fsimg_new_path, new_path); + break; + case S_IFDIR: + archive_write_header(tar, entry); + ret = searchdir(fsimg_new_path, new_path); + break; + case S_IFLNK: + ret = add_link(fsimg_new_path, new_path, entry); + /* fall through */ + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + if (ret) + break; + archive_write_header(tar, entry); + break; + default: + printf("skipping %s: unsupported entry type %d\n", new_path, + ftype); + } + + archive_entry_free(entry); + + if (ret) + printf("error processing entry %s, aborting\n", new_path); + + return ret; +} + +static int searchdir(const char *fsimg_path, const char *path) +{ + long ret, fd; + char buf[1024], *pos; + long buf_len; + + fd = lkl_sys_open(fsimg_path, LKL_O_RDONLY | LKL_O_DIRECTORY, 0); + if (fd < 0) { + fprintf(stderr, "failed to open dir %s: %s", fsimg_path, + lkl_strerror(fd)); + return fd; + } + + do { + struct lkl_dirent64 *de; + + buf_len = lkl_sys_getdents64(fd, buf, sizeof(buf)); + if (buf_len < 0) { + fprintf(stderr, "gentdents64 error: %s\n", + lkl_strerror(buf_len)); + break; + } + + for (pos = buf; pos - buf < buf_len; pos += de->d_reclen) { + de = (struct lkl_dirent64 *)pos; + + if (!strcmp(de->d_name, ".") || + !strcmp(de->d_name, "..")) + continue; + + ret = do_entry(fsimg_path, path, de); + if (ret) + goto out; + } + + } while (buf_len > 0); + +out: + lkl_sys_close(fd); + return ret; +} + +int main(int argc, char **argv) +{ + union lkl_disk_backstore bs; + long ret; + char mpoint[32]; + unsigned int disk_id; + + if (argp_parse(&argp, argc, argv, 0, 0, &cla) < 0) + return -1; + + if (!cla.printk) + lkl_host_ops.print = NULL; + + bs.fd = open(cla.fsimg_path, O_RDONLY); + if (bs.fd < 0) { + fprintf(stderr, "can't open fsimg %s: %s\n", cla.fsimg_path, + strerror(errno)); + ret = 1; + goto out; + } + + disk_id = lkl_disk_add(bs); + if (disk_id < 0) { + fprintf(stderr, "can't add disk: %s\n", lkl_strerror(ret)); + goto out_close; + } + + lkl_start_kernel(&lkl_host_ops, 10 * 1024 * 1024, ""); + + ret = lkl_mount_dev(disk_id, cla.fsimg_type, LKL_MS_RDONLY, NULL, + mpoint, sizeof(mpoint)); + if (ret) { + fprintf(stderr, "can't mount disk: %s\n", lkl_strerror(ret)); + goto out_close; + } + + ret = lkl_sys_chdir(mpoint); + if (ret) { + fprintf(stderr, "can't chdir to %s: %s\n", mpoint, + lkl_strerror(ret)); + goto out_umount; + } + + tar = archive_write_new(); + archive_write_set_format_pax_restricted(tar); + archive_write_open_filename(tar, cla.tar_path); + + ret = searchdir(mpoint, ""); + + archive_write_free(tar); + + if (cla.selinux) + fclose(cla.selinux); + +out_umount: + lkl_umount_dev(disk_id, 0, 1000); + +out_close: + close(bs.fd); + +out: + lkl_sys_halt(); + + return ret; +} -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/