libfstools: add rootdisk overlay volume support
authorFelix Fietkau <nbd@nbd.name>
Tue, 6 Sep 2016 14:37:13 +0000 (16:37 +0200)
committerFelix Fietkau <nbd@nbd.name>
Thu, 8 Sep 2016 11:05:57 +0000 (13:05 +0200)
Uses either ext4 or f2fs, depending on the overlay volume size

Signed-off-by: Felix Fietkau <nbd@nbd.name>
CMakeLists.txt
libfstools/rootdisk.c [new file with mode: 0644]
libfstools/volume.h

index 7db28c0ab2ad10e40c527d8d098063a577dcf75c..b6aa2de001d39557293defb497589aebccd31629 100644 (file)
@@ -13,6 +13,7 @@ ADD_LIBRARY(fstools SHARED
                libfstools/mtd.c
                libfstools/mount.c
                libfstools/ubi.c
+               libfstools/rootdisk.c
                libfstools/find.c)
 TARGET_LINK_LIBRARIES(fstools ubox)
 INSTALL(TARGETS fstools LIBRARY DESTINATION lib)
diff --git a/libfstools/rootdisk.c b/libfstools/rootdisk.c
new file mode 100644 (file)
index 0000000..98c749d
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define F2FS_MINSIZE   (100 * 1024 * 1024)
+#define _FILE_OFFSET_BITS 64
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "libfstools.h"
+#include "volume.h"
+
+#include <linux/loop.h>
+
+#define ROOTDEV_OVERLAY_ALIGN  (64ULL * 1024ULL)
+
+struct squashfs_super_block {
+       uint32_t s_magic;
+       uint32_t pad0[9];
+       uint64_t bytes_used;
+};
+
+struct rootdev_volume {
+       struct volume v;
+       uint64_t offset;
+       char loop_name[32];
+};
+
+static const char *rootdev;
+static struct driver rootdisk_driver;
+
+static char *get_blockdev(dev_t dev)
+{
+       const char *dirname = "/dev";
+       DIR *dir = opendir(dirname);
+       struct dirent *d;
+       struct stat st;
+       static char buf[256];
+       char *ret = NULL;
+
+       if (!dir)
+               return ret;
+
+       while ((d = readdir(dir)) != NULL) {
+               snprintf(buf, sizeof(buf), "%s/%s", dirname, d->d_name);
+
+               if (lstat(buf, &st) != 0)
+                       continue;
+
+               if (!S_ISBLK(st.st_mode))
+                       continue;
+
+               if (st.st_rdev != dev)
+                       continue;
+
+               ret = buf;
+               break;
+       }
+
+       closedir(dir);
+       return ret;
+}
+
+static char *get_rootdev(const char *dir)
+{
+       struct stat st;
+
+       if (stat(dir, &st))
+               return NULL;
+
+       return get_blockdev(S_ISBLK(st.st_mode) ? st.st_rdev : st.st_dev);
+}
+
+static int get_squashfs(struct squashfs_super_block *sb)
+{
+       FILE *f;
+       int len;
+
+       f = fopen(rootdev, "r");
+       if (!f)
+               return -1;
+
+       len = fread(sb, sizeof(*sb), 1, f);
+       fclose(f);
+
+       if (len != 1)
+               return -1;
+
+       return 0;
+}
+
+static bool rootdisk_use_f2fs(struct rootdev_volume *p)
+{
+       uint64_t size = 0;
+       bool ret = false;
+       int fd;
+
+       fd = open(rootdev, O_RDONLY);
+       if (ioctl(fd, BLKGETSIZE64, &size) == 0)
+               ret = size - p->offset > F2FS_MINSIZE;
+       close(fd);
+
+       return ret;
+}
+
+static struct volume *rootdisk_volume_find(char *name)
+{
+       struct squashfs_super_block sb;
+       struct rootdev_volume *p;
+
+       if (strcmp(name, "rootfs_data") != 0)
+               return NULL;
+
+       if (!rootdev)
+               rootdev = get_rootdev("/");
+       if (!rootdev)
+               rootdev = get_rootdev("/rom");
+       if (!rootdev)
+               return NULL;
+
+       if (strstr(rootdev, "mtdblock") ||
+           strstr(rootdev, "ubiblock"))
+               return NULL;
+
+       if (get_squashfs(&sb))
+               return NULL;
+
+       if (memcmp(&sb.s_magic, "hsqs", sizeof(sb.s_magic)) != 0)
+               return NULL;
+
+       p = calloc(1, sizeof(*p));
+       p->v.drv = &rootdisk_driver;
+       p->v.name = "rootfs_data";
+
+       p->offset = le64_to_cpu(sb.bytes_used);
+       p->offset = ((p->offset + (ROOTDEV_OVERLAY_ALIGN - 1)) &
+                    ~(ROOTDEV_OVERLAY_ALIGN - 1));
+
+       return &p->v;
+}
+
+static int rootdisk_volume_identify(struct volume *v)
+{
+       struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
+       int ret = FS_NONE;
+       uint32_t magic = 0;
+       FILE *f;
+
+       f = fopen(rootdev, "r");
+       if (!f)
+               return ret;
+
+       fseeko(f, p->offset + 0x400, SEEK_SET);
+       fread(&magic, sizeof(magic), 1, f);
+
+       if (magic == cpu_to_le32(0xF2F52010))
+               ret = FS_F2FS;
+
+       magic = 0;
+       fseeko(f, p->offset + 0x438, SEEK_SET);
+       fread(&magic, sizeof(magic), 1, f);
+       if ((le32_to_cpu(magic) & 0xffff) == 0xef53)
+               ret = FS_EXT4;
+
+       fclose(f);
+
+       return ret;
+}
+
+static int rootdisk_create_loop(struct rootdev_volume *p)
+{
+       struct loop_info64 info;
+       int ret = -1;
+       int fd = -1;
+       int i, ffd;
+
+       ffd = open(rootdev, O_RDWR);
+       if (ffd < 0)
+               return -1;
+
+       for (i = 0; i < 8; i++) {
+               snprintf(p->loop_name, sizeof(p->loop_name), "/dev/loop%d",
+                        i);
+
+               if (fd >= 0)
+                       close(fd);
+
+               fd = open(p->loop_name, O_RDWR);
+               if (fd < 0)
+                       continue;
+
+               if (ioctl(fd, LOOP_GET_STATUS64, &info) == 0) {
+                       if (strcmp((char *) info.lo_file_name, rootdev) != 0)
+                               continue;
+                       if (info.lo_offset != p->offset)
+                               continue;
+                       ret = 0;
+                       break;
+               }
+
+               if (errno != ENXIO)
+                       continue;
+
+               if (ioctl(fd, LOOP_SET_FD, ffd) != 0)
+                       continue;
+
+               memset(&info, 0, sizeof(info));
+               snprintf((char *) info.lo_file_name, sizeof(info.lo_file_name), "%s",
+                        rootdev);
+               info.lo_offset = p->offset;
+
+               if (ioctl(fd, LOOP_SET_STATUS64, &info) != 0) {
+                       ioctl(fd, LOOP_CLR_FD, 0);
+                       continue;
+               }
+
+               ret = 0;
+               break;
+       }
+
+       if (fd >= 0)
+               close(fd);
+
+       close(ffd);
+
+       if (ret)
+               p->loop_name[0] = 0;
+
+       return ret;
+}
+
+static int rootdisk_volume_init(struct volume *v)
+{
+       struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
+       char str[128];
+
+       if (!p->loop_name[0] && rootdisk_create_loop(p) != 0)
+               return -1;
+
+       v->type = BLOCKDEV;
+       v->blk = p->loop_name;
+
+       switch (rootdisk_volume_identify(v)) {
+       case FS_NONE:
+               ULOG_INFO("rootdisk overlay filesystem has not been formatted yet\n");
+               if (rootdisk_use_f2fs(p))
+                       snprintf(str, sizeof(str), "mkfs.f2fs -l rootfs_data %s", v->blk);
+               else
+                       snprintf(str, sizeof(str), "mkfs.ext4 %s", v->blk);
+               system(str);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static struct driver rootdisk_driver = {
+       .name = "rootdisk",
+       .find = rootdisk_volume_find,
+       .init = rootdisk_volume_init,
+       .identify = rootdisk_volume_identify,
+};
+
+DRIVER(rootdisk_driver);
index fdd97d16457e07b1a2613f41f375382e3678fbbc..912b711d4b9347346ff52bbff538d0266b0b16d0 100644 (file)
@@ -47,6 +47,7 @@ enum {
        NANDFLASH,
        NORFLASH,
        UBIVOLUME,
+       BLOCKDEV,
 };
 
 struct volume {