--- /dev/null
+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)
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/mount.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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);
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#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);
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <asm/byteorder.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glob.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#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);
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <mtd/mtd-user.h>
+
+#include <glob.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <libubox/list.h>
+#include <libubox/blob.h>
+#include <libubox/md5.h>
+
+#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);
--- /dev/null
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <libgen.h>
+#include <glob.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/swap.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <uci.h>
+#include <uci_blob.h>
+
+#include <libubox/list.h>
+#include <libubox/vlist.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/avl-cmp.h>
+
+#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 <info|mount|umount|detect>\n");
+
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <libubox/list.h>
+#include <libubox/blob.h>
+
+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
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#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;
+}
+
+
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#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");
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <asm/byteorder.h>
+#include <mtd/mtd-user.h>
+
+#include <errno.h>
+#include <glob.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <string.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * 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
--- /dev/null
+#ifndef BITOPS_H
+#define BITOPS_H
+
+#include <stdint.h>
+
+/*
+ * Bit map related macros. Usually provided by libc.
+ */
+#include <sys/param.h>
+
+#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 */
+
--- /dev/null
+#ifndef BLKDEV_H
+#define BLKDEV_H
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_IOCCOM_H
+# include <sys/ioccom.h> /* for _IO macro on e.g. Solaris */
+#endif
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_MKDEV_H
+# include <sys/mkdev.h> /* 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 */
--- /dev/null
+/*
+ * 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 <kzak@redhat.com>
+ *
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#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 */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include "c.h"
+#include "bitops.h" /* $(top_srcdir)/include/ */
+#include "blkdev.h"
+
+#include "blkid.h"
+#include <libubox/list.h>
+
+/*
+ * 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_<chain>_* */
+ 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=<udev,cache> options */
+ int nevals; /* number of elems in eval array */
+ int uevent; /* SEND_UEVENT=<yes|not> option */
+ char *cachefile; /* CACHE_FILE=<path> 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 */
--- /dev/null
+/*
+ * Fundamental C definitions.
+ */
+
+#ifndef UTIL_LINUX_C_H
+#define UTIL_LINUX_C_H
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_ERR_H
+# include <err.h>
+#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 <sys/types.h>
+#include <dirent.h>
+static inline int dirfd(DIR *d)
+{
+ return d->dd_fd;
+}
+#endif
+
+/*
+ * Fallback defines for old versions of glibc
+ */
+#include <fcntl.h>
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 1999, 2001 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#include <time.h>
+
+#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
+};
+
--- /dev/null
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ * Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#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 }
+ }
+};
--- /dev/null
+#include <stdio.h>
+#include <sys/utsname.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <libubox/list.h>
+
+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);
--- /dev/null
+#ifndef LINUX_VERSION_H
+#define LINUX_VERSION_H
+
+#ifdef HAVE_LINUX_VERSION_H
+# include <linux/version.h>
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <limits.h>
+#include <fnmatch.h>
+
+#include "libblkid-tiny.h"
+
+#include <syslog.h>
+
+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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ * Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "bitops.h" /* swab16() */
+#include "superblocks.h"
+
+#include <libubox/md5.h>
+
+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 }
+ }
+};
+
+
--- /dev/null
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * 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 */
--- /dev/null
+/*
+ * 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 <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#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 }
+ }
+};
+
--- /dev/null
+/*
+ * Copyright (C) 2009 Corentin Chary <corentincj@iksaif.net>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#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 }
+ }
+};
--- /dev/null
+/*
+ * 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 <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#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 }
+ }
+};
+