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