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.
16 #include <linux/loop.h>
18 #define ROOTDEV_OVERLAY_ALIGN (64ULL * 1024ULL)
19 #define F2FS_MINSIZE (100ULL * 1024ULL * 1024ULL)
21 struct squashfs_super_block
{
27 struct rootdev_volume
{
33 static const char *rootdev
;
34 static struct driver rootdisk_driver
;
36 static char *get_blockdev(dev_t dev
)
38 const char *dirname
= "/dev";
39 DIR *dir
= opendir(dirname
);
48 while ((d
= readdir(dir
)) != NULL
) {
49 snprintf(buf
, sizeof(buf
), "%s/%s", dirname
, d
->d_name
);
51 if (lstat(buf
, &st
) != 0)
54 if (!S_ISBLK(st
.st_mode
))
57 if (st
.st_rdev
!= dev
)
68 static char *get_rootdev(const char *dir
)
75 return get_blockdev(S_ISBLK(st
.st_mode
) ? st
.st_rdev
: st
.st_dev
);
78 static int get_squashfs(struct squashfs_super_block
*sb
)
83 f
= fopen(rootdev
, "r");
87 len
= fread(sb
, sizeof(*sb
), 1, f
);
96 static struct volume
*rootdisk_volume_find(char *name
)
98 struct squashfs_super_block sb
;
99 struct rootdev_volume
*p
;
101 if (strcmp(name
, "rootfs_data") != 0)
105 rootdev
= get_rootdev("/");
107 rootdev
= get_rootdev("/rom");
111 if (get_squashfs(&sb
))
114 if (memcmp(&sb
.s_magic
, "hsqs", sizeof(sb
.s_magic
)) != 0)
117 p
= calloc(1, sizeof(*p
));
118 p
->v
.drv
= &rootdisk_driver
;
119 p
->v
.name
= "rootfs_data";
121 p
->offset
= le64_to_cpu(sb
.bytes_used
);
122 p
->offset
= ((p
->offset
+ (ROOTDEV_OVERLAY_ALIGN
- 1)) &
123 ~(ROOTDEV_OVERLAY_ALIGN
- 1));
128 static int rootdisk_volume_identify(struct volume
*v
)
130 struct rootdev_volume
*p
= container_of(v
, struct rootdev_volume
, v
);
133 f
= fopen(rootdev
, "r");
137 ret
= block_file_identify(f
, p
->offset
);
144 static int rootdisk_create_loop(struct rootdev_volume
*p
)
146 struct loop_info64 info
;
151 ffd
= open(rootdev
, O_RDWR
);
155 for (i
= 0; i
< 8; i
++) {
156 snprintf(p
->loop_name
, sizeof(p
->loop_name
), "/dev/loop%d",
162 fd
= open(p
->loop_name
, O_RDWR
);
166 if (ioctl(fd
, LOOP_GET_STATUS64
, &info
) == 0) {
167 if (strcmp((char *) info
.lo_file_name
, rootdev
) != 0)
169 if (info
.lo_offset
!= p
->offset
)
178 if (ioctl(fd
, LOOP_SET_FD
, ffd
) != 0)
181 memset(&info
, 0, sizeof(info
));
182 snprintf((char *) info
.lo_file_name
, sizeof(info
.lo_file_name
), "%s",
184 info
.lo_offset
= p
->offset
;
185 info
.lo_flags
|= LO_FLAGS_AUTOCLEAR
;
187 if (ioctl(fd
, LOOP_SET_STATUS64
, &info
) != 0) {
188 ioctl(fd
, LOOP_CLR_FD
, 0);
193 * Don't close fd. Leave it open until this process exits, to avoid
194 * the autoclear from happening too soon.
213 static int rootdisk_volume_init(struct volume
*v
)
215 struct rootdev_volume
*p
= container_of(v
, struct rootdev_volume
, v
);
217 if (!p
->loop_name
[0] && rootdisk_create_loop(p
) != 0) {
218 ULOG_ERR("unable to create loop device\n");
223 v
->blk
= p
->loop_name
;
225 return block_volume_format(v
, p
->offset
, rootdev
);
228 static struct driver rootdisk_driver
= {
230 .find
= rootdisk_volume_find
,
231 .init
= rootdisk_volume_init
,
232 .identify
= rootdisk_volume_identify
,
235 DRIVER(rootdisk_driver
);