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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt