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 (get_squashfs(&sb)) 112 return NULL; 113 114 if (memcmp(&sb.s_magic, "hsqs", sizeof(sb.s_magic)) != 0) 115 return NULL; 116 117 p = calloc(1, sizeof(*p)); 118 p->v.drv = &rootdisk_driver; 119 p->v.name = "rootfs_data"; 120 121 p->offset = le64_to_cpu(sb.bytes_used); 122 p->offset = ((p->offset + (ROOTDEV_OVERLAY_ALIGN - 1)) & 123 ~(ROOTDEV_OVERLAY_ALIGN - 1)); 124 125 return &p->v; 126 } 127 128 static int rootdisk_volume_identify(struct volume *v) 129 { 130 struct rootdev_volume *p = container_of(v, struct rootdev_volume, v); 131 FILE *f; 132 int ret = FS_NONE; 133 f = fopen(rootdev, "r"); 134 if (!f) 135 return ret; 136 137 ret = block_file_identify(f, p->offset); 138 139 fclose(f); 140 141 return ret; 142 } 143 144 static int rootdisk_create_loop(struct rootdev_volume *p) 145 { 146 struct loop_info64 info; 147 int ret = -1; 148 int fd = -1; 149 int i, ffd; 150 151 ffd = open(rootdev, O_RDWR); 152 if (ffd < 0) 153 return -1; 154 155 for (i = 0; i < 8; i++) { 156 snprintf(p->loop_name, sizeof(p->loop_name), "/dev/loop%d", 157 i); 158 159 if (fd >= 0) 160 close(fd); 161 162 fd = open(p->loop_name, O_RDWR); 163 if (fd < 0) 164 continue; 165 166 if (ioctl(fd, LOOP_GET_STATUS64, &info) == 0) { 167 if (strcmp((char *) info.lo_file_name, rootdev) != 0) 168 continue; 169 if (info.lo_offset != p->offset) 170 continue; 171 ret = 0; 172 break; 173 } 174 175 if (errno != ENXIO) 176 continue; 177 178 if (ioctl(fd, LOOP_SET_FD, ffd) != 0) 179 continue; 180 181 memset(&info, 0, sizeof(info)); 182 snprintf((char *) info.lo_file_name, sizeof(info.lo_file_name), "%s", 183 rootdev); 184 info.lo_offset = p->offset; 185 info.lo_flags |= LO_FLAGS_AUTOCLEAR; 186 187 if (ioctl(fd, LOOP_SET_STATUS64, &info) != 0) { 188 ioctl(fd, LOOP_CLR_FD, 0); 189 continue; 190 } 191 192 /* 193 * Don't close fd. Leave it open until this process exits, to avoid 194 * the autoclear from happening too soon. 195 */ 196 fd = -1; 197 198 ret = 0; 199 break; 200 } 201 202 if (fd >= 0) 203 close(fd); 204 205 close(ffd); 206 207 if (ret) 208 p->loop_name[0] = 0; 209 210 return ret; 211 } 212 213 static int rootdisk_volume_init(struct volume *v) 214 { 215 struct rootdev_volume *p = container_of(v, struct rootdev_volume, v); 216 217 if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) { 218 ULOG_ERR("unable to create loop device\n"); 219 return -1; 220 } 221 222 v->type = BLOCKDEV; 223 v->blk = p->loop_name; 224 225 return block_volume_format(v, p->offset, rootdev); 226 } 227 228 static struct driver rootdisk_driver = { 229 .name = "rootdisk", 230 .find = rootdisk_volume_find, 231 .init = rootdisk_volume_init, 232 .identify = rootdisk_volume_identify, 233 }; 234 235 DRIVER(rootdisk_driver); 236
This page was automatically generated by LXR 0.3.1. • OpenWrt