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 _FILE_OFFSET_BITS 64
16 #include <sys/types.h>
18 #include <sys/ioctl.h>
19 #include <sys/mount.h>
27 #include "libfstools.h"
30 #include <linux/loop.h>
32 #define ROOTDEV_OVERLAY_ALIGN (64ULL * 1024ULL)
33 #define F2FS_MINSIZE (100ULL * 1024ULL * 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
);
168 f
= fopen(rootdev
, "r");
172 fseeko(f
, p
->offset
+ 0x400, SEEK_SET
);
173 n
= fread(&magic
, sizeof(magic
), 1, f
);
177 if (magic
== cpu_to_le32(0xF2F52010))
181 fseeko(f
, p
->offset
+ 0x438, SEEK_SET
);
182 n
= fread(&magic
, sizeof(magic
), 1, f
);
185 if ((le32_to_cpu(magic
) & 0xffff) == 0xef53)
193 static int rootdisk_create_loop(struct rootdev_volume
*p
)
195 struct loop_info64 info
;
200 ffd
= open(rootdev
, O_RDWR
);
204 for (i
= 0; i
< 8; i
++) {
205 snprintf(p
->loop_name
, sizeof(p
->loop_name
), "/dev/loop%d",
211 fd
= open(p
->loop_name
, O_RDWR
);
215 if (ioctl(fd
, LOOP_GET_STATUS64
, &info
) == 0) {
216 if (strcmp((char *) info
.lo_file_name
, rootdev
) != 0)
218 if (info
.lo_offset
!= p
->offset
)
227 if (ioctl(fd
, LOOP_SET_FD
, ffd
) != 0)
230 memset(&info
, 0, sizeof(info
));
231 snprintf((char *) info
.lo_file_name
, sizeof(info
.lo_file_name
), "%s",
233 info
.lo_offset
= p
->offset
;
235 if (ioctl(fd
, LOOP_SET_STATUS64
, &info
) != 0) {
236 ioctl(fd
, LOOP_CLR_FD
, 0);
255 static int rootdisk_volume_init(struct volume
*v
)
257 struct rootdev_volume
*p
= container_of(v
, struct rootdev_volume
, v
);
261 if (!p
->loop_name
[0] && rootdisk_create_loop(p
) != 0) {
262 ULOG_ERR("unable to create loop device\n");
267 v
->blk
= p
->loop_name
;
269 switch (rootdisk_volume_identify(v
)) {
271 ULOG_INFO("rootdisk overlay filesystem has not been formatted yet\n");
272 if (rootdisk_use_f2fs(p
))
273 snprintf(str
, sizeof(str
), "mkfs.f2fs -q -l rootfs_data %s", v
->blk
);
275 snprintf(str
, sizeof(str
), "mkfs.ext4 -q -L rootfs_data %s", v
->blk
);
284 static struct driver rootdisk_driver
= {
286 .find
= rootdisk_volume_find
,
287 .init
= rootdisk_volume_init
,
288 .identify
= rootdisk_volume_identify
,
291 DRIVER(rootdisk_driver
);