• 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 #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                 info.lo_flags |= LO_FLAGS_AUTOCLEAR;
235 
236                 if (ioctl(fd, LOOP_SET_STATUS64, &info) != 0) {
237                         ioctl(fd, LOOP_CLR_FD, 0);
238                         continue;
239                 }
240 
241                 /*
242                  * Don't close fd. Leave it open until this process exits, to avoid
243                  * the autoclear from happening too soon.
244                  */
245                 fd = -1;
246 
247                 ret = 0;
248                 break;
249         }
250 
251         if (fd >= 0)
252                 close(fd);
253 
254         close(ffd);
255 
256         if (ret)
257                 p->loop_name[0] = 0;
258 
259         return ret;
260 }
261 
262 static int rootdisk_volume_init(struct volume *v)
263 {
264         struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
265         char str[128];
266         int ret = 0;
267 
268         if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) {
269                 ULOG_ERR("unable to create loop device\n");
270                 return -1;
271         }
272 
273         v->type = BLOCKDEV;
274         v->blk = p->loop_name;
275 
276         switch (rootdisk_volume_identify(v)) {
277         case FS_NONE:
278                 ULOG_INFO("rootdisk overlay filesystem has not been formatted yet\n");
279                 if (rootdisk_use_f2fs(p))
280                         snprintf(str, sizeof(str), "mkfs.f2fs -q -l rootfs_data %s", v->blk);
281                 else
282                         snprintf(str, sizeof(str), "mkfs.ext4 -q -L rootfs_data %s", v->blk);
283                 ret = system(str);
284                 break;
285         default:
286                 break;
287         }
288         return ret;
289 }
290 
291 static struct driver rootdisk_driver = {
292         .name = "rootdisk",
293         .find = rootdisk_volume_find,
294         .init = rootdisk_volume_init,
295         .identify = rootdisk_volume_identify,
296 };
297 
298 DRIVER(rootdisk_driver);
299 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt