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