1 /* 2 * Copyright (C) 2014 John Crispin <blogic@openwrt.org> 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 <sys/stat.h> 15 #include <sys/types.h> 16 #include <sys/mount.h> 17 18 #include <asm/byteorder.h> 19 20 #include <errno.h> 21 #include <string.h> 22 #include <stdio.h> 23 #include <unistd.h> 24 #include <stdlib.h> 25 #include <glob.h> 26 #include <errno.h> 27 #include <dirent.h> 28 #include <fcntl.h> 29 30 #include "libfstools.h" 31 #include "volume.h" 32 33 #ifndef GLOB_ONLYDIR 34 #define GLOB_ONLYDIR 0x100 35 #endif 36 37 #define SWITCH_JFFS2 "/tmp/.switch_jffs2" 38 #define OVERLAYDIR "/rom/overlay" 39 40 static bool keep_sysupgrade; 41 42 static int 43 handle_rmdir(const char *dir) 44 { 45 struct dirent *dt; 46 struct stat st; 47 DIR *d; 48 int fd; 49 50 d = opendir(dir); 51 if (!d) 52 return -1; 53 54 fd = dirfd(d); 55 56 while ((dt = readdir(d)) != NULL) { 57 if (fstatat(fd, dt->d_name, &st, AT_SYMLINK_NOFOLLOW) || S_ISDIR(st.st_mode)) 58 continue; 59 60 if (keep_sysupgrade && !strcmp(dt->d_name, "sysupgrade.tgz")) 61 continue; 62 63 unlinkat(fd, dt->d_name, 0); 64 } 65 66 closedir(d); 67 rmdir(dir); 68 69 return 0; 70 } 71 72 void 73 foreachdir(const char *dir, int (*cb)(const char*)) 74 { 75 static char *globdir = NULL; 76 static size_t globdirlen = 0; 77 struct stat s = { 0 }; 78 size_t dirlen = strlen(dir); 79 glob_t gl; 80 int j; 81 82 if (dirlen + sizeof("/*") > globdirlen) { 83 /* Alloc extra 256 B to avoid too many reallocs */ 84 size_t len = dirlen + sizeof("/*") + 256; 85 char *tmp; 86 87 tmp = realloc(globdir, len); 88 if (!tmp) 89 return; 90 globdir = tmp; 91 globdirlen = len; 92 } 93 94 sprintf(globdir, "%s/*", dir); 95 96 /* Include GLOB_MARK as callbacks expect a trailing slash */ 97 if (!glob(globdir, GLOB_NOESCAPE | GLOB_MARK | GLOB_ONLYDIR, NULL, &gl)) 98 for (j = 0; j < gl.gl_pathc; j++) { 99 char *dir = gl.gl_pathv[j]; 100 int len = strlen(gl.gl_pathv[j]); 101 int err; 102 103 /* Quick way of skipping files */ 104 if (dir[len - 1] != '/') 105 continue; 106 107 /* lstat needs path without a trailing slash */ 108 if (len > 1) 109 dir[len - 1] = '\0'; 110 err = lstat(gl.gl_pathv[j], &s); 111 if (len > 1) 112 dir[len - 1] = '/'; 113 114 if (!err && !S_ISLNK(s.st_mode)) 115 foreachdir(gl.gl_pathv[j], cb); 116 } 117 cb(dir); 118 } 119 120 static void foreach_mount(int (*cb)(const char *, const char *)) 121 { 122 FILE *fp = fopen("/proc/mounts", "r"); 123 static char line[256]; 124 125 if (!fp) 126 return; 127 128 while (fgets(line, sizeof(line), fp)) { 129 char device[32], mount_point[32]; 130 131 if (sscanf(line, "%31s %31s %*s %*s %*u %*u", device, mount_point) == 2) 132 cb(device, mount_point); 133 } 134 135 fclose(fp); 136 } 137 138 void 139 overlay_delete(const char *dir, bool _keep_sysupgrade) 140 { 141 keep_sysupgrade = _keep_sysupgrade; 142 foreachdir(dir, handle_rmdir); 143 } 144 145 static int 146 overlay_mount(struct volume *v, char *fs) 147 { 148 if (mkdir("/tmp/overlay", 0755)) { 149 ULOG_ERR("failed to mkdir /tmp/overlay: %m\n"); 150 return -1; 151 } 152 153 if (mount(v->blk, "/tmp/overlay", fs, MS_NOATIME, NULL)) { 154 ULOG_ERR("failed to mount -t %s %s /tmp/overlay: %m\n", fs, v->blk); 155 return -1; 156 } 157 158 return 0; 159 } 160 161 /** 162 * move_mount - move mount point to the new root 163 */ 164 static int move_mount(const char *device, const char *mount_point) 165 { 166 static const char *prefix = "/tmp/root/"; 167 168 if (strncmp(mount_point, prefix, strlen(prefix))) 169 return 0; 170 171 return mount_move(prefix, "/", mount_point + strlen(prefix)); 172 } 173 174 static int 175 switch2jffs(struct volume *v) 176 { 177 struct stat s; 178 int ret, fd; 179 180 if (!stat(SWITCH_JFFS2, &s)) { 181 ULOG_ERR("jffs2 switch already running\n"); 182 return -1; 183 } 184 185 fd = creat(SWITCH_JFFS2, 0600); 186 if (fd == -1) { 187 ULOG_ERR("failed - cannot create jffs2 switch mark: %m\n"); 188 return -1; 189 } 190 close(fd); 191 192 ret = mount(v->blk, OVERLAYDIR, "jffs2", MS_NOATIME, NULL); 193 unlink(SWITCH_JFFS2); 194 if (ret) { 195 ULOG_ERR("failed - mount -t jffs2 %s %s: %m\n", v->blk, OVERLAYDIR); 196 return -1; 197 } 198 199 if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) { 200 ULOG_ERR("failed - mount -o remount,ro none: %m\n"); 201 return -1; 202 } 203 204 if (system("cp -a /tmp/root/* /rom/overlay")) { 205 ULOG_ERR("failed - cp -a /tmp/root/* /rom/overlay: %m\n"); 206 return -1; 207 } 208 209 if (pivot("/rom", "/mnt")) { 210 ULOG_ERR("failed - pivot /rom /mnt: %m\n"); 211 return -1; 212 } 213 214 if (mount_move("/mnt", "/tmp/root", "")) { 215 ULOG_ERR("failed - mount -o move /mnt /tmp/root %m\n"); 216 return -1; 217 } 218 219 ret = fopivot("/overlay", "/rom"); 220 221 /* 222 * Besides copying overlay data from "tmpfs" to "jffs2" we should also 223 * move mount points that user could create during JFFS2 formatting. 224 * This has to happen after fopivot call because: 225 * 1) It's trivial to find mount points to move then (/tmp/root/...). 226 * 2) We can't do that earlier using /rom/overlay/upper/ as overlay(fs) 227 * doesn't support mounts. Mounting to upper dir don't make overlay 228 * /propagate/ files to the target dir. 229 */ 230 foreach_mount(move_mount); 231 232 return ret; 233 } 234 235 int 236 handle_whiteout(const char *dir) 237 { 238 struct stat s; 239 char link[256]; 240 ssize_t sz; 241 struct dirent **namelist; 242 int n; 243 244 n = scandir(dir, &namelist, NULL, NULL); 245 246 if (n < 1) 247 return -1; 248 249 while (n--) { 250 char file[256]; 251 252 snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name); 253 if (!lstat(file, &s) && S_ISLNK(s.st_mode)) { 254 sz = readlink(file, link, sizeof(link) - 1); 255 if (sz > 0) { 256 char *orig; 257 258 link[sz] = '\0'; 259 orig = strstr(&file[1], "/"); 260 if (orig && !strcmp(link, "(overlay-whiteout)")) 261 unlink(orig); 262 } 263 } 264 free(namelist[n]); 265 } 266 free(namelist); 267 268 return 0; 269 } 270 271 static char *overlay_fs_name(int type) 272 { 273 switch (type) { 274 case FS_EXT4: 275 return "ext4"; 276 case FS_F2FS: 277 return "f2fs"; 278 case FS_UBIFS: 279 return "ubifs"; 280 case FS_JFFS2: 281 default: 282 return "jffs2"; 283 } 284 } 285 286 int 287 jffs2_switch(struct volume *v) 288 { 289 char *mp, *fs_name; 290 int type; 291 292 if (find_overlay_mount("overlayfs:/tmp/root")) 293 return -1; 294 295 if (find_filesystem("overlay")) { 296 ULOG_ERR("overlayfs not supported by kernel\n"); 297 return -1; 298 } 299 300 volume_init(v); 301 mp = find_mount_point(v->blk, 0); 302 if (mp) { 303 ULOG_ERR("rootfs_data:%s is already mounted as %s\n", v->blk, mp); 304 return -1; 305 } 306 307 type = volume_identify(v); 308 fs_name = overlay_fs_name(type); 309 310 switch (type) { 311 case FS_NONE: 312 ULOG_ERR("no jffs2 marker found\n"); 313 /* fall through */ 314 315 case FS_DEADCODE: 316 if (switch2jffs(v)) 317 return -1; 318 319 ULOG_INFO("performing overlay whiteout\n"); 320 umount2("/tmp/root", MNT_DETACH); 321 foreachdir("/overlay/", handle_whiteout); 322 323 /* try hard to be in sync */ 324 ULOG_INFO("synchronizing overlay\n"); 325 if (system("cp -a /tmp/root/upper/* / 2>/dev/null")) 326 ULOG_ERR("failed to sync jffs2 overlay\n"); 327 break; 328 329 case FS_EXT4: 330 case FS_F2FS: 331 case FS_UBIFS: 332 if (overlay_mount(v, fs_name)) 333 return -1; 334 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { 335 ULOG_ERR("switching to %s failed\n", fs_name); 336 return -1; 337 } 338 break; 339 } 340 341 sync(); 342 fs_state_set("/overlay", FS_STATE_READY); 343 return 0; 344 } 345 346 static int overlay_mount_fs(struct volume *v, const char *overlay_mp) 347 { 348 char *fstype = overlay_fs_name(volume_identify(v)); 349 350 if (mkdir(overlay_mp, 0755)) { 351 ULOG_ERR("failed to mkdir /tmp/overlay: %m\n"); 352 return -1; 353 } 354 355 if (mount(v->blk, overlay_mp, fstype, 356 #ifdef OVL_MOUNT_FULL_ACCESS_TIME 357 MS_RELATIME, 358 #else 359 MS_NOATIME, 360 #endif 361 #ifdef OVL_MOUNT_COMPRESS_ZLIB 362 "compr=zlib" 363 #else 364 NULL 365 #endif 366 )) { 367 ULOG_ERR("failed to mount -t %s %s /tmp/overlay: %m\n", 368 fstype, v->blk); 369 return -1; 370 } 371 372 return 0; 373 } 374 375 enum fs_state fs_state_get(const char *dir) 376 { 377 char *path; 378 char valstr[16]; 379 uint32_t val; 380 ssize_t len; 381 382 path = alloca(strlen(dir) + 1 + sizeof("/.fs_state")); 383 sprintf(path, "%s/.fs_state", dir); 384 len = readlink(path, valstr, sizeof(valstr) - 1); 385 if (len < 0) 386 return FS_STATE_UNKNOWN; 387 388 valstr[len] = 0; 389 val = atoi(valstr); 390 391 if (val > __FS_STATE_LAST) 392 return FS_STATE_UNKNOWN; 393 394 return val; 395 } 396 397 398 int fs_state_set(const char *dir, enum fs_state state) 399 { 400 char valstr[16]; 401 char *path; 402 403 if (fs_state_get(dir) == state) 404 return 0; 405 406 path = alloca(strlen(dir) + 1 + sizeof("/.fs_state")); 407 sprintf(path, "%s/.fs_state", dir); 408 unlink(path); 409 snprintf(valstr, sizeof(valstr), "%d", state); 410 411 return symlink(valstr, path); 412 } 413 414 415 int mount_overlay(struct volume *v) 416 { 417 const char *overlay_mp = "/tmp/overlay"; 418 char *mp, *fs_name; 419 int err; 420 421 if (!v) 422 return -1; 423 424 mp = find_mount_point(v->blk, 0); 425 if (mp) { 426 ULOG_ERR("rootfs_data:%s is already mounted as %s\n", v->blk, mp); 427 return -1; 428 } 429 430 err = overlay_mount_fs(v, overlay_mp); 431 if (err) 432 return err; 433 434 /* 435 * Check for extroot config in overlay (rootfs_data) and if present then 436 * prefer it over rootfs_data. 437 */ 438 if (!mount_extroot(overlay_mp)) { 439 ULOG_INFO("switched to extroot\n"); 440 return 0; 441 } 442 443 switch (fs_state_get(overlay_mp)) { 444 case FS_STATE_UNKNOWN: 445 fs_state_set(overlay_mp, FS_STATE_PENDING); 446 if (fs_state_get(overlay_mp) != FS_STATE_PENDING) { 447 ULOG_ERR("unable to set filesystem state\n"); 448 break; 449 } 450 case FS_STATE_PENDING: 451 ULOG_INFO("overlay filesystem has not been fully initialized yet\n"); 452 overlay_delete(overlay_mp, true); 453 break; 454 case FS_STATE_READY: 455 break; 456 } 457 458 fs_name = overlay_fs_name(volume_identify(v)); 459 ULOG_INFO("switching to %s overlay\n", fs_name); 460 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { 461 ULOG_ERR("switching to %s failed - fallback to ramoverlay\n", fs_name); 462 return ramoverlay(); 463 } 464 465 return -1; 466 } 467
This page was automatically generated by LXR 0.3.1. • OpenWrt