patches and low-level development discussion
 help / color / mirror / code / Atom feed
blob 8f714b384278f52c06f978a143089af5adb3bf41 5486 bytes (raw)
name: tools/vm-set-persist.c 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
 
// SPDX-FileCopyrightText: 2025-2026 Alyssa Ross <hi@alyssa.is>
// SPDX-License-Identifier: EUPL-1.2+

#include <err.h>
#include <fcntl.h>
#include <libgen.h>
#include <unistd.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

// No <sys/stat.h> until musl declares stx_mnt_id.
#include <sys/syscall.h>

#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/openat2.h>
#include <linux/stat.h>
#include <linux/unistd.h>

// Including trailing NUL bytes.
static const int MNT_ROOT_MAX_LEN = 43;
static const int SOURCE_MAX_LEN = 28;

static void set_mount_namespace(const char vm_id[static 1])
{
	char ns_path[28];
	int r = snprintf(ns_path, sizeof ns_path,
	                 "/run/vm/by-id/%s/ns/mnt", vm_id);

	if (r == -1)
		err(EXIT_FAILURE, "snprintf");
	if ((size_t)r >= sizeof ns_path)
		errx(EXIT_FAILURE, "VM ID unexpectedly long");

	if ((r = open(ns_path, O_RDONLY | O_CLOEXEC)) == -1)
		err(EXIT_FAILURE, "open");
	if (setns(r, CLONE_NEWNS) == -1)
		err(EXIT_FAILURE, "setns");
	close(r);
}

static void do_statx(const char path[static 1],
                     mode_t mode[static 1], uint64_t mnt_id[static 1])
{
	struct statx stx;

	if (syscall(__NR_statx, AT_FDCWD, path, AT_SYMLINK_NOFOLLOW,
	            STATX_MODE | STATX_MNT_ID_UNIQUE, &stx) == -1)
		err(EXIT_FAILURE, "statx");

	if (!(stx.stx_attributes & STATX_ATTR_MOUNT_ROOT)) {
		if (stx.stx_attributes_mask & STATX_ATTR_MOUNT_ROOT)
			errx(EXIT_FAILURE,
			     "VM disk-backed directory not mounted");

		errx(EXIT_FAILURE, "statx didn't return STATX_ATTR_MOUNT_ROOT");
	}

	if (!(stx.stx_mask & STATX_MNT_ID_UNIQUE))
		errx(EXIT_FAILURE, "statx didn't return STATX_MNT_ID_UNIQUE");
	if (!(stx.stx_mask & STATX_MODE))
		errx(EXIT_FAILURE, "statx didn't return STATX_MODE");

	*mode = stx.stx_mode;
	*mnt_id = stx.stx_mnt_id;
}

static int do_mount(const char source[static 1])
{
	int mnt, fs = syscall(__NR_fsopen, "btrfs", FSOPEN_CLOEXEC);
	if (fs == -1)
		err(EXIT_FAILURE, "fsopen");
	if (syscall(__NR_fsconfig, fs, FSCONFIG_SET_STRING,
	            "source", source, 0) == -1)
		err(EXIT_FAILURE, "FSCONFIG_SET_STRING source");
	if (syscall(__NR_fsconfig, fs, FSCONFIG_SET_FLAG,
	            "rw", nullptr, 0) == -1)
		err(EXIT_FAILURE, "FSCONFIG_SET_FLAG rw");
	if (syscall(__NR_fsconfig, fs, FSCONFIG_CMD_CREATE,
	            nullptr, nullptr, 0) == -1)
		err(EXIT_FAILURE, "FSCONFIG_CMD_CREATE");
	if ((mnt = syscall(__NR_fsmount, fs, FSMOUNT_CLOEXEC,
	                   MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOSYMFOLLOW |
	                   MOUNT_ATTR_NOEXEC | MOUNT_ATTR_NODEV)) == -1)
		err(EXIT_FAILURE, "fsmount");
	close(fs);
	return mnt;
}

static void do_statmount(uint64_t mnt_id,
                         char mnt_root[static MNT_ROOT_MAX_LEN],
                         char source[static SOURCE_MAX_LEN])
{
	int r;
	char sm_buf[sizeof(struct statmount) +
	            MNT_ROOT_MAX_LEN + SOURCE_MAX_LEN];
	struct statmount *sm = (struct statmount *)sm_buf;
	struct mnt_id_req req = {
		.size = sizeof req,
		.mnt_id = mnt_id,
		.param = STATMOUNT_MNT_ROOT | STATMOUNT_SB_SOURCE,
	};

	if (syscall(__NR_statmount, &req, sm, sizeof sm_buf, 0) == -1)
		err(EXIT_FAILURE, "statmount");

	r = snprintf(mnt_root, MNT_ROOT_MAX_LEN, "%s", sm->str + sm->mnt_root);
	if (r == -1)
		err(EXIT_FAILURE, "snprintf");
	if (r >= MNT_ROOT_MAX_LEN)
		errx(EXIT_FAILURE, "unexpectedly long mnt_root");

	r = snprintf(source, SOURCE_MAX_LEN, "%s", sm->str + sm->sb_source);
	if (r == -1)
		err(EXIT_FAILURE, "snprintf");
	if (r >= SOURCE_MAX_LEN)
		errx(EXIT_FAILURE, "unexpectedly long sb_source");
}

