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