various updates
authorJohn Crispin <blogic@openwrt.org>
Sun, 23 Mar 2014 10:25:25 +0000 (10:25 +0000)
committerJohn Crispin <blogic@openwrt.org>
Tue, 1 Apr 2014 09:11:43 +0000 (10:11 +0100)
Signed-off-by: John Crispin <blogic@openwrt.org>
13 files changed:
CMakeLists.txt
backend/base.c
backend/jffs2.c
backend/overlay.c [new file with mode: 0644]
backend/snapshot.c
driver/mtd.c [new file with mode: 0644]
driver/volume.c [new file with mode: 0644]
driver/volume.h [new file with mode: 0644]
lib/find.c
lib/mtd.c [deleted file]
lib/mtd.h [deleted file]
libblkid-tiny/hfs.c [new file with mode: 0644]
libblkid-tiny/libblkid-tiny.c

index f3712630a94d3ef399fa18a9bfd69183c73717d0..5266785102e9a5cb5b0a33f6e08a9ccd4f03b998 100644 (file)
@@ -10,7 +10,8 @@ ADD_EXECUTABLE(fs-state fs-state.c
                backend/snapshot.c
                backend/extroot.c
                backend/jffs2.c
-                       lib/mtd.c
+               driver/volume.c
+               driver/mtd.c
                lib/mount.c
                lib/find.c)
 
@@ -23,7 +24,9 @@ ADD_EXECUTABLE(block block.c
                libblkid-tiny/ext.c
                libblkid-tiny/jffs2.c
                libblkid-tiny/vfat.c
+               libblkid-tiny/hfs.c
                libblkid-tiny/swap.c
+               libblkid-tiny/ubifs.c
                libblkid-tiny/squashfs.c)
 TARGET_LINK_LIBRARIES(block uci ubox blobmsg_json)
 INSTALL(TARGETS block RUNTIME DESTINATION sbin)
index ecb4320f0929d651cb0c482d64c0e039a3a98946..6bb503e82aa446d3f344f044eaa65d721a2d9abe 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "../lib/mtd.h"
 #include "../fs-state.h"
 
+#include "../driver/volume.h"
+
 int
 backend_mount(char *name)
 {
@@ -43,17 +44,14 @@ backend_info(char *name)
 static int
 start(int argc, char **argv)
 {
-       char mtd[32];
+       struct volume *v = volume_find("rootfs_data");
 
        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);
-               }
+       if (!v) {
+               v = volume_find("rootfs");
+               volume_init(v);
                fprintf(stderr, "mounting /dev/root\n");
                mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0);
                return 0;
@@ -65,7 +63,7 @@ start(int argc, char **argv)
                return 0;
        }
 