static void do_rename(int mnt, const char dir_name[static 1],
                      const char old_name[static 1],
                      const char new_name[static 1], mode_t mode)
{
	struct open_how how = {
		.flags = O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW,
		.resolve = RESOLVE_NO_MAGICLINKS | RESOLVE_IN_ROOT |
		           RESOLVE_NO_SYMLINKS | RESOLVE_NO_XDEV,
	};
	int dir = syscall(__NR_openat2, mnt, dir_name, &how, sizeof how);
	if (dir == -1)
		err(EXIT_FAILURE, "openat2");

	if (syscall(__NR_mkdirat, dir, new_name, mode) == -1)
		err(EXIT_FAILURE, "mkdirat");
	if (syscall(__NR_renameat2, dir, old_name, dir, new_name,
	            RENAME_EXCHANGE) == -1)
		err(EXIT_FAILURE, "renameat2");
}

int main(int argc, char *argv[])
{
	int mnt, r;
	mode_t mode;
	uint64_t mnt_id;
	char *disk_path, *dir_name, *old_name, *new_name,
	     mnt_root[MNT_ROOT_MAX_LEN], mnt_root_copy[MNT_ROOT_MAX_LEN],
	     source[SOURCE_MAX_LEN];

	if (argc != 3) {
		fprintf(stderr, "Usage: vm-set-persist ID INSTANCE\n");
		exit(EXIT_FAILURE);
	}

	if (strchr(argv[1], '/'))
		errx(EXIT_FAILURE, "invalid VM ID");
	if (strchr(argv[2], '/'))
		errx(EXIT_FAILURE, "invalid persistent directory name");

	if (asprintf(&disk_path, "/run/fs/%s/disk", argv[1]) == -1)
		err(EXIT_FAILURE, "asprintf");
	if (asprintf(&new_name, "persist.%s", argv[2]) == -1)
		err(EXIT_FAILURE, "asprintf");

	set_mount_namespace(argv[1]);

	do_statx(disk_path, &mode, &mnt_id);
	do_statmount(mnt_id, mnt_root, source);

	r = snprintf(mnt_root_copy, sizeof mnt_root_copy, "%s", mnt_root);
	if (r == -1)
		err(EXIT_FAILURE, "snprintf");
	if ((size_t)r >= sizeof mnt_root_copy)
		errx(EXIT_FAILURE, "mnt_root longer than mnt_root_copy");
	dir_name = dirname(mnt_root);
	old_name = basename(mnt_root_copy);

	mnt = do_mount(source);

	do_rename(mnt, dir_name, old_name, new_name, mode);
}

debug log:

solving 8f714b38 ...
found 8f714b38 in https://inbox.spectrum-os.org/spectrum-devel/20260510165541.32975-1-hi@alyssa.is/
found ac759504 in https://inbox.spectrum-os.org/spectrum-devel/20251214014229.775825-10-hi@alyssa.is/

applying [1/2] https://inbox.spectrum-os.org/spectrum-devel/20251214014229.775825-10-hi@alyssa.is/
diff --git a/tools/vm-set-persist.c b/tools/vm-set-persist.c
new file mode 100644
index 00000000..ac759504


applying [2/2] https://inbox.spectrum-os.org/spectrum-devel/20260510165541.32975-1-hi@alyssa.is/
diff --git a/tools/vm-set-persist.c b/tools/vm-set-persist.c
index ac759504..8f714b38 100644

Checking patch tools/vm-set-persist.c...
Applied patch tools/vm-set-persist.c cleanly.
Checking patch tools/vm-set-persist.c...
Applied patch tools/vm-set-persist.c cleanly.

index at:
100644 8f714b384278f52c06f978a143089af5adb3bf41	tools/vm-set-persist.c

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://spectrum-os.org/git/crosvm
	https://spectrum-os.org/git/doc
	https://spectrum-os.org/git/mktuntap
	https://spectrum-os.org/git/nixpkgs
	https://spectrum-os.org/git/spectrum
	https://spectrum-os.org/git/ucspi-vsock
	https://spectrum-os.org/git/www

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).