5a6dcb9fc9aa66987630d0ff53e6796c3fda263b
[project/fstools.git] / libfstools / rootdisk.c
1 /*
2 * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
3 *
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
7 *
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.
12 */
13
14 #define _FILE_OFFSET_BITS 64
15
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/ioctl.h>
19 #include <sys/mount.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26
27 #include "libfstools.h"
28 #include "volume.h"
29
30 #include <linux/loop.h>
31
32 #define ROOTDEV_OVERLAY_ALIGN (64ULL * 1024ULL)
33 #define F2FS_MINSIZE (100ULL * 1024ULL * 1024ULL)
34
35 struct squashfs_super_block {
36 uint32_t s_magic;
37 uint32_t pad0[9];
38 uint64_t bytes_used;
39 };
40
41 struct rootdev_volume {
42 struct volume v;
43 uint64_t offset;
44 char loop_name[32];
45 };
46
47 static const char *rootdev;
48 static struct driver rootdisk_driver;
49
50 static char *get_blockdev(dev_t dev)
51 {
52 const char *dirname = "/dev";
53 DIR *dir = opendir(dirname);
54 struct dirent *d;
55 struct stat st;
56 static char buf[256];
57 char *ret = NULL;
58
59 if (!dir)
60 return ret;
61
62 while ((d = readdir(dir)) != NULL) {
63 snprintf(buf, sizeof(buf), "%s/%s", dirname, d->d_name);
64
65 if (lstat(buf, &st) != 0)
66 continue;
67
68 if (!S_ISBLK(st.st_mode))
69 continue;
70
71 if (st.st_rdev != dev)
72 continue;
73
74 ret = buf;
75 break;
76 }
77
78 closedir(dir);
79 return ret;
80 }
81
82 static char *get_rootdev(const char *dir)
83 {
84 struct stat st;
85
86 if (stat(dir, &st))
87 return NULL;
88
89 return get_blockdev(S_ISBLK(st.st_mode) ? st.st_rdev : st.st_dev);
90 }
91
92 static int get_squashfs(struct squashfs_super_block *sb)
93 {
94 FILE *f;
95 int len;
96
97 f = fopen(rootdev, "r");
98 if (!f)
99 return -1;
100
101 len = fread(sb, sizeof(*sb), 1, f);
102 fclose(f);
103
104 if (len != 1)
105 return -1;
106
107 return 0;
108 }
109
110 static bool rootdisk_use_f2fs(struct rootdev_volume *p)
111 {
112 uint64_t size = 0;
113 bool ret = false;
114 int fd;
115
116 fd = open(rootdev, O_RDONLY);
117 if (ioctl(fd, BLKGETSIZE64, &size) == 0)
118 ret = size - p->offset > F2FS_MINSIZE;
119 close(fd);
120
121 return ret;
122 }
123
124 static struct volume *rootdisk_volume_find(char *name)
125 {
126 struct squashfs_super_block sb;
127 struct rootdev_volume *p;
128
129 if (strcmp(name, "rootfs_data") != 0)
130 return NULL;
131
132 if (!rootdev)
133 rootdev = get_rootdev("/");
134 if (!rootdev)
135 rootdev = get_rootdev("/rom");
136 if (!rootdev)
137 return NULL;
138
139 if (strstr(rootdev, "mtdblock") ||
140 strstr(rootdev, "ubiblock"))
141 return NULL;
142
143 if (get_squashfs(&sb))
144 return NULL;
145
146 if (memcmp(&sb.s_magic, "hsqs", sizeof(sb.s_magic)) != 0)
147 return NULL;
148
149 p = calloc(1, sizeof(*p));
150 p->v.drv = &rootdisk_driver;
151 p->v.name = "rootfs_data";
152
153 p->offset = le64_to_cpu(sb.bytes_used);
154 p->offset = ((p->offset + (ROOTDEV_OVERLAY_ALIGN - 1)) &
155 ~(ROOTDEV_OVERLAY_ALIGN - 1));
156
157 return &p->v;
158 }
159
160 static int rootdisk_volume_identify(struct volume *v)
161 {
162 struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
163 int ret = FS_NONE;
164 uint32_t magic = 0;
165 size_t n;
166 FILE *f;
167
168 f = fopen(rootdev, "r");
169 if (!f)
170 return ret;
171
172 fseeko(f, p->offset + 0x400, SEEK_SET);
173 n = fread(&magic, sizeof(magic), 1, f);
174 if (n != 1)
175 return -1;
176
177 if (magic == cpu_to_le32(0xF2F52010))
178 ret = FS_F2FS;
179
180 magic = 0;
181 fseeko(f, p->offset + 0x438, SEEK_SET);
182 n = fread(&magic, sizeof(magic), 1, f);
183 if (n != 1)
184 return -1;
185 if ((le32_to_cpu(magic) & 0xffff) == 0xef53)
186 ret = FS_EXT4;
187
188 fclose(f);
189
190 return ret;
191 }
192
193 static int rootdisk_create_loop(struct rootdev_volume *p)
194 {
195 struct loop_info64 info;
196 int ret = -1;
197 int fd = -1;
198 int i, ffd;
199
200 ffd = open(rootdev, O_RDWR);
201 if (ffd < 0)
202 return -1;
203
204 for (i = 0; i < 8; i++) {
205 snprintf(p->loop_name, sizeof(p->loop_name), "/dev/loop%d",
206 i);
207
208 if (fd >= 0)
209 close(fd);
210
211 fd = open(p->loop_name, O_RDWR);
212 if (fd < 0)
213 continue;
214
215 if (ioctl(fd, LOOP_GET_STATUS64, &info) == 0) {
216 if (strcmp((char *) info.lo_file_name, rootdev) != 0)
217 continue;
218 if (info.lo_offset != p->offset)
219 continue;
220 ret = 0;
221 break;
222 }
223
224 if (errno != ENXIO)
225 continue;
226
227 if (ioctl(fd, LOOP_SET_FD, ffd) != 0)
228 continue;
229
230 memset(&info, 0, sizeof(info));
231 snprintf((char *) info.lo_file_name, sizeof(info.lo_file_name), "%s",
232 rootdev);
233 info.lo_offset = p->offset;
234
235 if (ioctl(fd, LOOP_SET_STATUS64, &info) != 0) {
236 ioctl(fd, LOOP_CLR_FD, 0);
237 continue;
238 }
239
240 ret = 0;
241 break;
242 }
243
244 if (fd >= 0)
245 close(fd);
246
247 close(ffd);
248
249 if (ret)
250 p->loop_name[0] = 0;
251
252 return ret;
253 }
254
255 static int rootdisk_volume_init(struct volume *v)
256 {
257 struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
258 char str[128];
259 int ret = 0;
260
261 if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) {
262 ULOG_ERR("unable to create loop device\n");
263 return -1;
264 }
265
266 v->type = BLOCKDEV;
267 v->blk = p->loop_name;
268
269 switch (rootdisk_volume_identify(v)) {
270 case FS_NONE:
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);
274 else
275 snprintf(str, sizeof(str), "mkfs.ext4 -q -L rootfs_data %s", v->blk);
276 ret = system(str);
277 break;
278 default:
279 break;
280 }
281 return ret;
282 }
283
284 static struct driver rootdisk_driver = {
285 .name = "rootdisk",
286 .find = rootdisk_volume_find,
287 .init = rootdisk_volume_init,
288 .identify = rootdisk_volume_identify,
289 };
290
291 DRIVER(rootdisk_driver);