-       switch (mtd_identify(mtd)) {
+       switch (volume_identify(v)) {
        case FS_NONE:
        case FS_DEADCODE:
                return ramoverlay();
@@ -94,12 +92,12 @@ stop(int argc, char **argv)
 static int
 done(int argc, char **argv)
 {
-       char mtd[32];
+       struct volume *v = volume_find("rootfs_data");
 
-       if (find_mtd_char("rootfs_data", mtd, sizeof(mtd)))
+       if (!v)
                return -1;
 
-       switch (mtd_identify(mtd)) {
+       switch (volume_identify(v)) {
        case FS_NONE:
        case FS_DEADCODE:
                return jffs2_switch(argc, argv);
@@ -111,12 +109,12 @@ done(int argc, char **argv)
 static int
 info(int argc, char **argv)
 {
-       char mtd[32];
+       struct volume *v = volume_find("rootfs_data");
 
-       if (find_mtd_char("rootfs_data", mtd, sizeof(mtd)))
+       if (!v)
                return -1;
 
-       switch (mtd_identify(mtd)) {
+       switch (volume_identify(v)) {
        case FS_SNAPSHOT:
                backend_info("snapshot");
                return 0;
index feb37d2913de3220d579018972c0cb1508f6881a..09c66cf3dfea2ee10a7b03606aa4d135eed821d8 100644 (file)
@@ -28,7 +28,7 @@
 #include <fcntl.h>
 
 #include "../fs-state.h"
-#include "../lib/mtd.h"
+#include "../driver/volume.h"
 
 #define SWITCH_JFFS2 "/tmp/.switch_jffs2"
 
@@ -54,41 +54,32 @@ foreachdir(const char *dir, int (*cb)(const char*))
 static int
 jffs2_mount(void)
 {
-       char rootfs_data[32];
-       int fd;
+       struct volume *v;
 
        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))) {
+       v = volume_find("rootfs_data");
+       if (!v) {
                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));
+       if (mount(v->blk, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
+               fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n", v->blk, 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;
+       return volume_init(v);
 }
 
 static int
 switch2jffs(void)
 {
+       struct volume *v = volume_find("rootfs_data");
        struct stat s;
-       char mtd[32];
        int ret;
 
        if (!stat(SWITCH_JFFS2, &s)) {
@@ -96,19 +87,16 @@ switch2jffs(void)
                return -1;
        }
 
-       if (!find_mtd_block("rootfs_patches", mtd, sizeof(mtd)))
-               return 0;
-
-       if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
+       if (!v) {
                fprintf(stderr, "no rootfs_data was found\n");
                return -1;
        }
 
        creat("/tmp/.switch_jffs2", 0600);
-       ret = mount(mtd, "/rom/overlay", "jffs2", MS_NOATIME, NULL);
+       ret = mount(v->blk, "/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));
+               fprintf(stderr, "failed - mount -t jffs2 %s /rom/overlay: %s\n", v->blk, strerror(errno));
                return -1;
        }
 
@@ -210,7 +198,7 @@ handle_rmdir(const char *dir)
 static int
 jffs2_reset(int argc, char **argv)
 {
-       char mtd[32];
+       struct volume *v;
        char *mp;
 
        if (ask_user(argc, argv))
@@ -221,25 +209,20 @@ jffs2_reset(int argc, char **argv)
                return -1;
        }
 
-       if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
+       v = volume_find("rootfs_data");
+       if (!v) {
                fprintf(stderr, "no rootfs_data was found\n");
                return -1;
        }
 
-       mp = find_mount_point(mtd, "jffs2");
+       mp = find_mount_point(v->blk, "jffs2");
        if (mp) {
-               fprintf(stderr, "%s is mounted as %s, only erasing files\n", mtd, mp);
+               fprintf(stderr, "%s is mounted as %s, only erasing files\n", v->blk, 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);
-               }
+               fprintf(stderr, "%s is not mounted, erasing it\n", v->blk);
+               volume_erase_all(v);
        }
 
        return 0;
@@ -248,31 +231,32 @@ jffs2_reset(int argc, char **argv)
 static int
 jffs2_mark(int argc, char **argv)
 {
-       FILE *fp;
        __u32 deadc0de = __cpu_to_be32(0xdeadc0de);
-       char mtd[32];
+       struct volume *v;
        size_t sz;
+       int fd;
 
        if (ask_user(argc, argv))
                return -1;
 
-       if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
+       v = volume_find("rootfs_data");
+       if (!v) {
                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);
+       fd = open(v->blk, O_WRONLY);
+       fprintf(stderr, "%s - marking with deadc0de\n", v->blk);
+       if (!fd) {
+               fprintf(stderr, "opening %s failed\n", v->blk);
                return -1;
        }
 
-       sz = fwrite(&deadc0de, sizeof(deadc0de), 1, fp);
-       fclose(fp);
+       sz = write(fd, &deadc0de, sizeof(deadc0de));
+       close(fd);
 
        if (sz != 1) {
-               fprintf(stderr, "writing %s failed: %s\n", mtd, strerror(errno));
+               fprintf(stderr, "writing %s failed: %s\n", v->blk, strerror(errno));
                return -1;
        }
 
@@ -282,7 +266,7 @@ jffs2_mark(int argc, char **argv)
 int
 jffs2_switch(int argc, char **argv)
 {
-       char mtd[32];
+       struct volume *v;
        char *mp;
        int ret = -1;
 
@@ -294,19 +278,14 @@ jffs2_switch(int argc, char **argv)
                return ret;
        }
 
-       find_mtd_block("rootfs_data", mtd, sizeof(mtd));
-       mp = find_mount_point(mtd, NULL);
+       v = volume_find("rootfs_data");
+       mp = find_mount_point(v->blk, NULL);
        if (mp) {
-               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", mtd, mp);
+               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, 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)) {
+       switch (volume_identify(v)) {
        case FS_NONE:
                fprintf(stderr, "no jffs2 marker found\n");
                /* fall through */
@@ -334,51 +313,47 @@ jffs2_switch(int argc, char **argv)
        return ret;
 }
 
-static int mtd_mount_jffs2(void)
+static int overlay_mount_fs(void)
 {
-       char rootfs_data[32];
-       int fd;
+       struct volume *v;
 
        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))) {
+       v = volume_find("rootfs_data");
+       if (!v) {
                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));
+       if (mount(v->blk, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
+               fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n",
+                               v->blk, 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;
-       }
+       volume_init(v);
 
        return -1;
 }
 
 static int overlay_mount(void)
 {
-       char mtd[32];
+       struct volume *v = volume_find("rootfs_data");;
        char *mp;
 
-       find_mtd_block("rootfs_data", mtd, sizeof(mtd));
-       mp = find_mount_point(mtd, NULL);
+       if (!v)
+               return -1;
+
+       mp = find_mount_point(v->blk, NULL);
        if (mp) {
-               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", mtd, mp);
+               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
                return -1;
        }
 
-       mtd_mount_jffs2();
+       overlay_mount_fs();
 
        extroot_prefix = "/tmp/overlay";
        if (!backend_mount("extroot")) {
diff --git a/backend/overlay.c b/backend/overlay.c
new file mode 100644 (file)
index 0000000..7cd308f
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * 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 "../driver/volume.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
+overlay_mount(struct volume *v, char *fs)
+{
+       if (mkdir("/tmp/overlay", 0755)) {
+               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (mount(v->blk, "/tmp/overlay", fs, MS_NOATIME, NULL)) {
+               fprintf(stderr, "failed to mount -t %s %s /tmp/overlay: %s\n", fs, v->blk, strerror(errno));
+               return -1;
+       }
+
+       return volume_init(v);
+}
+
+static int
+switch2jffs(struct volume *v)
+{
+       struct stat s;
+       int ret;
+
+       if (!stat(SWITCH_JFFS2, &s)) {
+               fprintf(stderr, "jffs2 switch already running\n");
+               return -1;
+       }
+
+       creat("/tmp/.switch_jffs2", 0600);
+       ret = mount(v->blk, "/rom/overlay", "jffs2", MS_NOATIME, NULL);
+       unlink("/tmp/.switch_jffs2");
+       if (ret) {
+               fprintf(stderr, "failed - mount -t jffs2 %s /rom/overlay: %s\n", v->blk, 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;
+
+}
+
+int
+jffs2_switch(int argc, char **argv)
+{
+       struct volume *v;
+       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;
+       }
+
+       v = volume_find("rootfs_data");
+       mp = find_mount_point(v->blk, NULL);
+       if (mp) {
+               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
+               return -1;
+       }
+
+       switch (volume_identify(v)) {
+       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 = overlay_mount(v, "jffs2");
+               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 overlay_mount_fs(void)
+{
+       struct volume *v;
+
+       if (mkdir("/tmp/overlay", 0755)) {
+               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
+               return -1;
+       }
+
+       v = volume_find("rootfs_data");
+       if (!v) {
+               fprintf(stderr, "rootfs_data does not exist\n");
+               return -1;
+       }
+
+       if (mount(v->blk, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
+               fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n",
+                               v->blk, strerror(errno));
+               return -1;
+       }
+
+       volume_init(v);
+
+       return -1;
+}
+
+static int overlay_mount(void)
+{
+       struct volume *v = volume_find("rootfs_data");;
+       char *mp;
+
+       if (!v)
+               return -1;
+
+       mp = find_mount_point(v->blk, NULL);
+       if (mp) {
+               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
+               return -1;
+       }
+
+       overlay_mount_fs();
+
+       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 overlay_backend = {
+       .name = "overlay",
+       .mount = overlay_mount,
+};
+BACKEND(overlay_backend);
index 34b9c5bf3380c609552d1a57fae723ff6c7a1043..5873f5c060952c4b705c2ace3d316f2e7ee5fa3b 100644 (file)
@@ -31,7 +31,7 @@
 #include <libubox/md5.h>
 
 #include "../fs-state.h"
-#include "../lib/mtd.h"
+#include "../driver/volume.h"
 
 #define PATH_MAX       256
 #define OWRT           0x4f575254
@@ -82,15 +82,15 @@ be32_to_hdr(struct file_header *hdr)
 }
 
 static int
-pad_file_size(int size)
+pad_file_size(struct volume *v, int size)
 {
        int mod;
 
        size += sizeof(struct file_header);
-       mod = size % erasesize;
+       mod = size % v->block_size;
        if (mod) {
                size -= mod;
-               size += erasesize;
+               size += v->block_size;
        }
 
        return size;
@@ -115,7 +115,7 @@ verify_file_hash(char *file, uint32_t *hash)
 }
 
 static int
-snapshot_next_free(int fd, uint32_t *seq)
+snapshot_next_free(struct volume *v, uint32_t *seq)
 {
        struct file_header hdr = { 0 };
        int block = 0;
@@ -123,7 +123,7 @@ snapshot_next_free(int fd, uint32_t *seq)
        *seq = rand();
 
        do {
-               if (mtd_read_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) {
+               if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
                        fprintf(stderr, "scanning for next free block failed\n");
                        return 0;
                }
@@ -137,7 +137,7 @@ snapshot_next_free(int fd, uint32_t *seq)
                        if (*seq + 1 != hdr.seq && block)
                                return block;
                        *seq = hdr.seq;
-                       block += pad_file_size(hdr.length) / erasesize;
+                       block += pad_file_size(v, hdr.length) / v->block_size;
                }
        } while (hdr.type == DATA);
 
@@ -145,18 +145,18 @@ snapshot_next_free(int fd, uint32_t *seq)
 }
 
 static int
-config_find(int fd, struct file_header *conf, struct file_header *sentinel)
+config_find(struct volume *v, struct file_header *conf, struct file_header *sentinel)
 {
        uint32_t seq;
-       int i, next = snapshot_next_free(fd, &seq);
+       int i, next = snapshot_next_free(v, &seq);
 
        conf->magic = sentinel->magic = 0;
 
-       if (!mtd_read_buffer(fd, conf, next, sizeof(*conf)))
+       if (!volume_read(v, 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))) {
+       for (i = (v->size / v->block_size) - 1; i > 0; i--) {
+               if (volume_read(v, sentinel,  i * v->block_size, sizeof(*sentinel))) {
                        fprintf(stderr, "failed to read header\n");
                        return -1;
                }
@@ -175,18 +175,17 @@ config_find(int fd, struct file_header *conf, struct file_header *sentinel)
 static int
 snapshot_info(void)
 {
-       int fd = mtd_load("rootfs_data");
+       struct volume *v = volume_find("rootfs_data");
        struct file_header hdr = { 0 }, conf;
        int block = 0;
 
-       if (fd < 1)
+       if (!v)
                return -1;
 
-       fprintf(stderr, "sectors:\t%d, erasesize:\t%dK\n", mtdsize / erasesize, erasesize / 1024);
+       fprintf(stderr, "sectors:\t%llu, block_size:\t%dK\n", v->size / v->block_size, v->block_size / 1024);
        do {
-               if (mtd_read_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) {
+               if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
                        fprintf(stderr, "scanning for next free block failed\n");
-                       close(fd);
                        return 0;
                }
 
@@ -196,22 +195,22 @@ snapshot_info(void)
                        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);
+                       fprintf(stderr, "block %d:\tsnapshot entry, size: %d, sectors: %d, sequence: %d\n", block,  hdr.length, pad_file_size(v, hdr.length) / v->block_size, 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);
+                       fprintf(stderr, "block %d:\tvolatile entry, size: %d, sectors: %d, sequence: %d\n", block,  hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
 
                if (hdr.type == DATA && !valid_file_size(hdr.length))
-                       block += pad_file_size(hdr.length) / erasesize;
+                       block += pad_file_size(v, hdr.length) / v->block_size;
        } while (hdr.type == DATA);
-       block = config_find(fd, &conf, &hdr);
+       block = config_find(v, &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);
+               fprintf(stderr, "block %d:\tsentinel entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
+
        return 0;
 }
 
 static int
-snapshot_write_file(int fd, int block, char *file, uint32_t seq, uint32_t type)
+snapshot_write_file(struct volume *v, int block, char *file, uint32_t seq, uint32_t type)
 {
        uint32_t md5[4] = { 0 };
        struct file_header hdr;
@@ -225,12 +224,12 @@ snapshot_write_file(int fd, int block, char *file, uint32_t seq, uint32_t type)
                goto out;
        }
 
-       if ((block * erasesize) + pad_file_size(s.st_size) > mtdsize) {
+       if ((block * v->block_size) + pad_file_size(v, s.st_size) > v->size) {
                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);
+       volume_erase(v, block * v->block_size, pad_file_size(v, s.st_size));
+       volume_erase(v, block * v->block_size + pad_file_size(v, s.st_size), v->block_size);
 
        hdr.length = s.st_size;
        hdr.magic = OWRT;
@@ -239,7 +238,7 @@ snapshot_write_file(int fd, int block, char *file, uint32_t seq, uint32_t type)
        memcpy(hdr.md5, md5, sizeof(md5));
        hdr_to_be32(&hdr);
 
-       if (mtd_write_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) {
+       if (volume_write(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
                fprintf(stderr, "failed to write header\n");
                goto out;
        }
@@ -250,10 +249,10 @@ snapshot_write_file(int fd, int block, char *file, uint32_t seq, uint32_t type)
                goto out;
        }
 
-       offset = (block * erasesize) + sizeof(struct file_header);
+       offset = (block * v->block_size) + sizeof(struct file_header);
 
        while ((len = read(in, buffer, sizeof(buffer))) > 0) {
-               if (mtd_write_buffer(fd, buffer, offset, len) < 0)
+               if (volume_write(v, buffer, offset, len) < 0)
                        goto out;
                offset += len;
        }
@@ -268,13 +267,13 @@ out:
 }
 
 static int
-snapshot_read_file(int fd, int block, char *file, uint32_t type)
+snapshot_read_file(struct volume *v, int block, char *file, uint32_t type)
 {
        struct file_header hdr;
        char buffer[256];
-       int out;
+       int out, offset = 0;
 
-       if (mtd_read_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) {
+       if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
                fprintf(stderr, "failed to read header\n");
                return -1;
        }
@@ -301,10 +300,10 @@ snapshot_read_file(int fd, int block, char *file, uint32_t type)
                if (hdr.length < len)
                        len = hdr.length;
 
-               if ((read(fd, buffer, len) != len) || (write(out, buffer, len) != len)) {
+               if ((volume_read(v, buffer, offset, len) != len) || (write(out, buffer, len) != len))
                        return -1;
-               }
 
+               offset += len;
                hdr.length -= len;
        }
 
@@ -316,13 +315,13 @@ snapshot_read_file(int fd, int block, char *file, uint32_t type)
                return 0;
        }
 
-        block += pad_file_size(hdr.length) / erasesize;
+        block += pad_file_size(v, hdr.length) / v->block_size;
 
        return block;
 }
 
 static int
-sentinel_write(int fd, uint32_t _seq)
+sentinel_write(struct volume *v, uint32_t _seq)
 {
        int ret, block;
        struct stat s;
@@ -333,15 +332,15 @@ sentinel_write(int fd, uint32_t _seq)
                return -1;
        }
 
-       snapshot_next_free(fd, &seq);
+       snapshot_next_free(v, &seq);
        if (_seq)
                seq = _seq;
-       block = mtdsize / erasesize;
-       block -= pad_file_size(s.st_size) / erasesize;
+       block = v->size / v->block_size;
+       block -= pad_file_size(v, s.st_size) / v->block_size;
        if (block < 0)
                block = 0;
 
-       ret = snapshot_write_file(fd, block, "/tmp/config.tar.gz", seq, CONF);
+       ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
        if (ret)
                fprintf(stderr, "failed to write sentinel\n");
        else
@@ -350,18 +349,18 @@ sentinel_write(int fd, uint32_t _seq)
 }
 
 static int
-volatile_write(int fd, uint32_t _seq)
+volatile_write(struct volume *v, uint32_t _seq)
 {
        int block, ret;
        uint32_t seq;
 
-       block = snapshot_next_free(fd, &seq);
+       block = snapshot_next_free(v, &seq);
        if (_seq)
                seq = _seq;
        if (block < 0)
                block = 0;
 
-       ret = snapshot_write_file(fd, block, "/tmp/config.tar.gz", seq, CONF);
+       ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
        if (ret)
                fprintf(stderr, "failed to write /tmp/config.tar.gz\n");
        else
@@ -372,19 +371,15 @@ volatile_write(int fd, uint32_t _seq)
 static int
 config_write(int argc, char **argv)
 {
-       int fd, ret;
+       struct volume *v = volume_find("rootfs_data");
+       int ret;
 
-       fd = mtd_load("rootfs_data");
-       if (fd < 1) {
-               fprintf(stderr, "failed to open rootfs_config\n");
+       if (!v)
                return -1;
-       }
 
-       ret = volatile_write(fd, 0);
+       ret = volatile_write(v, 0);
        if (!ret)
-               ret = sentinel_write(fd, 0);
-
-       close(fd);
+               ret = sentinel_write(v, 0);
 
        return ret;
 }
@@ -392,88 +387,83 @@ config_write(int argc, char **argv)
 static int
 config_read(int argc, char **argv)
 {
+       struct volume *v = volume_find("rootfs_data");
        struct file_header conf, sentinel;
-       int fd, next, block, ret = 0;
+       int next, block, ret = 0;
        uint32_t seq;
 
-       fd = mtd_load("rootfs_data");
-       if (fd < 1) {
-               fprintf(stderr, "failed to open rootfs_data\n");
+       if (!v)
                return -1;
-       }
 
-       block = config_find(fd, &conf, &sentinel);
-       next = snapshot_next_free(fd, &seq);
+       block = config_find(v, &conf, &sentinel);
+       next = snapshot_next_free(v, &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);
+       ret = snapshot_read_file(v, 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;
+       struct volume *v = volume_find("rootfs_data");
+       int block, ret;
        uint32_t seq;
 
-       mtd = mtd_load("rootfs_data");
-       if (mtd < 1) {
-               fprintf(stderr, "failed to open rootfs_data\n");
+       if (!v)
                return -1;
-       }
 
-       block = snapshot_next_free(mtd, &seq);
+       block = snapshot_next_free(v, &seq);
        if (block < 0)
                block = 0;
 
-       ret = snapshot_write_file(mtd, block, "/tmp/snapshot.tar.gz", seq + 1, DATA);
+       ret = snapshot_write_file(v, 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];
+       struct volume *v;
        size_t sz;
+       int fd;
 
        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))) {
+       v = volume_find("rootfs_data");
+       if (!v) {
                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);
+       fd = open(v->blk, O_WRONLY);
+       fprintf(stderr, "%s - marking with 0x%08x\n", v->blk, owrt);
+       if (fd < 0) {
+               fprintf(stderr, "opening %s failed\n", v->blk);
                return -1;
        }
 
-       sz = fwrite(&owrt, sizeof(owrt), 1, fp);
-       fclose(fp);
+       sz = write(fd, &owrt, sizeof(owrt));
+       close(fd);
 
        if (sz != 1) {
-               fprintf(stderr, "writing %s failed: %s\n", mtd, strerror(errno));
+               fprintf(stderr, "writing %s failed: %s\n", v->blk, strerror(errno));
                return -1;
        }
 
@@ -483,58 +473,55 @@ snapshot_mark(int argc, char **argv)
 static int
 snapshot_read(int argc, char **argv)
 {
+       struct volume *v = volume_find("rootfs_data");;
+       int block = 0, ret = 0;
        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");
+       if (!v)
                return -1;
-       }
 
        if (argc > 1) {
                block = atoi(argv[1]);
-               if (block >= (mtdsize / erasesize)) {
-                       fprintf(stderr, "invalid block %d > %d\n", block, mtdsize / erasesize);
+               if (block >= (v->size / v->block_size)) {
+                       fprintf(stderr, "invalid block %d > %llu\n", block, v->size / v->block_size);
                        goto out;
                }
                snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
 
-               ret = snapshot_read_file(fd, block, file, DATA);
+               ret = snapshot_read_file(v, 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);
+               block = snapshot_read_file(v, block, file, DATA);
        } while (block > 0);
 
 out:
-       close(fd);
        return ret;
 }
 
 static int
 snapshot_sync(void)
 {
-       int fd = mtd_load("rootfs_data");
+       struct volume *v = volume_find("rootfs_data");
        struct file_header sentinel, conf;
        int next, block = 0;
        uint32_t seq;
 
-       if (fd < 1)
+       if (!v)
                return -1;
 
-       next = snapshot_next_free(fd, &seq);
-       block = config_find(fd, &conf, &sentinel);
+       next = snapshot_next_free(v, &seq);
+       block = config_find(v, &conf, &sentinel);
        if (is_config(&conf) && conf.seq != seq) {
                conf.magic = 0;
-               mtd_erase(fd, next, 2);
+               volume_erase(v, next * v->block_size, 2 * v->block_size);
        }
 
        if (is_config(&sentinel) && (sentinel.seq != seq)) {
                sentinel.magic = 0;
-               mtd_erase(fd, block, 1);
+               volume_erase(v, block * v->block_size, v->block_size);
        }
 
        if (!is_config(&conf) && !is_config(&sentinel)) {
@@ -543,22 +530,21 @@ snapshot_sync(void)
                                (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);
+               int next = snapshot_next_free(v, &seq);
+               int ret = snapshot_read_file(v, next, "/tmp/config.tar.gz", CONF);
                if (ret > 0) {
-                       if (sentinel_write(fd, conf.seq))
+                       if (sentinel_write(v, 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);
+               int ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
                if (ret > 0)
-                       if (volatile_write(fd, sentinel.seq))
+                       if (volatile_write(v, 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;
 }
diff --git a/driver/mtd.c b/driver/mtd.c
new file mode 100644 (file)
index 0000000..a0005d7
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <asm/byteorder.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <mtd/mtd-user.h>
+
+#include "../fs-state.h"
+
+#include "volume.h"
+
+#define PATH_MAX               256
+
+struct mtd_priv {
+       int     fd;
+       int     idx;
+       char    *chr;
+};
+
+static struct driver mtd_driver;
+
+static 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);
+}
+
+static void mtd_volume_close(struct volume *v)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+
+       if (!p->fd)
+               return;
+
+       close(p->fd);
+       p->fd = 0;
+}
+
+static int mtd_volume_load(struct volume *v)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+       struct mtd_info_user mtdInfo;
+       struct erase_info_user mtdLockInfo;
+
+       if (p->fd)
+               return 0;
+
+       if (!p->chr)
+               return -1;
+
+       p->fd = mtd_open(p->chr, 0);
+       if (p->fd < 0) {
+               p->fd = 0;
+               fprintf(stderr, "Could not open mtd device: %s\n", p->chr);
+               return -1;
+       }
+
+       if (ioctl(p->fd, MEMGETINFO, &mtdInfo)) {
+               mtd_volume_close(v);
+               fprintf(stderr, "Could not get MTD device info from %s\n", p->chr);
+               return -1;
+       }
+
+       v->size = mtdInfo.size;
+       v->block_size = mtdInfo.erasesize;
+       switch (mtdInfo.type) {
+       case MTD_NORFLASH:
+               v->type = NORFLASH;
+               break;
+       case MTD_NANDFLASH:
+               v->type = NANDFLASH;
+               break;
+       case MTD_UBIVOLUME:
+               v->type = UBIVOLUME;
+               break;
+       default:
+               v->type = UNKNOWN_TYPE;
+               break;
+       }
+
+       mtdLockInfo.start = 0;
+       mtdLockInfo.length = v->size;
+       ioctl(p->fd, MEMUNLOCK, &mtdLockInfo);
+
+       return 0;
+}
+
+static char* mtd_find_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;
+}
+
+static int mtd_volume_find(struct volume *v, char *name)
+{
+       char *idx = mtd_find_index(name);
+       struct mtd_priv *p;
+       char buffer[32];
+
+       if (!idx)
+               return -1;
+
+       p = calloc(1, sizeof(struct mtd_priv));
+       if (!p)
+               return -1;
+
+       v->priv = p;
+       v->name = strdup(name);
+       v->drv = &mtd_driver;
+       p->idx = atoi(idx);
+
+       snprintf(buffer, sizeof(buffer), "/dev/mtdblock%s", idx);
+       v->blk = strdup(buffer);
+
+       snprintf(buffer, sizeof(buffer), "/dev/mtd%s", idx);
+       p->chr = strdup(buffer);
+
+       return 0;
+}
+
+static int mtd_volume_identify(struct volume *v)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+       __u32 deadc0de;
+       __u16 jffs2;
+       size_t sz;
+
+       if (mtd_volume_load(v)) {
+               fprintf(stderr, "reading %s failed\n", v->name);
+               return -1;
+       }
+
+       sz = read(p->fd, &deadc0de, sizeof(deadc0de));
+
+       if (sz != sizeof(deadc0de)) {
+               fprintf(stderr, "reading %s failed: %s\n", v->name, strerror(errno));
+               return -1;
+       }
+
+       if (deadc0de == 0x4f575254)
+               return FS_SNAPSHOT;
+
+       deadc0de = __be32_to_cpu(deadc0de);
+       if (deadc0de == 0xdeadc0de) {
+               fprintf(stderr, "jffs2 is not ready - marker found\n");
+               return FS_DEADCODE;
+       }
+
+       jffs2 = __be16_to_cpu(deadc0de >> 16);
+       if (jffs2 == 0x1985) {
+               fprintf(stderr, "jffs2 is ready\n");
+               return FS_JFFS2;
+       }
+
+       if (v->type == UBIVOLUME && deadc0de == 0xffffffff) {
+               fprintf(stderr, "jffs2 is ready\n");
+               return FS_JFFS2;
+       }
+
+       fprintf(stderr, "No jffs2 marker was found\n");
+
+       return FS_NONE;
+}
+
+static int mtd_volume_erase(struct volume *v, int offset, int len)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+       struct erase_info_user eiu;
+       int first_block, num_blocks;
+
+       if (mtd_volume_load(v))
+               return -1;
+
+       if (offset % v->block_size || len % v->block_size) {
+               fprintf(stderr, "mtd erase needs to be block aligned\n");
+               return -1;
+       }
+
+       first_block = offset / v->block_size;
+       num_blocks = len / v->block_size;
+       eiu.length = v->block_size;
+
+       for (eiu.start = first_block * v->block_size;
+                       eiu.start < v->size && eiu.start < (first_block + num_blocks) * v->block_size;
+                       eiu.start += v->block_size) {
+               fprintf(stderr, "erasing %x %x\n", eiu.start, v->block_size);
+               ioctl(p->fd, MEMUNLOCK, &eiu);
+               if (ioctl(p->fd, MEMERASE, &eiu))
+                       fprintf(stderr, "Failed to erase block at 0x%x\n", eiu.start);
+       }
+
+       mtd_volume_close(v);
+
+       return 0;
+}
+
+static int mtd_volume_erase_all(struct volume *v)
+{
+       mtd_volume_erase(v, 0, v->size);
+       mtd_volume_close(v);
+
+       return 0;
+}
+
+static int mtd_volume_init(struct volume *v)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+       struct mtd_info_user mtdinfo;
+       int ret;
+
+       if (mtd_volume_load(v))
+               return -1;
+
+       ret = ioctl(p->fd, MEMGETINFO, &mtdinfo);
+       if (ret) {
+               fprintf(stderr, "ioctl(%d, MEMGETINFO) failed: %s\n", p->fd, strerror(errno));
+       } else {
+               struct erase_info_user mtdlock;
+
+               mtdlock.start = 0;
+               mtdlock.length = mtdinfo.size;
+               ioctl(p->fd, MEMUNLOCK, &mtdlock);
+       }
+
+       return ret;
+}
+
+static int mtd_volume_read(struct volume *v, void *buf, int offset, int length)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+
+       if (mtd_volume_load(v))
+               return -1;
+
+       if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
+               fprintf(stderr, "lseek/read failed\n");
+               return -1;
+       }
+
+       if (read(p->fd, buf, length) == -1) {
+               fprintf(stderr, "read failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int mtd_volume_write(struct volume *v, void *buf, int offset, int length)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+
+       if (mtd_volume_load(v))
+               return -1;
+
+       if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
+               fprintf(stderr, "lseek/write failed at offset %d\n", offset);
+               perror("lseek");
+               return -1;
+       }
+
+       if (write(p->fd, buf, length) == -1) {
+               fprintf(stderr, "write failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static struct driver mtd_driver = {
+       .name = "mtd",
+       .find = mtd_volume_find,
+       .init = mtd_volume_init,
+       .erase = mtd_volume_erase,
+       .erase_all = mtd_volume_erase_all,
+       .read = mtd_volume_read,
+       .write = mtd_volume_write,
+       .identify = mtd_volume_identify,
+};
+DRIVER(mtd_driver);
diff --git a/driver/volume.c b/driver/volume.c
new file mode 100644 (file)
index 0000000..4dc0a8e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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 "../fs-state.h"
+#include "volume.h"
+
+enum {
+       FLASH_NOR,
+       FLASH_NAND,
+};
+
+static LIST_HEAD(drivers);
+
+void
+volume_register_driver(struct driver *d)
+{
+       list_add(&d->list, &drivers);
+}
+
+struct volume* volume_find(char *name)
+{
+       struct volume *v = malloc(sizeof(struct volume));
+       struct driver *d;
+
+       if (!v)
+               return NULL;
+
+       list_for_each_entry(d, &drivers, list) {
+               memset(v, 0, sizeof(struct volume));
+
+               if (d->find && !d->find(v, name))
+                       return v;
+       }
+
+       free(v);
+
+       return NULL;
+}
diff --git a/driver/volume.h b/driver/volume.h
new file mode 100644 (file)
index 0000000..4fa5641
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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 _VOLUME_H__
+#define _VOLUME_H__
+
+#include <asm/byteorder.h>
+
+struct volume;
+
+typedef int (*volume_probe_t)(void);
+typedef int (*volume_init_t)(struct volume *v);
+typedef void (*volume_stop_t)(struct volume *v);
+typedef int (*volume_find_t)(struct volume *v, char *name);
+typedef int (*volume_identify_t)(struct volume *v);
+typedef int (*volume_read_t)(struct volume *v, void *buf, int offset, int length);
+typedef int (*volume_write_t)(struct volume *v, void *buf, int offset, int length);
+typedef int (*volume_erase_t)(struct volume *v, int start, int len);
+typedef int (*volume_erase_all_t)(struct volume *v);
+
+struct driver {
+        struct list_head        list;
+
+       char                    *name;
+       volume_probe_t          probe;
+       volume_init_t           init;
+       volume_stop_t           stop;
+       volume_find_t           find;
+       volume_identify_t       identify;
+       volume_read_t           read;
+       volume_write_t          write;
+       volume_erase_t          erase;
+       volume_erase_all_t      erase_all;
+};
+
+enum {
+       UNKNOWN_TYPE,
+       NANDFLASH,
+       NORFLASH,
+       UBIVOLUME,
+};
+
+struct volume {
+       struct driver   *drv;
+       void            *priv;
+       char            *name;
+       char            *blk;
+
+       __u64           size;
+       __u32           block_size;
+       int             type;
+};
+
+extern struct volume* volume_find(char *name);
+extern void volume_register_driver(struct driver *drv);
+
+static inline int volume_init(struct volume *v)
+{
+       if (v && v->drv->init)
+               return v->drv->init(v);
+       return -1;
+}
+
+static inline int volume_identify(struct volume *v)
+{
+       if (v && v->drv->identify)
+               return v->drv->identify(v);
+       return -1;
+}
+
+static inline int volume_erase(struct volume *v, int offset, int len)
+{
+       if (v && v->drv->erase)
+               return v->drv->erase(v, offset, len);
+       return -1;
+}
+
+static inline int volume_erase_all(struct volume *v)
+{
+       if (v && v->drv->erase_all)
+               return v->drv->erase_all(v);
+       return -1;
+}
+
+static inline int volume_read(struct volume *v, void *buf, int offset, int length)
+{
+       if (v && v->drv->read)
+               return v->drv->read(v, buf, offset, length);
+       return -1;
+}
+
+static inline int volume_write(struct volume *v, void *buf, int offset, int length)
+{
+       if (v && v->drv->write)
+               return v->drv->write(v, buf, offset, length);
+       return -1;
+}
+
+#define DRIVER(x)                                      \
+       static void __attribute__((constructor))        \
+       drv_register_##x(void) {                        \
+               volume_register_driver(&x);             \
+       }
+
+#endif
index d13c4c191cb6c59b5dc12c9a7aa0edfadb10d647..35e37e7ffe061f92412eaec3fa7366f11f1bca40 100644 (file)
@@ -112,59 +112,6 @@ find_mount_point(char *block, char *fs)
        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)
 {
@@ -186,5 +133,3 @@ find_filesystem(char *fs)
 out:
        return ret;
 }
-
-
diff --git a/lib/mtd.c b/lib/mtd.c
deleted file mode 100644 (file)
index 0036078..0000000
--- a/lib/mtd.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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;
-}
diff --git a/lib/mtd.h b/lib/mtd.h
deleted file mode 100644 (file)
index adf1ebe..0000000
--- a/lib/mtd.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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
diff --git a/libblkid-tiny/hfs.c b/libblkid-tiny/hfs.c
new file mode 100644 (file)
index 0000000..1398e38
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2004-2008 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 <inttypes.h>
+
+#include "superblocks.h"
+//#include "md5.h"
+
+/* HFS / HFS+ */
+struct hfs_finder_info {
+        uint32_t        boot_folder;
+        uint32_t        start_app;
+        uint32_t        open_folder;
+        uint32_t        os9_folder;
+        uint32_t        reserved;
+        uint32_t        osx_folder;
+        uint8_t         id[8];
+} __attribute__((packed));
+
+struct hfs_mdb {
+        uint8_t         signature[2];
+        uint32_t        cr_date;
+        uint32_t        ls_Mod;
+        uint16_t        atrb;
+        uint16_t        nm_fls;
+        uint16_t        vbm_st;
+        uint16_t        alloc_ptr;
+        uint16_t        nm_al_blks;
+        uint32_t        al_blk_size;
+        uint32_t        clp_size;
+        uint16_t        al_bl_st;
+        uint32_t        nxt_cnid;
+        uint16_t        free_bks;
+        uint8_t         label_len;
+        uint8_t         label[27];
+        uint32_t        vol_bkup;
+        uint16_t        vol_seq_num;
+        uint32_t        wr_cnt;
+        uint32_t        xt_clump_size;
+        uint32_t        ct_clump_size;
+        uint16_t        num_root_dirs;
+        uint32_t        file_count;
+        uint32_t        dir_count;
+        struct hfs_finder_info finder_info;
+        uint8_t         embed_sig[2];
+        uint16_t        embed_startblock;
+        uint16_t        embed_blockcount;
+} __attribute__((packed));
+
+
+#define HFS_NODE_LEAF                  0xff
+#define HFSPLUS_POR_CNID               1
+
+struct hfsplus_bnode_descriptor {
+       uint32_t                next;
+       uint32_t                prev;
+       uint8_t         type;
+       uint8_t         height;
+       uint16_t                num_recs;
+       uint16_t                reserved;
+} __attribute__((packed));
+
+struct hfsplus_bheader_record {
+       uint16_t                depth;
+       uint32_t                root;
+       uint32_t                leaf_count;
+       uint32_t                leaf_head;
+       uint32_t                leaf_tail;
+       uint16_t                node_size;
+} __attribute__((packed));
+
+struct hfsplus_catalog_key {
+       uint16_t        key_len;
+       uint32_t        parent_id;
+       uint16_t        unicode_len;
+       uint8_t         unicode[255 * 2];
+} __attribute__((packed));
+
+struct hfsplus_extent {
+       uint32_t                start_block;
+       uint32_t                block_count;
+} __attribute__((packed));
+
+#define HFSPLUS_EXTENT_COUNT           8
+struct hfsplus_fork {
+       uint64_t                total_size;
+       uint32_t                clump_size;
+       uint32_t                total_blocks;
+       struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+} __attribute__((packed));
+
+struct hfsplus_vol_header {
+       uint8_t         signature[2];
+       uint16_t                version;
+       uint32_t                attributes;
+       uint32_t                last_mount_vers;
+       uint32_t                reserved;
+       uint32_t                create_date;
+       uint32_t                modify_date;
+       uint32_t                backup_date;
+       uint32_t                checked_date;
+       uint32_t                file_count;
+       uint32_t                folder_count;
+       uint32_t                blocksize;
+       uint32_t                total_blocks;
+       uint32_t                free_blocks;
+       uint32_t                next_alloc;
+       uint32_t                rsrc_clump_sz;
+       uint32_t                data_clump_sz;
+       uint32_t                next_cnid;
+       uint32_t                write_count;
+       uint64_t                encodings_bmp;
+       struct hfs_finder_info finder_info;
+       struct hfsplus_fork alloc_file;
+       struct hfsplus_fork ext_file;
+       struct hfsplus_fork cat_file;
+       struct hfsplus_fork attr_file;
+       struct hfsplus_fork start_file;
+}  __attribute__((packed));
+
+#define HFSPLUS_SECTOR_SIZE        512
+
+static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t len)
+{
+/*     static unsigned char const hash_init[MD5LENGTH] = {
+               0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
+               0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
+       };
+       unsigned char uuid[MD5LENGTH];
+       struct MD5Context md5c;
+
+       if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
+               return -1;
+       MD5Init(&md5c);
+       MD5Update(&md5c, hash_init, MD5LENGTH);
+       MD5Update(&md5c, hfs_info, len);
+       MD5Final(uuid, &md5c);
+       uuid[6] = 0x30 | (uuid[6] & 0x0f);
+       uuid[8] = 0x80 | (uuid[8] & 0x3f);
+       return blkid_probe_set_uuid(pr, uuid);*/
+       return -1;
+}
+
+static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+       struct hfs_mdb  *hfs;
+
+       hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
+       if (!hfs)
+               return -1;
+
+       if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
+           (memcmp(hfs->embed_sig, "HX", 2) == 0))
+               return 1;       /* Not hfs, but an embedded HFS+ */
+
+       hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
+
+       blkid_probe_set_label(pr, hfs->label, hfs->label_len);
+       return 0;
+}
+
+static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
+{
+       struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+       struct hfsplus_bnode_descriptor *descr;
+       struct hfsplus_bheader_record *bnode;
+       struct hfsplus_catalog_key *key;
+       struct hfsplus_vol_header *hfsplus;
+       struct hfs_mdb *sbd;
+       unsigned int alloc_block_size;
+       unsigned int alloc_first_block;
+       unsigned int embed_first_block;
+       unsigned int off = 0;
+       unsigned int blocksize;
+       unsigned int cat_block;
+       unsigned int ext_block_start;
+       unsigned int ext_block_count;
+       unsigned int record_count;
+       unsigned int leaf_node_head;
+       unsigned int leaf_node_count;
+       unsigned int leaf_node_size;
+       unsigned int leaf_block;
+       int ext;
+       uint64_t leaf_off;
+       unsigned char *buf;
+
+       sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
+       if (!sbd)
+               return -1;
+
+       /* Check for a HFS+ volume embedded in a HFS volume */
+       if (memcmp(sbd->signature, "BD", 2) == 0) {
+               if ((memcmp(sbd->embed_sig, "H+", 2) != 0) &&
+                   (memcmp(sbd->embed_sig, "HX", 2) != 0))
+                       /* This must be an HFS volume, so fail */
+                       return 1;
+
+               alloc_block_size = be32_to_cpu(sbd->al_blk_size);
+               alloc_first_block = be16_to_cpu(sbd->al_bl_st);
+               embed_first_block = be16_to_cpu(sbd->embed_startblock);
+               off = (alloc_first_block * 512) +
+                       (embed_first_block * alloc_block_size);
+
+               buf = blkid_probe_get_buffer(pr,
+                               off + (mag->kboff * 1024),
+                               sizeof(struct hfsplus_vol_header));
+               hfsplus = (struct hfsplus_vol_header *) buf;
+
+       } else
+               hfsplus = blkid_probe_get_sb(pr, mag,
+                               struct hfsplus_vol_header);
+
+       if (!hfsplus)
+               return -1;
+
+       if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
+           (memcmp(hfsplus->signature, "HX", 2) != 0))
+               return 1;
+
+       hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
+
+       blocksize = be32_to_cpu(hfsplus->blocksize);
+       if (blocksize < HFSPLUS_SECTOR_SIZE)
+               return -1;
+
+       memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+       cat_block = be32_to_cpu(extents[0].start_block);
+
+       buf = blkid_probe_get_buffer(pr,
+                       off + ((blkid_loff_t) cat_block * blocksize), 0x2000);
+       if (!buf)
+               return 0;
+
+       bnode = (struct hfsplus_bheader_record *)
+               &buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+       leaf_node_head = be32_to_cpu(bnode->leaf_head);
+       leaf_node_size = be16_to_cpu(bnode->node_size);
+       leaf_node_count = be32_to_cpu(bnode->leaf_count);
+       if (leaf_node_count == 0)
+               return 0;
+
+       leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
+
+       /* get physical location */
+       for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
+               ext_block_start = be32_to_cpu(extents[ext].start_block);
+               ext_block_count = be32_to_cpu(extents[ext].block_count);
+               if (ext_block_count == 0)
+                       return 0;
+
+               /* this is our extent */
+               if (leaf_block < ext_block_count)
+                       break;
+
+               leaf_block -= ext_block_count;
+       }
+       if (ext == HFSPLUS_EXTENT_COUNT)
+               return 0;
+
+       leaf_off = (ext_block_start + leaf_block) * blocksize;
+
+       buf = blkid_probe_get_buffer(pr,
+                               (blkid_loff_t) off + leaf_off,
+                               leaf_node_size);
+       if (!buf)
+               return 0;
+
+       descr = (struct hfsplus_bnode_descriptor *) buf;
+       record_count = be16_to_cpu(descr->num_recs);
+       if (record_count == 0)
+               return 0;
+
+       if (descr->type != HFS_NODE_LEAF)
+               return 0;
+
+       key = (struct hfsplus_catalog_key *)
+               &buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+       if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID)
+               return 0;
+
+       return 0;
+}
+
+const struct blkid_idinfo hfs_idinfo =
+{
+       .name           = "hfs",
+       .usage          = BLKID_USAGE_FILESYSTEM,
+       .probefunc      = probe_hfs,
+       .flags          = BLKID_IDINFO_TOLERANT,
+       .magics         =
+       {
+               { .magic = "BD", .len = 2, .kboff = 1 },
+               { NULL }
+       }
+};
+
+const struct blkid_idinfo hfsplus_idinfo =
+{
+       .name           = "hfsplus",
+       .usage          = BLKID_USAGE_FILESYSTEM,
+       .probefunc      = probe_hfsplus,
+       .magics         =
+       {
+               { .magic = "BD", .len = 2, .kboff = 1 },
+               { .magic = "H+", .len = 2, .kboff = 1 },
+               { .magic = "HX", .len = 2, .kboff = 1 },
+               { NULL }
+       }
+};
index dc18aa7b9f53aaec7ae47a8f472bffbc4be91962..a01a43e9ed4eafa43eef46ab274975f7e691d565 100644 (file)
@@ -163,7 +163,10 @@ static const struct blkid_idinfo *idinfos[] =
        &ext2_idinfo,
        &jbd_idinfo,
        &squashfs_idinfo,
+       &ubifs_idinfo,
        &jffs2_idinfo,
+       &hfsplus_idinfo,
+       &hfs_idinfo,
 };
 
 int probe_block(char *block, struct blkid_struct_probe *pr)