struct stat stbuf;
if (stat ("/proc/cmdline", &stbuf) < 0)
{
if (errno != ENOENT)
err (EXIT_FAILURE, "stat(\"/proc/cmdline\") failed");
/* We need /proc mounted for /proc/cmdline and realpath (on musl) to
work: */
if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
err (EXIT_FAILURE, "failed to mount proc on /proc");
we_mounted_proc = 1;
}
if (we_mounted_proc)
{
/ Leave the filesystem in the state that we found it: /
if (umount ("/proc"))
err (EXIT_FAILURE, "failed to umount proc from /proc");
}
/* Work-around for a kernel bug: for some reason the kernel
refuses switching root if any file systems are mounted
MS_SHARED. Hence remount them MS_PRIVATE here as a
/ Make deploy_path a bind mount, so we can move it later /
if (mount (deploy_path, deploy_path, NULL, MS_BIND, NULL) < 0)
err (EXIT_FAILURE, "failed to make initial bind mount %s", deploy_path);
/* chdir to our new root. We need to do this after bind-mounting it over
itself otherwise our cwd is still on the non-bind-mounted filesystem
below. */
if (chdir (deploy_path) < 0)
err (EXIT_FAILURE, "failed to chdir to deploy_path");
/* Default to true, but in the systemd case, default to false because it's handled by
/ file in /run can override the default behaviour so that we definitely mount /var /
if (lstat (INITRAMFS_MOUNT_VAR, &stbuf) == 0)
mount_var = true;
/ Link to the deployment's /var /
if (mount_var && mount ("../../var", "var", NULL, MS_MGC_VAL|MS_BIND, NULL) < 0)
err (EXIT_FAILURE, "failed to bind mount ../../var to var");
char srcpath[PATH_MAX];
/* If /boot is on the same partition, use a bind mount to make it visible
at /boot inside the deployment. */
snprintf (srcpath, sizeof(srcpath), "%s/boot/loader", root_mountpoint);
if (lstat (srcpath, &stbuf) == 0 && S_ISLNK (stbuf.st_mode))
{
if (lstat ("boot", &stbuf) == 0 && S_ISDIR (stbuf.st_mode))
{
snprintf (srcpath, sizeof(srcpath), "%s/boot", root_mountpoint);
if (mount (srcpath, "boot", NULL, MS_BIND, NULL) < 0)
err (EXIT_FAILURE, "failed to bind mount %s to boot", srcpath);
}
}
/ Do we have a persistent overlayfs for /usr? If so, mount it now. /
if (lstat (".usr-ovl-work", &stbuf) == 0)
{
const char usr_ovl_options[] = "lowerdir=usr,upperdir=.usr-ovl-upper,workdir=.usr-ovl-work";
/* Except overlayfs barfs if we try to mount it on a read-only
filesystem. For this use case I think admins are going to be
okay if we remount the rootfs here, rather than waiting until
later boot and systemd-remount-fs.service.
*/
if (path_is_on_readonly_fs ("."))
{
if (mount (".", ".", NULL, MS_REMOUNT | MS_SILENT, NULL) < 0)
err (EXIT_FAILURE, "failed to remount rootfs writable (for overlayfs)");
}
if (mount ("overlay", "usr", "overlay", 0, usr_ovl_options) < 0)
err (EXIT_FAILURE, "failed to mount /usr overlayfs");
}
else
{
/ Otherwise, a read-only bind mount for /usr /
if (mount ("usr", "usr", NULL, MS_BIND, NULL) < 0)
err (EXIT_FAILURE, "failed to bind mount (class:readonly) /usr");
if (mount ("usr", "usr", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0)
err (EXIT_FAILURE, "failed to bind mount (class:readonly) /usr");
}
/* We only stamp /run now if we're running in an initramfs, i.e. we're
not pid 1. Otherwise it's handled later via ostree-system-generator.
if (strcmp(root_mountpoint, "/") == 0)
{
/* pivot_root rotates two mount points around. In this instance . (the
deploy location) becomes / and the existing / becomes /sysroot. We
have to use pivot_root rather than mount --move in this instance
because our deploy location is mounted as a subdirectory of the real
sysroot, so moving sysroot would also move the deploy location. In
reality attempting mount --move would fail with EBUSY. /
if (pivot_root (".", "sysroot") < 0)
err (EXIT_FAILURE, "failed to pivot_root to deployment");
}
else
{
/ In this instance typically we have our ready made-up up root at
/sysroot/ostree/deploy/.../ (deploy_path) and the real rootfs at
/sysroot (root_mountpoint). We want to end up with our made-up root at
/sysroot/ and the real rootfs under /sysroot/sysroot as systemd will be
responsible for moving /sysroot to /.
We need to do this in 3 moves to avoid trying to move /sysroot under
if (mount (deploy_path, "/sysroot.tmp", NULL, MS_MOVE, NULL) < 0)
err (EXIT_FAILURE, "failed to MS_MOVE '%s' to '/sysroot.tmp'", deploy_path);
if (mount (root_mountpoint, "sysroot", NULL, MS_MOVE, NULL) < 0) {
system("ls -al /sysroot");
system("ls -al /sysroot.tmp");
sleep(3);
err (EXIT_FAILURE, "failed to MS_MOVE '%s' to 'sysroot', errno:%d", root_mountpoint, errno);
}
printf("last step passes\n");
if (mount (".", root_mountpoint, NULL, MS_MOVE, NULL) < 0)
err (EXIT_FAILURE, "failed to MS_MOVE %s to %s", deploy_path, root_mountpoint);
}
/* The /sysroot mount needs to be private to avoid having a mount for e.g. /var/cache
also propagate to /sysroot/ostree/deploy/$stateroot/var/cache
Now in reality, today this is overridden by systemd: the actual way we fix this up
is in ostree-remount.c. But let's do it here to express the semantics we want
at the very start (perhaps down the line systemd will have compile/runtime option
to say that the initramfs environment did everything right from the start).
*/
if (mount ("none", "sysroot", NULL, MS_PRIVATE, NULL) < 0)
err (EXIT_FAILURE, "remounting 'sysroot' private");
常见版本的rootfs无法使用OSTree进行管理,为了使用OSTree管理Layerscape的rootfs升级,需要针对性的对rootfs进行改造,同时需要对uboot的环境变量和initramfs进行更改,接下来,我们一步步介绍如何进行修改。
SD卡分区
升级管理基于SD卡进行,因此需要对SD卡进行分区,分区信息如下:
第一个分区起始地址为20480,原因是前面的空间用于刷写uboot。第一个分区刷写制作的FIT格式的Image镜像,第二个分区存放OSTree格式的rootfs。 刷写uboot:
sudo dd if=./firmware_ls1046ardb_uboot_sdboot_1133_5a59.img of=/dev/sdc bs=512 seek=8 conv=fsync
制作OSTree格式的rootfs
服务器端
服务器端使用ostree创建仓库管理要更新的rootfs,这里介绍ostree服务器的搭建示例,分为几个步骤:
创建repo文件夹用于存放ostree配置
mkdir repo ostree --repo=repo init --mode=archive-z2 # 创建模式为archive-z2的OSTree格式仓库 mkdir rfs # rfs文件夹用于存放要更新的rootfs
这样,服务端的准备工作就完成了。
板端配置
我们的板子测试使用SD卡完成,因此这些操作现在SD卡上完成后,再通过SD卡启动测试。前面已经对SD卡分好了区,现在需要对SD卡的rootfs分区进行操作:
*将刚才设置的服务器 ostree repo设置为remote repo
至此,SD卡的rootfs已经制作完成了。在这个过程中,任何一步都可以通过
sudo ostree admin --sysroot=rootfs status
查看状态的变化。但是,此时仍然不能直接用于启动,因为原始的initramfs是无法解析这个OSTree格式的rootfs的,我们需要对initramfs的脚本进行替换。initramfs和uboot修改
initramfs修改
initramfs需要替换为meta-updater官方仓库的init脚本:
include "config.h"
include <sys/mount.h>
include <sys/types.h>
include <sys/stat.h>
include <sys/param.h>
include <sys/syscall.h>
include
include
include
include
include
include
include
include
include
include
include "ostree-mount-util.h"
/ Initialized early in main / static bool running_as_pid1;
static char resolve_deploy_path (const char root_mountpoint) { char destpath[PATH_MAX]; struct stat stbuf; char ostree_target, deploy_path;
ostree_target = read_proc_cmdline_ostree (); if (!ostree_target) errx (EXIT_FAILURE, "No OSTree target; expected ostree=/ostree/boot.N/...");
snprintf (destpath, sizeof(destpath), "%s/%s", root_mountpoint, ostree_target); if (lstat (destpath, &stbuf) < 0) err (EXIT_FAILURE, "Couldn't find specified OSTree root '%s'", destpath); if (!S_ISLNK (stbuf.st_mode)) errx (EXIT_FAILURE, "OSTree target is not a symbolic link: %s", destpath); deploy_path = realpath (destpath, NULL); if (deploy_path == NULL) err (EXIT_FAILURE, "realpath(%s) failed", destpath); / Quiet logs if there's no journal / if (!running_as_pid1) printf ("Resolved OSTree target to: %s\n", deploy_path); return deploy_path; }
static int pivot_root(const char new_root, const char put_old) { return syscall(__NR_pivot_root, new_root, put_old); }
int main(int argc, char argv[]) { / If we're pid 1, that means there's no initramfs; in this situation
etc. */ running_as_pid1 = (getpid () == 1);
const char *root_arg = NULL; bool we_mounted_proc = false; if (running_as_pid1) { root_arg = "/"; } else { if (argc < 2) err (EXIT_FAILURE, "usage: ostree-prepare-root SYSROOT"); root_arg = argv[1]; }
struct stat stbuf; if (stat ("/proc/cmdline", &stbuf) < 0) { if (errno != ENOENT) err (EXIT_FAILURE, "stat(\"/proc/cmdline\") failed"); /* We need /proc mounted for /proc/cmdline and realpath (on musl) to
const char root_mountpoint = realpath (root_arg, NULL); if (root_mountpoint == NULL) err (EXIT_FAILURE, "realpath(\"%s\")", root_arg); char deploy_path = resolve_deploy_path (root_mountpoint);
if (we_mounted_proc) { / Leave the filesystem in the state that we found it: / if (umount ("/proc")) err (EXIT_FAILURE, "failed to umount proc from /proc"); }
/* Work-around for a kernel bug: for some reason the kernel
https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ if (mount (NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) err (EXIT_FAILURE, "failed to make \"/\" private mount");
/ Make deploy_path a bind mount, so we can move it later / if (mount (deploy_path, deploy_path, NULL, MS_BIND, NULL) < 0) err (EXIT_FAILURE, "failed to make initial bind mount %s", deploy_path);
/* chdir to our new root. We need to do this after bind-mounting it over
below. */ if (chdir (deploy_path) < 0) err (EXIT_FAILURE, "failed to chdir to deploy_path");
/* Default to true, but in the systemd case, default to false because it's handled by
ostree-system-generator. */ bool mount_var = true;
ifdef HAVE_SYSTEMD_AND_LIBMOUNT
mount_var = false;
endif
/ file in /run can override the default behaviour so that we definitely mount /var / if (lstat (INITRAMFS_MOUNT_VAR, &stbuf) == 0) mount_var = true;
/ Link to the deployment's /var / if (mount_var && mount ("../../var", "var", NULL, MS_MGC_VAL|MS_BIND, NULL) < 0) err (EXIT_FAILURE, "failed to bind mount ../../var to var");
char srcpath[PATH_MAX]; /* If /boot is on the same partition, use a bind mount to make it visible
at /boot inside the deployment. */ snprintf (srcpath, sizeof(srcpath), "%s/boot/loader", root_mountpoint); if (lstat (srcpath, &stbuf) == 0 && S_ISLNK (stbuf.st_mode)) { if (lstat ("boot", &stbuf) == 0 && S_ISDIR (stbuf.st_mode)) { snprintf (srcpath, sizeof(srcpath), "%s/boot", root_mountpoint); if (mount (srcpath, "boot", NULL, MS_BIND, NULL) < 0) err (EXIT_FAILURE, "failed to bind mount %s to boot", srcpath); } }
/ Do we have a persistent overlayfs for /usr? If so, mount it now. / if (lstat (".usr-ovl-work", &stbuf) == 0) { const char usr_ovl_options[] = "lowerdir=usr,upperdir=.usr-ovl-upper,workdir=.usr-ovl-work";
/* Except overlayfs barfs if we try to mount it on a read-only
later boot and
systemd-remount-fs.service
. */ if (path_is_on_readonly_fs (".")) { if (mount (".", ".", NULL, MS_REMOUNT | MS_SILENT, NULL) < 0) err (EXIT_FAILURE, "failed to remount rootfs writable (for overlayfs)"); }if (mount ("overlay", "usr", "overlay", 0, usr_ovl_options) < 0) err (EXIT_FAILURE, "failed to mount /usr overlayfs"); } else { / Otherwise, a read-only bind mount for /usr / if (mount ("usr", "usr", NULL, MS_BIND, NULL) < 0) err (EXIT_FAILURE, "failed to bind mount (class:readonly) /usr"); if (mount ("usr", "usr", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0) err (EXIT_FAILURE, "failed to bind mount (class:readonly) /usr"); }
/* We only stamp /run now if we're running in an initramfs, i.e. we're
https://github.com/ostreedev/ostree/pull/1675 */ if (!running_as_pid1) touch_run_ostree ();
if (strcmp(root_mountpoint, "/") == 0) { /* pivot_root rotates two mount points around. In this instance . (the
if (mount (deploy_path, "/sysroot.tmp", NULL, MS_MOVE, NULL) < 0) err (EXIT_FAILURE, "failed to MS_MOVE '%s' to '/sysroot.tmp'", deploy_path);
if (mount (root_mountpoint, "sysroot", NULL, MS_MOVE, NULL) < 0) { system("ls -al /sysroot"); system("ls -al /sysroot.tmp"); sleep(3); err (EXIT_FAILURE, "failed to MS_MOVE '%s' to 'sysroot', errno:%d", root_mountpoint, errno); }
printf("last step passes\n"); if (mount (".", root_mountpoint, NULL, MS_MOVE, NULL) < 0) err (EXIT_FAILURE, "failed to MS_MOVE %s to %s", deploy_path, root_mountpoint); }
/* The /sysroot mount needs to be private to avoid having a mount for e.g. /var/cache
to say that the initramfs environment did everything right from the start). */ if (mount ("none", "sysroot", NULL, MS_PRIVATE, NULL) < 0) err (EXIT_FAILURE, "remounting 'sysroot' private");
if (running_as_pid1) { execl ("/sbin/init", "/sbin/init", NULL); err (EXIT_FAILURE, "failed to exec init inside ostree"); } else { exit (EXIT_SUCCESS); } }
setenv bootargs "earlycon=uart8250,mmio,0x21c0500 console=ttyS0,115200 root=/dev/ram0 rw rootfstype=ext4 rootwait rootdelay=2 ostree=ostree/boot.0/tcu/cb774860fd0fe8232bae38d000e4dad985e35a39a95cf98b160bf0d9b2025590/0 ostree_root=/dev/mmcblk0p2"