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