From d292ffc85f48dd9c8774a0860f50397dd2966e7a Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 18 Feb 2014 00:55:53 +0000 Subject: [PATCH] initial import of fs-tools package Signed-off-by: John Crispin --- CMakeLists.txt | 29 + backend/base.c | 150 +++++ backend/extroot.c | 101 ++++ backend/jffs2.c | 413 ++++++++++++++ backend/snapshot.c | 616 +++++++++++++++++++++ block.c | 1217 +++++++++++++++++++++++++++++++++++++++++ fs-state.c | 80 +++ fs-state.h | 80 +++ lib/find.c | 190 +++++++ lib/mount.c | 110 ++++ lib/mtd.c | 201 +++++++ lib/mtd.h | 28 + libblkid-tiny/bitops.h | 84 +++ libblkid-tiny/blkdev.h | 113 ++++ libblkid-tiny/blkid.h | 353 ++++++++++++ libblkid-tiny/blkidP.h | 421 ++++++++++++++ libblkid-tiny/c.h | 247 +++++++++ libblkid-tiny/ext.c | 531 ++++++++++++++++++ libblkid-tiny/jffs2.c | 35 ++ libblkid-tiny/libblkid-tiny.c | 217 ++++++++ libblkid-tiny/libblkid-tiny.h | 58 ++ libblkid-tiny/linux_version.h | 14 + libblkid-tiny/mkdev.c | 94 ++++ libblkid-tiny/squashfs.c | 87 +++ libblkid-tiny/superblocks.h | 92 ++++ libblkid-tiny/swap.c | 171 ++++++ libblkid-tiny/ubifs.c | 120 ++++ libblkid-tiny/vfat.c | 444 +++++++++++++++ 28 files changed, 6296 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 backend/base.c create mode 100644 backend/extroot.c create mode 100644 backend/jffs2.c create mode 100644 backend/snapshot.c create mode 100644 block.c create mode 100644 fs-state.c create mode 100644 fs-state.h create mode 100644 lib/find.c create mode 100644 lib/mount.c create mode 100644 lib/mtd.c create mode 100644 lib/mtd.h create mode 100644 libblkid-tiny/bitops.h create mode 100644 libblkid-tiny/blkdev.h create mode 100644 libblkid-tiny/blkid.h create mode 100644 libblkid-tiny/blkidP.h create mode 100644 libblkid-tiny/c.h create mode 100644 libblkid-tiny/ext.c create mode 100644 libblkid-tiny/jffs2.c create mode 100644 libblkid-tiny/libblkid-tiny.c create mode 100644 libblkid-tiny/libblkid-tiny.h create mode 100644 libblkid-tiny/linux_version.h create mode 100644 libblkid-tiny/mkdev.c create mode 100644 libblkid-tiny/squashfs.c create mode 100644 libblkid-tiny/superblocks.h create mode 100644 libblkid-tiny/swap.c create mode 100644 libblkid-tiny/ubifs.c create mode 100644 libblkid-tiny/vfat.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f371263 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(fs-tools C) +ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) + +SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") + +ADD_EXECUTABLE(fs-state fs-state.c + backend/base.c + backend/snapshot.c + backend/extroot.c + backend/jffs2.c + lib/mtd.c + lib/mount.c + lib/find.c) + +TARGET_LINK_LIBRARIES(fs-state ubox) +INSTALL(TARGETS fs-state RUNTIME DESTINATION sbin) + +ADD_EXECUTABLE(block block.c + libblkid-tiny/libblkid-tiny.c + libblkid-tiny/mkdev.c + libblkid-tiny/ext.c + libblkid-tiny/jffs2.c + libblkid-tiny/vfat.c + libblkid-tiny/swap.c + libblkid-tiny/squashfs.c) +TARGET_LINK_LIBRARIES(block uci ubox blobmsg_json) +INSTALL(TARGETS block RUNTIME DESTINATION sbin) diff --git a/backend/base.c b/backend/base.c new file mode 100644 index 0000000..ecb4320 --- /dev/null +++ b/backend/base.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "../lib/mtd.h" +#include "../fs-state.h" + +int +backend_mount(char *name) +{ + struct backend *b = find_backend(name); + + if (!b || !b->mount) + return -1; + + return b->mount(); +} + +static int +backend_info(char *name) +{ + struct backend *b = find_backend(name); + + if (!b || !b->info) + return -1; + + return b->info(); +} + +static int +start(int argc, char **argv) +{ + char mtd[32]; + + if (!getenv("PREINIT")) + return -1; + + if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) { + if (!find_mtd_char("rootfs", mtd, sizeof(mtd))) { + int fd = mtd_load(mtd); + if (fd > 0) + mtd_unlock(fd); + } + fprintf(stderr, "mounting /dev/root\n"); + mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0); + return 0; + } + + extroot_prefix = ""; + if (!backend_mount("extroot")) { + fprintf(stderr, "fs-state: switched to extroot\n"); + return 0; + } + + switch (mtd_identify(mtd)) { + case FS_NONE: + case FS_DEADCODE: + return ramoverlay(); + + case FS_JFFS2: + backend_mount("overlay"); + break; + + case FS_SNAPSHOT: + backend_mount("snapshot"); + break; + } + + return 0; +} + +static int +stop(int argc, char **argv) +{ + if (!getenv("SHUTDOWN")) + return -1; + + return 0; +} + +static int +done(int argc, char **argv) +{ + char mtd[32]; + + if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) + return -1; + + switch (mtd_identify(mtd)) { + case FS_NONE: + case FS_DEADCODE: + return jffs2_switch(argc, argv); + } + + return 0; +} + +static int +info(int argc, char **argv) +{ + char mtd[32]; + + if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) + return -1; + + switch (mtd_identify(mtd)) { + case FS_SNAPSHOT: + backend_info("snapshot"); + return 0; + } + + return 0; +} + +static struct backend start_backend = { + .name = "start", + .cli = start, +}; +BACKEND(start_backend); + +static struct backend stop_backend = { + .name = "stop", + .cli = stop, +}; +BACKEND(stop_backend); + +static struct backend done_backend = { + .name = "done", + .cli = done, +}; +BACKEND(done_backend); + +static struct backend info_backend = { + .name = "info", + .cli = info, +}; +BACKEND(info_backend); diff --git a/backend/extroot.c b/backend/extroot.c new file mode 100644 index 0000000..2ed9b37 --- /dev/null +++ b/backend/extroot.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fs-state.h" + +char const *extroot_prefix = NULL; + +static int mount_extroot(void) +{ + char block_path[32]; + char kmod_loader[64]; + struct stat s; + pid_t pid; + + if (!extroot_prefix) + return -1; + + sprintf(block_path, "%s/sbin/block", extroot_prefix); + + if (stat(block_path, &s)) + return -1; + + sprintf(kmod_loader, "/sbin/kmodloader %s/etc/modules-boot.d/ %s", extroot_prefix, extroot_prefix); + system(kmod_loader); + + pid = fork(); + if (!pid) { + mkdir("/tmp/extroot", 0755); + execl(block_path, block_path, "extroot", NULL); + exit(-1); + } else if (pid > 0) { + int status; + + waitpid(pid, &status, 0); + if (!WEXITSTATUS(status)) { + if (find_mount("/tmp/extroot/mnt")) { + mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT | MS_RDONLY, 0); + + mkdir("/tmp/extroot/mnt/proc", 0755); + mkdir("/tmp/extroot/mnt/dev", 0755); + mkdir("/tmp/extroot/mnt/sys", 0755); + mkdir("/tmp/extroot/mnt/tmp", 0755); + mkdir("/tmp/extroot/mnt/rom", 0755); + + if (mount_move("/tmp/extroot", "", "/mnt")) { + fprintf(stderr, "moving pivotroot failed - continue normal boot\n"); + umount("/tmp/extroot/mnt"); + } else if (pivot("/mnt", "/rom")) { + fprintf(stderr, "switching to pivotroot failed - continue normal boot\n"); + umount("/mnt"); + } else { + umount("/tmp/overlay"); + rmdir("/tmp/overlay"); + rmdir("/tmp/extroot/mnt"); + rmdir("/tmp/extroot"); + return 0; + } + } else if (find_mount("/tmp/extroot/overlay")) { + if (mount_move("/tmp/extroot", "", "/overlay")) { + fprintf(stderr, "moving extroot failed - continue normal boot\n"); + umount("/tmp/extroot/overlay"); + } else if (fopivot("/overlay", "/rom")) { + fprintf(stderr, "switching to extroot failed - continue normal boot\n"); + umount("/overlay"); + } else { + umount("/tmp/overlay"); + rmdir("/tmp/overlay"); + rmdir("/tmp/extroot/overlay"); + rmdir("/tmp/extroot"); + return 0; + } + } + } + } + return -1; +} + +static struct backend extroot_backend = { + .name = "extroot", + .mount = mount_extroot, +}; +BACKEND(extroot_backend); diff --git a/backend/jffs2.c b/backend/jffs2.c new file mode 100644 index 0000000..feb37d2 --- /dev/null +++ b/backend/jffs2.c @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fs-state.h" +#include "../lib/mtd.h" + +#define SWITCH_JFFS2 "/tmp/.switch_jffs2" + +void +foreachdir(const char *dir, int (*cb)(const char*)) +{ + char globdir[256]; + glob_t gl; + int j; + + if (dir[strlen(dir) - 1] == '/') + snprintf(globdir, 256, "%s*", dir); + else + snprintf(globdir, 256, "%s/*", dir); + + if (!glob(globdir, GLOB_NOESCAPE | GLOB_MARK | GLOB_ONLYDIR, NULL, &gl)) + for (j = 0; j < gl.gl_pathc; j++) + foreachdir(gl.gl_pathv[j], cb); + + cb(dir); +} + +static int +jffs2_mount(void) +{ + char rootfs_data[32]; + int fd; + + if (mkdir("/tmp/overlay", 0755)) { + fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno)); + return -1; + } + + if (find_mtd_block("rootfs_data", rootfs_data, sizeof(rootfs_data))) { + fprintf(stderr, "rootfs_data does not exist\n"); + return -1; + } + + if (mount(rootfs_data, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) { + fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n", rootfs_data, strerror(errno)); + return -1; + } + + find_mtd_char("rootfs_data", rootfs_data, sizeof(rootfs_data)); + + fd = mtd_load(rootfs_data); + if (fd > 0) { + int ret = mtd_unlock(fd); + close(fd); + return ret; + } + + return -1; +} + +static int +switch2jffs(void) +{ + struct stat s; + char mtd[32]; + int ret; + + if (!stat(SWITCH_JFFS2, &s)) { + fprintf(stderr, "jffs2 switch already running\n"); + return -1; + } + + if (!find_mtd_block("rootfs_patches", mtd, sizeof(mtd))) + return 0; + + if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) { + fprintf(stderr, "no rootfs_data was found\n"); + return -1; + } + + creat("/tmp/.switch_jffs2", 0600); + ret = mount(mtd, "/rom/overlay", "jffs2", MS_NOATIME, NULL); + unlink("/tmp/.switch_jffs2"); + if (ret) { + fprintf(stderr, "failed - mount -t jffs2 %s /rom/overlay: %s\n", mtd, strerror(errno)); + return -1; + } + + if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) { + fprintf(stderr, "failed - mount -o remount,ro none: %s\n", strerror(errno)); + return -1; + } + + system("cp -a /tmp/root/* /rom/overlay"); + + if (pivot("/rom", "/mnt")) { + fprintf(stderr, "failed - pivot /rom /mnt: %s\n", strerror(errno)); + return -1; + } + + if (mount_move("/mnt", "/tmp/root", "")) { + fprintf(stderr, "failed - mount -o move /mnt /tmp/root %s\n", strerror(errno)); + return -1; + } + + return fopivot("/overlay", "/rom"); +} + +int +handle_whiteout(const char *dir) +{ + struct stat s; + char link[256]; + ssize_t sz; + struct dirent **namelist; + int n; + + n = scandir(dir, &namelist, NULL, NULL); + + if (n < 1) + return -1; + + while (n--) { + char file[256]; + + snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name); + if (!lstat(file, &s) && S_ISLNK(s.st_mode)) { + sz = readlink(file, link, sizeof(link) - 1); + if (sz > 0) { + char *orig; + + link[sz] = '\0'; + orig = strstr(&file[1], "/"); + if (orig && !strcmp(link, "(overlay-whiteout)")) + unlink(orig); + } + } + free(namelist[n]); + } + free(namelist); + + return 0; +} + +static int +ask_user(int argc, char **argv) +{ + if ((argc < 2) || strcmp(argv[1], "-y")) { + fprintf(stderr, "This will erase all settings and remove any installed packages. Are you sure? [N/y]\n"); + if (getchar() != 'y') + return -1; + } + return 0; + +} + +static int +handle_rmdir(const char *dir) +{ + struct stat s; + struct dirent **namelist; + int n; + + n = scandir(dir, &namelist, NULL, NULL); + + if (n < 1) + return -1; + + while (n--) { + char file[256]; + + snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name); + if (!lstat(file, &s) && !S_ISDIR(s.st_mode)) + unlink(file); + free(namelist[n]); + } + free(namelist); + + rmdir(dir); + + return 0; +} + +static int +jffs2_reset(int argc, char **argv) +{ + char mtd[32]; + char *mp; + + if (ask_user(argc, argv)) + return -1; + + if (find_filesystem("overlay")) { + fprintf(stderr, "overlayfs not found\n"); + return -1; + } + + if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) { + fprintf(stderr, "no rootfs_data was found\n"); + return -1; + } + + mp = find_mount_point(mtd, "jffs2"); + if (mp) { + fprintf(stderr, "%s is mounted as %s, only erasing files\n", mtd, mp); + foreachdir(mp, handle_rmdir); + mount(mp, "/", NULL, MS_REMOUNT, 0); + } else { + int fd; + fprintf(stderr, "%s is not mounted, erasing it\n", mtd); + find_mtd_char("rootfs_data", mtd, sizeof(mtd)); + fd = mtd_load(mtd); + if (fd > 0) { + mtd_erase(fd, 0, mtdsize / erasesize); + close(fd); + } + } + + return 0; +} + +static int +jffs2_mark(int argc, char **argv) +{ + FILE *fp; + __u32 deadc0de = __cpu_to_be32(0xdeadc0de); + char mtd[32]; + size_t sz; + + if (ask_user(argc, argv)) + return -1; + + if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) { + fprintf(stderr, "no rootfs_data was found\n"); + return -1; + } + + fp = fopen(mtd, "w"); + fprintf(stderr, "%s - marking with deadc0de\n", mtd); + if (!fp) { + fprintf(stderr, "opening %s failed\n", mtd); + return -1; + } + + sz = fwrite(&deadc0de, sizeof(deadc0de), 1, fp); + fclose(fp); + + if (sz != 1) { + fprintf(stderr, "writing %s failed: %s\n", mtd, strerror(errno)); + return -1; + } + + return 0; +} + +int +jffs2_switch(int argc, char **argv) +{ + char mtd[32]; + char *mp; + int ret = -1; + + if (find_overlay_mount("overlayfs:/tmp/root")) + return -1; + + if (find_filesystem("overlay")) { + fprintf(stderr, "overlayfs not found\n"); + return ret; + } + + find_mtd_block("rootfs_data", mtd, sizeof(mtd)); + mp = find_mount_point(mtd, NULL); + if (mp) { + fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", mtd, mp); + return -1; + } + + if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) { + fprintf(stderr, "no rootfs_data was found\n"); + return ret; + } + + switch (mtd_identify(mtd)) { + case FS_NONE: + fprintf(stderr, "no jffs2 marker found\n"); + /* fall through */ + + case FS_DEADCODE: + ret = switch2jffs(); + if (!ret) { + fprintf(stderr, "doing fo cleanup\n"); + umount2("/tmp/root", MNT_DETACH); + foreachdir("/overlay/", handle_whiteout); + } + break; + + case FS_JFFS2: + ret = jffs2_mount(); + if (ret) + break; + if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { + fprintf(stderr, "switching to jffs2 failed\n"); + ret = -1; + } + break; + } + + return ret; +} + +static int mtd_mount_jffs2(void) +{ + char rootfs_data[32]; + int fd; + + if (mkdir("/tmp/overlay", 0755)) { + fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno)); + return -1; + } + + if (find_mtd_block("rootfs_data", rootfs_data, sizeof(rootfs_data))) { + fprintf(stderr, "rootfs_data does not exist\n"); + return -1; + } + + if (mount(rootfs_data, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) { + fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n", rootfs_data, strerror(errno)); + return -1; + } + + find_mtd_char("rootfs_data", rootfs_data, sizeof(rootfs_data)); + + fd = mtd_load(rootfs_data); + if (fd) { + int ret = mtd_unlock(fd); + close(fd); + return ret; + } + + return -1; +} + +static int overlay_mount(void) +{ + char mtd[32]; + char *mp; + + find_mtd_block("rootfs_data", mtd, sizeof(mtd)); + mp = find_mount_point(mtd, NULL); + if (mp) { + fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", mtd, mp); + return -1; + } + + mtd_mount_jffs2(); + + extroot_prefix = "/tmp/overlay"; + if (!backend_mount("extroot")) { + fprintf(stderr, "fs-state: switched to extroot\n"); + return 0; + } + + fprintf(stderr, "switching to jffs2\n"); + if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { + fprintf(stderr, "switching to jffs2 failed - fallback to ramoverlay\n"); + return ramoverlay(); + } + + return -1; +} + +static struct backend_handler jffs2_handlers[] = { +{ + .name = "jffs2reset", + .cli = jffs2_reset, +}, { + .name = "jffs2mark", + .cli = jffs2_mark, +}}; + +static struct backend overlay_backend = { + .name = "overlay", + .num_handlers = ARRAY_SIZE(jffs2_handlers), + .handlers = jffs2_handlers, + .mount = overlay_mount, +}; +BACKEND(overlay_backend); diff --git a/backend/snapshot.c b/backend/snapshot.c new file mode 100644 index 0000000..34b9c5b --- /dev/null +++ b/backend/snapshot.c @@ -0,0 +1,616 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../fs-state.h" +#include "../lib/mtd.h" + +#define PATH_MAX 256 +#define OWRT 0x4f575254 +#define DATA 0x44415441 +#define CONF 0x434f4e46 + +struct file_header { + uint32_t magic; + uint32_t type; + uint32_t seq; + uint32_t length; + uint32_t md5[4]; +}; + +static inline int +is_config(struct file_header *h) +{ + return ((h->magic == OWRT) && (h->type == CONF)); +} + +static inline int +valid_file_size(int fs) +{ + if ((fs > 8 * 1024 * 1204) || (fs <= 0)) + return -1; + + return 0; +} + +static void +hdr_to_be32(struct file_header *hdr) +{ + uint32_t *h = (uint32_t *) hdr; + int i; + + for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++) + h[i] = cpu_to_be32(h[i]); +} + +static void +be32_to_hdr(struct file_header *hdr) +{ + uint32_t *h = (uint32_t *) hdr; + int i; + + for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++) + h[i] = be32_to_cpu(h[i]); +} + +static int +pad_file_size(int size) +{ + int mod; + + size += sizeof(struct file_header); + mod = size % erasesize; + if (mod) { + size -= mod; + size += erasesize; + } + + return size; +} + +static int +verify_file_hash(char *file, uint32_t *hash) +{ + uint32_t md5[4]; + + if (md5sum(file, md5)) { + fprintf(stderr, "failed to generate md5 sum\n"); + return -1; + } + + if (memcmp(md5, hash, sizeof(md5))) { + fprintf(stderr, "failed to verify hash of %s.\n", file); + return -1; + } + + return 0; +} + +static int +snapshot_next_free(int fd, uint32_t *seq) +{ + struct file_header hdr = { 0 }; + int block = 0; + + *seq = rand(); + + do { + if (mtd_read_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) { + fprintf(stderr, "scanning for next free block failed\n"); + return 0; + } + + be32_to_hdr(&hdr); + + if (hdr.magic != OWRT) + break; + + if (hdr.type == DATA && !valid_file_size(hdr.length)) { + if (*seq + 1 != hdr.seq && block) + return block; + *seq = hdr.seq; + block += pad_file_size(hdr.length) / erasesize; + } + } while (hdr.type == DATA); + + return block; +} + +static int +config_find(int fd, struct file_header *conf, struct file_header *sentinel) +{ + uint32_t seq; + int i, next = snapshot_next_free(fd, &seq); + + conf->magic = sentinel->magic = 0; + + if (!mtd_read_buffer(fd, conf, next, sizeof(*conf))) + be32_to_hdr(conf); + + for (i = (mtdsize / erasesize) - 1; i > 0; i--) { + if (mtd_read_buffer(fd, sentinel, i * erasesize, sizeof(*sentinel))) { + fprintf(stderr, "failed to read header\n"); + return -1; + } + be32_to_hdr(sentinel); + + if (sentinel->magic == OWRT && sentinel->type == CONF && !valid_file_size(sentinel->length)) { + if (next == i) + return -1; + return i; + } + } + + return -1; +} + +static int +snapshot_info(void) +{ + int fd = mtd_load("rootfs_data"); + struct file_header hdr = { 0 }, conf; + int block = 0; + + if (fd < 1) + return -1; + + fprintf(stderr, "sectors:\t%d, erasesize:\t%dK\n", mtdsize / erasesize, erasesize / 1024); + do { + if (mtd_read_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) { + fprintf(stderr, "scanning for next free block failed\n"); + close(fd); + return 0; + } + + be32_to_hdr(&hdr); + + if (hdr.magic != OWRT) + break; + + if (hdr.type == DATA) + fprintf(stderr, "block %d:\tsnapshot entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(hdr.length) / erasesize, hdr.seq); + else if (hdr.type == CONF) + fprintf(stderr, "block %d:\tvolatile entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(hdr.length) / erasesize, hdr.seq); + + if (hdr.type == DATA && !valid_file_size(hdr.length)) + block += pad_file_size(hdr.length) / erasesize; + } while (hdr.type == DATA); + block = config_find(fd, &conf, &hdr); + if (block > 0) + fprintf(stderr, "block %d:\tsentinel entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(hdr.length) / erasesize, hdr.seq); + close(fd); + return 0; +} + +static int +snapshot_write_file(int fd, int block, char *file, uint32_t seq, uint32_t type) +{ + uint32_t md5[4] = { 0 }; + struct file_header hdr; + struct stat s; + char buffer[256]; + int in = 0, len, offset; + int ret = -1; + + if (stat(file, &s) || md5sum(file, md5)) { + fprintf(stderr, "stat failed on %s\n", file); + goto out; + } + + if ((block * erasesize) + pad_file_size(s.st_size) > mtdsize) { + fprintf(stderr, "upgrade is too big for the flash\n"); + goto out; + } + mtd_erase(fd, block, (pad_file_size(s.st_size) / erasesize)); + mtd_erase(fd, block + (pad_file_size(s.st_size) / erasesize), 1); + + hdr.length = s.st_size; + hdr.magic = OWRT; + hdr.type = type; + hdr.seq = seq; + memcpy(hdr.md5, md5, sizeof(md5)); + hdr_to_be32(&hdr); + + if (mtd_write_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) { + fprintf(stderr, "failed to write header\n"); + goto out; + } + + in = open(file, O_RDONLY); + if (in < 1) { + fprintf(stderr, "failed to open %s\n", file); + goto out; + } + + offset = (block * erasesize) + sizeof(struct file_header); + + while ((len = read(in, buffer, sizeof(buffer))) > 0) { + if (mtd_write_buffer(fd, buffer, offset, len) < 0) + goto out; + offset += len; + } + + ret = 0; + +out: + if (in > 0) + close(in); + + return ret; +} + +static int +snapshot_read_file(int fd, int block, char *file, uint32_t type) +{ + struct file_header hdr; + char buffer[256]; + int out; + + if (mtd_read_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) { + fprintf(stderr, "failed to read header\n"); + return -1; + } + be32_to_hdr(&hdr); + + if (hdr.magic != OWRT) + return -1; + + if (hdr.type != type) + return -1; + + if (valid_file_size(hdr.length)) + return -1; + + out = open(file, O_WRONLY | O_CREAT, 0700); + if (!out) { + fprintf(stderr, "failed to open %s\n", file); + return -1; + } + + while (hdr.length > 0) { + int len = sizeof(buffer); + + if (hdr.length < len) + len = hdr.length; + + if ((read(fd, buffer, len) != len) || (write(out, buffer, len) != len)) { + return -1; + } + + hdr.length -= len; + } + + close(out); + + if (verify_file_hash(file, hdr.md5)) { + fprintf(stderr, "md5 verification failed\n"); + unlink(file); + return 0; + } + + block += pad_file_size(hdr.length) / erasesize; + + return block; +} + +static int +sentinel_write(int fd, uint32_t _seq) +{ + int ret, block; + struct stat s; + uint32_t seq; + + if (stat("/tmp/config.tar.gz", &s)) { + fprintf(stderr, "failed to stat /tmp/config.tar.gz\n"); + return -1; + } + + snapshot_next_free(fd, &seq); + if (_seq) + seq = _seq; + block = mtdsize / erasesize; + block -= pad_file_size(s.st_size) / erasesize; + if (block < 0) + block = 0; + + ret = snapshot_write_file(fd, block, "/tmp/config.tar.gz", seq, CONF); + if (ret) + fprintf(stderr, "failed to write sentinel\n"); + else + fprintf(stderr, "wrote /tmp/config.tar.gz sentinel\n"); + return ret; +} + +static int +volatile_write(int fd, uint32_t _seq) +{ + int block, ret; + uint32_t seq; + + block = snapshot_next_free(fd, &seq); + if (_seq) + seq = _seq; + if (block < 0) + block = 0; + + ret = snapshot_write_file(fd, block, "/tmp/config.tar.gz", seq, CONF); + if (ret) + fprintf(stderr, "failed to write /tmp/config.tar.gz\n"); + else + fprintf(stderr, "wrote /tmp/config.tar.gz\n"); + return ret; +} + +static int +config_write(int argc, char **argv) +{ + int fd, ret; + + fd = mtd_load("rootfs_data"); + if (fd < 1) { + fprintf(stderr, "failed to open rootfs_config\n"); + return -1; + } + + ret = volatile_write(fd, 0); + if (!ret) + ret = sentinel_write(fd, 0); + + close(fd); + + return ret; +} + +static int +config_read(int argc, char **argv) +{ + struct file_header conf, sentinel; + int fd, next, block, ret = 0; + uint32_t seq; + + fd = mtd_load("rootfs_data"); + if (fd < 1) { + fprintf(stderr, "failed to open rootfs_data\n"); + return -1; + } + + block = config_find(fd, &conf, &sentinel); + next = snapshot_next_free(fd, &seq); + if (is_config(&conf) && conf.seq == seq) + block = next; + else if (!is_config(&sentinel) || sentinel.seq != seq) + return -1; + + unlink("/tmp/config.tar.gz"); + ret = snapshot_read_file(fd, block, "/tmp/config.tar.gz", CONF); + + if (ret < 1) + fprintf(stderr, "failed to read /tmp/config.tar.gz\n"); + close(fd); + return ret; +} + +static int +snapshot_write(int argc, char **argv) +{ + int mtd, block, ret; + uint32_t seq; + + mtd = mtd_load("rootfs_data"); + if (mtd < 1) { + fprintf(stderr, "failed to open rootfs_data\n"); + return -1; + } + + block = snapshot_next_free(mtd, &seq); + if (block < 0) + block = 0; + + ret = snapshot_write_file(mtd, block, "/tmp/snapshot.tar.gz", seq + 1, DATA); + if (ret) + fprintf(stderr, "failed to write /tmp/snapshot.tar.gz\n"); + else + fprintf(stderr, "wrote /tmp/snapshot.tar.gz\n"); + + close(mtd); + + return ret; +} + +static int +snapshot_mark(int argc, char **argv) +{ + FILE *fp; + __be32 owrt = cpu_to_be32(OWRT); + char mtd[32]; + size_t sz; + + fprintf(stderr, "This will remove all snapshot data stored on the system. Are you sure? [N/y]\n"); + if (getchar() != 'y') + return -1; + + if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) { + fprintf(stderr, "no rootfs_data was found\n"); + return -1; + } + + fp = fopen(mtd, "w"); + fprintf(stderr, "%s - marking with 0x4f575254\n", mtd); + if (!fp) { + fprintf(stderr, "opening %s failed\n", mtd); + return -1; + } + + sz = fwrite(&owrt, sizeof(owrt), 1, fp); + fclose(fp); + + if (sz != 1) { + fprintf(stderr, "writing %s failed: %s\n", mtd, strerror(errno)); + return -1; + } + + return 0; +} + +static int +snapshot_read(int argc, char **argv) +{ + char file[64]; + int block = 0, fd, ret = 0; + + fd = mtd_load("rootfs_data"); + if (fd < 1) { + fprintf(stderr, "failed to open rootfs_data\n"); + return -1; + } + + if (argc > 1) { + block = atoi(argv[1]); + if (block >= (mtdsize / erasesize)) { + fprintf(stderr, "invalid block %d > %d\n", block, mtdsize / erasesize); + goto out; + } + snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block); + + ret = snapshot_read_file(fd, block, file, DATA); + goto out; + } + + do { + snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block); + block = snapshot_read_file(fd, block, file, DATA); + } while (block > 0); + +out: + close(fd); + return ret; +} + +static int +snapshot_sync(void) +{ + int fd = mtd_load("rootfs_data"); + struct file_header sentinel, conf; + int next, block = 0; + uint32_t seq; + + if (fd < 1) + return -1; + + next = snapshot_next_free(fd, &seq); + block = config_find(fd, &conf, &sentinel); + if (is_config(&conf) && conf.seq != seq) { + conf.magic = 0; + mtd_erase(fd, next, 2); + } + + if (is_config(&sentinel) && (sentinel.seq != seq)) { + sentinel.magic = 0; + mtd_erase(fd, block, 1); + } + + if (!is_config(&conf) && !is_config(&sentinel)) { + // fprintf(stderr, "no config found\n"); + } else if (((is_config(&conf) && is_config(&sentinel)) && + (memcmp(conf.md5, sentinel.md5, sizeof(conf.md5)) || (conf.seq != sentinel.seq))) || + (is_config(&conf) && !is_config(&sentinel))) { + uint32_t seq; + int next = snapshot_next_free(fd, &seq); + int ret = snapshot_read_file(fd, next, "/tmp/config.tar.gz", CONF); + if (ret > 0) { + if (sentinel_write(fd, conf.seq)) + fprintf(stderr, "failed to write sentinel data"); + } + } else if (!is_config(&conf) && is_config(&sentinel) && next) { + int ret = snapshot_read_file(fd, block, "/tmp/config.tar.gz", CONF); + if (ret > 0) + if (volatile_write(fd, sentinel.seq)) + fprintf(stderr, "failed to write sentinel data"); + } else + fprintf(stderr, "config in sync\n"); + + unlink("/tmp/config.tar.gz"); + close(fd); + + return 0; +} + +static int +_ramoverlay(char *rom, char *overlay) +{ + mount("tmpfs", overlay, "tmpfs", MS_NOATIME, "mode=0755"); + return fopivot(overlay, rom); +} + +static int +snapshot_mount(void) +{ + snapshot_sync(); + setenv("SNAPSHOT", "magic", 1); + _ramoverlay("/rom", "/overlay"); + system("/sbin/snapshot unpack"); + foreachdir("/overlay/", handle_whiteout); + mkdir("/volatile", 0700); + _ramoverlay("/rom", "/volatile"); + mount_move("/rom/volatile", "/volatile", ""); + mount_move("/rom/rom", "/rom", ""); + system("/sbin/snapshot config_unpack"); + foreachdir("/volatile/", handle_whiteout); + unsetenv("SNAPSHOT"); + return -1; +} + +static struct backend_handler snapshot_handlers[] = { +{ + .name = "config_read", + .cli = config_read, +}, { + .name = "config_write", + .cli = config_write, +}, { + .name = "read", + .cli = snapshot_read, +}, { + .name = "write", + .cli = snapshot_write, +}, { + .name = "mark", + .cli = snapshot_mark, +}}; + +static struct backend snapshot_backend = { + .name = "snapshot", + .num_handlers = ARRAY_SIZE(snapshot_handlers), + .handlers = snapshot_handlers, + .mount = snapshot_mount, + .info = snapshot_info, +}; +BACKEND(snapshot_backend); diff --git a/block.c b/block.c new file mode 100644 index 0000000..9122ebf --- /dev/null +++ b/block.c @@ -0,0 +1,1217 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "libblkid-tiny/libblkid-tiny.h" + +#define ERROR(fmt, ...) do { \ + syslog(LOG_ERR, fmt, ## __VA_ARGS__); \ + fprintf(stderr, "block: "fmt, ## __VA_ARGS__); \ + } while (0) + +enum { + TYPE_MOUNT, + TYPE_SWAP, +}; + +struct mount { + struct vlist_node node; + int type; + + char *target; + char *path; + char *options; + uint32_t flags; + char *uuid; + char *label; + char *device; + int extroot; + int overlay; + int disabled_fsck; + unsigned int prio; +}; + +static struct vlist_tree mounts; +static struct blob_buf b; +static LIST_HEAD(devices); +static int anon_mount, anon_swap, auto_mount, auto_swap, check_fs; +static unsigned int delay_root; + +enum { + CFG_ANON_MOUNT, + CFG_ANON_SWAP, + CFG_AUTO_MOUNT, + CFG_AUTO_SWAP, + CFG_DELAY_ROOT, + CFG_CHECK_FS, + __CFG_MAX +}; + +static const struct blobmsg_policy config_policy[__CFG_MAX] = { + [CFG_ANON_SWAP] = { .name = "anon_swap", .type = BLOBMSG_TYPE_INT32 }, + [CFG_ANON_MOUNT] = { .name = "anon_mount", .type = BLOBMSG_TYPE_INT32 }, + [CFG_AUTO_SWAP] = { .name = "auto_swap", .type = BLOBMSG_TYPE_INT32 }, + [CFG_AUTO_MOUNT] = { .name = "auto_mount", .type = BLOBMSG_TYPE_INT32 }, + [CFG_DELAY_ROOT] = { .name = "delay_root", .type = BLOBMSG_TYPE_INT32 }, + [CFG_CHECK_FS] = { .name = "check_fs", .type = BLOBMSG_TYPE_INT32 }, +}; + +enum { + MOUNT_UUID, + MOUNT_LABEL, + MOUNT_ENABLE, + MOUNT_TARGET, + MOUNT_DEVICE, + MOUNT_OPTIONS, + __MOUNT_MAX +}; + +static const struct uci_blob_param_list config_attr_list = { + .n_params = __CFG_MAX, + .params = config_policy, +}; + +static const struct blobmsg_policy mount_policy[__MOUNT_MAX] = { + [MOUNT_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING }, + [MOUNT_LABEL] = { .name = "label", .type = BLOBMSG_TYPE_STRING }, + [MOUNT_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING }, + [MOUNT_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING }, + [MOUNT_OPTIONS] = { .name = "options", .type = BLOBMSG_TYPE_STRING }, + [MOUNT_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 }, +}; + +static const struct uci_blob_param_list mount_attr_list = { + .n_params = __MOUNT_MAX, + .params = mount_policy, +}; + +enum { + SWAP_ENABLE, + SWAP_UUID, + SWAP_LABEL, + SWAP_DEVICE, + SWAP_PRIO, + __SWAP_MAX +}; + +static const struct blobmsg_policy swap_policy[__SWAP_MAX] = { + [SWAP_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 }, + [SWAP_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING }, + [SWAP_LABEL] = { .name = "label", .type = BLOBMSG_TYPE_STRING }, + [SWAP_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING }, + [SWAP_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 }, +}; + +static const struct uci_blob_param_list swap_attr_list = { + .n_params = __SWAP_MAX, + .params = swap_policy, +}; + +struct mount_flag { + const char *name; + int32_t flag; +}; + +#ifndef MS_DIRSYNC +# define MS_DIRSYNC (1 << 7) +#endif + +#ifndef MS_RELATIME +# define MS_RELATIME (1 << 21) +#endif + +#ifndef MS_STRICTATIME +# define MS_STRICTATIME (1 << 24) +#endif + +static const struct mount_flag mount_flags[] = { + { "sync", MS_SYNCHRONOUS }, + { "async", ~MS_SYNCHRONOUS }, + { "dirsync", MS_DIRSYNC }, + { "mand", MS_MANDLOCK }, + { "nomand", ~MS_MANDLOCK }, + { "atime", ~MS_NOATIME }, + { "noatime", MS_NOATIME }, + { "dev", ~MS_NODEV }, + { "nodev", MS_NODEV }, + { "diratime", ~MS_NODIRATIME }, + { "nodiratime", MS_NODIRATIME }, + { "exec", ~MS_NOEXEC }, + { "noexec", MS_NOEXEC }, + { "suid", ~MS_NOSUID }, + { "nosuid", MS_NOSUID }, + { "rw", ~MS_RDONLY }, + { "ro", MS_RDONLY }, + { "relatime", MS_RELATIME }, + { "norelatime", ~MS_RELATIME }, + { "strictatime", MS_STRICTATIME }, +}; + +static char *blobmsg_get_strdup(struct blob_attr *attr) +{ + if (!attr) + return NULL; + + return strdup(blobmsg_get_string(attr)); +} + +static char *blobmsg_get_basename(struct blob_attr *attr) +{ + if (!attr) + return NULL; + + return strdup(basename(blobmsg_get_string(attr))); +} + +static void parse_mount_options(struct mount *m, char *optstr) +{ + int i; + bool is_flag; + char *p, *opts, *last; + + m->flags = 0; + m->options = NULL; + + if (!optstr || !*optstr) + return; + + m->options = opts = calloc(1, strlen(optstr) + 1); + + if (!m->options) + return; + + p = last = optstr; + + do { + p = strchr(p, ','); + + if (p) + *p++ = 0; + + for (i = 0, is_flag = false; i < ARRAY_SIZE(mount_flags); i++) { + if (!strcmp(last, mount_flags[i].name)) { + if (mount_flags[i].flag < 0) + m->flags &= (uint32_t)mount_flags[i].flag; + else + m->flags |= (uint32_t)mount_flags[i].flag; + is_flag = true; + break; + } + } + + if (!is_flag) + opts += sprintf(opts, "%s%s", (opts > m->options) ? "," : "", last); + + last = p; + + } while (p); + + free(optstr); +} + +static int mount_add(struct uci_section *s) +{ + struct blob_attr *tb[__MOUNT_MAX] = { 0 }; + struct mount *m; + + blob_buf_init(&b, 0); + uci_to_blob(&b, s, &mount_attr_list); + blobmsg_parse(mount_policy, __MOUNT_MAX, tb, blob_data(b.head), blob_len(b.head)); + + if (!tb[MOUNT_LABEL] && !tb[MOUNT_UUID] && !tb[MOUNT_DEVICE]) + return -1; + + if (tb[MOUNT_ENABLE] && !blobmsg_get_u32(tb[MOUNT_ENABLE])) + return -1; + + m = malloc(sizeof(struct mount)); + m->type = TYPE_MOUNT; + m->uuid = blobmsg_get_strdup(tb[MOUNT_UUID]); + m->label = blobmsg_get_strdup(tb[MOUNT_LABEL]); + m->target = blobmsg_get_strdup(tb[MOUNT_TARGET]); + m->device = blobmsg_get_basename(tb[MOUNT_DEVICE]); + + parse_mount_options(m, blobmsg_get_strdup(tb[MOUNT_OPTIONS])); + + m->overlay = m->extroot = 0; + if (m->target && !strcmp(m->target, "/")) + m->extroot = 1; + if (m->target && !strcmp(m->target, "/overlay")) + m->extroot = m->overlay = 1; + + if (m->uuid) + vlist_add(&mounts, &m->node, m->uuid); + else if (m->label) + vlist_add(&mounts, &m->node, m->label); + else if (m->device) + vlist_add(&mounts, &m->node, m->device); + + return 0; +} + +static int swap_add(struct uci_section *s) +{ + struct blob_attr *tb[__SWAP_MAX] = { 0 }; + struct mount *m; + + blob_buf_init(&b, 0); + uci_to_blob(&b, s, &swap_attr_list); + blobmsg_parse(swap_policy, __SWAP_MAX, tb, blob_data(b.head), blob_len(b.head)); + + if (!tb[SWAP_UUID] && !tb[SWAP_LABEL] && !tb[SWAP_DEVICE]) + return -1; + + m = malloc(sizeof(struct mount)); + memset(m, 0, sizeof(struct mount)); + m->type = TYPE_SWAP; + m->uuid = blobmsg_get_strdup(tb[SWAP_UUID]); + m->label = blobmsg_get_strdup(tb[SWAP_LABEL]); + m->device = blobmsg_get_basename(tb[SWAP_DEVICE]); + if (tb[SWAP_PRIO]) + m->prio = blobmsg_get_u32(tb[SWAP_PRIO]); + if (m->prio) + m->prio = ((m->prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER; + + if ((!tb[SWAP_ENABLE]) || blobmsg_get_u32(tb[SWAP_ENABLE])) { + /* store complete swap path */ + if (tb[SWAP_DEVICE]) + m->target = blobmsg_get_strdup(tb[SWAP_DEVICE]); + + if (m->uuid) + vlist_add(&mounts, &m->node, m->uuid); + else if (m->label) + vlist_add(&mounts, &m->node, m->label); + else if (m->device) + vlist_add(&mounts, &m->node, m->device); + } + + return 0; +} + +static int global_add(struct uci_section *s) +{ + struct blob_attr *tb[__CFG_MAX] = { 0 }; + + blob_buf_init(&b, 0); + uci_to_blob(&b, s, &config_attr_list); + blobmsg_parse(config_policy, __CFG_MAX, tb, blob_data(b.head), blob_len(b.head)); + + if ((tb[CFG_ANON_MOUNT]) && blobmsg_get_u32(tb[CFG_ANON_MOUNT])) + anon_mount = 1; + if ((tb[CFG_ANON_SWAP]) && blobmsg_get_u32(tb[CFG_ANON_SWAP])) + anon_swap = 1; + + if ((tb[CFG_AUTO_MOUNT]) && blobmsg_get_u32(tb[CFG_AUTO_MOUNT])) + auto_mount = 1; + if ((tb[CFG_AUTO_SWAP]) && blobmsg_get_u32(tb[CFG_AUTO_SWAP])) + auto_swap = 1; + + if (tb[CFG_DELAY_ROOT]) + delay_root = blobmsg_get_u32(tb[CFG_DELAY_ROOT]); + + if ((tb[CFG_CHECK_FS]) && blobmsg_get_u32(tb[CFG_CHECK_FS])) + check_fs = 1; + + return 0; +} + +static struct mount* find_swap(const char *uuid, const char *label, const char *device) +{ + struct mount *m; + + vlist_for_each_element(&mounts, m, node) { + if (m->type != TYPE_SWAP) + continue; + if (uuid && m->uuid && !strcmp(m->uuid, uuid)) + return m; + if (label && m->label && !strcmp(m->label, label)) + return m; + if (device && m->device && !strcmp(m->device, device)) + return m; + } + + return NULL; +} + +static struct mount* find_block(const char *uuid, const char *label, const char *device, + const char *target) +{ + struct mount *m; + + vlist_for_each_element(&mounts, m, node) { + if (m->type != TYPE_MOUNT) + continue; + if (m->uuid && uuid && !strcmp(m->uuid, uuid)) + return m; + if (m->label && label && !strcmp(m->label, label)) + return m; + if (m->target && target && !strcmp(m->target, target)) + return m; + if (m->device && device && !strcmp(m->device, device)) + return m; + } + + return NULL; +} + +static void mounts_update(struct vlist_tree *tree, struct vlist_node *node_new, + struct vlist_node *node_old) +{ +} + +static int config_load(char *cfg) +{ + struct uci_context *ctx; + struct uci_package *pkg; + struct uci_element *e; + + vlist_init(&mounts, avl_strcmp, mounts_update); + + ctx = uci_alloc_context(); + if (cfg) { + char path[32]; + snprintf(path, 32, "%s/etc/config", cfg); + uci_set_confdir(ctx, path); + } + + if (uci_load(ctx, "fstab", &pkg)) + { + char *err; + uci_get_errorstr(ctx, &err, "fstab"); + ERROR("extroot: failed to load %s/etc/config/%s\n", + cfg ? cfg : "", err); + free(err); + return -1; + } + + vlist_update(&mounts); + uci_foreach_element(&pkg->sections, e) { + struct uci_section *s = uci_to_section(e); + + if (!strcmp(s->type, "mount")) + mount_add(s); + if (!strcmp(s->type, "swap")) + swap_add(s); + if (!strcmp(s->type, "global")) + global_add(s); + } + vlist_flush(&mounts); + + return 0; +} + +static struct blkid_struct_probe* _probe_path(char *path) +{ + struct blkid_struct_probe *pr; + + pr = malloc(sizeof(*pr)); + + if (!pr) + return NULL; + + memset(pr, 0, sizeof(*pr)); + probe_block(path, pr); + + if (pr->err || !pr->id) { + free(pr); + return NULL; + } + + return pr; +} + +static int _cache_load(const char *path) +{ + int gl_flags = GLOB_NOESCAPE | GLOB_MARK; + int j; + glob_t gl; + + if (glob(path, gl_flags, NULL, &gl) < 0) + return -1; + + for (j = 0; j < gl.gl_pathc; j++) { + struct blkid_struct_probe *pr = _probe_path(gl.gl_pathv[j]); + if (pr) + list_add_tail(&pr->list, &devices); + } + + globfree(&gl); + + return 0; +} + +static void cache_load(int mtd) +{ + if (mtd) + _cache_load("/dev/mtdblock*"); + _cache_load("/dev/mmcblk*"); + _cache_load("/dev/sd*"); + _cache_load("/dev/hd*"); + _cache_load("/dev/md*"); + _cache_load("/dev/mapper/*"); +} + +static int print_block_info(struct blkid_struct_probe *pr) +{ + printf("%s:", pr->dev); + if (pr->uuid[0]) + printf(" UUID=\"%s\"", pr->uuid); + + if (pr->label[0]) + printf(" LABEL=\"%s\"", pr->label); + + if (pr->name[0]) + printf(" NAME=\"%s\"", pr->name); + + if (pr->version[0]) + printf(" VERSION=\"%s\"", pr->version); + + printf(" TYPE=\"%s\"\n", pr->id->name); + + return 0; +} + +static int print_block_uci(struct blkid_struct_probe *pr) +{ + if (!strcmp(pr->id->name, "swap")) { + printf("config 'swap'\n"); + } else { + printf("config 'mount'\n"); + printf("\toption\ttarget\t'/mnt/%s'\n", basename(pr->dev)); + } + if (pr->uuid[0]) + printf("\toption\tuuid\t'%s'\n", pr->uuid); + else + printf("\toption\tdevice\t'%s'\n", pr->dev); + printf("\toption\tenabled\t'0'\n\n"); + + return 0; +} + +static struct blkid_struct_probe* find_block_info(char *uuid, char *label, char *path) +{ + struct blkid_struct_probe *pr = NULL; + + if (uuid) + list_for_each_entry(pr, &devices, list) + if (!strcmp(pr->uuid, uuid)) + return pr; + + if (label) + list_for_each_entry(pr, &devices, list) + if (!strcmp(pr->label, label)) + return pr; + + if (path) + list_for_each_entry(pr, &devices, list) + if (!strcmp(basename(pr->dev), basename(path))) + return pr; + + return NULL; +} + +static char* find_mount_point(char *block) +{ + FILE *fp = fopen("/proc/mounts", "r"); + static char line[256]; + int len = strlen(block); + char *point = NULL; + + if(!fp) + return NULL; + + while (fgets(line, sizeof(line), fp)) { + if (!strncmp(line, block, len)) { + char *p = &line[len + 1]; + char *t = strstr(p, " "); + + if (!t) { + fclose(fp); + return NULL; + } + *t = '\0'; + point = p; + break; + } + } + + fclose(fp); + + return point; +} + +static void mkdir_p(char *dir) +{ + char *l = strrchr(dir, '/'); + + if (l) { + *l = '\0'; + mkdir_p(dir); + *l = '/'; + mkdir(dir, 0755); + } +} + +static void check_filesystem(struct blkid_struct_probe *pr) +{ + pid_t pid; + struct stat statbuf; + char *e2fsck = "/usr/sbin/e2fsck"; + + if (strncmp(pr->id->name, "ext", 3)) { + ERROR("check_filesystem: %s is not supported\n", pr->id->name); + return; + } + + if (stat(e2fsck, &statbuf) < 0) { + ERROR("check_filesystem: %s not found\n", e2fsck); + return; + } + + pid = fork(); + if (!pid) { + execl(e2fsck, e2fsck, "-p", pr->dev, NULL); + exit(-1); + } else if (pid > 0) { + int status; + + waitpid(pid, &status, 0); + if (WEXITSTATUS(status)) + ERROR("check_filesystem: %s returned %d\n", e2fsck, WEXITSTATUS(status)); + } +} + +static void handle_swapfiles(bool on) +{ + struct stat s; + struct mount *m; + struct blkid_struct_probe *pr; + + vlist_for_each_element(&mounts, m, node) + { + if (m->type != TYPE_SWAP || !m->target) + continue; + + if (stat(m->target, &s) || !S_ISREG(s.st_mode)) + continue; + + pr = _probe_path(m->target); + + if (!pr) + continue; + + if (!strcmp(pr->id->name, "swap")) { + if (on) + swapon(pr->dev, m->prio); + else + swapoff(pr->dev); + } + + free(pr); + } +} + +static int mount_device(struct blkid_struct_probe *pr, int hotplug) +{ + struct mount *m; + char *device; + + if (!pr) + return -1; + + device = basename(pr->dev); + + if (!strcmp(pr->id->name, "swap")) { + if (hotplug && !auto_swap) + return -1; + m = find_swap(pr->uuid, pr->label, device); + if (m || anon_swap) + swapon(pr->dev, (m) ? (m->prio) : (0)); + + return 0; + } + + if (hotplug && !auto_mount) + return -1; + + if (find_mount_point(pr->dev)) { + ERROR("%s is already mounted\n", pr->dev); + return -1; + } + + m = find_block(pr->uuid, pr->label, device, NULL); + if (m && m->extroot) + return -1; + + if (m) { + char *target = m->target; + char _target[32]; + int err = 0; + + if (!target) { + snprintf(_target, sizeof(_target), "/mnt/%s", device); + target = _target; + } + mkdir_p(target); + + if (check_fs) + check_filesystem(pr); + + err = mount(pr->dev, target, pr->id->name, m->flags, + (m->options) ? (m->options) : ("")); + if (err) + ERROR("mounting %s (%s) as %s failed (%d) - %s\n", + pr->dev, pr->id->name, target, err, strerror(err)); + else + handle_swapfiles(true); + return err; + } + + if (anon_mount) { + char target[] = "/mnt/mmcblk123"; + int err = 0; + + snprintf(target, sizeof(target), "/mnt/%s", device); + mkdir_p(target); + + if (check_fs) + check_filesystem(pr); + + err = mount(pr->dev, target, pr->id->name, 0, ""); + if (err) + ERROR("mounting %s (%s) as %s failed (%d) - %s\n", + pr->dev, pr->id->name, target, err, strerror(err)); + else + handle_swapfiles(true); + return err; + } + + return 0; +} + +static int umount_device(struct blkid_struct_probe *pr) +{ + struct mount *m; + char *device = basename(pr->dev); + char *mp; + int err; + + if (!pr) + return -1; + + if (!strcmp(pr->id->name, "swap")) + return -1; + + mp = find_mount_point(pr->dev); + if (!mp) + return -1; + + m = find_block(pr->uuid, pr->label, device, NULL); + if (m && m->extroot) + return -1; + + err = umount2(mp, MNT_DETACH); + if (err) + ERROR("unmounting %s (%s) failed (%d) - %s\n", + pr->dev, mp, err, strerror(err)); + else + ERROR("unmounted %s (%s)\n", + pr->dev, mp); + + return err; +} + +static int main_hotplug(int argc, char **argv) +{ + char path[32]; + char *action, *device, *mount_point; + + action = getenv("ACTION"); + device = getenv("DEVNAME"); + + if (!action || !device) + return -1; + snprintf(path, sizeof(path), "/dev/%s", device); + + if (!strcmp(action, "remove")) { + int err = 0; + mount_point = find_mount_point(path); + if (mount_point) + err = umount2(mount_point, MNT_DETACH); + + if (err) + ERROR("umount of %s failed (%d) - %s\n", + mount_point, err, strerror(err)); + + return 0; + } else if (strcmp(action, "add")) { + ERROR("Unkown action %s\n", action); + + return -1; + } + + if (config_load(NULL)) + return -1; + cache_load(0); + + return mount_device(find_block_info(NULL, NULL, path), 1); +} + +static int find_block_mtd(char *name, char *part, int plen) +{ + FILE *fp = fopen("/proc/mtd", "r"); + static char line[256]; + char *index = NULL; + + if(!fp) + return -1; + + while (!index && fgets(line, sizeof(line), fp)) { + if (strstr(line, name)) { + char *eol = strstr(line, ":"); + + if (!eol) + continue; + + *eol = '\0'; + index = &line[3]; + } + } + + fclose(fp); + + if (!index) + return -1; + + snprintf(part, plen, "/dev/mtdblock%s", index); + + return 0; +} + +static int check_extroot(char *path) +{ + struct blkid_struct_probe *pr = NULL; + char fs[32]; + + if (find_block_mtd("rootfs", fs, sizeof(fs))) + return -1; + + list_for_each_entry(pr, &devices, list) { + if (!strcmp(pr->dev, fs)) { + struct stat s; + FILE *fp = NULL; + char tag[64]; + char uuid[64] = { 0 }; + + snprintf(tag, sizeof(tag), "%s/etc/.extroot-uuid", path); + if (stat(tag, &s)) { + fp = fopen(tag, "w+"); + if (!fp) { + ERROR("extroot: failed to write uuid tag file\n"); + /* return 0 to continue boot regardless of error */ + return 0; + } + fputs(pr->uuid, fp); + fclose(fp); + return 0; + } + + fp = fopen(tag, "r"); + if (!fp) { + ERROR("extroot: failed to open uuid tag file\n"); + return -1; + } + + fgets(uuid, sizeof(uuid), fp); + fclose(fp); + if (!strcmp(uuid, pr->uuid)) + return 0; + ERROR("extroot: uuid tag does not match rom uuid\n"); + } + } + return -1; +} + +static int mount_extroot(char *cfg) +{ + char overlay[] = "/tmp/extroot/overlay"; + char mnt[] = "/tmp/extroot/mnt"; + char *path = mnt; + struct blkid_struct_probe *pr; + struct mount *m; + int err = -1; + + if (config_load(cfg)) + return -2; + + m = find_block(NULL, NULL, NULL, "/"); + if (!m) + m = find_block(NULL, NULL, NULL, "/overlay"); + + if (!m || !m->extroot) + { + ERROR("extroot: no root or overlay mount defined\n"); + return -1; + } + + pr = find_block_info(m->uuid, m->label, m->device); + + if (!pr && delay_root){ + ERROR("extroot: is not ready yet, retrying in %u seconds\n", delay_root); + sleep(delay_root); + mkblkdev(); + cache_load(0); + pr = find_block_info(m->uuid, m->label, m->device); + } + if (pr) { + if (strncmp(pr->id->name, "ext", 3)) { + ERROR("extroot: %s is not supported, try ext4\n", pr->id->name); + return -1; + } + if (m->overlay) + path = overlay; + mkdir_p(path); + + if (check_fs) + check_filesystem(pr); + + err = mount(pr->dev, path, pr->id->name, 0, (m->options) ? (m->options) : ("")); + + if (err) { + ERROR("mounting %s (%s) as %s failed (%d) - %s\n", + pr->dev, pr->id->name, path, err, strerror(err)); + } else if (m->overlay) { + err = check_extroot(path); + if (err) + umount(path); + } + } else { + ERROR("extroot: cannot find block device\n"); + } + + return err; +} + +static int main_extroot(int argc, char **argv) +{ + struct blkid_struct_probe *pr; + char fs[32] = { 0 }; + char fs_data[32] = { 0 }; + int err = -1; + + if (!getenv("PREINIT")) + return -1; + + if (argc != 2) { + fprintf(stderr, "Usage: block extroot mountpoint\n"); + return -1; + } + + mkblkdev(); + cache_load(1); + + find_block_mtd("rootfs", fs, sizeof(fs)); + if (!fs[0]) { + ERROR("extroot: unable to locate rootfs mtdblock\n"); + return -2; + } + + pr = find_block_info(NULL, NULL, fs); + if (!pr) { + ERROR("extroot: unable to retrieve rootfs information\n"); + return -3; + } + + find_block_mtd("rootfs_data", fs_data, sizeof(fs_data)); + if (fs_data[0]) { + pr = find_block_info(NULL, NULL, fs_data); + if (pr && !strcmp(pr->id->name, "jffs2")) { + char cfg[] = "/tmp/jffs_cfg"; + + mkdir_p(cfg); + if (!mount(fs_data, cfg, "jffs2", MS_NOATIME, NULL)) { + err = mount_extroot(cfg); + umount2(cfg, MNT_DETACH); + } + if (err < 0) + rmdir("/tmp/overlay"); + rmdir(cfg); + return err; + } + } + + return mount_extroot(NULL); +} + +static int main_mount(int argc, char **argv) +{ + struct blkid_struct_probe *pr; + + if (config_load(NULL)) + return -1; + + cache_load(1); + list_for_each_entry(pr, &devices, list) + mount_device(pr, 0); + + handle_swapfiles(true); + + return 0; +} + +static int main_umount(int argc, char **argv) +{ + struct blkid_struct_probe *pr; + + if (config_load(NULL)) + return -1; + + handle_swapfiles(false); + + cache_load(0); + list_for_each_entry(pr, &devices, list) + umount_device(pr); + + return 0; +} + +static int main_detect(int argc, char **argv) +{ + struct blkid_struct_probe *pr; + + cache_load(0); + printf("config 'global'\n"); + printf("\toption\tanon_swap\t'0'\n"); + printf("\toption\tanon_mount\t'0'\n"); + printf("\toption\tauto_swap\t'1'\n"); + printf("\toption\tauto_mount\t'1'\n"); + printf("\toption\tdelay_root\t'5'\n"); + printf("\toption\tcheck_fs\t'0'\n\n"); + list_for_each_entry(pr, &devices, list) + print_block_uci(pr); + + return 0; +} + +static int main_info(int argc, char **argv) +{ + int i; + struct blkid_struct_probe *pr; + + cache_load(1); + if (argc == 2) { + list_for_each_entry(pr, &devices, list) + print_block_info(pr); + + return 0; + }; + + for (i = 2; i < argc; i++) { + struct stat s; + + if (stat(argv[i], &s)) { + ERROR("failed to stat %s\n", argv[i]); + continue; + } + if (!S_ISBLK(s.st_mode)) { + ERROR("%s is not a block device\n", argv[i]); + continue; + } + pr = find_block_info(NULL, NULL, argv[i]); + if (pr) + print_block_info(pr); + } + + return 0; +} + +static int swapon_usage(void) +{ + fprintf(stderr, "Usage: swapon [-s] [-a] [[-p pri] DEVICE]\n\n" + "\tStart swapping on [DEVICE]\n" + " -a\tStart swapping on all swap devices\n" + " -p pri\tSet priority of swap device\n" + " -s\tShow summary\n"); + return -1; +} + +static int main_swapon(int argc, char **argv) +{ + int ch; + FILE *fp; + char *lineptr; + size_t s; + struct blkid_struct_probe *pr; + int flags = 0; + int pri; + struct stat st; + int err; + + while ((ch = getopt(argc, argv, "ap:s")) != -1) { + switch(ch) { + case 's': + fp = fopen("/proc/swaps", "r"); + lineptr = NULL; + + if (!fp) { + ERROR("failed to open /proc/swaps\n"); + return -1; + } + while (getline(&lineptr, &s, fp) > 0) + printf(lineptr); + if (lineptr) + free(lineptr); + fclose(fp); + return 0; + case 'a': + cache_load(0); + list_for_each_entry(pr, &devices, list) { + if (strcmp(pr->id->name, "swap")) + continue; + if (swapon(pr->dev, 0)) + ERROR("failed to swapon %s\n", pr->dev); + } + return 0; + case 'p': + pri = atoi(optarg); + if (pri >= 0) + flags = ((pri << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER; + break; + default: + return swapon_usage(); + } + + } + + if (optind != (argc - 1)) + return swapon_usage(); + + if (stat(argv[optind], &st) || (!S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode))) { + ERROR("%s is not a block device or file\n", argv[optind]); + return -1; + } + err = swapon(argv[optind], flags); + if (err) { + ERROR("failed to swapon %s (%d)\n", argv[optind], err); + return err; + } + + return 0; +} + +static int main_swapoff(int argc, char **argv) +{ + if (argc != 2) { + ERROR("Usage: swapoff [-a] [DEVICE]\n\n" + "\tStop swapping on DEVICE\n" + " -a\tStop swapping on all swap devices\n"); + return -1; + } + + if (!strcmp(argv[1], "-a")) { + FILE *fp = fopen("/proc/swaps", "r"); + char line[256]; + + if (!fp) { + ERROR("failed to open /proc/swaps\n"); + return -1; + } + fgets(line, sizeof(line), fp); + while (fgets(line, sizeof(line), fp)) { + char *end = strchr(line, ' '); + int err; + + if (!end) + continue; + *end = '\0'; + err = swapoff(line); + if (err) + ERROR("failed to swapoff %s (%d)\n", line, err); + } + fclose(fp); + } else { + struct stat s; + int err; + + if (stat(argv[1], &s) || (!S_ISBLK(s.st_mode) && !S_ISREG(s.st_mode))) { + ERROR("%s is not a block device or file\n", argv[1]); + return -1; + } + err = swapoff(argv[1]); + if (err) { + ERROR("fsiled to swapoff %s (%d)\n", argv[1], err); + return err; + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + char *base = basename(*argv); + + umask(0); + + if (!strcmp(base, "swapon")) + return main_swapon(argc, argv); + + if (!strcmp(base, "swapoff")) + return main_swapoff(argc, argv); + + if ((argc > 1) && !strcmp(base, "block")) { + if (!strcmp(argv[1], "info")) + return main_info(argc, argv); + + if (!strcmp(argv[1], "detect")) + return main_detect(argc, argv); + + if (!strcmp(argv[1], "hotplug")) + return main_hotplug(argc, argv); + + if (!strcmp(argv[1], "extroot")) + return main_extroot(argc, argv); + + if (!strcmp(argv[1], "mount")) + return main_mount(argc, argv); + + if (!strcmp(argv[1], "umount")) + return main_umount(argc, argv); + } + + fprintf(stderr, "Usage: block \n"); + + return -1; +} diff --git a/fs-state.c b/fs-state.c new file mode 100644 index 0000000..b99a2ff --- /dev/null +++ b/fs-state.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include "fs-state.h" + +static LIST_HEAD(backends); + +void +register_backend(struct backend *b) +{ + list_add(&b->list, &backends); +} + +struct backend* +find_backend(char *name) +{ + struct backend *b; + + list_for_each_entry(b, &backends, list) + if (!strcmp(name, b->name)) + return b; + return NULL; +} + +static void +help(void) +{ + struct backend *b; + + list_for_each_entry(b, &backends, list) { + int i; + + if (b->desc) + fprintf(stderr, "-> %s\n", b->name); + for (i = 0; i < b->num_handlers; i++) + if (b->handlers[i].desc) + fprintf(stderr, "--> %s\n", b->handlers[i].name); + } +} + +int +main(int argc, char **argv) +{ + struct backend *b; + + if (argc > 1) list_for_each_entry(b, &backends, list) { + int i; + + srand(time(NULL)); + + if (strcmp(argv[1], b->name)) + continue; + + for (i = 0; i < b->num_handlers; i++) + if (!strcmp(argv[2], b->handlers[i].name)) + return b->handlers[i].cli(argc - 2, &argv[2]); + + if (b->cli) + return b->cli(argc - 1, &argv[1]); + + break; + } + + help(); + + return 0; +} diff --git a/fs-state.h b/fs-state.h new file mode 100644 index 0000000..d168b76 --- /dev/null +++ b/fs-state.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _FS_STATE_H__ +#define _FS_STATE_H__ + +#include +#include + +enum { + FS_NONE, + FS_SNAPSHOT, + FS_JFFS2, + FS_DEADCODE, +}; + +typedef int (*backend_cli_t)(int argc, char **argv); +typedef int (*backend_mount_t)(void); +typedef int (*backend_info_t)(void); + +extern char const *extroot_prefix; + +struct backend_handler +{ + char *name; + char *desc; + backend_cli_t cli; +}; + +struct backend +{ + struct list_head list; + char *name; + char *desc; + int num_handlers; + backend_cli_t cli; + backend_mount_t mount; + backend_info_t info; + struct backend_handler *handlers; +}; + +void register_backend(struct backend *); +struct backend* find_backend(char *); +int backend_mount(char *name); + +#define BACKEND(x) \ + static void __attribute__((constructor)) \ + register_##x(void) { \ + register_backend(&x); \ + } + +int mount_move(char *oldroot, char *newroot, char *dir); +int pivot(char *new, char *old); +int fopivot(char *rw_root, char *ro_root); +int ramoverlay(void); + +int find_overlay_mount(char *overlay); +char* find_mount(char *mp); +char* find_mount_point(char *block, char *fs); +int find_filesystem(char *fs); +int find_mtd_block(char *name, char *part, int plen); +int find_mtd_char(char *name, char *part, int plen); + +int jffs2_ready(char *mtd); +int jffs2_switch(int argc, char **argv); + +int handle_whiteout(const char *dir); +void foreachdir(const char *dir, int (*cb)(const char*)); + +#endif diff --git a/lib/find.c b/lib/find.c new file mode 100644 index 0000000..d13c4c1 --- /dev/null +++ b/lib/find.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "../fs-state.h" + +int +find_overlay_mount(char *overlay) +{ + FILE *fp = fopen("/proc/mounts", "r"); + static char line[256]; + int ret = -1; + + if(!fp) + return ret; + + while (ret && fgets(line, sizeof(line), fp)) + if (!strncmp(line, overlay, strlen(overlay))) + ret = 0; + + fclose(fp); + + return ret; +} + +char* +find_mount(char *mp) +{ + FILE *fp = fopen("/proc/mounts", "r"); + static char line[256]; + char *point = NULL; + + if(!fp) + return NULL; + + while (fgets(line, sizeof(line), fp)) { + char *s, *t = strstr(line, " "); + + if (!t) { + fclose(fp); + return NULL; + } + t++; + s = strstr(t, " "); + if (!s) { + fclose(fp); + return NULL; + } + *s = '\0'; + + if (!strcmp(t, mp)) { + fclose(fp); + return t; + } + } + + fclose(fp); + + return point; +} + +char* +find_mount_point(char *block, char *fs) +{ + FILE *fp = fopen("/proc/mounts", "r"); + static char line[256]; + int len = strlen(block); + char *point = NULL; + + if(!fp) + return NULL; + + while (fgets(line, sizeof(line), fp)) { + if (!strncmp(line, block, len)) { + char *p = &line[len + 1]; + char *t = strstr(p, " "); + + if (!t) { + fclose(fp); + return NULL; + } + + *t = '\0'; + t++; + + if (fs && strncmp(t, fs, strlen(fs))) { + fclose(fp); + fprintf(stderr, "block is mounted with wrong fs\n"); + return NULL; + } + point = p; + + break; + } + } + + fclose(fp); + + return point; +} + +static char* +find_mtd_index(char *name) +{ + FILE *fp = fopen("/proc/mtd", "r"); + static char line[256]; + char *index = NULL; + + if(!fp) + return index; + + while (!index && fgets(line, sizeof(line), fp)) { + if (strstr(line, name)) { + char *eol = strstr(line, ":"); + + if (!eol) + continue; + + *eol = '\0'; + index = &line[3]; + } + } + + fclose(fp); + + return index; +} + +int +find_mtd_block(char *name, char *part, int plen) +{ + char *index = find_mtd_index(name); + + if (!index) + return -1; + + snprintf(part, plen, "/dev/mtdblock%s", index); + + return 0; +} + +int +find_mtd_char(char *name, char *part, int plen) +{ + char *index = find_mtd_index(name); + + if (!index) + return -1; + + snprintf(part, plen, "/dev/mtd%s", index); + + return 0; +} + +int +find_filesystem(char *fs) +{ + FILE *fp = fopen("/proc/filesystems", "r"); + static char line[256]; + int ret = -1; + + if (!fp) { + fprintf(stderr, "opening /proc/filesystems failed: %s\n", strerror(errno)); + goto out; + } + + while (ret && fgets(line, sizeof(line), fp)) + if (strstr(line, fs)) + ret = 0; + + fclose(fp); + +out: + return ret; +} + + diff --git a/lib/mount.c b/lib/mount.c new file mode 100644 index 0000000..e7b57f0 --- /dev/null +++ b/lib/mount.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "../fs-state.h" + +/* this is a raw syscall - man 2 pivot_root */ +extern int pivot_root(const char *new_root, const char *put_old); + +int +mount_move(char *oldroot, char *newroot, char *dir) +{ +#ifndef MS_MOVE +#define MS_MOVE (1 << 13) +#endif + struct stat s; + char olddir[64]; + char newdir[64]; + int ret; + + snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir); + snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir); + + if (stat(olddir, &s) || !S_ISDIR(s.st_mode)) + return -1; + + if (stat(newdir, &s) || !S_ISDIR(s.st_mode)) + return -1; + + ret = mount(olddir, newdir, NULL, MS_NOATIME | MS_MOVE, NULL); + +/* if (ret) + fprintf(stderr, "failed %s %s: %s\n", olddir, newdir, strerror(errno));*/ + + return ret; +} + +int +pivot(char *new, char *old) +{ + char pivotdir[64]; + int ret; + + if (mount_move("", new, "/proc")) + return -1; + + snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old); + + ret = pivot_root(new, pivotdir); + + if (ret < 0) { + fprintf(stderr, "pivot_root failed %s %s: %s\n", new, pivotdir, strerror(errno)); + return -1; + } + + mount_move(old, "", "/dev"); + mount_move(old, "", "/tmp"); + mount_move(old, "", "/sys"); + mount_move(old, "", "/overlay"); + + return 0; +} + +int +fopivot(char *rw_root, char *ro_root) +{ + char overlay[64], lowerdir[64]; + + if (find_filesystem("overlay")) { + fprintf(stderr, "BUG: no suitable fs found\n"); + return -1; + } + + snprintf(overlay, sizeof(overlay), "overlayfs:%s", rw_root); + snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s", rw_root); + + if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) { + fprintf(stderr, "mount failed: %s\n", strerror(errno)); + return -1; + } + + return pivot("/mnt", ro_root); +} + +int +ramoverlay(void) +{ + mkdir("/tmp/root", 0755); + mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755"); + + return fopivot("/tmp/root", "/rom"); +} diff --git a/lib/mtd.c b/lib/mtd.c new file mode 100644 index 0000000..0036078 --- /dev/null +++ b/lib/mtd.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fs-state.h" +#include "mtd.h" + +#define PATH_MAX 256 + +int mtdsize = 0; +int erasesize = 0; + +int +mtd_open(const char *mtd, int block) +{ + FILE *fp; + char dev[PATH_MAX]; + int i, ret, flags = O_RDWR | O_SYNC; + + if ((fp = fopen("/proc/mtd", "r"))) { + while (fgets(dev, sizeof(dev), fp)) { + if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) { + snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i); + ret = open(dev, flags); + if (ret < 0) { + snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i); + ret = open(dev, flags); + } + fclose(fp); + return ret; + } + } + fclose(fp); + } + + return open(mtd, flags); +} + +int +mtd_load(const char *mtd) +{ + struct mtd_info_user mtdInfo; + struct erase_info_user mtdLockInfo; + int fd; + + fd = mtd_open(mtd, 0); + if (fd < 0) { + fprintf(stderr, "Could not open mtd device: %s\n", mtd); + return -1; + } + + if (ioctl(fd, MEMGETINFO, &mtdInfo)) { + fprintf(stderr, "Could not get MTD device info from %s\n", mtd); + close(fd); + return -1; + } + + mtdsize = mtdInfo.size; + erasesize = mtdInfo.erasesize; + + mtdLockInfo.start = 0; + mtdLockInfo.length = mtdsize; + ioctl(fd, MEMUNLOCK, &mtdLockInfo); + + return fd; +} + +void +mtd_erase(int fd, int first_block, int num_blocks) +{ + struct erase_info_user eiu; + + eiu.length = erasesize; + for (eiu.start = first_block * erasesize; + eiu.start < mtdsize && eiu.start < (first_block + num_blocks) * erasesize; + eiu.start += erasesize) { + fprintf(stderr, "erasing %x %x\n", eiu.start, erasesize); + ioctl(fd, MEMUNLOCK, &eiu); + if (ioctl(fd, MEMERASE, &eiu)) + fprintf(stderr, "Failed to erase block at 0x%x\n", eiu.start); + } +} + +int +mtd_unlock(int fd) +{ + struct mtd_info_user mtdinfo; + int ret = ioctl(fd, MEMGETINFO, &mtdinfo); + + if (ret) { + fprintf(stderr, "ioctl(%d, MEMGETINFO) failed: %s\n", fd, strerror(errno)); + } else { + struct erase_info_user mtdlock; + + mtdlock.start = 0; + mtdlock.length = mtdinfo.size; + ioctl(fd, MEMUNLOCK, &mtdlock); + } + + return ret; +} + +int +mtd_read_buffer(int fd, void *buf, int offset, int length) +{ + if (lseek(fd, offset, SEEK_SET) == (off_t) -1) { + fprintf(stderr, "lseek/read failed\n"); + return -1; + } + + if (read(fd, buf, length) == -1) { + fprintf(stderr, "read failed\n"); + return -1; + } + + return 0; +} + +int +mtd_write_buffer(int fd, void *buf, int offset, int length) +{ + if (lseek(fd, offset, SEEK_SET) == (off_t) -1) { + fprintf(stderr, "lseek/write failed at offset %d\n", offset); + perror("lseek"); + return -1; + } + + if (write(fd, buf, length) == -1) { + fprintf(stderr, "write failed\n"); + return -1; + } + + return 0; +} + +int +mtd_identify(char *mtd) +{ + int fd = mtd_load(mtd); + __u32 deadc0de; + __u16 jffs2; + size_t sz; + + if (!fd) { + fprintf(stderr, "reading %s failed\n", mtd); + return -1; + } + + sz = read(fd, &deadc0de, sizeof(deadc0de)); + close(fd); + + if (sz != sizeof(deadc0de)) { + fprintf(stderr, "reading %s failed: %s\n", mtd, strerror(errno)); + return -1; + } + + if (deadc0de == 0x4f575254) + return FS_SNAPSHOT; + + deadc0de = __be32_to_cpu(deadc0de); + jffs2 = __be16_to_cpu(deadc0de >> 16); + + if (jffs2 == 0x1985) { + fprintf(stderr, "jffs2 is ready\n"); + return FS_JFFS2; + } + + if (deadc0de == 0xdeadc0de) { + fprintf(stderr, "jffs2 is not ready - marker found\n"); + return FS_DEADCODE; + } + + fprintf(stderr, "No jffs2 marker was found\n"); + + return FS_NONE; +} diff --git a/lib/mtd.h b/lib/mtd.h new file mode 100644 index 0000000..adf1ebe --- /dev/null +++ b/lib/mtd.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _FS_MTD_H__ +#define _FS_MTD_H__ + +extern int mtdsize; +extern int erasesize; + +int mtd_open(const char *mtd, int block); +int mtd_load(const char *mtd); +void mtd_erase(int fd, int first_block, int num_blocks); +int mtd_unlock(int fd); +int mtd_read_buffer(int fd, void *buf, int offset, int length); +int mtd_write_buffer(int fd, void *buf, int offset, int length); +int mtd_identify(char *mtd); + +#endif diff --git a/libblkid-tiny/bitops.h b/libblkid-tiny/bitops.h new file mode 100644 index 0000000..5bf2057 --- /dev/null +++ b/libblkid-tiny/bitops.h @@ -0,0 +1,84 @@ +#ifndef BITOPS_H +#define BITOPS_H + +#include + +/* + * Bit map related macros. Usually provided by libc. + */ +#include + +#ifndef NBBY +# define NBBY CHAR_BIT +#endif + +#ifndef setbit +# define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY)) +# define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) +# define isset(a,i) ((a)[(i)/NBBY] & (1<<((i)%NBBY))) +# define isclr(a,i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) +#endif + +/* + * Byte swab macros (based on linux/byteorder/swab.h) + */ +#define swab16(x) \ + ((uint16_t)( \ + (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ + (((uint16_t)(x) & (uint16_t)0xff00U) >> 8) )) + +#define swab32(x) \ + ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24) )) + +#define swab64(x) \ + ((uint64_t)( \ + (uint64_t)(((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (uint64_t)(((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (uint64_t)(((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (uint64_t)(((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (uint64_t)(((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (uint64_t)(((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (uint64_t)(((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (uint64_t)(((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56) )) + + +#if __BYTE_ORDER == __BIG_ENDIAN + +#define cpu_to_le16(x) swab16(x) +#define cpu_to_le32(x) swab32(x) +#define cpu_to_le64(x) swab64(x) +#define cpu_to_be16(x) ((uint16_t)(x)) +#define cpu_to_be32(x) ((uint32_t)(x)) +#define cpu_to_be64(x) ((uint64_t)(x)) + +#define le16_to_cpu(x) swab16(x) +#define le32_to_cpu(x) swab32(x) +#define le64_to_cpu(x) swab64(x) +#define be16_to_cpu(x) ((uint16_t)(x)) +#define be32_to_cpu(x) ((uint32_t)(x)) +#define be64_to_cpu(x) ((uint64_t)(x)) + +#else /* !WORDS_BIGENDIAN */ + +#define cpu_to_le16(x) ((uint16_t)(x)) +#define cpu_to_le32(x) ((uint32_t)(x)) +#define cpu_to_le64(x) ((uint64_t)(x)) +#define cpu_to_be16(x) swab16(x) +#define cpu_to_be32(x) swab32(x) +#define cpu_to_be64(x) swab64(x) + +#define le16_to_cpu(x) ((uint16_t)(x)) +#define le32_to_cpu(x) ((uint32_t)(x)) +#define le64_to_cpu(x) ((uint64_t)(x)) +#define be16_to_cpu(x) swab16(x) +#define be32_to_cpu(x) swab32(x) +#define be64_to_cpu(x) swab64(x) + +#endif /* WORDS_BIGENDIAN */ + +#endif /* BITOPS_H */ + diff --git a/libblkid-tiny/blkdev.h b/libblkid-tiny/blkdev.h new file mode 100644 index 0000000..6b18879 --- /dev/null +++ b/libblkid-tiny/blkdev.h @@ -0,0 +1,113 @@ +#ifndef BLKDEV_H +#define BLKDEV_H + +#include +#include +#ifdef HAVE_SYS_IOCCOM_H +# include /* for _IO macro on e.g. Solaris */ +#endif +#include +#include + +#ifdef HAVE_SYS_MKDEV_H +# include /* major and minor on Solaris */ +#endif + +#define DEFAULT_SECTOR_SIZE 512 + +#ifdef __linux__ +/* very basic ioclts, should be available everywhere */ +# ifndef BLKROSET +# define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */ +# define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */ +# define BLKRRPART _IO(0x12,95) /* re-read partition table */ +# define BLKGETSIZE _IO(0x12,96) /* return device size /512 (long *arg) */ +# define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ +# define BLKRASET _IO(0x12,98) /* set read ahead for block device */ +# define BLKRAGET _IO(0x12,99) /* get current read ahead setting */ +# define BLKFRASET _IO(0x12,100) /* set filesystem (mm/filemap.c) read-ahead */ +# define BLKFRAGET _IO(0x12,101) /* get filesystem (mm/filemap.c) read-ahead */ +# define BLKSECTSET _IO(0x12,102) /* set max sectors per request (ll_rw_blk.c) */ +# define BLKSECTGET _IO(0x12,103) /* get max sectors per request (ll_rw_blk.c) */ +# define BLKSSZGET _IO(0x12,104) /* get block device sector size */ + +/* ioctls introduced in 2.2.16, removed in 2.5.58 */ +# define BLKELVGET _IOR(0x12,106,size_t) /* elevator get */ +# define BLKELVSET _IOW(0x12,107,size_t) /* elevator set */ + +# define BLKBSZGET _IOR(0x12,112,size_t) +# define BLKBSZSET _IOW(0x12,113,size_t) +# endif /* !BLKROSET */ + +# ifndef BLKGETSIZE64 +# define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ +# endif + +/* block device topology ioctls, introduced in 2.6.32 (commit ac481c20) */ +# ifndef BLKIOMIN +# define BLKIOMIN _IO(0x12,120) +# define BLKIOOPT _IO(0x12,121) +# define BLKALIGNOFF _IO(0x12,122) +# define BLKPBSZGET _IO(0x12,123) +# endif + +/* discard zeroes support, introduced in 2.6.33 (commit 98262f27) */ +# ifndef BLKDISCARDZEROES +# define BLKDISCARDZEROES _IO(0x12,124) +# endif + +/* filesystem freeze, introduced in 2.6.29 (commit fcccf502) */ +# ifndef FIFREEZE +# define FIFREEZE _IOWR('X', 119, int) /* Freeze */ +# define FITHAW _IOWR('X', 120, int) /* Thaw */ +# endif + +#endif /* __linux */ + +#ifdef APPLE_DARWIN +# define BLKGETSIZE DKIOCGETBLOCKCOUNT32 +#endif + +#ifndef HDIO_GETGEO +# ifdef __linux__ +# define HDIO_GETGEO 0x0301 +# endif + +/* uniform CD-ROM information */ +#ifndef CDROM_GET_CAPABILITY +# define CDROM_GET_CAPABILITY 0x5331 +#endif + +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; /* truncated */ + unsigned long start; +}; +#endif + +/* are we working with block device? */ +int is_blkdev(int fd); + +/* Determine size in bytes */ +off_t blkdev_find_size (int fd); + +/* get size in bytes */ +int blkdev_get_size(int fd, unsigned long long *bytes); + +/* get 512-byte sector count */ +int blkdev_get_sectors(int fd, unsigned long long *sectors); + +/* get hardware sector size */ +int blkdev_get_sector_size(int fd, int *sector_size); + +/* specifies whether or not the device is misaligned */ +int blkdev_is_misaligned(int fd); + +/* get physical block device size */ +int blkdev_get_physector_size(int fd, int *sector_size); + +/* is the device cdrom capable? */ +int blkdev_is_cdrom(int fd); + +#endif /* BLKDEV_H */ diff --git a/libblkid-tiny/blkid.h b/libblkid-tiny/blkid.h new file mode 100644 index 0000000..9ecfa6b --- /dev/null +++ b/libblkid-tiny/blkid.h @@ -0,0 +1,353 @@ +/* + * blkid.h - Interface for libblkid, a library to identify block devices + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * Copyright (C) 2008 Karel Zak + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _BLKID_BLKID_H +#define _BLKID_BLKID_H + +#include +#include + +#include "libblkid-tiny.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLKID_VERSION "2.21.0" +#define BLKID_DATE "25-May-2012" + +/** + * blkid_dev: + * + * The device object keeps information about one device + */ +typedef struct blkid_struct_dev *blkid_dev; + +/** + * blkid_cache: + * + * information about all system devices + */ +typedef struct blkid_struct_cache *blkid_cache; + +/** + * blkid_probe: + * + * low-level probing setting + */ +typedef struct blkid_struct_probe *blkid_probe; + +/** + * blkid_topology: + * + * device topology information + */ +typedef struct blkid_struct_topology *blkid_topology; + +/** + * blkid_partlist + * + * list of all detected partitions and partitions tables + */ +typedef struct blkid_struct_partlist *blkid_partlist; + +/** + * blkid_partition: + * + * information about a partition + */ +typedef struct blkid_struct_partition *blkid_partition; + +/** + * blkid_parttable: + * + * information about a partition table + */ +typedef struct blkid_struct_parttable *blkid_parttable; + +/** + * blkid_loff_t: + * + * 64-bit signed number for offsets and sizes + */ +typedef int64_t blkid_loff_t; + +/** + * blkid_tag_iterate: + * + * tags iterator for high-level (blkid_cache) API + */ +typedef struct blkid_struct_tag_iterate *blkid_tag_iterate; + +/** + * blkid_dev_iterate: + * + * devices iterator for high-level (blkid_cache) API + */ +typedef struct blkid_struct_dev_iterate *blkid_dev_iterate; + +/* + * Flags for blkid_get_dev + * + * BLKID_DEV_CREATE Create an empty device structure if not found + * in the cache. + * BLKID_DEV_VERIFY Make sure the device structure corresponds + * with reality. + * BLKID_DEV_FIND Just look up a device entry, and return NULL + * if it is not found. + * BLKID_DEV_NORMAL Get a valid device structure, either from the + * cache or by probing the device. + */ +#define BLKID_DEV_FIND 0x0000 +#define BLKID_DEV_CREATE 0x0001 +#define BLKID_DEV_VERIFY 0x0002 +#define BLKID_DEV_NORMAL (BLKID_DEV_CREATE | BLKID_DEV_VERIFY) + +/* cache.c */ +extern void blkid_put_cache(blkid_cache cache); +extern int blkid_get_cache(blkid_cache *cache, const char *filename); +extern void blkid_gc_cache(blkid_cache cache); + +/* dev.c */ +extern const char *blkid_dev_devname(blkid_dev dev); + +extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache); +extern int blkid_dev_set_search(blkid_dev_iterate iter, + char *search_type, char *search_value); +extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev); +extern void blkid_dev_iterate_end(blkid_dev_iterate iterate); + +/* devno.c */ +extern char *blkid_devno_to_devname(dev_t devno); +extern int blkid_devno_to_wholedisk(dev_t dev, char *diskname, + size_t len, dev_t *diskdevno); + +/* devname.c */ +extern int blkid_probe_all(blkid_cache cache); +extern int blkid_probe_all_new(blkid_cache cache); +extern int blkid_probe_all_removable(blkid_cache cache); +extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, + int flags); + +/* getsize.c */ +extern blkid_loff_t blkid_get_dev_size(int fd); + +/* verify.c */ +extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev); + +/* read.c */ + +/* resolve.c */ +extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname, + const char *devname); +extern char *blkid_get_devname(blkid_cache cache, const char *token, + const char *value); + +/* tag.c */ +extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev); +extern int blkid_tag_next(blkid_tag_iterate iterate, + const char **type, const char **value); +extern void blkid_tag_iterate_end(blkid_tag_iterate iterate); +extern int blkid_dev_has_tag(blkid_dev dev, const char *type, + const char *value); +extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, + const char *type, + const char *value); +extern int blkid_parse_tag_string(const char *token, char **ret_type, + char **ret_val); + +/* version.c */ +extern int blkid_parse_version_string(const char *ver_string); +extern int blkid_get_library_version(const char **ver_string, + const char **date_string); + +/* encode.c */ +extern int blkid_encode_string(const char *str, char *str_enc, size_t len); +extern int blkid_safe_string(const char *str, char *str_safe, size_t len); + +/* evaluate.c */ +extern int blkid_send_uevent(const char *devname, const char *action); +extern char *blkid_evaluate_tag(const char *token, const char *value, + blkid_cache *cache); +extern char *blkid_evaluate_spec(const char *spec, blkid_cache *cache); + +/* probe.c */ +extern blkid_probe blkid_new_probe(void); +extern blkid_probe blkid_new_probe_from_filename(const char *filename); +extern void blkid_free_probe(blkid_probe pr); +extern void blkid_reset_probe(blkid_probe pr); + +extern int blkid_probe_set_device(blkid_probe pr, int fd, + blkid_loff_t off, blkid_loff_t size); + +extern dev_t blkid_probe_get_devno(blkid_probe pr); +extern dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr); +extern int blkid_probe_is_wholedisk(blkid_probe pr); + +extern blkid_loff_t blkid_probe_get_size(blkid_probe pr); +extern blkid_loff_t blkid_probe_get_offset(blkid_probe pr); +extern unsigned int blkid_probe_get_sectorsize(blkid_probe pr); +extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr); + +extern int blkid_probe_get_fd(blkid_probe pr); + +/* + * superblocks probing + */ +extern int blkid_known_fstype(const char *fstype); +extern int blkid_superblocks_get_name(size_t idx, const char **name, int *usage); + +extern int blkid_probe_enable_superblocks(blkid_probe pr, int enable); + +#define BLKID_SUBLKS_LABEL (1 << 1) /* read LABEL from superblock */ +#define BLKID_SUBLKS_LABELRAW (1 << 2) /* read and define LABEL_RAW result value*/ +#define BLKID_SUBLKS_UUID (1 << 3) /* read UUID from superblock */ +#define BLKID_SUBLKS_UUIDRAW (1 << 4) /* read and define UUID_RAW result value */ +#define BLKID_SUBLKS_TYPE (1 << 5) /* define TYPE result value */ +#define BLKID_SUBLKS_SECTYPE (1 << 6) /* define compatible fs type (second type) */ +#define BLKID_SUBLKS_USAGE (1 << 7) /* define USAGE result value */ +#define BLKID_SUBLKS_VERSION (1 << 8) /* read FS type from superblock */ +#define BLKID_SUBLKS_MAGIC (1 << 9) /* define SBMAGIC and SBMAGIC_OFFSET */ + +#define BLKID_SUBLKS_DEFAULT (BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | \ + BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE) + +extern int blkid_probe_set_superblocks_flags(blkid_probe pr, int flags); + +extern int blkid_probe_reset_superblocks_filter(blkid_probe pr); +extern int blkid_probe_invert_superblocks_filter(blkid_probe pr); + +/** + * BLKID_FLTR_NOTIN + */ +#define BLKID_FLTR_NOTIN 1 +/** + * BLKID_FLTR_ONLYIN + */ +#define BLKID_FLTR_ONLYIN 2 +extern int blkid_probe_filter_superblocks_type(blkid_probe pr, int flag, char *names[]); + +#define BLKID_USAGE_FILESYSTEM (1 << 1) +#define BLKID_USAGE_RAID (1 << 2) +#define BLKID_USAGE_CRYPTO (1 << 3) +#define BLKID_USAGE_OTHER (1 << 4) +extern int blkid_probe_filter_superblocks_usage(blkid_probe pr, int flag, int usage); + +/* + * topology probing + */ +extern int blkid_probe_enable_topology(blkid_probe pr, int enable); + +/* binary interface */ +extern blkid_topology blkid_probe_get_topology(blkid_probe pr); + +extern unsigned long blkid_topology_get_alignment_offset(blkid_topology tp); +extern unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp); +extern unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp); +extern unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp); +extern unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp); + +/* + * partitions probing + */ +extern int blkid_known_pttype(const char *pttype); +extern int blkid_probe_enable_partitions(blkid_probe pr, int enable); + +extern int blkid_probe_reset_partitions_filter(blkid_probe pr); +extern int blkid_probe_invert_partitions_filter(blkid_probe pr); +extern int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[]); + + +/* partitions probing flags */ +#define BLKID_PARTS_FORCE_GPT (1 << 1) +#define BLKID_PARTS_ENTRY_DETAILS (1 << 2) +#define BLKID_PARTS_MAGIC (1 << 3) +extern int blkid_probe_set_partitions_flags(blkid_probe pr, int flags); + +/* binary interface */ +extern blkid_partlist blkid_probe_get_partitions(blkid_probe pr); + +extern int blkid_partlist_numof_partitions(blkid_partlist ls); +extern blkid_parttable blkid_partlist_get_table(blkid_partlist ls); +extern blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n); +extern blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno); + +extern blkid_parttable blkid_partition_get_table(blkid_partition par); +extern const char *blkid_partition_get_name(blkid_partition par); +extern const char *blkid_partition_get_uuid(blkid_partition par); +extern int blkid_partition_get_partno(blkid_partition par); +extern blkid_loff_t blkid_partition_get_start(blkid_partition par); +extern blkid_loff_t blkid_partition_get_size(blkid_partition par); +extern int blkid_partition_get_type(blkid_partition par); +extern const char *blkid_partition_get_type_string(blkid_partition par); +extern unsigned long long blkid_partition_get_flags(blkid_partition par); +extern int blkid_partition_is_logical(blkid_partition par); +extern int blkid_partition_is_extended(blkid_partition par); +extern int blkid_partition_is_primary(blkid_partition par); + +extern const char *blkid_parttable_get_type(blkid_parttable tab); +extern blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab); +extern blkid_partition blkid_parttable_get_parent(blkid_parttable tab); + +/* + * NAME=value low-level interface + */ +extern int blkid_do_probe(blkid_probe pr); +extern int blkid_do_safeprobe(blkid_probe pr); +extern int blkid_do_fullprobe(blkid_probe pr); + +extern int blkid_probe_numof_values(blkid_probe pr); +extern int blkid_probe_get_value(blkid_probe pr, int num, const char **name, + const char **data, size_t *len); +extern int blkid_probe_lookup_value(blkid_probe pr, const char *name, + const char **data, size_t *len); +extern int blkid_probe_has_value(blkid_probe pr, const char *name); + +extern int blkid_do_wipe(blkid_probe pr, int dryrun); + +/* + * Deprecated functions/macros + */ +#ifndef BLKID_DISABLE_DEPRECATED + +#define BLKID_PROBREQ_LABEL BLKID_SUBLKS_LABEL +#define BLKID_PROBREQ_LABELRAW BLKID_SUBLKS_LABELRAW +#define BLKID_PROBREQ_UUID BLKID_SUBLKS_UUID +#define BLKID_PROBREQ_UUIDRAW BLKID_SUBLKS_UUIDRAW +#define BLKID_PROBREQ_TYPE BLKID_SUBLKS_TYPE +#define BLKID_PROBREQ_SECTYPE BLKID_SUBLKS_SECTYPE +#define BLKID_PROBREQ_USAGE BLKID_SUBLKS_USAGE +#define BLKID_PROBREQ_VERSION BLKID_SUBLKS_VERSION + +extern int blkid_probe_set_request(blkid_probe pr, int flags); +extern int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage); +extern int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[]); +extern int blkid_probe_invert_filter(blkid_probe pr); +extern int blkid_probe_reset_filter(blkid_probe pr); + +#endif /* BLKID_DISABLE_DEPRECATED */ + +#ifdef __cplusplus +} +#endif + +#endif /* _BLKID_BLKID_H */ diff --git a/libblkid-tiny/blkidP.h b/libblkid-tiny/blkidP.h new file mode 100644 index 0000000..0f372a5 --- /dev/null +++ b/libblkid-tiny/blkidP.h @@ -0,0 +1,421 @@ +/* + * blkidP.h - Internal interfaces for libblkid + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#ifndef _BLKID_BLKIDP_H +#define _BLKID_BLKIDP_H + + +#define CONFIG_BLKID_DEBUG 1 + +#include +#include +#include +#include +#include +#include + +#include "c.h" +#include "bitops.h" /* $(top_srcdir)/include/ */ +#include "blkdev.h" + +#include "blkid.h" +#include + +/* + * This describes the attributes of a specific device. + * We can traverse all of the tags by bid_tags (linking to the tag bit_names). + * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag + * values, if they exist. + */ +struct blkid_struct_dev +{ + struct list_head bid_devs; /* All devices in the cache */ + struct list_head bid_tags; /* All tags for this device */ + blkid_cache bid_cache; /* Dev belongs to this cache */ + char *bid_name; /* Device inode pathname */ + char *bid_type; /* Preferred device TYPE */ + int bid_pri; /* Device priority */ + dev_t bid_devno; /* Device major/minor number */ + time_t bid_time; /* Last update time of device */ + suseconds_t bid_utime; /* Last update time (microseconds) */ + unsigned int bid_flags; /* Device status bitflags */ + char *bid_label; /* Shortcut to device LABEL */ + char *bid_uuid; /* Shortcut to binary UUID */ +}; + +#define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */ +#define BLKID_BID_FL_INVALID 0x0004 /* Device is invalid */ +#define BLKID_BID_FL_REMOVABLE 0x0008 /* Device added by blkid_probe_all_removable() */ + +/* + * Each tag defines a NAME=value pair for a particular device. The tags + * are linked via bit_names for a single device, so that traversing the + * names list will get you a list of all tags associated with a device. + * They are also linked via bit_values for all devices, so one can easily + * search all tags with a given NAME for a specific value. + */ +struct blkid_struct_tag +{ + struct list_head bit_tags; /* All tags for this device */ + struct list_head bit_names; /* All tags with given NAME */ + char *bit_name; /* NAME of tag (shared) */ + char *bit_val; /* value of tag */ + blkid_dev bit_dev; /* pointer to device */ +}; +typedef struct blkid_struct_tag *blkid_tag; + +/* + * Chain IDs + */ +enum { + BLKID_CHAIN_SUBLKS, /* FS/RAID superblocks (enabled by default) */ + BLKID_CHAIN_TOPLGY, /* Block device topology */ + BLKID_CHAIN_PARTS, /* Partition tables */ + + BLKID_NCHAINS /* number of chains */ +}; + +struct blkid_chain { + const struct blkid_chaindrv *driver; /* chain driver */ + + int enabled; /* boolean */ + int flags; /* BLKID__* */ + int binary; /* boolean */ + int idx; /* index of the current prober (or -1) */ + unsigned long *fltr; /* filter or NULL */ + void *data; /* private chain data or NULL */ +}; + +/* + * Chain driver + */ +struct blkid_chaindrv { + const size_t id; /* BLKID_CHAIN_* */ + const char *name; /* name of chain (for debug purpose) */ + const int dflt_flags; /* default chain flags */ + const int dflt_enabled; /* default enabled boolean */ + int has_fltr; /* boolean */ + + const struct blkid_idinfo **idinfos; /* description of probing functions */ + const size_t nidinfos; /* number of idinfos */ + + /* driver operations */ + int (*probe)(blkid_probe, struct blkid_chain *); + int (*safeprobe)(blkid_probe, struct blkid_chain *); + void (*free_data)(blkid_probe, void *); +}; + +/* + * Low-level probe result + */ +#define BLKID_PROBVAL_BUFSIZ 64 + +#define BLKID_NVALS_SUBLKS 14 +#define BLKID_NVALS_TOPLGY 5 +#define BLKID_NVALS_PARTS 13 + +/* Max number of all values in probing result */ +#define BLKID_NVALS (BLKID_NVALS_SUBLKS + \ + BLKID_NVALS_TOPLGY + \ + BLKID_NVALS_PARTS) + +struct blkid_prval +{ + const char *name; /* value name */ + unsigned char data[BLKID_PROBVAL_BUFSIZ]; /* value data */ + size_t len; /* length of value data */ + + struct blkid_chain *chain; /* owner */ +}; + +/* + * Filesystem / Raid magic strings + */ +/* + * Filesystem / Raid description + */ +#define BLKID_NONE_MAGIC {{ NULL }} + +/* + * tolerant FS - can share the same device with more filesystems (e.g. typical + * on CD-ROMs). We need this flag to detect ambivalent results (e.g. valid fat + * and valid linux swap on the same device). + */ +#define BLKID_IDINFO_TOLERANT (1 << 1) + +struct blkid_bufinfo { + unsigned char *data; + blkid_loff_t off; + blkid_loff_t len; + struct list_head bufs; /* list of buffers */ +}; + +/* private flags library flags */ +#define BLKID_FL_PRIVATE_FD (1 << 1) /* see blkid_new_probe_from_filename() */ +#define BLKID_FL_TINY_DEV (1 << 2) /* <= 1.47MiB (floppy or so) */ +#define BLKID_FL_CDROM_DEV (1 << 3) /* is a CD/DVD drive */ + +/* private per-probing flags */ +#define BLKID_PROBE_FL_IGNORE_PT (1 << 1) /* ignore partition table */ + +extern blkid_probe blkid_clone_probe(blkid_probe parent); +extern blkid_probe blkid_probe_get_wholedisk_probe(blkid_probe pr); + +/* + * Evaluation methods (for blkid_eval_* API) + */ +enum { + BLKID_EVAL_UDEV = 0, + BLKID_EVAL_SCAN, + + __BLKID_EVAL_LAST +}; + +/* + * Library config options + */ +struct blkid_config { + int eval[__BLKID_EVAL_LAST]; /* array with EVALUATION= options */ + int nevals; /* number of elems in eval array */ + int uevent; /* SEND_UEVENT= option */ + char *cachefile; /* CACHE_FILE= option */ +}; + +extern struct blkid_config *blkid_read_config(const char *filename); +extern void blkid_free_config(struct blkid_config *conf); + +/* + * Minimum number of seconds between device probes, even when reading + * from the cache. This is to avoid re-probing all devices which were + * just probed by another program that does not share the cache. + */ +#define BLKID_PROBE_MIN 2 + +/* + * Time in seconds an entry remains verified in the in-memory cache + * before being reverified (in case of long-running processes that + * keep a cache in memory and continue to use it for a long time). + */ +#define BLKID_PROBE_INTERVAL 200 + +/* This describes an entire blkid cache file and probed devices. + * We can traverse all of the found devices via bic_list. + * We can traverse all of the tag types by bic_tags, which hold empty tags + * for each tag type. Those tags can be used as list_heads for iterating + * through all devices with a specific tag type (e.g. LABEL). + */ +struct blkid_struct_cache +{ + struct list_head bic_devs; /* List head of all devices */ + struct list_head bic_tags; /* List head of all tag types */ + time_t bic_time; /* Last probe time */ + time_t bic_ftime; /* Mod time of the cachefile */ + unsigned int bic_flags; /* Status flags of the cache */ + char *bic_filename; /* filename of cache */ + blkid_probe probe; /* low-level probing stuff */ +}; + +#define BLKID_BIC_FL_PROBED 0x0002 /* We probed /proc/partition devices */ +#define BLKID_BIC_FL_CHANGED 0x0004 /* Cache has changed from disk */ + +extern char *blkid_strdup(const char *s); +extern char *blkid_strndup(const char *s, const int length); +extern char *blkid_strconcat(const char *a, const char *b, const char *c); + +/* config file */ +#define BLKID_CONFIG_FILE "/etc/blkid.conf" + +/* cache file on systemds with /run */ +#define BLKID_RUNTIME_TOPDIR "/run" +#define BLKID_RUNTIME_DIR BLKID_RUNTIME_TOPDIR "/blkid" +#define BLKID_CACHE_FILE BLKID_RUNTIME_DIR "/blkid.tab" + +/* old systems */ +#define BLKID_CACHE_FILE_OLD "/etc/blkid.tab" + +#define BLKID_ERR_IO 5 +#define BLKID_ERR_PROC 9 +#define BLKID_ERR_MEM 12 +#define BLKID_ERR_CACHE 14 +#define BLKID_ERR_DEV 19 +#define BLKID_ERR_PARAM 22 +#define BLKID_ERR_BIG 27 + +/* + * Priority settings for different types of devices + */ +#define BLKID_PRI_UBI 50 +#define BLKID_PRI_DM 40 +#define BLKID_PRI_EVMS 30 +#define BLKID_PRI_LVM 20 +#define BLKID_PRI_MD 10 + +#if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG) +#define CONFIG_BLKID_DEBUG +#endif + +#define DEBUG_CACHE 0x0001 +#define DEBUG_DUMP 0x0002 +#define DEBUG_DEV 0x0004 +#define DEBUG_DEVNAME 0x0008 +#define DEBUG_DEVNO 0x0010 +#define DEBUG_PROBE 0x0020 +#define DEBUG_READ 0x0040 +#define DEBUG_RESOLVE 0x0080 +#define DEBUG_SAVE 0x0100 +#define DEBUG_TAG 0x0200 +#define DEBUG_LOWPROBE 0x0400 +#define DEBUG_CONFIG 0x0800 +#define DEBUG_EVALUATE 0x1000 +#define DEBUG_INIT 0x8000 +#define DEBUG_ALL 0xFFFF + +#ifdef CONFIG_BLKID_DEBUG +extern int blkid_debug_mask; +extern void blkid_init_debug(int mask); +extern void blkid_debug_dump_dev(blkid_dev dev); +extern void blkid_debug_dump_tag(blkid_tag tag); + +#define DBG(m,x) if ((m) & blkid_debug_mask) x; + +#else /* !CONFIG_BLKID_DEBUG */ +#define DBG(m,x) +#define blkid_init_debug(x) +#endif /* CONFIG_BLKID_DEBUG */ + +/* devno.c */ +struct dir_list { + char *name; + struct dir_list *next; +}; +extern void blkid__scan_dir(char *, dev_t, struct dir_list **, char **); +extern int blkid_driver_has_major(const char *drvname, int major); + +/* lseek.c */ +extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence); + +/* read.c */ +extern void blkid_read_cache(blkid_cache cache); + +/* save.c */ +extern int blkid_flush_cache(blkid_cache cache); + +/* cache */ +extern char *blkid_safe_getenv(const char *arg); +extern char *blkid_get_cache_filename(struct blkid_config *conf); + +/* + * Functions to create and find a specific tag type: tag.c + */ +extern void blkid_free_tag(blkid_tag tag); +extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type); +extern int blkid_set_tag(blkid_dev dev, const char *name, + const char *value, const int vlength); + +/* + * Functions to create and find a specific tag type: dev.c + */ +extern blkid_dev blkid_new_dev(void); +extern void blkid_free_dev(blkid_dev dev); + +/* probe.c */ +extern int blkid_probe_is_tiny(blkid_probe pr); +extern int blkid_probe_is_cdrom(blkid_probe pr); +extern unsigned char *blkid_probe_get_buffer(blkid_probe pr, + blkid_loff_t off, blkid_loff_t len); + +extern unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector); + +extern int blkid_probe_get_dimension(blkid_probe pr, + blkid_loff_t *off, blkid_loff_t *size); + +extern int blkid_probe_set_dimension(blkid_probe pr, + blkid_loff_t off, blkid_loff_t size); + +extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id, + blkid_loff_t *offset, const struct blkid_idmag **res); + +/* returns superblok according to 'struct blkid_idmag' */ +#define blkid_probe_get_sb(_pr, _mag, type) \ + ((type *) blkid_probe_get_buffer((_pr),\ + (_mag)->kboff << 10, sizeof(type))) + +extern blkid_partlist blkid_probe_get_partlist(blkid_probe pr); + +extern int blkid_probe_is_covered_by_pt(blkid_probe pr, + blkid_loff_t offset, blkid_loff_t size); + +extern void blkid_probe_chain_reset_vals(blkid_probe pr, struct blkid_chain *chn); +extern int blkid_probe_chain_copy_vals(blkid_probe pr, struct blkid_chain *chn, + struct blkid_prval *vals, int nvals); +extern struct blkid_prval *blkid_probe_assign_value(blkid_probe pr, const char *name); +extern int blkid_probe_reset_last_value(blkid_probe pr); +extern void blkid_probe_append_vals(blkid_probe pr, struct blkid_prval *vals, int nvals); + +extern struct blkid_chain *blkid_probe_get_chain(blkid_probe pr); + +extern struct blkid_prval *__blkid_probe_get_value(blkid_probe pr, int num); +extern struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name); + +extern unsigned long *blkid_probe_get_filter(blkid_probe pr, int chain, int create); +extern int __blkid_probe_invert_filter(blkid_probe pr, int chain); +extern int __blkid_probe_reset_filter(blkid_probe pr, int chain); +extern int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[]); + +extern void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn); + +extern int blkid_probe_set_value(blkid_probe pr, const char *name, + unsigned char *data, size_t len); +extern int blkid_probe_vsprintf_value(blkid_probe pr, const char *name, + const char *fmt, va_list ap); +extern int blkid_probe_sprintf_value(blkid_probe pr, const char *name, + const char *fmt, ...); +extern int blkid_probe_set_magic(blkid_probe pr, blkid_loff_t offset, + size_t len, unsigned char *magic); + +extern void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len); +extern size_t blkid_rtrim_whitespace(unsigned char *str); + +extern void blkid_probe_set_wiper(blkid_probe pr, blkid_loff_t off, + blkid_loff_t size); +extern int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn, + blkid_loff_t off, blkid_loff_t size); +extern void blkid_probe_use_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size); + +/* filter bitmap macros */ +#define blkid_bmp_wordsize (8 * sizeof(unsigned long)) +#define blkid_bmp_idx_bit(item) (1UL << ((item) % blkid_bmp_wordsize)) +#define blkid_bmp_idx_byte(item) ((item) / blkid_bmp_wordsize) + +#define blkid_bmp_set_item(bmp, item) \ + ((bmp)[ blkid_bmp_idx_byte(item) ] |= blkid_bmp_idx_bit(item)) + +#define blkid_bmp_unset_item(bmp, item) \ + ((bmp)[ blkid_bmp_idx_byte(item) ] &= ~blkid_bmp_idx_bit(item)) + +#define blkid_bmp_get_item(bmp, item) \ + ((bmp)[ blkid_bmp_idx_byte(item) ] & blkid_bmp_idx_bit(item)) + +#define blkid_bmp_nwords(max_items) \ + (((max_items) + blkid_bmp_wordsize) / blkid_bmp_wordsize) + +#define blkid_bmp_nbytes(max_items) \ + (blkid_bmp_nwords(max_items) * sizeof(unsigned long)) + +/* encode.c */ +extern size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len, + const unsigned char *src, size_t count); + +#define BLKID_ENC_UTF16BE 0 +#define BLKID_ENC_UTF16LE 1 + +#endif /* _BLKID_BLKIDP_H */ diff --git a/libblkid-tiny/c.h b/libblkid-tiny/c.h new file mode 100644 index 0000000..8c8db73 --- /dev/null +++ b/libblkid-tiny/c.h @@ -0,0 +1,247 @@ +/* + * Fundamental C definitions. + */ + +#ifndef UTIL_LINUX_C_H +#define UTIL_LINUX_C_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ERR_H +# include +#endif + +/* + * Compiler specific stuff + */ +#ifndef __GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +# else +# define __GNUC_PREREQ(maj, min) 0 +# endif +#endif + +#ifdef __GNUC__ + +/* &a[0] degrades to a pointer: a different type from an array */ +# define __must_be_array(a) \ + UL_BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(__typeof__(a), __typeof__(&a[0]))) + +# define ignore_result(x) ({ \ + __typeof__(x) __dummy __attribute__((__unused__)) = (x); (void) __dummy; \ +}) + +#else /* !__GNUC__ */ +# define __must_be_array(a) 0 +# define __attribute__(_arg_) +# define ignore_result(x) ((void) (x)) +#endif /* !__GNUC__ */ + +/* + * Function attributes + */ +#ifndef __ul_alloc_size +# if __GNUC_PREREQ (4, 3) +# define __ul_alloc_size(s) __attribute__((alloc_size(s))) +# else +# define __ul_alloc_size(s) +# endif +#endif + +#ifndef __ul_calloc_size +# if __GNUC_PREREQ (4, 3) +# define __ul_calloc_size(n, s) __attribute__((alloc_size(n, s))) +# else +# define __ul_calloc_size(n, s) +# endif +#endif + +/* Force a compilation error if condition is true, but also produce a + * result (of value 0 and type size_t), so the expression can be used + * e.g. in a structure initializer (or where-ever else comma expressions + * aren't permitted). + */ +#define UL_BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) +#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) +#endif + +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef min +# define min(x, y) ({ \ + __typeof__(x) _min1 = (x); \ + __typeof__(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) +#endif + +#ifndef max +# define max(x, y) ({ \ + __typeof__(x) _max1 = (x); \ + __typeof__(y) _max2 = (y); \ + (void) (&_max1 == &_max2); \ + _max1 > _max2 ? _max1 : _max2; }) +#endif + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +#ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME +# ifdef HAVE___PROGNAME +extern char *__progname; +# define program_invocation_short_name __progname +# else +# ifdef HAVE_GETEXECNAME +# define program_invocation_short_name \ + prog_inv_sh_nm_from_file(getexecname(), 0) +# else +# define program_invocation_short_name \ + prog_inv_sh_nm_from_file(__FILE__, 1) +# endif +static char prog_inv_sh_nm_buf[256]; +static inline char * +prog_inv_sh_nm_from_file(char *f, char stripext) +{ + char *t; + + if ((t = strrchr(f, '/')) != NULL) + t++; + else + t = f; + + strncpy(prog_inv_sh_nm_buf, t, sizeof(prog_inv_sh_nm_buf) - 1); + prog_inv_sh_nm_buf[sizeof(prog_inv_sh_nm_buf) - 1] = '\0'; + + if (stripext && (t = strrchr(prog_inv_sh_nm_buf, '.')) != NULL) + *t = '\0'; + + return prog_inv_sh_nm_buf; +} +# endif +#endif + + +#ifndef HAVE_ERR_H +static inline void +errmsg(char doexit, int excode, char adderr, const char *fmt, ...) +{ + fprintf(stderr, "%s: ", program_invocation_short_name); + if (fmt != NULL) { + va_list argp; + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + if (adderr) + fprintf(stderr, ": "); + } + if (adderr) + fprintf(stderr, "%m"); + fprintf(stderr, "\n"); + if (doexit) + exit(excode); +} + +#ifndef HAVE_ERR +# define err(E, FMT...) errmsg(1, E, 1, FMT) +#endif + +#ifndef HAVE_ERRX +# define errx(E, FMT...) errmsg(1, E, 0, FMT) +#endif + +#ifndef HAVE_WARN +# define warn(FMT...) errmsg(0, 0, 1, FMT) +#endif + +#ifndef HAVE_WARNX +# define warnx(FMT...) errmsg(0, 0, 0, FMT) +#endif +#endif /* !HAVE_ERR_H */ + + +static inline __attribute__((const)) int is_power_of_2(unsigned long num) +{ + return (num != 0 && ((num & (num - 1)) == 0)); +} + +#ifndef HAVE_LOFF_T +typedef int64_t loff_t; +#endif + +#if !defined(HAVE_DIRFD) && (!defined(HAVE_DECL_DIRFD) || HAVE_DECL_DIRFD == 0) && defined(HAVE_DIR_DD_FD) +#include +#include +static inline int dirfd(DIR *d) +{ + return d->dd_fd; +} +#endif + +/* + * Fallback defines for old versions of glibc + */ +#include +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0x0020 +#endif + +#ifndef IUTF8 +#define IUTF8 0040000 +#endif + +/* + * Constant strings for usage() functions. For more info see + * Documentation/howto-usage-function.txt and sys-utils/arch.c + */ +#define USAGE_HEADER _("\nUsage:\n") +#define USAGE_OPTIONS _("\nOptions:\n") +#define USAGE_SEPARATOR _("\n") +#define USAGE_HELP _(" -h, --help display this help and exit\n") +#define USAGE_VERSION _(" -V, --version output version information and exit\n") +#define USAGE_MAN_TAIL(_man) _("\nFor more details see %s.\n"), _man + +#define UTIL_LINUX_VERSION _("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING + +/* + * scanf modifiers for "strings allocation" + */ +#ifdef HAVE_SCANF_MS_MODIFIER +#define UL_SCNsA "%ms" +#elif defined(HAVE_SCANF_AS_MODIFIER) +#define UL_SCNsA "%as" +#endif + +#endif /* UTIL_LINUX_C_H */ diff --git a/libblkid-tiny/ext.c b/libblkid-tiny/ext.c new file mode 100644 index 0000000..687127d --- /dev/null +++ b/libblkid-tiny/ext.c @@ -0,0 +1,531 @@ +/* + * Copyright (C) 1999, 2001 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2008 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include + +#include "linux_version.h" +#include "superblocks.h" + +struct ext2_super_block { + uint32_t s_inodes_count; + uint32_t s_blocks_count; + uint32_t s_r_blocks_count; + uint32_t s_free_blocks_count; + uint32_t s_free_inodes_count; + uint32_t s_first_data_block; + uint32_t s_log_block_size; + uint32_t s_dummy3[7]; + unsigned char s_magic[2]; + uint16_t s_state; + uint16_t s_errors; + uint16_t s_minor_rev_level; + uint32_t s_lastcheck; + uint32_t s_checkinterval; + uint32_t s_creator_os; + uint32_t s_rev_level; + uint16_t s_def_resuid; + uint16_t s_def_resgid; + uint32_t s_first_ino; + uint16_t s_inode_size; + uint16_t s_block_group_nr; + uint32_t s_feature_compat; + uint32_t s_feature_incompat; + uint32_t s_feature_ro_compat; + unsigned char s_uuid[16]; + char s_volume_name[16]; + char s_last_mounted[64]; + uint32_t s_algorithm_usage_bitmap; + uint8_t s_prealloc_blocks; + uint8_t s_prealloc_dir_blocks; + uint16_t s_reserved_gdt_blocks; + uint8_t s_journal_uuid[16]; + uint32_t s_journal_inum; + uint32_t s_journal_dev; + uint32_t s_last_orphan; + uint32_t s_hash_seed[4]; + uint8_t s_def_hash_version; + uint8_t s_jnl_backup_type; + uint16_t s_reserved_word_pad; + uint32_t s_default_mount_opts; + uint32_t s_first_meta_bg; + uint32_t s_mkfs_time; + uint32_t s_jnl_blocks[17]; + uint32_t s_blocks_count_hi; + uint32_t s_r_blocks_count_hi; + uint32_t s_free_blocks_hi; + uint16_t s_min_extra_isize; + uint16_t s_want_extra_isize; + uint32_t s_flags; + uint16_t s_raid_stride; + uint16_t s_mmp_interval; + uint64_t s_mmp_block; + uint32_t s_raid_stripe_width; + uint32_t s_reserved[163]; +} __attribute__((packed)); + +/* magic string */ +#define EXT_SB_MAGIC "\123\357" +/* supper block offset */ +#define EXT_SB_OFF 0x400 +/* supper block offset in kB */ +#define EXT_SB_KBOFF (EXT_SB_OFF >> 10) +/* magic string offset within super block */ +#define EXT_MAG_OFF 0x38 + + + +/* for s_flags */ +#define EXT2_FLAGS_TEST_FILESYS 0x0004 + +/* for s_feature_compat */ +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 + +/* for s_feature_ro_compat */ +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 + +/* for s_feature_incompat */ +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */ +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 + +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT2_FEATURE_INCOMPAT_META_BG) +#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP +#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP + +#define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT3_FEATURE_INCOMPAT_RECOVER| \ + EXT2_FEATURE_INCOMPAT_META_BG) +#define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP +#define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP + +/* + * Check to see if a filesystem is in /proc/filesystems. + * Returns 1 if found, 0 if not + */ +static int fs_proc_check(const char *fs_name) +{ + FILE *f; + char buf[80], *cp, *t; + + f = fopen("/proc/filesystems", "r"); + if (!f) + return 0; + while (!feof(f)) { + if (!fgets(buf, sizeof(buf), f)) + break; + cp = buf; + if (!isspace(*cp)) { + while (*cp && !isspace(*cp)) + cp++; + } + while (*cp && isspace(*cp)) + cp++; + if ((t = strchr(cp, '\n')) != NULL) + *t = 0; + if ((t = strchr(cp, '\t')) != NULL) + *t = 0; + if ((t = strchr(cp, ' ')) != NULL) + *t = 0; + if (!strcmp(fs_name, cp)) { + fclose(f); + return 1; + } + } + fclose(f); + return (0); +} + +/* + * Check to see if a filesystem is available as a module + * Returns 1 if found, 0 if not + */ +static int check_for_modules(const char *fs_name) +{ +#ifdef __linux__ + struct utsname uts; + FILE *f; + char buf[1024], *cp; + int namesz; + + if (uname(&uts)) + return 0; + snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release); + + f = fopen(buf, "r"); + if (!f) + return 0; + + namesz = strlen(fs_name); + + while (!feof(f)) { + if (!fgets(buf, sizeof(buf), f)) + break; + if ((cp = strchr(buf, ':')) != NULL) + *cp = 0; + else + continue; + if ((cp = strrchr(buf, '/')) == NULL) + continue; + cp++; + + if (!strncmp(cp, fs_name, namesz) && + (!strcmp(cp + namesz, ".ko") || + !strcmp(cp + namesz, ".ko.gz"))) { + fclose(f); + return 1; + } + } + fclose(f); +#endif /* __linux__ */ + return 0; +} + +/* + * Starting in 2.6.29, ext4 can be used to support filesystems + * without a journal. + */ +#define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29) + +static int system_supports_ext2(void) +{ + static time_t last_check = 0; + static int ret = -1; + time_t now = time(0); + + if (ret != -1 || (now - last_check) < 5) + return ret; + last_check = now; + ret = (fs_proc_check("ext2") || check_for_modules("ext2")); + return ret; +} + +static int system_supports_ext4(void) +{ + static time_t last_check = 0; + static int ret = -1; + time_t now = time(0); + + if (ret != -1 || (now - last_check) < 5) + return ret; + last_check = now; + ret = (fs_proc_check("ext4") || check_for_modules("ext4")); + return ret; +} + +static int system_supports_ext4dev(void) +{ + static time_t last_check = 0; + static int ret = -1; + time_t now = time(0); + + if (ret != -1 || (now - last_check) < 5) + return ret; + last_check = now; + ret = (fs_proc_check("ext4dev") || check_for_modules("ext4dev")); + return ret; +} +/* + * reads superblock and returns: + * fc = feature_compat + * fi = feature_incompat + * frc = feature_ro_compat + */ +static struct ext2_super_block *ext_get_super( + blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc) +{ + struct ext2_super_block *es; + + es = (struct ext2_super_block *) + blkid_probe_get_buffer(pr, EXT_SB_OFF, 0x200); + if (!es) + return NULL; + if (fc) + *fc = le32_to_cpu(es->s_feature_compat); + if (fi) + *fi = le32_to_cpu(es->s_feature_incompat); + if (frc) + *frc = le32_to_cpu(es->s_feature_ro_compat); + + return es; +} + +static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es) +{ + //struct blkid_chain *chn = blkid_probe_get_chain(pr); + + DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n", + le32_to_cpu(es->s_feature_compat), + le32_to_cpu(es->s_feature_incompat), + le32_to_cpu(es->s_feature_ro_compat))); + + if (strlen(es->s_volume_name)) + blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name, + sizeof(es->s_volume_name)); + blkid_probe_set_uuid(pr, es->s_uuid); + + if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) + blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL"); + +/* if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) && + ((le32_to_cpu(es->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0)) + blkid_probe_set_value(pr, "SEC_TYPE", + (unsigned char *) "ext2", + sizeof("ext2"));*/ + + blkid_probe_sprintf_version(pr, "%u.%u", + le32_to_cpu(es->s_rev_level), + le16_to_cpu(es->s_minor_rev_level)); +} + + +static int probe_jbd(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fi; + + es = ext_get_super(pr, NULL, &fi, NULL); + if (!es) + return -BLKID_ERR_PARAM; + if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) + return -BLKID_ERR_PARAM; + + ext_get_info(pr, 2, es); + return 0; +} + +static int probe_ext2(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return -BLKID_ERR_PARAM; + + /* Distinguish between ext3 and ext2 */ + if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) + return -BLKID_ERR_PARAM; + + /* Any features which ext2 doesn't understand */ + if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) || + (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED)) + return -BLKID_ERR_PARAM; + + /* + * If ext2 is not present, but ext4 or ext4dev are, then + * disclaim we are ext2 + */ + if (!system_supports_ext2() && + (system_supports_ext4() || system_supports_ext4dev()) && + get_linux_version() >= EXT4_SUPPORTS_EXT2) + return -BLKID_ERR_PARAM; + + ext_get_info(pr, 2, es); + return 0; +} + +static int probe_ext3(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return -BLKID_ERR_PARAM; + + /* ext3 requires journal */ + if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) + return -BLKID_ERR_PARAM; + + /* Any features which ext3 doesn't understand */ + if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) || + (fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) + return -BLKID_ERR_PARAM; + + ext_get_info(pr, 3, es); + return 0; +} + + +static int probe_ext4dev(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return -BLKID_ERR_PARAM; + + /* Distinguish from jbd */ + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return -BLKID_ERR_PARAM; + + /* + * If the filesystem does not have a journal and ext2 and ext4 + * is not present, then force this to be detected as an + * ext4dev filesystem. + */ + if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && + !system_supports_ext2() && !system_supports_ext4() && + system_supports_ext4dev() && + get_linux_version() >= EXT4_SUPPORTS_EXT2) + goto force_ext4dev; + + /* + * If the filesystem is marked as OK for use by in-development + * filesystem code, but ext4dev is not supported, and ext4 is, + * then don't call ourselves ext4dev, since we should be + * detected as ext4 in that case. + * + * If the filesystem is marked as in use by production + * filesystem, then it can only be used by ext4 and NOT by + * ext4dev, so always disclaim we are ext4dev in that case. + */ + if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) { + if (!system_supports_ext4dev() && system_supports_ext4()) + return -BLKID_ERR_PARAM; + } else + return -BLKID_ERR_PARAM; + +force_ext4dev: + ext_get_info(pr, 4, es); + return 0; +} + +static int probe_ext4(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return -1; + + /* Distinguish from jbd */ + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return -BLKID_ERR_PARAM; + + /* + * If the filesystem does not have a journal and ext2 is not + * present, then force this to be detected as an ext2 + * filesystem. + */ + if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && + !system_supports_ext2() && system_supports_ext4() && + get_linux_version() >= EXT4_SUPPORTS_EXT2) + goto force_ext4; + + /* Ext4 has at least one feature which ext3 doesn't understand */ + if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) && + !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) + return -BLKID_ERR_PARAM; + +force_ext4: + /* + * If the filesystem is a OK for use by in-development + * filesystem code, and ext4dev is supported or ext4 is not + * supported, then don't call ourselves ext4, so we can redo + * the detection and mark the filesystem as ext4dev. + * + * If the filesystem is marked as in use by production + * filesystem, then it can only be used by ext4 and NOT by + * ext4dev. + */ + if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) { + if (system_supports_ext4dev() || !system_supports_ext4()) + return -BLKID_ERR_PARAM; + } + + ext_get_info(pr, 4, es); + return 0; +} + +#define BLKID_EXT_MAGICS \ + { \ + { \ + .magic = EXT_SB_MAGIC, \ + .len = sizeof(EXT_SB_MAGIC) - 1, \ + .kboff = EXT_SB_KBOFF, \ + .sboff = EXT_MAG_OFF \ + }, \ + { NULL } \ + } + +const struct blkid_idinfo jbd_idinfo = +{ + .name = "jbd", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_jbd, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext2_idinfo = +{ + .name = "ext2", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext2, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext3_idinfo = +{ + .name = "ext3", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext3, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext4_idinfo = +{ + .name = "ext4", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext4, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext4dev_idinfo = +{ + .name = "ext4dev", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext4dev, + .magics = BLKID_EXT_MAGICS +}; + diff --git a/libblkid-tiny/jffs2.c b/libblkid-tiny/jffs2.c new file mode 100644 index 0000000..098c557 --- /dev/null +++ b/libblkid-tiny/jffs2.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 Karel Zak + * + * Inspired by libvolume_id by + * Kay Sievers + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include +#include +#include +#include +#include + +#include "bitops.h" /* swab16() */ +#include "superblocks.h" + +static int probe_jffs2(blkid_probe pr, const struct blkid_idmag *mag) +{ + return 0; +} + +const struct blkid_idinfo jffs2_idinfo = +{ + .name = "jffs2", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_jffs2, + .magics = + { + { .magic = "\x19\x85", .len = 2 }, + { .magic = "\x85\x19", .len = 2 }, + { NULL } + } +}; diff --git a/libblkid-tiny/libblkid-tiny.c b/libblkid-tiny/libblkid-tiny.c new file mode 100644 index 0000000..dc18aa7 --- /dev/null +++ b/libblkid-tiny/libblkid-tiny.c @@ -0,0 +1,217 @@ +#include +#include + +#include "superblocks.h" +#include "linux_version.h" + +#if 0 +#define DEBUG(fmt, ...) printf(fmt, __VA_ARGS__) +#else +#define DEBUG(fmt, ...) +#endif + +int blkid_debug_mask = 0; + +static unsigned char *probe_buffer; +static unsigned int probe_buffer_size = 0; + +int get_linux_version (void) +{ + static int kver = -1; + struct utsname uts; + int major = 0; + int minor = 0; + int teeny = 0; + int n; + + if (kver != -1) + return kver; + if (uname (&uts)) + return kver = 0; + + n = sscanf(uts.release, "%d.%d.%d", &major, &minor, &teeny); + if (n < 1 || n > 3) + return kver = 0; + + return kver = KERNEL_VERSION(major, minor, teeny); +} + +int blkid_probe_is_tiny(blkid_probe pr) +{ + /* never true ? */ + return 0; +} + +int blkid_probe_set_value(blkid_probe pr, const char *name, + unsigned char *data, size_t len) +{ + /* empty stub */ + return 0; +} + +int blkid_probe_set_version(blkid_probe pr, const char *version) +{ + int len = strlen(version); + if (len > (sizeof(pr->version) - 1)) { + fprintf(stderr, "version buffer too small %d\n", len); + return -1; + } + + strncpy(pr->version, version, sizeof(pr->version)); + + return 0; +} + +int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...) +{ + va_list ap; + int n; + + va_start(ap, fmt); + n = vsnprintf(pr->version, sizeof(pr->version), fmt, ap); + va_end(ap); + + if (n >= sizeof(pr->version)) + fprintf(stderr, "version buffer too small %d\n", n); + + return 0; +} + +unsigned char *blkid_probe_get_buffer(blkid_probe pr, + blkid_loff_t off, blkid_loff_t len) +{ + int ret; + unsigned char *buf; + + if (len > probe_buffer_size) { + buf = realloc(probe_buffer, len); + + if (!buf) { + fprintf(stderr, "failed to allocate %d byte buffer\n", + (int)len); + + return NULL; + } + + probe_buffer = buf; + probe_buffer_size = len; + } + + memset(probe_buffer, 0, probe_buffer_size); + + lseek(pr->fd, off, SEEK_SET); + ret = read(pr->fd, probe_buffer, len); + + if (ret != len) + fprintf(stderr, "faile to read blkid\n"); + + return probe_buffer; +} + +int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len) +{ + if (len > (sizeof(pr->label) - 1)) { + fprintf(stderr, "label buffer too small %d > %d\n", + (int) len, (int) sizeof(pr->label) - 1); + return -1; + } + memcpy(pr->label, label, len + 1); + + return 0; +} + +int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name) +{ + short unsigned int*u = (short unsigned int*) uuid; + + if (u[0]) + sprintf(pr->uuid, + "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + be16_to_cpu(u[0]), be16_to_cpu(u[1]), be16_to_cpu(u[2]), be16_to_cpu(u[3]), + be16_to_cpu(u[4]), be16_to_cpu(u[5]), be16_to_cpu(u[6]), be16_to_cpu(u[7])); + if (name) + strncpy(pr->name, name, sizeof(pr->name)); + + return 0; +} + +int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid) +{ + return blkid_probe_set_uuid_as(pr, uuid, NULL); +} + +int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid, + size_t len, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(pr->uuid, sizeof(pr->uuid), fmt, ap); + va_end(ap); + + return 0; +} + +static const struct blkid_idinfo *idinfos[] = +{ + &vfat_idinfo, + &swsuspend_idinfo, + &swap_idinfo, + &ext4dev_idinfo, + &ext4_idinfo, + &ext3_idinfo, + &ext2_idinfo, + &jbd_idinfo, + &squashfs_idinfo, + &jffs2_idinfo, +}; + +int probe_block(char *block, struct blkid_struct_probe *pr) +{ + struct stat s; + int i; + + if (stat(block, &s) || (!S_ISBLK(s.st_mode) && !S_ISREG(s.st_mode))) + return -1; + + pr->err = -1; + pr->fd = open(block, O_RDONLY); + if (!pr->fd) + return -1; + + for (i = 0; i < ARRAY_SIZE(idinfos); i++) { + /* loop over all magic handlers */ + const struct blkid_idmag *mag; + + /* loop over all probe handlers */ + DEBUG("scanning %s\n", idinfos[i]->name); + + mag = &idinfos[i]->magics[0]; + + while (mag->magic) { + int off = (mag->kboff * 1024) + mag->sboff; + char magic[32] = { 0 }; + + lseek(pr->fd, off, SEEK_SET); + read(pr->fd, magic, mag->len); + + DEBUG("magic: %s %s %d\n", mag->magic, magic, mag->len); + if (!memcmp(mag->magic, magic, mag->len)) + break; + mag++; + } + + if (mag && mag->magic) { + DEBUG("probing %s\n", idinfos[i]->name); + pr->err = idinfos[i]->probefunc(pr, mag); + pr->id = idinfos[i]; + strcpy(pr->dev, block); + if (!pr->err) + break; + } + } + + close(pr->fd); + + return 0; +} diff --git a/libblkid-tiny/libblkid-tiny.h b/libblkid-tiny/libblkid-tiny.h new file mode 100644 index 0000000..af12658 --- /dev/null +++ b/libblkid-tiny/libblkid-tiny.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +struct blkid_idmag; +struct blkid_idmag; +struct blkid_idinfo; + +struct blkid_idmag +{ + const char *magic; + unsigned int len; + + long kboff; + unsigned int sboff; +}; + + +struct blkid_idinfo; + +struct blkid_struct_probe +{ + const struct blkid_idinfo *id; + struct list_head list; + + int fd; + int err; + char dev[32]; + char uuid[64]; + char label[64]; + char name[64]; + char version[64]; +}; + +struct blkid_idinfo +{ + const char *name; + int usage; + int flags; + int minsz; + int (*probefunc)(struct blkid_struct_probe *pr, const struct blkid_idmag *mag); + struct blkid_idmag magics[]; +}; + +extern int probe_block(char *block, struct blkid_struct_probe *pr); +extern int mkblkdev(void); diff --git a/libblkid-tiny/linux_version.h b/libblkid-tiny/linux_version.h new file mode 100644 index 0000000..a6a1e99 --- /dev/null +++ b/libblkid-tiny/linux_version.h @@ -0,0 +1,14 @@ +#ifndef LINUX_VERSION_H +#define LINUX_VERSION_H + +#ifdef HAVE_LINUX_VERSION_H +# include +#endif + +#ifndef KERNEL_VERSION +# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +int get_linux_version(void); + +#endif /* LINUX_VERSION_H */ diff --git a/libblkid-tiny/mkdev.c b/libblkid-tiny/mkdev.c new file mode 100644 index 0000000..3d067b2 --- /dev/null +++ b/libblkid-tiny/mkdev.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define _BSD_SOURCE + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libblkid-tiny.h" + +#include + +static char buf[PATH_MAX]; +static char buf2[PATH_MAX]; +static unsigned int mode = 0600; + +static void make_dev(const char *path, bool block, int major, int minor) +{ + unsigned int _mode = mode | (block ? S_IFBLK : S_IFCHR); + + mknod(path, _mode, makedev(major, minor)); +} + +static void find_devs(bool block) +{ + char *path = block ? "/sys/dev/block" : "/sys/dev/char"; + struct dirent *dp; + DIR *dir; + + dir = opendir(path); + if (!dir) + return; + + path = buf2 + sprintf(buf2, "%s/", path); + while ((dp = readdir(dir)) != NULL) { + char *c; + int major = 0, minor = 0; + int len; + + if (dp->d_type != DT_LNK) + continue; + + if (sscanf(dp->d_name, "%d:%d", &major, &minor) != 2) + continue; + + strcpy(path, dp->d_name); + len = readlink(buf2, buf, sizeof(buf)); + if (len <= 0) + continue; + + buf[len] = 0; + + c = strrchr(buf, '/'); + if (!c) + continue; + + + c++; + make_dev(c, block, major, minor); + } + closedir(dir); +} + +int mkblkdev(void) +{ + if (chdir("/dev")) + return 1; + + mode = 0600; + find_devs(true); + chdir("/"); + + return 0; +} diff --git a/libblkid-tiny/squashfs.c b/libblkid-tiny/squashfs.c new file mode 100644 index 0000000..d04f0b1 --- /dev/null +++ b/libblkid-tiny/squashfs.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008 Karel Zak + * + * Inspired by libvolume_id by + * Kay Sievers + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include +#include +#include +#include +#include + +#include "bitops.h" /* swab16() */ +#include "superblocks.h" + +#include + +struct squashfs_super_block { + uint32_t s_magic; + uint32_t inodes; + uint32_t mkfs_time; + uint32_t block_size; + uint32_t fragments; + uint16_t compression; + uint16_t block_log; + uint16_t flags; + uint16_t no_ids; + uint16_t s_major; + uint16_t s_minor; + uint64_t root_inode; + uint64_t bytes_used; + uint64_t id_table_start; + uint64_t xattr_id_table_start; + uint64_t inode_table_start; + uint64_t directory_table_start; + uint64_t fragment_table_start; + uint64_t lookup_table_start; +} __attribute__((packed)); + +static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + md5_ctx_t ctx = { 0 }; + uint32_t md5[4]; + struct squashfs_super_block *sq; + + sq = blkid_probe_get_sb(pr, mag, struct squashfs_super_block); + if (!sq) + return -1; + + if (strcmp(mag->magic, "sqsh") == 0 || + strcmp(mag->magic, "qshs") == 0) + blkid_probe_sprintf_version(pr, "%u.%u", + sq->s_major, + sq->s_minor); + else + blkid_probe_sprintf_version(pr, "%u.%u", + swab16(sq->s_major), + swab16(sq->s_minor)); + md5_begin(&ctx); + md5_hash(sq, sizeof(*sq), &ctx); + md5_end(&md5, &ctx); + blkid_probe_sprintf_uuid(pr, NULL, 4, "%08x-%08x-%08x-%08x", + md5[3], md5[2], md5[1], md5[0]); + return 0; +} + +const struct blkid_idinfo squashfs_idinfo = +{ + .name = "squashfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_squashfs, + .magics = + { + { .magic = "sqsh", .len = 4 }, + { .magic = "hsqs", .len = 4 }, /* swap */ + + /* LZMA version */ + { .magic = "qshs", .len = 4 }, + { .magic = "shsq", .len = 4 }, /* swap */ + { NULL } + } +}; + + diff --git a/libblkid-tiny/superblocks.h b/libblkid-tiny/superblocks.h new file mode 100644 index 0000000..24f59bb --- /dev/null +++ b/libblkid-tiny/superblocks.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008-2009 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#ifndef _BLKID_SUPERBLOCKS_H +#define _BLKID_SUPERBLOCKS_H + +#include "blkidP.h" + +extern const struct blkid_idinfo cramfs_idinfo; +extern const struct blkid_idinfo swap_idinfo; +extern const struct blkid_idinfo swsuspend_idinfo; +extern const struct blkid_idinfo adraid_idinfo; +extern const struct blkid_idinfo ddfraid_idinfo; +extern const struct blkid_idinfo iswraid_idinfo; +extern const struct blkid_idinfo jmraid_idinfo; +extern const struct blkid_idinfo lsiraid_idinfo; +extern const struct blkid_idinfo nvraid_idinfo; +extern const struct blkid_idinfo pdcraid_idinfo; +extern const struct blkid_idinfo silraid_idinfo; +extern const struct blkid_idinfo viaraid_idinfo; +extern const struct blkid_idinfo linuxraid_idinfo; +extern const struct blkid_idinfo ext4dev_idinfo; +extern const struct blkid_idinfo ext4_idinfo; +extern const struct blkid_idinfo ext3_idinfo; +extern const struct blkid_idinfo ext2_idinfo; +extern const struct blkid_idinfo jbd_idinfo; +extern const struct blkid_idinfo jfs_idinfo; +extern const struct blkid_idinfo xfs_idinfo; +extern const struct blkid_idinfo gfs_idinfo; +extern const struct blkid_idinfo gfs2_idinfo; +extern const struct blkid_idinfo romfs_idinfo; +extern const struct blkid_idinfo ocfs_idinfo; +extern const struct blkid_idinfo ocfs2_idinfo; +extern const struct blkid_idinfo oracleasm_idinfo; +extern const struct blkid_idinfo reiser_idinfo; +extern const struct blkid_idinfo reiser4_idinfo; +extern const struct blkid_idinfo hfs_idinfo; +extern const struct blkid_idinfo hfsplus_idinfo; +extern const struct blkid_idinfo ntfs_idinfo; +extern const struct blkid_idinfo iso9660_idinfo; +extern const struct blkid_idinfo udf_idinfo; +extern const struct blkid_idinfo vxfs_idinfo; +extern const struct blkid_idinfo minix_idinfo; +extern const struct blkid_idinfo vfat_idinfo; +extern const struct blkid_idinfo ufs_idinfo; +extern const struct blkid_idinfo hpfs_idinfo; +extern const struct blkid_idinfo lvm2_idinfo; +extern const struct blkid_idinfo lvm1_idinfo; +extern const struct blkid_idinfo snapcow_idinfo; +extern const struct blkid_idinfo luks_idinfo; +extern const struct blkid_idinfo highpoint37x_idinfo; +extern const struct blkid_idinfo highpoint45x_idinfo; +extern const struct blkid_idinfo squashfs_idinfo; +extern const struct blkid_idinfo netware_idinfo; +extern const struct blkid_idinfo sysv_idinfo; +extern const struct blkid_idinfo xenix_idinfo; +extern const struct blkid_idinfo btrfs_idinfo; +extern const struct blkid_idinfo ubifs_idinfo; +extern const struct blkid_idinfo zfs_idinfo; +extern const struct blkid_idinfo bfs_idinfo; +extern const struct blkid_idinfo vmfs_volume_idinfo; +extern const struct blkid_idinfo vmfs_fs_idinfo; +extern const struct blkid_idinfo drbd_idinfo; +extern const struct blkid_idinfo drbdproxy_datalog_idinfo; +extern const struct blkid_idinfo befs_idinfo; +extern const struct blkid_idinfo nilfs2_idinfo; +extern const struct blkid_idinfo exfat_idinfo; +extern const struct blkid_idinfo jffs2_idinfo; + +/* + * superblock functions + */ +extern int blkid_probe_set_version(blkid_probe pr, const char *version); +extern int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); + +extern int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len); +extern int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label, + size_t len, int enc); +extern int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid, + size_t len, const char *fmt, ...) + __attribute__ ((format (printf, 4, 5))); +extern int blkid_probe_strncpy_uuid(blkid_probe pr, unsigned char *str, size_t len); + +extern int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid); +extern int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name); + + +#endif /* _BLKID_SUPERBLOCKS_H */ diff --git a/libblkid-tiny/swap.c b/libblkid-tiny/swap.c new file mode 100644 index 0000000..a598a7c --- /dev/null +++ b/libblkid-tiny/swap.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers + * Copyright (C) 2008 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "superblocks.h" + +/* linux-2.6/include/linux/swap.h */ +struct swap_header_v1_2 { + /* char bootbits[1024]; */ /* Space for disklabel etc. */ + uint32_t version; + uint32_t lastpage; + uint32_t nr_badpages; + unsigned char uuid[16]; + unsigned char volume[16]; + uint32_t padding[117]; + uint32_t badpages[1]; +} __attribute__((packed)); + +#define PAGESIZE_MIN 0xff6 /* 4086 (arm, i386, ...) */ +#define PAGESIZE_MAX 0xfff6 /* 65526 (ia64) */ + +#define TOI_MAGIC_STRING "\xed\xc3\x02\xe9\x98\x56\xe5\x0c" +#define TOI_MAGIC_STRLEN (sizeof(TOI_MAGIC_STRING) - 1) + +static int swap_set_info(blkid_probe pr, const char *version) +{ + struct swap_header_v1_2 *hdr; + + /* Swap header always located at offset of 1024 bytes */ + hdr = (struct swap_header_v1_2 *) blkid_probe_get_buffer(pr, 1024, + sizeof(struct swap_header_v1_2)); + if (!hdr) + return -1; + + /* SWAPSPACE2 - check for wrong version or zeroed pagecount */ + if (strcmp(version, "2") == 0 && + ((hdr->version != 1 && swab32(hdr->version) != 1) || hdr->lastpage == 0)) + return -1; + + /* arbitrary sanity check.. is there any garbage down there? */ + if (hdr->padding[32] == 0 && hdr->padding[33] == 0) { + if (hdr->volume[0] && blkid_probe_set_label(pr, hdr->volume, + sizeof(hdr->volume)) < 0) + return -1; + if (blkid_probe_set_uuid(pr, hdr->uuid) < 0) + return -1; + } + + blkid_probe_set_version(pr, version); + return 0; +} + +static int probe_swap(blkid_probe pr, const struct blkid_idmag *mag) +{ + unsigned char *buf; + + if (!mag) + return -1; + + /* TuxOnIce keeps valid swap header at the end of the 1st page */ + buf = blkid_probe_get_buffer(pr, 0, TOI_MAGIC_STRLEN); + if (!buf) + return -1; + + if (memcmp(buf, TOI_MAGIC_STRING, TOI_MAGIC_STRLEN) == 0) + return 1; /* Ignore swap signature, it's TuxOnIce */ + + if (!memcmp(mag->magic, "SWAP-SPACE", mag->len)) { + /* swap v0 doesn't support LABEL or UUID */ + blkid_probe_set_version(pr, "1"); + return 0; + + } else if (!memcmp(mag->magic, "SWAPSPACE2", mag->len)) + return swap_set_info(pr, "2"); + + return -1; +} + +static int probe_swsuspend(blkid_probe pr, const struct blkid_idmag *mag) +{ + if (!mag) + return -1; + if (!memcmp(mag->magic, "S1SUSPEND", mag->len)) + return swap_set_info(pr, "s1suspend"); + if (!memcmp(mag->magic, "S2SUSPEND", mag->len)) + return swap_set_info(pr, "s2suspend"); + if (!memcmp(mag->magic, "ULSUSPEND", mag->len)) + return swap_set_info(pr, "ulsuspend"); + if (!memcmp(mag->magic, TOI_MAGIC_STRING, mag->len)) + return swap_set_info(pr, "tuxonice"); + if (!memcmp(mag->magic, "LINHIB0001", mag->len)) + return swap_set_info(pr, "linhib0001"); + + return -1; /* no signature detected */ +} + +const struct blkid_idinfo swap_idinfo = +{ + .name = "swap", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_swap, + .minsz = 10 * 4096, /* 10 pages */ + .magics = + { + { "SWAP-SPACE", 10, 0, 0xff6 }, + { "SWAPSPACE2", 10, 0, 0xff6 }, + { "SWAP-SPACE", 10, 0, 0x1ff6 }, + { "SWAPSPACE2", 10, 0, 0x1ff6 }, + { "SWAP-SPACE", 10, 0, 0x3ff6 }, + { "SWAPSPACE2", 10, 0, 0x3ff6 }, + { "SWAP-SPACE", 10, 0, 0x7ff6 }, + { "SWAPSPACE2", 10, 0, 0x7ff6 }, + { "SWAP-SPACE", 10, 0, 0xfff6 }, + { "SWAPSPACE2", 10, 0, 0xfff6 }, + { NULL } + } +}; + + +const struct blkid_idinfo swsuspend_idinfo = +{ + .name = "swsuspend", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_swsuspend, + .minsz = 10 * 4096, /* 10 pages */ + .magics = + { + { TOI_MAGIC_STRING, TOI_MAGIC_STRLEN, 0, 0 }, + { "S1SUSPEND", 9, 0, 0xff6 }, + { "S2SUSPEND", 9, 0, 0xff6 }, + { "ULSUSPEND", 9, 0, 0xff6 }, + { "LINHIB0001",10,0, 0xff6 }, + + { "S1SUSPEND", 9, 0, 0x1ff6 }, + { "S2SUSPEND", 9, 0, 0x1ff6 }, + { "ULSUSPEND", 9, 0, 0x1ff6 }, + { "LINHIB0001",10,0, 0x1ff6 }, + + { "S1SUSPEND", 9, 0, 0x3ff6 }, + { "S2SUSPEND", 9, 0, 0x3ff6 }, + { "ULSUSPEND", 9, 0, 0x3ff6 }, + { "LINHIB0001",10,0, 0x3ff6 }, + + { "S1SUSPEND", 9, 0, 0x7ff6 }, + { "S2SUSPEND", 9, 0, 0x7ff6 }, + { "ULSUSPEND", 9, 0, 0x7ff6 }, + { "LINHIB0001",10,0, 0x7ff6 }, + + { "S1SUSPEND", 9, 0, 0xfff6 }, + { "S2SUSPEND", 9, 0, 0xfff6 }, + { "ULSUSPEND", 9, 0, 0xfff6 }, + { "LINHIB0001",10,0, 0xfff6 }, + + { NULL } + } +}; + diff --git a/libblkid-tiny/ubifs.c b/libblkid-tiny/ubifs.c new file mode 100644 index 0000000..2d69c2b --- /dev/null +++ b/libblkid-tiny/ubifs.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2009 Corentin Chary + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include +#include +#include +#include +#include + +#include "superblocks.h" + +/* + * struct ubifs_ch - common header node. + * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC) + * @crc: CRC-32 checksum of the node header + * @sqnum: sequence number + * @len: full node length + * @node_type: node type + * @group_type: node group type + * @padding: reserved for future, zeroes + * + * Every UBIFS node starts with this common part. If the node has a key, the + * key always goes next. + */ +struct ubifs_ch { + uint32_t magic; + uint32_t crc; + uint64_t sqnum; + uint32_t len; + uint8_t node_type; + uint8_t group_type; + uint8_t padding[2]; +} __attribute__ ((packed)); + +/* + * struct ubifs_sb_node - superblock node. + * @ch: common header + * @padding: reserved for future, zeroes + * @key_hash: type of hash function used in keys + * @key_fmt: format of the key + * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc) + * @min_io_size: minimal input/output unit size + * @leb_size: logical eraseblock size in bytes + * @leb_cnt: count of LEBs used by file-system + * @max_leb_cnt: maximum count of LEBs used by file-system + * @max_bud_bytes: maximum amount of data stored in buds + * @log_lebs: log size in logical eraseblocks + * @lpt_lebs: number of LEBs used for lprops table + * @orph_lebs: number of LEBs used for recording orphans + * @jhead_cnt: count of journal heads + * @fanout: tree fanout (max. number of links per indexing node) + * @lsave_cnt: number of LEB numbers in LPT's save table + * @fmt_version: UBIFS on-flash format version + * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc) + * @padding1: reserved for future, zeroes + * @rp_uid: reserve pool UID + * @rp_gid: reserve pool GID + * @rp_size: size of the reserved pool in bytes + * @padding2: reserved for future, zeroes + * @time_gran: time granularity in nanoseconds + * @uuid: UUID generated when the file system image was created + * @ro_compat_version: UBIFS R/O compatibility version + */ +struct ubifs_sb_node { + struct ubifs_ch ch; + uint8_t padding[2]; + uint8_t key_hash; + uint8_t key_fmt; + uint32_t flags; + uint32_t min_io_size; + uint32_t leb_size; + uint32_t leb_cnt; + uint32_t max_leb_cnt; + uint64_t max_bud_bytes; + uint32_t log_lebs; + uint32_t lpt_lebs; + uint32_t orph_lebs; + uint32_t jhead_cnt; + uint32_t fanout; + uint32_t lsave_cnt; + uint32_t fmt_version; + uint16_t default_compr; + uint8_t padding1[2]; + uint32_t rp_uid; + uint32_t rp_gid; + uint64_t rp_size; + uint32_t time_gran; + uint8_t uuid[16]; + uint32_t ro_compat_version; + uint8_t padding2[3968]; +} __attribute__ ((packed)); + +static int probe_ubifs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct ubifs_sb_node *sb; + + sb = blkid_probe_get_sb(pr, mag, struct ubifs_sb_node); + if (!sb) + return -1; + + blkid_probe_set_uuid(pr, sb->uuid); + blkid_probe_sprintf_version(pr, "w%dr%d", + sb->fmt_version, sb->ro_compat_version); + return 0; +} + +const struct blkid_idinfo ubifs_idinfo = +{ + .name = "ubifs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ubifs, + .magics = + { + { .magic = "\x31\x18\x10\x06", .len = 4 }, + { NULL } + } +}; diff --git a/libblkid-tiny/vfat.c b/libblkid-tiny/vfat.c new file mode 100644 index 0000000..ef66769 --- /dev/null +++ b/libblkid-tiny/vfat.c @@ -0,0 +1,444 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers + * Copyright (C) 2008 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "superblocks.h" + +/* Yucky misaligned values */ +struct vfat_super_block { +/* 00*/ unsigned char vs_ignored[3]; +/* 03*/ unsigned char vs_sysid[8]; +/* 0b*/ unsigned char vs_sector_size[2]; +/* 0d*/ uint8_t vs_cluster_size; +/* 0e*/ uint16_t vs_reserved; +/* 10*/ uint8_t vs_fats; +/* 11*/ unsigned char vs_dir_entries[2]; +/* 13*/ unsigned char vs_sectors[2]; +/* 15*/ unsigned char vs_media; +/* 16*/ uint16_t vs_fat_length; +/* 18*/ uint16_t vs_secs_track; +/* 1a*/ uint16_t vs_heads; +/* 1c*/ uint32_t vs_hidden; +/* 20*/ uint32_t vs_total_sect; +/* 24*/ uint32_t vs_fat32_length; +/* 28*/ uint16_t vs_flags; +/* 2a*/ uint8_t vs_version[2]; +/* 2c*/ uint32_t vs_root_cluster; +/* 30*/ uint16_t vs_fsinfo_sector; +/* 32*/ uint16_t vs_backup_boot; +/* 34*/ uint16_t vs_reserved2[6]; +/* 40*/ unsigned char vs_unknown[3]; +/* 43*/ unsigned char vs_serno[4]; +/* 47*/ unsigned char vs_label[11]; +/* 52*/ unsigned char vs_magic[8]; +/* 5a*/ unsigned char vs_dummy2[0x1fe - 0x5a]; +/*1fe*/ unsigned char vs_pmagic[2]; +} __attribute__((packed)); + +/* Yucky misaligned values */ +struct msdos_super_block { +/* 00*/ unsigned char ms_ignored[3]; +/* 03*/ unsigned char ms_sysid[8]; +/* 0b*/ unsigned char ms_sector_size[2]; +/* 0d*/ uint8_t ms_cluster_size; +/* 0e*/ uint16_t ms_reserved; +/* 10*/ uint8_t ms_fats; +/* 11*/ unsigned char ms_dir_entries[2]; +/* 13*/ unsigned char ms_sectors[2]; /* =0 iff V3 or later */ +/* 15*/ unsigned char ms_media; +/* 16*/ uint16_t ms_fat_length; /* Sectors per FAT */ +/* 18*/ uint16_t ms_secs_track; +/* 1a*/ uint16_t ms_heads; +/* 1c*/ uint32_t ms_hidden; +/* V3 BPB */ +/* 20*/ uint32_t ms_total_sect; /* iff ms_sectors == 0 */ +/* V4 BPB */ +/* 24*/ unsigned char ms_unknown[3]; /* Phys drive no., resvd, V4 sig (0x29) */ +/* 27*/ unsigned char ms_serno[4]; +/* 2b*/ unsigned char ms_label[11]; +/* 36*/ unsigned char ms_magic[8]; +/* 3e*/ unsigned char ms_dummy2[0x1fe - 0x3e]; +/*1fe*/ unsigned char ms_pmagic[2]; +} __attribute__((packed)); + +struct vfat_dir_entry { + uint8_t name[11]; + uint8_t attr; + uint16_t time_creat; + uint16_t date_creat; + uint16_t time_acc; + uint16_t date_acc; + uint16_t cluster_high; + uint16_t time_write; + uint16_t date_write; + uint16_t cluster_low; + uint32_t size; +} __attribute__((packed)); + +struct fat32_fsinfo { + uint8_t signature1[4]; + uint32_t reserved1[120]; + uint8_t signature2[4]; + uint32_t free_clusters; + uint32_t next_cluster; + uint32_t reserved2[4]; +} __attribute__((packed)); + +/* maximum number of clusters */ +#define FAT12_MAX 0xFF4 +#define FAT16_MAX 0xFFF4 +#define FAT32_MAX 0x0FFFFFF6 + +#define FAT_ATTR_VOLUME_ID 0x08 +#define FAT_ATTR_DIR 0x10 +#define FAT_ATTR_LONG_NAME 0x0f +#define FAT_ATTR_MASK 0x3f +#define FAT_ENTRY_FREE 0xe5 + +static const char *no_name = "NO NAME "; + +#define unaligned_le16(x) \ + (((unsigned char *) x)[0] + (((unsigned char *) x)[1] << 8)) + +/* + * Look for LABEL (name) in the FAT root directory. + */ +static unsigned char *search_fat_label(blkid_probe pr, + uint64_t offset, uint32_t entries) +{ + struct vfat_dir_entry *ent, *dir = NULL; + uint32_t i; + + DBG(DEBUG_LOWPROBE, + printf("\tlook for label in root-dir " + "(entries: %d, offset: %jd)\n", entries, offset)); + + if (!blkid_probe_is_tiny(pr)) { + /* large disk, read whole root directory */ + dir = (struct vfat_dir_entry *) + blkid_probe_get_buffer(pr, + offset, + (blkid_loff_t) entries * + sizeof(struct vfat_dir_entry)); + if (!dir) + return NULL; + } + + for (i = 0; i < entries; i++) { + /* + * The root directory could be relatively large (4-16kB). + * Fortunately, the LABEL is usually the first entry in the + * directory. On tiny disks we call read() per entry. + */ + if (!dir) + ent = (struct vfat_dir_entry *) + blkid_probe_get_buffer(pr, + (blkid_loff_t) offset + (i * + sizeof(struct vfat_dir_entry)), + sizeof(struct vfat_dir_entry)); + else + ent = &dir[i]; + + if (!ent || ent->name[0] == 0x00) + break; + + if ((ent->name[0] == FAT_ENTRY_FREE) || + (ent->cluster_high != 0 || ent->cluster_low != 0) || + ((ent->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)) + continue; + + if ((ent->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == + FAT_ATTR_VOLUME_ID) { + DBG(DEBUG_LOWPROBE, + printf("\tfound fs LABEL at entry %d\n", i)); + return ent->name; + } + } + return NULL; +} + +static int fat_valid_superblock(const struct blkid_idmag *mag, + struct msdos_super_block *ms, + struct vfat_super_block *vs, + uint32_t *cluster_count, uint32_t *fat_size) +{ + uint16_t sector_size, dir_entries, reserved; + uint32_t sect_count, __fat_size, dir_size, __cluster_count, fat_length; + uint32_t max_count; + + /* extra check for FATs without magic strings */ + if (mag->len <= 2) { + /* Old floppies have a valid MBR signature */ + if (ms->ms_pmagic[0] != 0x55 || ms->ms_pmagic[1] != 0xAA) + return 0; + + /* + * OS/2 and apparently DFSee will place a FAT12/16-like + * pseudo-superblock in the first 512 bytes of non-FAT + * filesystems --- at least JFS and HPFS, and possibly others. + * So we explicitly check for those filesystems at the + * FAT12/16 filesystem magic field identifier, and if they are + * present, we rule this out as a FAT filesystem, despite the + * FAT-like pseudo-header. + */ + if ((memcmp(ms->ms_magic, "JFS ", 8) == 0) || + (memcmp(ms->ms_magic, "HPFS ", 8) == 0)) + return 0; + } + + /* fat counts(Linux kernel expects at least 1 FAT table) */ + if (!ms->ms_fats) + return 0; + if (!ms->ms_reserved) + return 0; + if (!(0xf8 <= ms->ms_media || ms->ms_media == 0xf0)) + return 0; + if (!is_power_of_2(ms->ms_cluster_size)) + return 0; + + sector_size = unaligned_le16(&ms->ms_sector_size); + if (!is_power_of_2(sector_size) || + sector_size < 512 || sector_size > 4096) + return 0; + + dir_entries = unaligned_le16(&ms->ms_dir_entries); + reserved = le16_to_cpu(ms->ms_reserved); + sect_count = unaligned_le16(&ms->ms_sectors); + + if (sect_count == 0) + sect_count = le32_to_cpu(ms->ms_total_sect); + + fat_length = le16_to_cpu(ms->ms_fat_length); + if (fat_length == 0) + fat_length = le32_to_cpu(vs->vs_fat32_length); + + __fat_size = fat_length * ms->ms_fats; + dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) + + (sector_size-1)) / sector_size; + + __cluster_count = (sect_count - (reserved + __fat_size + dir_size)) / + ms->ms_cluster_size; + if (!ms->ms_fat_length && vs->vs_fat32_length) + max_count = FAT32_MAX; + else + max_count = __cluster_count > FAT12_MAX ? FAT16_MAX : FAT12_MAX; + + if (__cluster_count > max_count) + return 0; + + if (fat_size) + *fat_size = __fat_size; + if (cluster_count) + *cluster_count = __cluster_count; + + return 1; /* valid */ +} + +/* + * This function is used by MBR partition table parser to avoid + * misinterpretation of FAT filesystem. + */ +/*static int blkid_probe_is_vfat(blkid_probe pr) +{ + struct vfat_super_block *vs; + struct msdos_super_block *ms; + const struct blkid_idmag *mag = NULL; + + if (blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag) || !mag) + return 0; + + ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block); + if (!ms) + return 0; + vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block); + if (!vs) + return 0; + + return fat_valid_superblock(mag, ms, vs, NULL, NULL); +}*/ +static struct vfat_super_block vs; +static struct msdos_super_block ms; + +static int set_label(blkid_probe pr, unsigned char *vol_label) +{ + unsigned char *c; + + for (c = vol_label + 10; c >= vol_label && *c == ' '; c--) + *c = 0; + + if (!*vol_label) + return 0; + + return blkid_probe_set_label(pr, vol_label, 11); +} + +/* FAT label extraction from the root directory taken from Kay + * Sievers's volume_id library */ +static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct vfat_super_block *_vs; + struct msdos_super_block *_ms; + const unsigned char *vol_label = 0; + unsigned char *vol_serno = NULL, vol_label_buf[11]; + uint16_t sector_size = 0, reserved; + uint32_t cluster_count, fat_size; + const char *version = NULL; + + _ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block); + if (!_ms) + return 0; + _vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block); + if (!_vs) + return 0; + memcpy(&ms, _ms, sizeof(struct vfat_super_block)); + memcpy(&vs, _vs, sizeof(struct msdos_super_block)); + if (!fat_valid_superblock(mag, &ms, &vs, &cluster_count, &fat_size)) + return 1; + + sector_size = unaligned_le16(&ms.ms_sector_size); + reserved = le16_to_cpu(ms.ms_reserved); + + if (ms.ms_fat_length) { + /* the label may be an attribute in the root directory */ + uint32_t root_start = (reserved + fat_size) * sector_size; + uint32_t root_dir_entries = unaligned_le16(&vs.vs_dir_entries); + + vol_label = search_fat_label(pr, root_start, root_dir_entries); + if (vol_label) { + memcpy(vol_label_buf, vol_label, 11); + vol_label = vol_label_buf; + } + + if (!vol_label || !memcmp(vol_label, no_name, 11)) + vol_label = ms.ms_label; + vol_serno = ms.ms_serno; + + blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos", + sizeof("msdos")); + + if (cluster_count < FAT12_MAX) + version = "FAT12"; + else if (cluster_count < FAT16_MAX) + version = "FAT16"; + + } else if (vs.vs_fat32_length) { + unsigned char *buf; + uint16_t fsinfo_sect; + int maxloop = 100; + + /* Search the FAT32 root dir for the label attribute */ + uint32_t buf_size = vs.vs_cluster_size * sector_size; + uint32_t start_data_sect = reserved + fat_size; + uint32_t entries = le32_to_cpu(vs.vs_fat32_length) * + sector_size / sizeof(uint32_t); + uint32_t next = le32_to_cpu(vs.vs_root_cluster); + + while (next && next < entries && --maxloop) { + uint32_t next_sect_off; + uint64_t next_off, fat_entry_off; + int count; + + next_sect_off = (next - 2) * le32_to_cpu(vs.vs_cluster_size); + next_off = (uint64_t)(start_data_sect + next_sect_off) * + sector_size; + + count = buf_size / sizeof(struct vfat_dir_entry); + + vol_label = search_fat_label(pr, next_off, count); + if (vol_label) { + memcpy(vol_label_buf, vol_label, 11); + vol_label = vol_label_buf; + break; + } + + /* get FAT entry */ + fat_entry_off = ((uint64_t) reserved * sector_size) + + (next * sizeof(uint32_t)); + buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size); + if (buf == NULL) + break; + + /* set next cluster */ + next = le32_to_cpu(*((uint32_t *) buf)) & 0x0fffffff; + } + + version = "FAT32"; + + if (!vol_label || !memcmp(vol_label, no_name, 11)) + vol_label = NULL; + vol_serno = vs.vs_serno; + + /* + * FAT32 should have a valid signature in the fsinfo block, + * but also allow all bytes set to '\0', because some volumes + * do not set the signature at all. + */ + fsinfo_sect = le16_to_cpu(vs.vs_fsinfo_sector); + if (fsinfo_sect) { + struct fat32_fsinfo *fsinfo; + + buf = blkid_probe_get_buffer(pr, + (blkid_loff_t) fsinfo_sect * sector_size, + sizeof(struct fat32_fsinfo)); + if (buf == NULL) + return -1; + + fsinfo = (struct fat32_fsinfo *) buf; + if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 && + memcmp(fsinfo->signature1, "\x52\x52\x64\x41", 4) != 0 && + memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0) + return -1; + if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 && + memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0) + return -1; + } + } + + if (vol_label && memcmp(vol_label, no_name, 11)) + set_label(pr, (unsigned char *) vol_label); + + /* We can't just print them as %04X, because they are unaligned */ + if (vol_serno) + blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02x%02x-%02x%02x", + vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]); + if (version) + blkid_probe_set_version(pr, version); + + return 0; +} + + +const struct blkid_idinfo vfat_idinfo = +{ + .name = "vfat", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_vfat, + .magics = + { + { .magic = "MSWIN", .len = 5, .sboff = 0x52 }, + { .magic = "FAT32 ", .len = 8, .sboff = 0x52 }, + { .magic = "MSDOS", .len = 5, .sboff = 0x36 }, + { .magic = "FAT16 ", .len = 8, .sboff = 0x36 }, + { .magic = "FAT12 ", .len = 8, .sboff = 0x36 }, + { .magic = "FAT ", .len = 8, .sboff = 0x36 }, + { .magic = "\353", .len = 1, }, + { .magic = "\351", .len = 1, }, + { .magic = "\125\252", .len = 2, .sboff = 0x1fe }, + { NULL } + } +}; + -- 2.11.0