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 * ovl_move_mount - move mount point to the new root 163 */ 164 static int ovl_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 selinux_restorecon(OVERLAYDIR); 199 200 if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) { 201 ULOG_ERR("failed - mount -o remount,ro none: %m\n"); 202 return -1; 203 } 204 205 if (system("cp -a /tmp/root/* /rom/overlay")) { 206 ULOG_ERR("failed - cp -a /tmp/root/* /rom/overlay: %m\n"); 207 return -1; 208 } 209 210 if (pivot("/rom", "/mnt")) { 211 ULOG_ERR("failed - pivot /rom /mnt: %m\n"); 212 return -1; 213 } 214 215 if (mount_move("/mnt", "/tmp/root", "")) { 216 ULOG_ERR("failed - mount -o move /mnt /tmp/root %m\n"); 217 return -1; 218 } 219 220 ret = fopivot("/overlay", "/rom"); 221 222 /* 223 * Besides copying overlay data from "tmpfs" to "jffs2" we should also 224 * move mount points that user could create during JFFS2 formatting. 225 * This has to happen after fopivot call because: 226 * 1) It's trivial to find mount points to move then (/tmp/root/...). 227 * 2) We can't do that earlier using /rom/overlay/upper/ as overlay(fs) 228 * doesn't support mounts. Mounting to upper dir don't make overlay 229 * /propagate/ files to the target dir. 230 */ 231 foreach_mount(ovl_move_mount); 232 233 return ret; 234 } 235 236 int 237 handle_whiteout(const char *dir) 238 { 239 struct stat s; 240 char link[256]; 241 ssize_t sz; 242 struct dirent **namelist; 243 int n; 244 245 n = scandir(dir, &namelist, NULL, NULL); 246 247 if (n < 1) 248 return -1; 249 250 while (n--) { 251 char file[256]; 252 253 snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name); 254 if (!lstat(file, &s) && S_ISLNK(s.st_mode)) { 255 sz = readlink(file, link, sizeof(link) - 1); 256 if (sz > 0) { 257 char *orig; 258 259 link[sz] = '\0'; 260 orig = strstr(&file[1], "/"); 261 if (orig && !strcmp(link, "(overlay-whiteout)")) 262 unlink(orig); 263 } 264 } 265 free(namelist[n]); 266 } 267 free(namelist); 268 269 return 0; 270 } 271 272 static char *overlay_fs_name(int type) 273 { 274 switch (type) { 275 case FS_EXT4: 276 return "ext4"; 277 case FS_F2FS: 278 return "f2fs"; 279 case FS_UBIFS: 280 return "ubifs"; 281 case FS_JFFS2: 282 default: 283 return "jffs2"; 284 } 285 } 286 287 int 288 jffs2_switch(struct volume *v) 289 { 290 char *mp, *fs_name; 291 int type; 292 293 if (find_overlay_mount("overlayfs:/tmp/root")) 294 return -1; 295 296 if (find_filesystem("overlay")) { 297 ULOG_ERR("overlayfs not supported by kernel\n"); 298 return -1; 299 } 300 301 volume_init(v); 302 mp = find_mount_point(v->blk, 0); 303 if (mp) { 304 ULOG_ERR("rootfs_data:%s is already mounted as %s\n", v->blk, mp); 305 return -1; 306 } 307 308 type = volume_identify(v); 309 fs_name = overlay_fs_name(type); 310 311 switch (type) { 312 case FS_NONE: 313 ULOG_ERR("no jffs2 marker found\n"); 314 /* fall through */ 315 316 case FS_DEADCODE: 317 if (switch2jffs(v)) 318 return -1; 319 320 ULOG_INFO("performing overlay whiteout\n"); 321 umount2("/tmp/root", MNT_DETACH); 322 foreachdir("/overlay/", handle_whiteout); 323 324 /* try hard to be in sync */ 325 ULOG_INFO("synchronizing overlay\n"); 326 if (system("cp -a /tmp/root/upper/* / 2>/dev/null")) 327 ULOG_ERR("failed to sync jffs2 overlay\n"); 328 break; 329 330 case FS_EXT4: 331 case FS_F2FS: 332 case FS_UBIFS: 333 if (overlay_mount(v, fs_name)) 334 return -1; 335 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { 336 ULOG_ERR("switching to %s failed\n", fs_name); 337 return -1; 338 } 339 break; 340 } 341 342 sync(); 343 fs_state_set("/overlay", FS_STATE_READY); 344 return 0; 345 } 346 347 static int overlay_mount_fs(struct volume *v, const char *overlay_mp) 348 { 349 char *fstype = overlay_fs_name(volume_identify(v)); 350 351 if (mkdir(overlay_mp, 0755)) { 352 ULOG_ERR("failed to mkdir /tmp/overlay: %m\n"); 353 return -1; 354 } 355 356 if (mount(v->blk, overlay_mp, fstype, 357 #ifdef OVL_MOUNT_FULL_ACCESS_TIME 358 MS_RELATIME, 359 #else 360 MS_NOATIME, 361 #endif 362 #ifdef OVL_MOUNT_COMPRESS_ZLIB 363 "compr=zlib" 364 #else 365 NULL 366 #endif 367 )) { 368 ULOG_ERR("failed to mount -t %s %s /tmp/overlay: %m\n", 369 fstype, v->blk); 370 return -1; 371 } 372 373 return 0; 374 } 375 376 enum fs_state fs_state_get(const char *dir) 377 { 378 char *path; 379 char valstr[16]; 380 uint32_t val; 381 ssize_t len; 382 383 path = alloca(strlen(dir) + 1 + sizeof("/.fs_state")); 384 sprintf(path, "%s/.fs_state", dir); 385 len = readlink(path, valstr, sizeof(valstr) - 1); 386 if (len < 0) 387 return FS_STATE_UNKNOWN; 388 389 valstr[len] = 0; 390 val = atoi(valstr); 391 392 if (val > __FS_STATE_LAST) 393 return FS_STATE_UNKNOWN; 394 395 return val; 396 } 397 398 399 int fs_state_set(const char *dir, enum fs_state state) 400 { 401 char valstr[16]; 402 char *path; 403 404 if (fs_state_get(dir) == state) 405 return 0; 406 407 path = alloca(strlen(dir) + 1 + sizeof("/.fs_state")); 408 sprintf(path, "%s/.fs_state", dir); 409 unlink(path); 410 snprintf(valstr, sizeof(valstr), "%d", state); 411 412 return symlink(valstr, path); 413 } 414 415 416 int mount_overlay(struct volume *v) 417 { 418 const char *overlay_mp = "/tmp/overlay"; 419 char *mp, *fs_name; 420 int err; 421 422 if (!v) 423 return -1; 424 425 mp = find_mount_point(v->blk, 0); 426 if (mp) { 427 ULOG_ERR("rootfs_data:%s is already mounted as %s\n", v->blk, mp); 428 return -1; 429 } 430 431 err = overlay_mount_fs(v, overlay_mp); 432 if (err) 433 return err; 434 435 /* 436 * Check for extroot config in overlay (rootfs_data) and if present then 437 * prefer it over rootfs_data. 438 */ 439 if (!mount_extroot(overlay_mp)) { 440 ULOG_INFO("switched to extroot\n"); 441 return 0; 442 } 443 444 switch (fs_state_get(overlay_mp)) { 445 case FS_STATE_UNKNOWN: 446 fs_state_set(overlay_mp, FS_STATE_PENDING); 447 if (fs_state_get(overlay_mp) != FS_STATE_PENDING) { 448 ULOG_ERR("unable to set filesystem state\n"); 449 break; 450 } 451 case FS_STATE_PENDING: 452 ULOG_INFO("overlay filesystem has not been fully initialized yet\n"); 453 overlay_delete(overlay_mp, true); 454 break; 455 case FS_STATE_READY: 456 break; 457 } 458 459 fs_name = overlay_fs_name(volume_identify(v)); 460 ULOG_INFO("switching to %s overlay\n", fs_name); 461 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { 462 ULOG_ERR("switching to %s failed - fallback to ramoverlay\n", fs_name); 463 return ramoverlay(); 464 } 465 466 return -1; 467 } 468
This page was automatically generated by LXR 0.3.1. • OpenWrt