• 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 ROOTDEV_OVERLAY_ALIGN   (64ULL * 1024ULL)
 19 #define F2FS_MINSIZE            (100ULL * 1024ULL * 1024ULL)
 20 
 21 struct squashfs_super_block {
 22         uint32_t s_magic;
 23         uint32_t pad0[9];
 24         uint64_t bytes_used;
 25 };
 26 
 27 struct rootdev_volume {
 28         struct volume v;
 29         uint64_t offset;
 30         char loop_name[32];
 31 };
 32 
 33 static const char *rootdev;
 34 static struct driver rootdisk_driver;
 35 
 36 static char *get_blockdev(dev_t dev)
 37 {
 38         const char *dirname = "/dev";
 39         DIR *dir = opendir(dirname);
 40         struct dirent *d;
 41         struct stat st;
 42         static char buf[256];
 43         char *ret = NULL;
 44 
 45         if (!dir)
 46                 return ret;
 47 
 48         while ((d = readdir(dir)) != NULL) {
 49                 snprintf(buf, sizeof(buf), "%s/%s", dirname, d->d_name);
 50 
 51                 if (lstat(buf, &st) != 0)
 52                         continue;
 53 
 54                 if (!S_ISBLK(st.st_mode))
 55                         continue;
 56 
 57                 if (st.st_rdev != dev)
 58                         continue;
 59 
 60                 ret = buf;
 61                 break;
 62         }
 63 
 64         closedir(dir);
 65         return ret;
 66 }
 67 
 68 static char *get_rootdev(const char *dir)
 69 {
 70         struct stat st;
 71 
 72         if (stat(dir, &st))
 73                 return NULL;
 74 
 75         return get_blockdev(S_ISBLK(st.st_mode) ? st.st_rdev : st.st_dev);
 76 }
 77 
 78 static int get_squashfs(struct squashfs_super_block *sb)
 79 {
 80         FILE *f;
 81         int len;
 82 
 83         f = fopen(rootdev, "r");
 84         if (!f)
 85                 return -1;
 86 
 87         len = fread(sb, sizeof(*sb), 1, f);
 88         fclose(f);
 89 
 90         if (len != 1)
 91                 return -1;
 92 
 93         return 0;
 94 }
 95 
 96 static struct volume *rootdisk_volume_find(char *name)
 97 {
 98         struct squashfs_super_block sb;
 99         struct rootdev_volume *p;
100 
101         if (strcmp(name, "rootfs_data") != 0)
102                 return NULL;
103 
104         if (!rootdev)
105                 rootdev = get_rootdev("/");
106         if (!rootdev)
107                 rootdev = get_rootdev("/rom");
108         if (!rootdev)
109                 return NULL;
110 
111         if (strstr(rootdev, "mtdblock") ||
112             strstr(rootdev, "ubiblock"))
113                 return NULL;
114 
115         if (get_squashfs(&sb))
116                 return NULL;
117 
118         if (memcmp(&sb.s_magic, "hsqs", sizeof(sb.s_magic)) != 0)
119                 return NULL;
120 
121         p = calloc(1, sizeof(*p));
122         p->v.drv = &rootdisk_driver;
123         p->v.name = "rootfs_data";
124 
125         p->offset = le64_to_cpu(sb.bytes_used);
126         p->offset = ((p->offset + (ROOTDEV_OVERLAY_ALIGN - 1)) &
127                      ~(ROOTDEV_OVERLAY_ALIGN - 1));
128 
129         return &p->v;
130 }
131 
132 static int rootdisk_volume_identify(struct volume *v)
133 {
134         struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
135         FILE *f;
136         int ret = FS_NONE;
137         f = fopen(rootdev, "r");
138         if (!f)
139                 return ret;
140 
141         ret = block_file_identify(f, p->offset);
142 
143         fclose(f);
144 
145         return ret;
146 }
147 
148 static int rootdisk_create_loop(struct rootdev_volume *p)
149 {
150         struct loop_info64 info;
151         int ret = -1;
152         int fd = -1;
153         int i, ffd;
154 
155         ffd = open(rootdev, O_RDWR);
156         if (ffd < 0)
157                 return -1;
158 
159         for (i = 0; i < 8; i++) {
160                 snprintf(p->loop_name, sizeof(p->loop_name), "/dev/loop%d",
161                          i);
162 
163                 if (fd >= 0)
164                         close(fd);
165 
166                 fd = open(p->loop_name, O_RDWR);
167                 if (fd < 0)
168                         continue;
169 
170                 if (ioctl(fd, LOOP_GET_STATUS64, &info) == 0) {
171                         if (strcmp((char *) info.lo_file_name, rootdev) != 0)
172                                 continue;
173                         if (info.lo_offset != p->offset)
174                                 continue;
175                         ret = 0;
176                         break;
177                 }
178 
179                 if (errno != ENXIO)
180                         continue;
181 
182                 if (ioctl(fd, LOOP_SET_FD, ffd) != 0)
183                         continue;
184 
185                 memset(&info, 0, sizeof(info));
186                 snprintf((char *) info.lo_file_name, sizeof(info.lo_file_name), "%s",
187                          rootdev);
188                 info.lo_offset = p->offset;
189                 info.lo_flags |= LO_FLAGS_AUTOCLEAR;
190 
191                 if (ioctl(fd, LOOP_SET_STATUS64, &info) != 0) {
192                         ioctl(fd, LOOP_CLR_FD, 0);
193                         continue;
194                 }
195 
196                 /*
197                  * Don't close fd. Leave it open until this process exits, to avoid
198                  * the autoclear from happening too soon.
199                  */
200                 fd = -1;
201 
202                 ret = 0;
203                 break;
204         }
205 
206         if (fd >= 0)
207                 close(fd);
208 
209         close(ffd);
210 
211         if (ret)
212                 p->loop_name[0] = 0;
213 
214         return ret;
215 }
216 
217 static int rootdisk_volume_init(struct volume *v)
218 {
219         struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
220 
221         if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) {
222                 ULOG_ERR("unable to create loop device\n");
223                 return -1;
224         }
225 
226         v->type = BLOCKDEV;
227         v->blk = p->loop_name;
228 
229         return block_volume_format(v, p->offset, rootdev);
230 }
231 
232 static struct driver rootdisk_driver = {
233         .name = "rootdisk",
234         .find = rootdisk_volume_find,
235         .init = rootdisk_volume_init,
236         .identify = rootdisk_volume_identify,
237 };
238 
239 DRIVER(rootdisk_driver);
240 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt