From: John Crispin Date: Sun, 23 Mar 2014 10:25:25 +0000 (+0000) Subject: various updates X-Git-Url: http://git.archive.openwrt.org/?p=project%2Ffstools.git;a=commitdiff_plain;h=4e3268f90ae97ccb047c47a78b83bcebeb9593cd;hp=d292ffc85f48dd9c8774a0860f50397dd2966e7a various updates Signed-off-by: John Crispin --- diff --git a/CMakeLists.txt b/CMakeLists.txt index f371263..5266785 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/backend/base.c b/backend/base.c index ecb4320..6bb503e 100644 --- a/backend/base.c +++ b/backend/base.c @@ -15,9 +15,10 @@ #include #include -#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; diff --git a/backend/jffs2.c b/backend/jffs2.c index feb37d2..09c66cf 100644 --- a/backend/jffs2.c +++ b/backend/jffs2.c @@ -28,7 +28,7 @@ #include #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 index 0000000..7cd308f --- /dev/null +++ b/backend/overlay.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../fs-state.h" +#include "../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); diff --git a/backend/snapshot.c b/backend/snapshot.c index 34b9c5b..5873f5c 100644 --- a/backend/snapshot.c +++ b/backend/snapshot.c @@ -31,7 +31,7 @@ #include #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 index 0000000..a0005d7 --- /dev/null +++ b/driver/mtd.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../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 index 0000000..4dc0a8e --- /dev/null +++ b/driver/volume.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "../fs-state.h" +#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 index 0000000..4fa5641 --- /dev/null +++ b/driver/volume.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _VOLUME_H__ +#define _VOLUME_H__ + +#include + +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 diff --git a/lib/find.c b/lib/find.c index d13c4c1..35e37e7 100644 --- a/lib/find.c +++ b/lib/find.c @@ -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 index 0036078..0000000 --- a/lib/mtd.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2014 John Crispin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../fs-state.h" -#include "mtd.h" - -#define PATH_MAX 256 - -int mtdsize = 0; -int erasesize = 0; - -int -mtd_open(const char *mtd, int block) -{ - FILE *fp; - char dev[PATH_MAX]; - int i, ret, flags = O_RDWR | O_SYNC; - - if ((fp = fopen("/proc/mtd", "r"))) { - while (fgets(dev, sizeof(dev), fp)) { - if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) { - snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i); - ret = open(dev, flags); - if (ret < 0) { - snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i); - ret = open(dev, flags); - } - fclose(fp); - return ret; - } - } - fclose(fp); - } - - return open(mtd, flags); -} - -int -mtd_load(const char *mtd) -{ - struct mtd_info_user mtdInfo; - struct erase_info_user mtdLockInfo; - int fd; - - fd = mtd_open(mtd, 0); - if (fd < 0) { - fprintf(stderr, "Could not open mtd device: %s\n", mtd); - return -1; - } - - if (ioctl(fd, MEMGETINFO, &mtdInfo)) { - fprintf(stderr, "Could not get MTD device info from %s\n", mtd); - close(fd); - return -1; - } - - mtdsize = mtdInfo.size; - erasesize = mtdInfo.erasesize; - - mtdLockInfo.start = 0; - mtdLockInfo.length = mtdsize; - ioctl(fd, MEMUNLOCK, &mtdLockInfo); - - return fd; -} - -void -mtd_erase(int fd, int first_block, int num_blocks) -{ - struct erase_info_user eiu; - - eiu.length = erasesize; - for (eiu.start = first_block * erasesize; - eiu.start < mtdsize && eiu.start < (first_block + num_blocks) * erasesize; - eiu.start += erasesize) { - fprintf(stderr, "erasing %x %x\n", eiu.start, erasesize); - ioctl(fd, MEMUNLOCK, &eiu); - if (ioctl(fd, MEMERASE, &eiu)) - fprintf(stderr, "Failed to erase block at 0x%x\n", eiu.start); - } -} - -int -mtd_unlock(int fd) -{ - struct mtd_info_user mtdinfo; - int ret = ioctl(fd, MEMGETINFO, &mtdinfo); - - if (ret) { - fprintf(stderr, "ioctl(%d, MEMGETINFO) failed: %s\n", fd, strerror(errno)); - } else { - struct erase_info_user mtdlock; - - mtdlock.start = 0; - mtdlock.length = mtdinfo.size; - ioctl(fd, MEMUNLOCK, &mtdlock); - } - - return ret; -} - -int -mtd_read_buffer(int fd, void *buf, int offset, int length) -{ - if (lseek(fd, offset, SEEK_SET) == (off_t) -1) { - fprintf(stderr, "lseek/read failed\n"); - return -1; - } - - if (read(fd, buf, length) == -1) { - fprintf(stderr, "read failed\n"); - return -1; - } - - return 0; -} - -int -mtd_write_buffer(int fd, void *buf, int offset, int length) -{ - if (lseek(fd, offset, SEEK_SET) == (off_t) -1) { - fprintf(stderr, "lseek/write failed at offset %d\n", offset); - perror("lseek"); - return -1; - } - - if (write(fd, buf, length) == -1) { - fprintf(stderr, "write failed\n"); - return -1; - } - - return 0; -} - -int -mtd_identify(char *mtd) -{ - int fd = mtd_load(mtd); - __u32 deadc0de; - __u16 jffs2; - size_t sz; - - if (!fd) { - fprintf(stderr, "reading %s failed\n", mtd); - return -1; - } - - sz = read(fd, &deadc0de, sizeof(deadc0de)); - close(fd); - - if (sz != sizeof(deadc0de)) { - fprintf(stderr, "reading %s failed: %s\n", mtd, strerror(errno)); - return -1; - } - - if (deadc0de == 0x4f575254) - return FS_SNAPSHOT; - - deadc0de = __be32_to_cpu(deadc0de); - jffs2 = __be16_to_cpu(deadc0de >> 16); - - if (jffs2 == 0x1985) { - fprintf(stderr, "jffs2 is ready\n"); - return FS_JFFS2; - } - - if (deadc0de == 0xdeadc0de) { - fprintf(stderr, "jffs2 is not ready - marker found\n"); - return FS_DEADCODE; - } - - fprintf(stderr, "No jffs2 marker was found\n"); - - return FS_NONE; -} diff --git a/lib/mtd.h b/lib/mtd.h deleted file mode 100644 index adf1ebe..0000000 --- a/lib/mtd.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2014 John Crispin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _FS_MTD_H__ -#define _FS_MTD_H__ - -extern int mtdsize; -extern int erasesize; - -int mtd_open(const char *mtd, int block); -int mtd_load(const char *mtd); -void mtd_erase(int fd, int first_block, int num_blocks); -int mtd_unlock(int fd); -int mtd_read_buffer(int fd, void *buf, int offset, int length); -int mtd_write_buffer(int fd, void *buf, int offset, int length); -int mtd_identify(char *mtd); - -#endif diff --git a/libblkid-tiny/hfs.c b/libblkid-tiny/hfs.c new file mode 100644 index 0000000..1398e38 --- /dev/null +++ b/libblkid-tiny/hfs.c @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2004-2008 Kay Sievers + * Copyright (C) 2008 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include +#include +#include +#include +#include + +#include "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 } + } +}; diff --git a/libblkid-tiny/libblkid-tiny.c b/libblkid-tiny/libblkid-tiny.c index dc18aa7..a01a43e 100644 --- a/libblkid-tiny/libblkid-tiny.c +++ b/libblkid-tiny/libblkid-tiny.c @@ -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)