2 * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #define F2FS_MINSIZE (100 * 1024 * 1024)
15 #define _FILE_OFFSET_BITS 64
17 #include <sys/types.h>
19 #include <sys/ioctl.h>
20 #include <sys/mount.h>
28 #include "libfstools.h"
31 #include <linux/loop.h>
33 #define ROOTDEV_OVERLAY_ALIGN (64ULL * 1024ULL)
35 struct squashfs_super_block {
41 struct rootdev_volume {
47 static const char *rootdev;
48 static struct driver rootdisk_driver;
50 static char *get_blockdev(dev_t dev)
52 const char *dirname = "/dev";
53 DIR *dir = opendir(dirname);
62 while ((d = readdir(dir)) != NULL) {
63 snprintf(buf, sizeof(buf), "%s/%s", dirname, d->d_name);
65 if (lstat(buf, &st) != 0)
68 if (!S_ISBLK(st.st_mode))
71 if (st.st_rdev != dev)
82 static char *get_rootdev(const char *dir)
89 return get_blockdev(S_ISBLK(st.st_mode) ? st.st_rdev : st.st_dev);
92 static int get_squashfs(struct squashfs_super_block *sb)
97 f = fopen(rootdev, "r");
101 len = fread(sb, sizeof(*sb), 1, f);
110 static bool rootdisk_use_f2fs(struct rootdev_volume *p)
116 fd = open(rootdev, O_RDONLY);
117 if (ioctl(fd, BLKGETSIZE64, &size) == 0)
118 ret = size - p->offset > F2FS_MINSIZE;
124 static struct volume *rootdisk_volume_find(char *name)
126 struct squashfs_super_block sb;
127 struct rootdev_volume *p;
129 if (strcmp(name, "rootfs_data") != 0)
133 rootdev = get_rootdev("/");
135 rootdev = get_rootdev("/rom");
139 if (strstr(rootdev, "mtdblock") ||
140 strstr(rootdev, "ubiblock"))
143 if (get_squashfs(&sb))
146 if (memcmp(&sb.s_magic, "hsqs", sizeof(sb.s_magic)) != 0)
149 p = calloc(1, sizeof(*p));
150 p->v.drv = &rootdisk_driver;
151 p->v.name = "rootfs_data";
153 p->offset = le64_to_cpu(sb.bytes_used);
154 p->offset = ((p->offset + (ROOTDEV_OVERLAY_ALIGN - 1)) &
155 ~(ROOTDEV_OVERLAY_ALIGN - 1));
160 static int rootdisk_volume_identify(struct volume *v)
162 struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
167 f = fopen(rootdev, "r");
171 fseeko(f, p->offset + 0x400, SEEK_SET);
172 fread(&magic, sizeof(magic), 1, f);
174 if (magic == cpu_to_le32(0xF2F52010))
178 fseeko(f, p->offset + 0x438, SEEK_SET);
179 fread(&magic, sizeof(magic), 1, f);
180 if ((le32_to_cpu(magic) & 0xffff) == 0xef53)
188 static int rootdisk_create_loop(struct rootdev_volume *p)
190 struct loop_info64 info;
195 ffd = open(rootdev, O_RDWR);
199 for (i = 0; i < 8; i++) {
200 snprintf(p->loop_name, sizeof(p->loop_name), "/dev/loop%d",
206 fd = open(p->loop_name, O_RDWR);
210 if (ioctl(fd, LOOP_GET_STATUS64, &info) == 0) {
211 if (strcmp((char *) info.lo_file_name, rootdev) != 0)
213 if (info.lo_offset != p->offset)
222 if (ioctl(fd, LOOP_SET_FD, ffd) != 0)
225 memset(&info, 0, sizeof(info));
226 snprintf((char *) info.lo_file_name, sizeof(info.lo_file_name), "%s",
228 info.lo_offset = p->offset;
230 if (ioctl(fd, LOOP_SET_STATUS64, &info) != 0) {
231 ioctl(fd, LOOP_CLR_FD, 0);
250 static int rootdisk_volume_init(struct volume *v)
252 struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
255 if (!p->loop_name[0] && rootdisk_create_loop(p) != 0)
259 v->blk = p->loop_name;
261 switch (rootdisk_volume_identify(v)) {
263 ULOG_INFO("rootdisk overlay filesystem has not been formatted yet\n");
264 if (rootdisk_use_f2fs(p))
265 snprintf(str, sizeof(str), "mkfs.f2fs -l rootfs_data %s", v->blk);
267 snprintf(str, sizeof(str), "mkfs.ext4 %s", v->blk);
276 static struct driver rootdisk_driver = {
278 .find = rootdisk_volume_find,
279 .init = rootdisk_volume_init,
280 .identify = rootdisk_volume_identify,
283 DRIVER(rootdisk_driver);