• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/fstools/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 #include "common.h"
 15 
 16 #include <linux/loop.h>
 17 
 18 #define SQUASHFS_MAGIC          "hsqs"
 19 #define EROFS_MAGIC             0xE0F5E1E2
 20 #define ROOTDEV_OVERLAY_ALIGN   (64ULL * 1024ULL)
 21 
 22 struct squashfs_super_block {
 23         uint32_t s_magic;
 24         uint32_t pad0[9];
 25         uint64_t bytes_used;
 26 };
 27 
 28 struct erofs_super_block {
 29         uint32_t s_magic;
 30         uint32_t pad0[2];
 31         uint8_t blkszbits;
 32         uint8_t pad1[23];
 33         uint32_t blocks;
 34 };
 35 
 36 struct rootdev_volume {
 37         struct volume v;
 38         uint64_t offset;
 39         char loop_name[32];
 40 };
 41 
 42 static const char *rootdev;
 43 static struct driver rootdisk_driver;
 44 
 45 static char *get_blockdev(dev_t dev)
 46 {
 47         const char *dirname = "/dev";
 48         DIR *dir = opendir(dirname);
 49         struct dirent *d;
 50         struct stat st;
 51         static char buf[256];
 52         char *ret = NULL;
 53 
 54         if (!dir)
 55                 return ret;
 56 
 57         while ((d = readdir(dir)) != NULL) {
 58                 snprintf(buf, sizeof(buf), "%s/%s", dirname, d->d_name);
 59 
 60                 if (lstat(buf, &st) != 0)
 61                         continue;
 62 
 63                 if (!S_ISBLK(st.st_mode))
 64                         continue;
 65 
 66                 if (st.st_rdev != dev)
 67                         continue;
 68 
 69                 ret = buf;
 70                 break;
 71         }
 72 
 73         closedir(dir);
 74         return ret;
 75 }
 76 
 77 static char *get_rootdev(const char *dir)
 78 {
 79         struct stat st;
 80 
 81         if (stat(dir, &st))
 82                 return NULL;
 83 
 84         return get_blockdev(S_ISBLK(st.st_mode) ? st.st_rdev : st.st_dev);
 85 }
 86 
 87 static int get_squashfs(struct squashfs_super_block *sb)
 88 {
 89         FILE *f;
 90         int len;
 91 
 92         f = fopen(rootdev, "r");
 93         if (!f)
 94                 return -1;
 95 
 96         len = fread(sb, sizeof(*sb), 1, f);
 97         fclose(f);
 98 
 99         if (len != 1)
100                 return -1;
101 
102         return 0;
103 }
104 
105 static int check_squashfs(uint64_t *offset)
106 {
107         const char *s_magic = SQUASHFS_MAGIC;
108         struct squashfs_super_block sb;
109         int ret;
110 
111         ret = get_squashfs(&sb);
112         if (ret)
113                 return ret;
114 
115         if (memcmp(&sb.s_magic, s_magic, sizeof(sb.s_magic)))
116                 return -1;
117 
118         *offset = le64_to_cpu(sb.bytes_used);
119         return 0;
120 }
121 
122 static int get_erofs(struct erofs_super_block *sb)
123 {
124         FILE *f;
125         int len;
126 
127         f = fopen(rootdev, "r");
128         if (!f)
129                 return -1;
130 
131         if (fseek(f, 1024, SEEK_SET))
132                 return -1;
133 
134         len = fread(sb, sizeof(*sb), 1, f);
135         fclose(f);
136 
137         if (len != 1)
138                 return -1;
139 
140         return 0;
141 }
142 
143 static int check_erofs(uint64_t *offset)
144 {
145         uint32_t s_magic = cpu_to_le32(EROFS_MAGIC);
146         struct erofs_super_block sb;
147         int ret;
148 
149         ret = get_erofs(&sb);
150         if (ret)
151                 return ret;
152 
153         if (memcmp(&sb.s_magic, &s_magic, sizeof(sb.s_magic)))
154                 return -1;
155 
156         *offset = (uint64_t)le32_to_cpu(sb.blocks) << sb.blkszbits;
157         return 0;
158 }
159 
160 static struct volume *rootdisk_volume_find(char *name)
161 {
162         struct rootdev_volume *p;
163         uint64_t offset;
164         int ret;
165 
166         if (strcmp(name, "rootfs_data") != 0)
167                 return NULL;
168 
169         if (!rootdev)
170                 rootdev = get_rootdev("/");
171         if (!rootdev)
172                 rootdev = get_rootdev("/rom");
173         if (!rootdev)
174                 return NULL;
175 
176         /*
177          * We support both SquashFS and EroFS.
178          * First check for SquashFS and then check
179          * for EroFS on new images.
180          */
181         ret = check_squashfs(&offset);
182         if (ret < 0 || !offset)
183                 ret = check_erofs(&offset);
184         if (ret < 0 || !offset)
185                 return NULL;
186 
187         p = calloc(1, sizeof(*p));
188         p->v.drv = &rootdisk_driver;
189         p->v.name = "rootfs_data";
190 
191         p->offset = offset;
192         p->offset = ((p->offset + (ROOTDEV_OVERLAY_ALIGN - 1)) &
193                      ~(ROOTDEV_OVERLAY_ALIGN - 1));
194 
195         return &p->v;
196 }
197 
198 static int rootdisk_volume_identify(struct volume *v)
199 {
200         struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
201         FILE *f;
202         int ret = FS_NONE;
203         f = fopen(rootdev, "r");
204         if (!f)
205                 return ret;
206 
207         ret = block_file_identify(f, p->offset);
208 
209         fclose(f);
210 
211         return ret;
212 }
213 
214 static int rootdisk_create_loop(struct rootdev_volume *p)
215 {
216         struct loop_info64 info;
217         int ret = -1;
218         int fd = -1;
219         int i, ffd;
220 
221         ffd = open(rootdev, O_RDWR);
222         if (ffd < 0)
223                 return -1;
224 
225         for (i = 0; i < 8; i++) {
226                 snprintf(p->loop_name, sizeof(p->loop_name), "/dev/loop%d",
227                          i);
228 
229                 if (fd >= 0)
230                         close(fd);
231 
232                 fd = open(p->loop_name, O_RDWR);
233                 if (fd < 0)
234                         continue;
235 
236                 if (ioctl(fd, LOOP_GET_STATUS64, &info) == 0) {
237                         if (strcmp((char *) info.lo_file_name, rootdev) != 0)
238                                 continue;
239                         if (info.lo_offset != p->offset)
240                                 continue;
241                         ret = 0;
242                         break;
243                 }
244 
245                 if (errno != ENXIO)
246                         continue;
247 
248                 if (ioctl(fd, LOOP_SET_FD, ffd) != 0)
249                         continue;
250 
251                 memset(&info, 0, sizeof(info));
252                 snprintf((char *) info.lo_file_name, sizeof(info.lo_file_name), "%s",
253                          rootdev);
254                 info.lo_offset = p->offset;
255                 info.lo_flags |= LO_FLAGS_AUTOCLEAR;
256 
257                 if (ioctl(fd, LOOP_SET_STATUS64, &info) != 0) {
258                         ioctl(fd, LOOP_CLR_FD, 0);
259                         continue;
260                 }
261 
262                 /*
263                  * Don't close fd. Leave it open until this process exits, to avoid
264                  * the autoclear from happening too soon.
265                  */
266                 fd = -1;
267 
268                 ret = 0;
269                 break;
270         }
271 
272         if (fd >= 0)
273                 close(fd);
274 
275         close(ffd);
276 
277         if (ret)
278                 p->loop_name[0] = 0;
279 
280         return ret;
281 }
282 
283 static int rootdisk_volume_init(struct volume *v)
284 {
285         struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
286 
287         if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) {
288                 ULOG_ERR("unable to create loop device\n");
289                 return -1;
290         }
291 
292         v->type = BLOCKDEV;
293         v->blk = p->loop_name;
294 
295         return block_volume_format(v, p->offset, rootdev);
296 }
297 
298 static struct driver rootdisk_driver = {
299         .name = "rootdisk",
300         .find = rootdisk_volume_find,
301         .init = rootdisk_volume_init,
302         .identify = rootdisk_volume_identify,
303 };
304 
305 DRIVER(rootdisk_driver);
306 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt