patches and low-level development discussion
 help / color / mirror / code / Atom feed
blob ac759504ab690049ae7577120fd0e0607a22d366 5287 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
 
// SPDX-FileCopyrightText: 2025 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;
	mode_t mode;
	uint64_t mnt_id;
	char *disk_path, *dir_name, *old_name, *new_name,
	     mnt_root[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);

	if (!(dir_name = strdup(mnt_root)))
		err(EXIT_FAILURE, "strdup");
	dir_name = dirname(dir_name);
	old_name = basename(mnt_root);

	mnt = do_mount(source);

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

debug log:

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

applying [1/1] 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

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

index at:
100644 ac759504ab690049ae7577120fd0e0607a22d366	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).