backend/snapshot.c
backend/extroot.c
backend/jffs2.c
- lib/mtd.c
+ driver/volume.c
+ driver/mtd.c
lib/mount.c
lib/find.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)
#include <stdio.h>
#include <stdlib.h>
-#include "../lib/mtd.h"
#include "../fs-state.h"
+#include "../driver/volume.h"
+
int
backend_mount(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;
return 0;
}
- switch (mtd_identify(mtd)) {
+ switch (volume_identify(v)) {
case FS_NONE:
case FS_DEADCODE:
return ramoverlay();
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);
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;
#include <fcntl.h>
#include "../fs-state.h"
-#include "../lib/mtd.h"
+#include "../driver/volume.h"
#define SWITCH_JFFS2 "/tmp/.switch_jffs2"
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)) {
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;
}
static int
jffs2_reset(int argc, char **argv)
{
- char mtd[32];
+ struct volume *v;
char *mp;
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;
}
- 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;
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;
}
int
jffs2_switch(int argc, char **argv)
{
- char mtd[32];
+ struct volume *v;
char *mp;
int ret = -1;
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 */
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")) {
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <asm/byteorder.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glob.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "../fs-state.h"
+#include "../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);
#include <libubox/md5.h>
#include "../fs-state.h"
-#include "../lib/mtd.h"
+#include "../driver/volume.h"
#define PATH_MAX 256
#define OWRT 0x4f575254
}
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;
}
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;
*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;
}
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);
}
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;
}
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;
}
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;
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;
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;
}
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;
}
}
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;
}
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;
}
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;
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
}
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
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;
}
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;
}
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)) {
(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;
}
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/mount.h>
+#include <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);
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/mount.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _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
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)
{
out:
return ret;
}
-
-
+++ /dev/null
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <sys/stat.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <asm/byteorder.h>
-#include <mtd/mtd-user.h>
-
-#include <errno.h>
-#include <glob.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "../fs-state.h"
-#include "mtd.h"
-
-#define PATH_MAX 256
-
-int mtdsize = 0;
-int erasesize = 0;
-
-int
-mtd_open(const char *mtd, int block)
-{
- FILE *fp;
- char dev[PATH_MAX];
- int i, ret, flags = O_RDWR | O_SYNC;
-
- if ((fp = fopen("/proc/mtd", "r"))) {
- while (fgets(dev, sizeof(dev), fp)) {
- if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
- snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
- ret = open(dev, flags);
- if (ret < 0) {
- snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
- ret = open(dev, flags);
- }
- fclose(fp);
- return ret;
- }
- }
- fclose(fp);
- }
-
- return open(mtd, flags);
-}
-
-int
-mtd_load(const char *mtd)
-{
- struct mtd_info_user mtdInfo;
- struct erase_info_user mtdLockInfo;
- int fd;
-
- fd = mtd_open(mtd, 0);
- if (fd < 0) {
- fprintf(stderr, "Could not open mtd device: %s\n", mtd);
- return -1;
- }
-
- if (ioctl(fd, MEMGETINFO, &mtdInfo)) {
- fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
- close(fd);
- return -1;
- }
-
- mtdsize = mtdInfo.size;
- erasesize = mtdInfo.erasesize;
-
- mtdLockInfo.start = 0;
- mtdLockInfo.length = mtdsize;
- ioctl(fd, MEMUNLOCK, &mtdLockInfo);
-
- return fd;
-}
-
-void
-mtd_erase(int fd, int first_block, int num_blocks)
-{
- struct erase_info_user eiu;
-
- eiu.length = erasesize;
- for (eiu.start = first_block * erasesize;
- eiu.start < mtdsize && eiu.start < (first_block + num_blocks) * erasesize;
- eiu.start += erasesize) {
- fprintf(stderr, "erasing %x %x\n", eiu.start, erasesize);
- ioctl(fd, MEMUNLOCK, &eiu);
- if (ioctl(fd, MEMERASE, &eiu))
- fprintf(stderr, "Failed to erase block at 0x%x\n", eiu.start);
- }
-}
-
-int
-mtd_unlock(int fd)
-{
- struct mtd_info_user mtdinfo;
- int ret = ioctl(fd, MEMGETINFO, &mtdinfo);
-
- if (ret) {
- fprintf(stderr, "ioctl(%d, MEMGETINFO) failed: %s\n", fd, strerror(errno));
- } else {
- struct erase_info_user mtdlock;
-
- mtdlock.start = 0;
- mtdlock.length = mtdinfo.size;
- ioctl(fd, MEMUNLOCK, &mtdlock);
- }
-
- return ret;
-}
-
-int
-mtd_read_buffer(int fd, void *buf, int offset, int length)
-{
- if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
- fprintf(stderr, "lseek/read failed\n");
- return -1;
- }
-
- if (read(fd, buf, length) == -1) {
- fprintf(stderr, "read failed\n");
- return -1;
- }
-
- return 0;
-}
-
-int
-mtd_write_buffer(int fd, void *buf, int offset, int length)
-{
- if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
- fprintf(stderr, "lseek/write failed at offset %d\n", offset);
- perror("lseek");
- return -1;
- }
-
- if (write(fd, buf, length) == -1) {
- fprintf(stderr, "write failed\n");
- return -1;
- }
-
- return 0;
-}
-
-int
-mtd_identify(char *mtd)
-{
- int fd = mtd_load(mtd);
- __u32 deadc0de;
- __u16 jffs2;
- size_t sz;
-
- if (!fd) {
- fprintf(stderr, "reading %s failed\n", mtd);
- return -1;
- }
-
- sz = read(fd, &deadc0de, sizeof(deadc0de));
- close(fd);
-
- if (sz != sizeof(deadc0de)) {
- fprintf(stderr, "reading %s failed: %s\n", mtd, strerror(errno));
- return -1;
- }
-
- if (deadc0de == 0x4f575254)
- return FS_SNAPSHOT;
-
- deadc0de = __be32_to_cpu(deadc0de);
- jffs2 = __be16_to_cpu(deadc0de >> 16);
-
- if (jffs2 == 0x1985) {
- fprintf(stderr, "jffs2 is ready\n");
- return FS_JFFS2;
- }
-
- if (deadc0de == 0xdeadc0de) {
- fprintf(stderr, "jffs2 is not ready - marker found\n");
- return FS_DEADCODE;
- }
-
- fprintf(stderr, "No jffs2 marker was found\n");
-
- return FS_NONE;
-}
+++ /dev/null
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef _FS_MTD_H__
-#define _FS_MTD_H__
-
-extern int mtdsize;
-extern int erasesize;
-
-int mtd_open(const char *mtd, int block);
-int mtd_load(const char *mtd);
-void mtd_erase(int fd, int first_block, int num_blocks);
-int mtd_unlock(int fd);
-int mtd_read_buffer(int fd, void *buf, int offset, int length);
-int mtd_write_buffer(int fd, void *buf, int offset, int length);
-int mtd_identify(char *mtd);
-
-#endif
--- /dev/null
+/*
+ * 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 }
+ }
+};
&ext2_idinfo,
&jbd_idinfo,
&squashfs_idinfo,
+ &ubifs_idinfo,
&jffs2_idinfo,
+ &hfsplus_idinfo,
+ &hfs_idinfo,
};
int probe_block(char *block, struct blkid_struct_probe *pr)