blob: 1b1f3b9ec81e5df94a91cdb51c658519946e16b3 [file] [log] [blame]
Anthony Liguori9f107512010-04-29 17:44:44 +05301/*
Wei Liuf00d4f52015-11-18 18:05:29 +00002 * 9p Posix callback
Anthony Liguori9f107512010-04-29 17:44:44 +05303 *
4 * Copyright IBM, Corp. 2010
5 *
6 * Authors:
7 * Anthony Liguori <aliguori@us.ibm.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
Anthony Liguori9f107512010-04-29 17:44:44 +053011 */
Stefan Weil873c3212011-06-01 12:35:14 +053012
Christian Schoenebeck6f569082021-05-06 15:12:23 +020013/*
14 * Not so fast! You might want to read the 9p developer docs first:
15 * https://wiki.qemu.org/Documentation/9p
16 */
17
Peter Maydellfbc04122016-01-26 18:17:10 +000018#include "qemu/osdep.h"
Wei Liuebe74f82016-01-07 18:18:02 +000019#include "9p.h"
Greg Kurz996a0d72017-02-26 23:42:18 +010020#include "9p-local.h"
Wei Liu267ae092015-11-18 18:31:52 +000021#include "9p-xattr.h"
Greg Kurz0e35a372017-02-26 23:42:10 +010022#include "9p-util.h"
Stefan Weil69b15212014-05-02 22:22:32 +020023#include "fsdev/qemu-fsdev.h" /* local_ops */
Anthony Liguoric494dd62010-04-29 17:44:59 +053024#include <arpa/inet.h>
Anthony Liguori131dcb22010-04-29 17:44:47 +053025#include <pwd.h>
26#include <grp.h>
Anthony Liguoric494dd62010-04-29 17:44:59 +053027#include <sys/socket.h>
28#include <sys/un.h>
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010029#include "qemu/xattr.h"
Markus Armbrustere688df62018-02-01 12:18:31 +010030#include "qapi/error.h"
Veronia Bahaaf348b6d2016-03-20 19:16:19 +020031#include "qemu/cutils.h"
Greg Kurz63325b12016-01-22 15:12:17 +010032#include "qemu/error-report.h"
Markus Armbruster922a01a2018-02-01 12:18:46 +010033#include "qemu/option.h"
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +053034#include <libgen.h>
Keno Fischere0bd7432022-02-27 17:35:12 -050035#ifdef CONFIG_LINUX
Harsh Prateek Borae06a7652011-10-12 19:11:25 +053036#include <linux/fs.h>
37#ifdef CONFIG_LINUX_MAGIC_H
38#include <linux/magic.h>
39#endif
Keno Fischere0bd7432022-02-27 17:35:12 -050040#endif
Harsh Prateek Borae06a7652011-10-12 19:11:25 +053041#include <sys/ioctl.h>
42
43#ifndef XFS_SUPER_MAGIC
44#define XFS_SUPER_MAGIC 0x58465342
45#endif
46#ifndef EXT2_SUPER_MAGIC
47#define EXT2_SUPER_MAGIC 0xEF53
48#endif
49#ifndef REISERFS_SUPER_MAGIC
50#define REISERFS_SUPER_MAGIC 0x52654973
51#endif
52#ifndef BTRFS_SUPER_MAGIC
53#define BTRFS_SUPER_MAGIC 0x9123683E
54#endif
Anthony Liguori131dcb22010-04-29 17:44:47 +053055
Greg Kurz0e35a372017-02-26 23:42:10 +010056typedef struct {
57 int mountfd;
58} LocalData;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +053059
Greg Kurz996a0d72017-02-26 23:42:18 +010060int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
61 mode_t mode)
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +053062{
Greg Kurz996a0d72017-02-26 23:42:18 +010063 LocalData *data = fs_ctx->private;
Greg Kurz3dbcf272017-05-25 10:30:14 +020064 int fd = data->mountfd;
Greg Kurz996a0d72017-02-26 23:42:18 +010065
Greg Kurz3dbcf272017-05-25 10:30:14 +020066 while (*path && fd != -1) {
67 const char *c;
68 int next_fd;
69 char *head;
70
71 /* Only relative paths without consecutive slashes */
72 assert(*path != '/');
73
74 head = g_strdup(path);
Keno Fischer5c99fa32018-06-29 12:32:10 +020075 c = qemu_strchrnul(path, '/');
Greg Kurz3dbcf272017-05-25 10:30:14 +020076 if (*c) {
77 /* Intermediate path element */
78 head[c - path] = 0;
79 path = c + 1;
80 next_fd = openat_dir(fd, head);
81 } else {
82 /* Rightmost path element */
83 next_fd = openat_file(fd, head, flags, mode);
84 path = c;
85 }
86 g_free(head);
87 if (fd != data->mountfd) {
88 close_preserve_errno(fd);
89 }
90 fd = next_fd;
Michael Tokarev1b6f85e2015-03-12 09:52:30 +030091 }
Greg Kurz996a0d72017-02-26 23:42:18 +010092
Greg Kurz3dbcf272017-05-25 10:30:14 +020093 assert(fd != data->mountfd);
94 return fd;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +053095}
96
Greg Kurz996a0d72017-02-26 23:42:18 +010097int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
98{
99 return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
100}
101
Greg Kurz99f2cf42017-02-26 23:43:55 +0100102static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd,
103 const char *npath)
104{
105 int serrno = errno;
Bin Meng6ca60cd2022-12-19 18:20:07 +0800106 qemu_renameat(odirfd, opath, ndirfd, npath);
Greg Kurz99f2cf42017-02-26 23:43:55 +0100107 errno = serrno;
108}
109
Greg Kurzad0b46e2017-02-26 23:44:20 +0100110static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
111{
112 int serrno = errno;
Bin Meng6ca60cd2022-12-19 18:20:07 +0800113 qemu_unlinkat(dirfd, path, flags);
Greg Kurzad0b46e2017-02-26 23:44:20 +0100114 errno = serrno;
115}
116
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530117#define VIRTFS_META_DIR ".virtfs_metadata"
Greg Kurz81ffbf52017-05-25 10:30:14 +0200118#define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root"
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530119
Greg Kurzf9aef992017-02-26 23:43:48 +0100120static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
Aneesh Kumar K.V0ceb0922013-05-20 19:43:15 +0530121{
122 int fd, o_mode = 0;
123 FILE *fp;
Greg Kurzf9aef992017-02-26 23:43:48 +0100124 int flags;
Aneesh Kumar K.V0ceb0922013-05-20 19:43:15 +0530125 /*
126 * only supports two modes
127 */
128 if (mode[0] == 'r') {
Greg Kurzf9aef992017-02-26 23:43:48 +0100129 flags = O_RDONLY;
Aneesh Kumar K.V0ceb0922013-05-20 19:43:15 +0530130 } else if (mode[0] == 'w') {
Greg Kurzf9aef992017-02-26 23:43:48 +0100131 flags = O_WRONLY | O_TRUNC | O_CREAT;
Aneesh Kumar K.V0ceb0922013-05-20 19:43:15 +0530132 o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
133 } else {
134 return NULL;
135 }
Greg Kurzf9aef992017-02-26 23:43:48 +0100136 fd = openat_file(dirfd, name, flags, o_mode);
Aneesh Kumar K.V0ceb0922013-05-20 19:43:15 +0530137 if (fd == -1) {
138 return NULL;
139 }
140 fp = fdopen(fd, mode);
141 if (!fp) {
142 close(fd);
143 }
144 return fp;
145}
146
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530147#define ATTR_MAX 100
Greg Kurzf9aef992017-02-26 23:43:48 +0100148static void local_mapped_file_attr(int dirfd, const char *name,
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530149 struct stat *stbuf)
150{
151 FILE *fp;
152 char buf[ATTR_MAX];
Greg Kurzf9aef992017-02-26 23:43:48 +0100153 int map_dirfd;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530154
Greg Kurz81ffbf52017-05-25 10:30:14 +0200155 if (strcmp(name, ".")) {
156 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
157 if (map_dirfd == -1) {
158 return;
159 }
Greg Kurzf9aef992017-02-26 23:43:48 +0100160
Greg Kurz81ffbf52017-05-25 10:30:14 +0200161 fp = local_fopenat(map_dirfd, name, "r");
162 close_preserve_errno(map_dirfd);
163 } else {
164 fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r");
165 }
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530166 if (!fp) {
167 return;
168 }
169 memset(buf, 0, ATTR_MAX);
170 while (fgets(buf, ATTR_MAX, fp)) {
171 if (!strncmp(buf, "virtfs.uid", 10)) {
Xinhao Zhang01011732020-10-30 12:35:13 +0800172 stbuf->st_uid = atoi(buf + 11);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530173 } else if (!strncmp(buf, "virtfs.gid", 10)) {
Xinhao Zhang01011732020-10-30 12:35:13 +0800174 stbuf->st_gid = atoi(buf + 11);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530175 } else if (!strncmp(buf, "virtfs.mode", 11)) {
Xinhao Zhang01011732020-10-30 12:35:13 +0800176 stbuf->st_mode = atoi(buf + 12);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530177 } else if (!strncmp(buf, "virtfs.rdev", 11)) {
Xinhao Zhang01011732020-10-30 12:35:13 +0800178 stbuf->st_rdev = atoi(buf + 12);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530179 }
180 memset(buf, 0, ATTR_MAX);
181 }
182 fclose(fp);
183}
184
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530185static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
Anthony Liguori131dcb22010-04-29 17:44:47 +0530186{
Greg Kurzf9aef992017-02-26 23:43:48 +0100187 int err = -1;
188 char *dirpath = g_path_get_dirname(fs_path->data);
189 char *name = g_path_get_basename(fs_path->data);
190 int dirfd;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530191
Greg Kurzf9aef992017-02-26 23:43:48 +0100192 dirfd = local_opendir_nofollow(fs_ctx, dirpath);
193 if (dirfd == -1) {
194 goto out;
195 }
196
Bin Meng6ca60cd2022-12-19 18:20:07 +0800197 err = qemu_fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700198 if (err) {
Chen Gang4fa4ce72014-03-02 01:36:19 +0800199 goto err_out;
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700200 }
Aneesh Kumar K.Vb97400c2011-10-13 13:21:00 +0530201 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700202 /* Actual credentials are part of extended attrs */
203 uid_t tmp_uid;
204 gid_t tmp_gid;
205 mode_t tmp_mode;
206 dev_t tmp_dev;
Greg Kurzf9aef992017-02-26 23:43:48 +0100207
208 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid,
209 sizeof(uid_t)) > 0) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530210 stbuf->st_uid = le32_to_cpu(tmp_uid);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700211 }
Greg Kurzf9aef992017-02-26 23:43:48 +0100212 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid,
213 sizeof(gid_t)) > 0) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530214 stbuf->st_gid = le32_to_cpu(tmp_gid);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700215 }
Greg Kurzf9aef992017-02-26 23:43:48 +0100216 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode,
217 sizeof(mode_t)) > 0) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530218 stbuf->st_mode = le32_to_cpu(tmp_mode);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700219 }
Greg Kurzf9aef992017-02-26 23:43:48 +0100220 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev,
221 sizeof(dev_t)) > 0) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530222 stbuf->st_rdev = le64_to_cpu(tmp_dev);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700223 }
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530224 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Greg Kurzf9aef992017-02-26 23:43:48 +0100225 local_mapped_file_attr(dirfd, name, stbuf);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700226 }
Chen Gang4fa4ce72014-03-02 01:36:19 +0800227
228err_out:
Greg Kurzf9aef992017-02-26 23:43:48 +0100229 close_preserve_errno(dirfd);
230out:
231 g_free(name);
232 g_free(dirpath);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700233 return err;
Anthony Liguori131dcb22010-04-29 17:44:47 +0530234}
235
Greg Kurze3187a42017-02-26 23:44:28 +0100236static int local_set_mapped_file_attrat(int dirfd, const char *name,
237 FsCred *credp)
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530238{
239 FILE *fp;
Greg Kurze3187a42017-02-26 23:44:28 +0100240 int ret;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530241 char buf[ATTR_MAX];
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530242 int uid = -1, gid = -1, mode = -1, rdev = -1;
Greg Kurz81ffbf52017-05-25 10:30:14 +0200243 int map_dirfd = -1, map_fd;
244 bool is_root = !strcmp(name, ".");
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530245
Greg Kurz81ffbf52017-05-25 10:30:14 +0200246 if (is_root) {
247 fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r");
248 if (!fp) {
249 if (errno == ENOENT) {
250 goto update_map_file;
251 } else {
252 return -1;
253 }
254 }
255 } else {
Bin Meng6ca60cd2022-12-19 18:20:07 +0800256 ret = qemu_mkdirat(dirfd, VIRTFS_META_DIR, 0700);
Greg Kurz81ffbf52017-05-25 10:30:14 +0200257 if (ret < 0 && errno != EEXIST) {
Greg Kurze3187a42017-02-26 23:44:28 +0100258 return -1;
259 }
Greg Kurz81ffbf52017-05-25 10:30:14 +0200260
261 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
262 if (map_dirfd == -1) {
263 return -1;
264 }
265
266 fp = local_fopenat(map_dirfd, name, "r");
267 if (!fp) {
268 if (errno == ENOENT) {
269 goto update_map_file;
270 } else {
271 close_preserve_errno(map_dirfd);
272 return -1;
273 }
274 }
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530275 }
276 memset(buf, 0, ATTR_MAX);
277 while (fgets(buf, ATTR_MAX, fp)) {
278 if (!strncmp(buf, "virtfs.uid", 10)) {
Greg Kurze3187a42017-02-26 23:44:28 +0100279 uid = atoi(buf + 11);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530280 } else if (!strncmp(buf, "virtfs.gid", 10)) {
Greg Kurze3187a42017-02-26 23:44:28 +0100281 gid = atoi(buf + 11);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530282 } else if (!strncmp(buf, "virtfs.mode", 11)) {
Greg Kurze3187a42017-02-26 23:44:28 +0100283 mode = atoi(buf + 12);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530284 } else if (!strncmp(buf, "virtfs.rdev", 11)) {
Greg Kurze3187a42017-02-26 23:44:28 +0100285 rdev = atoi(buf + 12);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530286 }
287 memset(buf, 0, ATTR_MAX);
288 }
289 fclose(fp);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530290
291update_map_file:
Greg Kurz81ffbf52017-05-25 10:30:14 +0200292 if (is_root) {
293 fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "w");
294 } else {
295 fp = local_fopenat(map_dirfd, name, "w");
296 /* We can't go this far with map_dirfd not being a valid file descriptor
297 * but some versions of gcc aren't smart enough to see it.
298 */
299 if (map_dirfd != -1) {
300 close_preserve_errno(map_dirfd);
301 }
302 }
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530303 if (!fp) {
Greg Kurze3187a42017-02-26 23:44:28 +0100304 return -1;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530305 }
306
Greg Kurz81ffbf52017-05-25 10:30:14 +0200307 map_fd = fileno(fp);
308 assert(map_fd != -1);
309 ret = fchmod(map_fd, 0600);
310 assert(ret == 0);
311
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530312 if (credp->fc_uid != -1) {
313 uid = credp->fc_uid;
314 }
315 if (credp->fc_gid != -1) {
316 gid = credp->fc_gid;
317 }
Keno Fischer230f1b32018-06-29 12:32:10 +0200318 if (credp->fc_mode != (mode_t)-1) {
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530319 mode = credp->fc_mode;
320 }
321 if (credp->fc_rdev != -1) {
322 rdev = credp->fc_rdev;
323 }
324
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530325 if (uid != -1) {
326 fprintf(fp, "virtfs.uid=%d\n", uid);
327 }
328 if (gid != -1) {
329 fprintf(fp, "virtfs.gid=%d\n", gid);
330 }
331 if (mode != -1) {
332 fprintf(fp, "virtfs.mode=%d\n", mode);
333 }
334 if (rdev != -1) {
335 fprintf(fp, "virtfs.rdev=%d\n", rdev);
336 }
337 fclose(fp);
338
Greg Kurze3187a42017-02-26 23:44:28 +0100339 return 0;
340}
341
342static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
343{
Greg Kurz4751fd52017-08-10 14:21:04 +0200344 struct stat stbuf;
Greg Kurze3187a42017-02-26 23:44:28 +0100345 int fd, ret;
346
347 /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
Greg Kurz4751fd52017-08-10 14:21:04 +0200348 * Unfortunately, the linux kernel doesn't implement it yet.
Greg Kurze3187a42017-02-26 23:44:28 +0100349 */
Greg Kurz4751fd52017-08-10 14:21:04 +0200350
351 /* First, we clear non-racing symlinks out of the way. */
Bin Meng6ca60cd2022-12-19 18:20:07 +0800352 if (qemu_fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW)) {
Greg Kurz4751fd52017-08-10 14:21:04 +0200353 return -1;
354 }
355 if (S_ISLNK(stbuf.st_mode)) {
356 errno = ELOOP;
357 return -1;
358 }
359
Greg Kurzaa5e85a2017-09-04 09:24:53 +0200360 fd = openat_file(dirfd, name, O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
Greg Kurz4751fd52017-08-10 14:21:04 +0200361#if O_PATH_9P_UTIL == 0
Greg Kurzaa5e85a2017-09-04 09:24:53 +0200362 /* Fallback for systems that don't support O_PATH: we depend on the file
363 * being readable or writable.
364 */
Greg Kurze3187a42017-02-26 23:44:28 +0100365 if (fd == -1) {
366 /* In case the file is writable-only and isn't a directory. */
367 if (errno == EACCES) {
368 fd = openat_file(dirfd, name, O_WRONLY, 0);
369 }
370 if (fd == -1 && errno == EISDIR) {
371 errno = EACCES;
372 }
373 }
374 if (fd == -1) {
375 return -1;
376 }
377 ret = fchmod(fd, mode);
Greg Kurz4751fd52017-08-10 14:21:04 +0200378#else
Greg Kurzaa5e85a2017-09-04 09:24:53 +0200379 /* Access modes are ignored when O_PATH is supported. If name is a symbolic
380 * link, O_PATH | O_NOFOLLOW causes openat(2) to return a file descriptor
381 * referring to the symbolic link.
382 */
Greg Kurz4751fd52017-08-10 14:21:04 +0200383 if (fd == -1) {
384 return -1;
385 }
386
387 /* Now we handle racing symlinks. */
388 ret = fstat(fd, &stbuf);
389 if (!ret) {
390 if (S_ISLNK(stbuf.st_mode)) {
391 errno = ELOOP;
392 ret = -1;
393 } else {
394 char *proc_path = g_strdup_printf("/proc/self/fd/%d", fd);
395 ret = chmod(proc_path, mode);
396 g_free(proc_path);
397 }
398 }
399#endif
Greg Kurze3187a42017-02-26 23:44:28 +0100400 close_preserve_errno(fd);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530401 return ret;
402}
403
Greg Kurze3187a42017-02-26 23:44:28 +0100404static int local_set_xattrat(int dirfd, const char *path, FsCred *credp)
Anthony Liguori131dcb22010-04-29 17:44:47 +0530405{
Venkateswararao Jujjuri (JV)758e8e32010-06-14 13:34:41 -0700406 int err;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530407
Venkateswararao Jujjuri (JV)758e8e32010-06-14 13:34:41 -0700408 if (credp->fc_uid != -1) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530409 uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
Greg Kurze3187a42017-02-26 23:44:28 +0100410 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.uid", &tmp_uid,
411 sizeof(uid_t), 0);
Venkateswararao Jujjuri (JV)758e8e32010-06-14 13:34:41 -0700412 if (err) {
413 return err;
414 }
Anthony Liguori131dcb22010-04-29 17:44:47 +0530415 }
Venkateswararao Jujjuri (JV)758e8e32010-06-14 13:34:41 -0700416 if (credp->fc_gid != -1) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530417 uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
Greg Kurze3187a42017-02-26 23:44:28 +0100418 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.gid", &tmp_gid,
419 sizeof(gid_t), 0);
Venkateswararao Jujjuri (JV)758e8e32010-06-14 13:34:41 -0700420 if (err) {
421 return err;
422 }
Anthony Liguori131dcb22010-04-29 17:44:47 +0530423 }
Keno Fischer230f1b32018-06-29 12:32:10 +0200424 if (credp->fc_mode != (mode_t)-1) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530425 uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
Greg Kurze3187a42017-02-26 23:44:28 +0100426 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.mode", &tmp_mode,
427 sizeof(mode_t), 0);
Venkateswararao Jujjuri (JV)758e8e32010-06-14 13:34:41 -0700428 if (err) {
429 return err;
430 }
Anthony Liguori131dcb22010-04-29 17:44:47 +0530431 }
Venkateswararao Jujjuri (JV)758e8e32010-06-14 13:34:41 -0700432 if (credp->fc_rdev != -1) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530433 uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
Greg Kurze3187a42017-02-26 23:44:28 +0100434 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.rdev", &tmp_rdev,
435 sizeof(dev_t), 0);
Venkateswararao Jujjuri (JV)758e8e32010-06-14 13:34:41 -0700436 if (err) {
437 return err;
438 }
Anthony Liguori131dcb22010-04-29 17:44:47 +0530439 }
Anthony Liguori131dcb22010-04-29 17:44:47 +0530440 return 0;
441}
442
Greg Kurzd815e722017-02-26 23:44:54 +0100443static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd,
444 const char *name, FsCred *credp)
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700445{
Greg Kurzd815e722017-02-26 23:44:54 +0100446 if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
Greg Kurzb314f6a2017-03-06 17:34:01 +0100447 AT_SYMLINK_NOFOLLOW) < 0) {
Aneesh Kumar K.V12848bf2010-09-02 11:09:07 +0530448 /*
449 * If we fail to change ownership and if we are
450 * using security model none. Ignore the error
451 */
Aneesh Kumar K.Vb97400c2011-10-13 13:21:00 +0530452 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
Greg Kurzd815e722017-02-26 23:44:54 +0100453 return -1;
Aneesh Kumar K.V12848bf2010-09-02 11:09:07 +0530454 }
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700455 }
M. Mohan Kumar2d405642012-01-19 12:21:12 +0530456
Greg Kurzd815e722017-02-26 23:44:54 +0100457 return fchmodat_nofollow(dirfd, name, credp->fc_mode & 07777);
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700458}
459
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530460static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
461 char *buf, size_t bufsz)
Anthony Liguori131dcb22010-04-29 17:44:47 +0530462{
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700463 ssize_t tsize = -1;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530464
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530465 if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
466 (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700467 int fd;
Greg Kurzbec1e952017-02-26 23:43:40 +0100468
469 fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0);
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700470 if (fd == -1) {
471 return -1;
472 }
Nikita Ivanov37b0b242022-10-23 12:04:22 +0300473 tsize = RETRY_ON_EINTR(read(fd, (void *)buf, bufsz));
Greg Kurzbec1e952017-02-26 23:43:40 +0100474 close_preserve_errno(fd);
Aneesh Kumar K.Vb97400c2011-10-13 13:21:00 +0530475 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
476 (fs_ctx->export_flags & V9FS_SM_NONE)) {
Greg Kurzbec1e952017-02-26 23:43:40 +0100477 char *dirpath = g_path_get_dirname(fs_path->data);
478 char *name = g_path_get_basename(fs_path->data);
479 int dirfd;
480
481 dirfd = local_opendir_nofollow(fs_ctx, dirpath);
482 if (dirfd == -1) {
483 goto out;
484 }
485
486 tsize = readlinkat(dirfd, name, buf, bufsz);
487 close_preserve_errno(dirfd);
488 out:
489 g_free(name);
490 g_free(dirpath);
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700491 }
492 return tsize;
Anthony Liguori131dcb22010-04-29 17:44:47 +0530493}
494
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530495static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
Anthony Liguori131dcb22010-04-29 17:44:47 +0530496{
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530497 return close(fs->fd);
Anthony Liguori131dcb22010-04-29 17:44:47 +0530498}
499
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530500static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
Anthony Liguori131dcb22010-04-29 17:44:47 +0530501{
Greg Kurzf314ea42016-06-06 11:52:34 +0200502 return closedir(fs->dir.stream);
Anthony Liguori131dcb22010-04-29 17:44:47 +0530503}
Anthony Liguori9f107512010-04-29 17:44:44 +0530504
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530505static int local_open(FsContext *ctx, V9fsPath *fs_path,
506 int flags, V9fsFidOpenState *fs)
Anthony Liguoria6568fe2010-04-29 17:44:55 +0530507{
Greg Kurz21328e12017-02-26 23:41:55 +0100508 int fd;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530509
Greg Kurz996a0d72017-02-26 23:42:18 +0100510 fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
Greg Kurz21328e12017-02-26 23:41:55 +0100511 if (fd == -1) {
512 return -1;
513 }
514 fs->fd = fd;
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530515 return fs->fd;
Anthony Liguoria6568fe2010-04-29 17:44:55 +0530516}
517
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530518static int local_opendir(FsContext *ctx,
519 V9fsPath *fs_path, V9fsFidOpenState *fs)
Anthony Liguoria6568fe2010-04-29 17:44:55 +0530520{
Greg Kurz996a0d72017-02-26 23:42:18 +0100521 int dirfd;
Greg Kurz21328e12017-02-26 23:41:55 +0100522 DIR *stream;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530523
Greg Kurz996a0d72017-02-26 23:42:18 +0100524 dirfd = local_opendir_nofollow(ctx, fs_path->data);
525 if (dirfd == -1) {
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530526 return -1;
527 }
Greg Kurz996a0d72017-02-26 23:42:18 +0100528
529 stream = fdopendir(dirfd);
Greg Kurz21328e12017-02-26 23:41:55 +0100530 if (!stream) {
Greg Kurzfaab2072017-03-06 17:34:01 +0100531 close(dirfd);
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530532 return -1;
533 }
Greg Kurz21328e12017-02-26 23:41:55 +0100534 fs->dir.stream = stream;
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530535 return 0;
Anthony Liguoria6568fe2010-04-29 17:44:55 +0530536}
537
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530538static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
Anthony Liguoria9231552010-04-29 17:44:56 +0530539{
Greg Kurzf314ea42016-06-06 11:52:34 +0200540 rewinddir(fs->dir.stream);
Anthony Liguoria9231552010-04-29 17:44:56 +0530541}
542
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530543static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
Anthony Liguoria9231552010-04-29 17:44:56 +0530544{
Greg Kurzf314ea42016-06-06 11:52:34 +0200545 return telldir(fs->dir.stream);
Anthony Liguoria9231552010-04-29 17:44:56 +0530546}
547
Greg Kurz7a954342017-05-05 14:48:08 +0200548static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name)
549{
Greg Kurz81ffbf52017-05-25 10:30:14 +0200550 return
551 !strcmp(name, VIRTFS_META_DIR) || !strcmp(name, VIRTFS_META_ROOT_FILE);
Greg Kurz7a954342017-05-05 14:48:08 +0200552}
553
Greg Kurz635324e2016-06-06 11:52:34 +0200554static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
Anthony Liguoria9231552010-04-29 17:44:56 +0530555{
Greg Kurz635324e2016-06-06 11:52:34 +0200556 struct dirent *entry;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530557
558again:
Greg Kurz635324e2016-06-06 11:52:34 +0200559 entry = readdir(fs->dir.stream);
560 if (!entry) {
561 return NULL;
562 }
Keno Fischer6b3b2792022-02-27 17:35:15 -0500563#ifdef CONFIG_DARWIN
564 int off;
565 off = telldir(fs->dir.stream);
566 /* If telldir fails, fail the entire readdir call */
567 if (off < 0) {
568 return NULL;
569 }
570 entry->d_seekoff = off;
571#endif
Greg Kurz635324e2016-06-06 11:52:34 +0200572
Bastian Blank840a1bf2014-08-22 13:22:21 +0400573 if (ctx->export_flags & V9FS_SM_MAPPED) {
574 entry->d_type = DT_UNKNOWN;
575 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Greg Kurz7a954342017-05-05 14:48:08 +0200576 if (local_is_mapped_file_metadata(ctx, entry->d_name)) {
Greg Kurz81ffbf52017-05-25 10:30:14 +0200577 /* skip the meta data */
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530578 goto again;
579 }
Bastian Blank840a1bf2014-08-22 13:22:21 +0400580 entry->d_type = DT_UNKNOWN;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530581 }
Greg Kurz635324e2016-06-06 11:52:34 +0200582
583 return entry;
Anthony Liguoria9231552010-04-29 17:44:56 +0530584}
585
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530586static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
Anthony Liguoria9231552010-04-29 17:44:56 +0530587{
Greg Kurzf314ea42016-06-06 11:52:34 +0200588 seekdir(fs->dir.stream, off);
Anthony Liguoria9231552010-04-29 17:44:56 +0530589}
590
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530591static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
592 const struct iovec *iov,
Sanchit Garg56d15a52010-10-08 11:30:16 +0530593 int iovcnt, off_t offset)
Anthony Liguoria9231552010-04-29 17:44:56 +0530594{
Sanchit Garg56d15a52010-10-08 11:30:16 +0530595#ifdef CONFIG_PREADV
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530596 return preadv(fs->fd, iov, iovcnt, offset);
Sanchit Garg56d15a52010-10-08 11:30:16 +0530597#else
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530598 int err = lseek(fs->fd, offset, SEEK_SET);
Sanchit Garg56d15a52010-10-08 11:30:16 +0530599 if (err == -1) {
600 return err;
601 } else {
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530602 return readv(fs->fd, iov, iovcnt);
Sanchit Garg56d15a52010-10-08 11:30:16 +0530603 }
604#endif
Anthony Liguoria9231552010-04-29 17:44:56 +0530605}
606
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530607static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
608 const struct iovec *iov,
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530609 int iovcnt, off_t offset)
Anthony Liguoria9231552010-04-29 17:44:56 +0530610{
Greg Kurz6fe76ac2017-01-23 09:46:13 +0100611 ssize_t ret;
Sanchit Garg56d15a52010-10-08 11:30:16 +0530612#ifdef CONFIG_PREADV
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530613 ret = pwritev(fs->fd, iov, iovcnt, offset);
Sanchit Garg56d15a52010-10-08 11:30:16 +0530614#else
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530615 int err = lseek(fs->fd, offset, SEEK_SET);
Sanchit Garg56d15a52010-10-08 11:30:16 +0530616 if (err == -1) {
617 return err;
618 } else {
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530619 ret = writev(fs->fd, iov, iovcnt);
Sanchit Garg56d15a52010-10-08 11:30:16 +0530620 }
621#endif
Aneesh Kumar K.Vd3ab98e2011-10-12 19:11:23 +0530622#ifdef CONFIG_SYNC_FILE_RANGE
623 if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
624 /*
625 * Initiate a writeback. This is not a data integrity sync.
626 * We want to ensure that we don't leave dirty pages in the cache
Michael Tokarev28cbbdd2023-07-14 14:13:50 +0300627 * after write when writeout=immediate is specified.
Aneesh Kumar K.Vd3ab98e2011-10-12 19:11:23 +0530628 */
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530629 sync_file_range(fs->fd, offset, ret,
Aneesh Kumar K.Vd3ab98e2011-10-12 19:11:23 +0530630 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
631 }
632#endif
633 return ret;
Anthony Liguori84493602010-04-29 17:44:58 +0530634}
635
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530636static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
Anthony Liguoric494dd62010-04-29 17:44:59 +0530637{
Greg Kurze3187a42017-02-26 23:44:28 +0100638 char *dirpath = g_path_get_dirname(fs_path->data);
639 char *name = g_path_get_basename(fs_path->data);
Chen Gang4fa4ce72014-03-02 01:36:19 +0800640 int ret = -1;
Greg Kurze3187a42017-02-26 23:44:28 +0100641 int dirfd;
642
643 dirfd = local_opendir_nofollow(fs_ctx, dirpath);
644 if (dirfd == -1) {
645 goto out;
646 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530647
Aneesh Kumar K.Vb97400c2011-10-13 13:21:00 +0530648 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
Greg Kurze3187a42017-02-26 23:44:28 +0100649 ret = local_set_xattrat(dirfd, name, credp);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530650 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Greg Kurze3187a42017-02-26 23:44:28 +0100651 ret = local_set_mapped_file_attrat(dirfd, name, credp);
652 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
653 fs_ctx->export_flags & V9FS_SM_NONE) {
654 ret = fchmodat_nofollow(dirfd, name, credp->fc_mode);
Venkateswararao Jujjuri (JV)e95ead32010-06-14 13:34:42 -0700655 }
Greg Kurze3187a42017-02-26 23:44:28 +0100656 close_preserve_errno(dirfd);
657
658out:
659 g_free(dirpath);
660 g_free(name);
Chen Gang4fa4ce72014-03-02 01:36:19 +0800661 return ret;
Anthony Liguoric494dd62010-04-29 17:44:59 +0530662}
663
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530664static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
665 const char *name, FsCred *credp)
Anthony Liguoric494dd62010-04-29 17:44:59 +0530666{
Venkateswararao Jujjuri (JV)1c293312010-06-14 13:34:48 -0700667 int err = -1;
Greg Kurzd815e722017-02-26 23:44:54 +0100668 int dirfd;
Venkateswararao Jujjuri (JV)1c293312010-06-14 13:34:48 -0700669
Greg Kurz7a954342017-05-05 14:48:08 +0200670 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
671 local_is_mapped_file_metadata(fs_ctx, name)) {
672 errno = EINVAL;
673 return -1;
674 }
675
Greg Kurzd815e722017-02-26 23:44:54 +0100676 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
677 if (dirfd == -1) {
678 return -1;
679 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530680
Greg Kurzd815e722017-02-26 23:44:54 +0100681 if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
682 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Keno Fischer029ed1b2022-02-27 17:35:20 -0500683 err = qemu_mknodat(dirfd, name, fs_ctx->fmode | S_IFREG, 0);
Venkateswararao Jujjuri (JV)1c293312010-06-14 13:34:48 -0700684 if (err == -1) {
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530685 goto out;
Venkateswararao Jujjuri (JV)1c293312010-06-14 13:34:48 -0700686 }
Greg Kurzd815e722017-02-26 23:44:54 +0100687
688 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
689 err = local_set_xattrat(dirfd, name, credp);
690 } else {
691 err = local_set_mapped_file_attrat(dirfd, name, credp);
692 }
Venkateswararao Jujjuri (JV)1c293312010-06-14 13:34:48 -0700693 if (err == -1) {
Venkateswararao Jujjuri (JV)1c293312010-06-14 13:34:48 -0700694 goto err_end;
695 }
Greg Kurzd815e722017-02-26 23:44:54 +0100696 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
697 fs_ctx->export_flags & V9FS_SM_NONE) {
Keno Fischer029ed1b2022-02-27 17:35:20 -0500698 err = qemu_mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530699 if (err == -1) {
700 goto out;
701 }
Greg Kurzd815e722017-02-26 23:44:54 +0100702 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530703 if (err == -1) {
Venkateswararao Jujjuri (JV)1c293312010-06-14 13:34:48 -0700704 goto err_end;
705 }
706 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530707 goto out;
Venkateswararao Jujjuri (JV)1c293312010-06-14 13:34:48 -0700708
709err_end:
Greg Kurzd815e722017-02-26 23:44:54 +0100710 unlinkat_preserve_errno(dirfd, name, 0);
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530711out:
Greg Kurzd815e722017-02-26 23:44:54 +0100712 close_preserve_errno(dirfd);
Venkateswararao Jujjuri (JV)1c293312010-06-14 13:34:48 -0700713 return err;
Anthony Liguoric494dd62010-04-29 17:44:59 +0530714}
715
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530716static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
717 const char *name, FsCred *credp)
Anthony Liguoric494dd62010-04-29 17:44:59 +0530718{
Venkateswararao Jujjuri (JV)00ec5c32010-06-14 13:34:46 -0700719 int err = -1;
Greg Kurz3f3a1692017-02-26 23:45:02 +0100720 int dirfd;
Venkateswararao Jujjuri (JV)00ec5c32010-06-14 13:34:46 -0700721
Greg Kurz7a954342017-05-05 14:48:08 +0200722 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
723 local_is_mapped_file_metadata(fs_ctx, name)) {
724 errno = EINVAL;
725 return -1;
726 }
727
Greg Kurz3f3a1692017-02-26 23:45:02 +0100728 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
729 if (dirfd == -1) {
730 return -1;
731 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530732
Greg Kurz3f3a1692017-02-26 23:45:02 +0100733 if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
734 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Bin Meng6ca60cd2022-12-19 18:20:07 +0800735 err = qemu_mkdirat(dirfd, name, fs_ctx->dmode);
Venkateswararao Jujjuri (JV)00ec5c32010-06-14 13:34:46 -0700736 if (err == -1) {
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530737 goto out;
Venkateswararao Jujjuri (JV)00ec5c32010-06-14 13:34:46 -0700738 }
Greg Kurz3f3a1692017-02-26 23:45:02 +0100739 credp->fc_mode = credp->fc_mode | S_IFDIR;
740
741 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
742 err = local_set_xattrat(dirfd, name, credp);
743 } else {
744 err = local_set_mapped_file_attrat(dirfd, name, credp);
745 }
Venkateswararao Jujjuri (JV)00ec5c32010-06-14 13:34:46 -0700746 if (err == -1) {
Venkateswararao Jujjuri (JV)00ec5c32010-06-14 13:34:46 -0700747 goto err_end;
748 }
Greg Kurz3f3a1692017-02-26 23:45:02 +0100749 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
750 fs_ctx->export_flags & V9FS_SM_NONE) {
Bin Meng6ca60cd2022-12-19 18:20:07 +0800751 err = qemu_mkdirat(dirfd, name, credp->fc_mode);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530752 if (err == -1) {
753 goto out;
754 }
Greg Kurz3f3a1692017-02-26 23:45:02 +0100755 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530756 if (err == -1) {
Venkateswararao Jujjuri (JV)00ec5c32010-06-14 13:34:46 -0700757 goto err_end;
758 }
759 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530760 goto out;
Venkateswararao Jujjuri (JV)00ec5c32010-06-14 13:34:46 -0700761
762err_end:
Greg Kurz3f3a1692017-02-26 23:45:02 +0100763 unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR);
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530764out:
Greg Kurz3f3a1692017-02-26 23:45:02 +0100765 close_preserve_errno(dirfd);
Venkateswararao Jujjuri (JV)00ec5c32010-06-14 13:34:46 -0700766 return err;
Anthony Liguoric494dd62010-04-29 17:44:59 +0530767}
768
Aneesh Kumar K.V8b888272011-12-04 22:35:28 +0530769static int local_fstat(FsContext *fs_ctx, int fid_type,
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530770 V9fsFidOpenState *fs, struct stat *stbuf)
Anthony Liguoric494dd62010-04-29 17:44:59 +0530771{
Aneesh Kumar K.V8b888272011-12-04 22:35:28 +0530772 int err, fd;
773
774 if (fid_type == P9_FID_DIR) {
Greg Kurzf314ea42016-06-06 11:52:34 +0200775 fd = dirfd(fs->dir.stream);
Aneesh Kumar K.V8b888272011-12-04 22:35:28 +0530776 } else {
777 fd = fs->fd;
778 }
779
780 err = fstat(fd, stbuf);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700781 if (err) {
782 return err;
783 }
Aneesh Kumar K.Vb97400c2011-10-13 13:21:00 +0530784 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700785 /* Actual credentials are part of extended attrs */
786 uid_t tmp_uid;
787 gid_t tmp_gid;
788 mode_t tmp_mode;
789 dev_t tmp_dev;
790
Keno Fischerb5989322022-02-27 17:35:19 -0500791 if (qemu_fgetxattr(fd, "user.virtfs.uid",
792 &tmp_uid, sizeof(uid_t)) > 0) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530793 stbuf->st_uid = le32_to_cpu(tmp_uid);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700794 }
Keno Fischerb5989322022-02-27 17:35:19 -0500795 if (qemu_fgetxattr(fd, "user.virtfs.gid",
796 &tmp_gid, sizeof(gid_t)) > 0) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530797 stbuf->st_gid = le32_to_cpu(tmp_gid);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700798 }
Keno Fischerb5989322022-02-27 17:35:19 -0500799 if (qemu_fgetxattr(fd, "user.virtfs.mode",
800 &tmp_mode, sizeof(mode_t)) > 0) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530801 stbuf->st_mode = le32_to_cpu(tmp_mode);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700802 }
Keno Fischerb5989322022-02-27 17:35:19 -0500803 if (qemu_fgetxattr(fd, "user.virtfs.rdev",
804 &tmp_dev, sizeof(dev_t)) > 0) {
Aneesh Kumar K.Vf8ad4a82014-08-03 17:02:55 +0530805 stbuf->st_rdev = le64_to_cpu(tmp_dev);
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700806 }
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530807 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
808 errno = EOPNOTSUPP;
809 return -1;
Venkateswararao Jujjuri (JV)1237ad72010-06-14 13:34:44 -0700810 }
811 return err;
Anthony Liguoric494dd62010-04-29 17:44:59 +0530812}
813
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530814static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530815 int flags, FsCred *credp, V9fsFidOpenState *fs)
Anthony Liguoric494dd62010-04-29 17:44:59 +0530816{
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700817 int fd = -1;
818 int err = -1;
Greg Kurza565fea2017-02-26 23:45:09 +0100819 int dirfd;
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700820
Greg Kurz7a954342017-05-05 14:48:08 +0200821 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
822 local_is_mapped_file_metadata(fs_ctx, name)) {
823 errno = EINVAL;
824 return -1;
825 }
826
Aneesh Kumar K.V0ceb0922013-05-20 19:43:15 +0530827 /*
828 * Mark all the open to not follow symlinks
829 */
830 flags |= O_NOFOLLOW;
831
Greg Kurza565fea2017-02-26 23:45:09 +0100832 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
833 if (dirfd == -1) {
834 return -1;
835 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530836
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700837 /* Determine the security model */
Greg Kurza565fea2017-02-26 23:45:09 +0100838 if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
839 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Tobias Schrammb96feb22017-06-29 15:11:50 +0200840 fd = openat_file(dirfd, name, flags, fs_ctx->fmode);
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700841 if (fd == -1) {
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530842 goto out;
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700843 }
Xinhao Zhang01011732020-10-30 12:35:13 +0800844 credp->fc_mode = credp->fc_mode | S_IFREG;
Greg Kurza565fea2017-02-26 23:45:09 +0100845 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
Michael Tokarev28cbbdd2023-07-14 14:13:50 +0300846 /* Set client credentials in xattr */
Greg Kurza565fea2017-02-26 23:45:09 +0100847 err = local_set_xattrat(dirfd, name, credp);
848 } else {
849 err = local_set_mapped_file_attrat(dirfd, name, credp);
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700850 }
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530851 if (err == -1) {
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530852 goto err_end;
853 }
Aneesh Kumar K.Vb97400c2011-10-13 13:21:00 +0530854 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
855 (fs_ctx->export_flags & V9FS_SM_NONE)) {
Greg Kurza565fea2017-02-26 23:45:09 +0100856 fd = openat_file(dirfd, name, flags, credp->fc_mode);
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700857 if (fd == -1) {
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530858 goto out;
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700859 }
Greg Kurza565fea2017-02-26 23:45:09 +0100860 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700861 if (err == -1) {
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700862 goto err_end;
863 }
864 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530865 err = fd;
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +0530866 fs->fd = fd;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530867 goto out;
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700868
869err_end:
Greg Kurza565fea2017-02-26 23:45:09 +0100870 unlinkat_preserve_errno(dirfd, name,
871 flags & O_DIRECTORY ? AT_REMOVEDIR : 0);
872 close_preserve_errno(fd);
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530873out:
Greg Kurza565fea2017-02-26 23:45:09 +0100874 close_preserve_errno(dirfd);
Venkateswararao Jujjuri (JV)4750a962010-06-14 13:34:45 -0700875 return err;
Anthony Liguoric494dd62010-04-29 17:44:59 +0530876}
877
Venkateswararao Jujjuri (JV)758e8e32010-06-14 13:34:41 -0700878
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700879static int local_symlink(FsContext *fs_ctx, const char *oldpath,
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530880 V9fsPath *dir_path, const char *name, FsCred *credp)
Anthony Liguoric494dd62010-04-29 17:44:59 +0530881{
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700882 int err = -1;
Greg Kurz38771612017-02-26 23:44:46 +0100883 int dirfd;
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700884
Greg Kurz7a954342017-05-05 14:48:08 +0200885 if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
886 local_is_mapped_file_metadata(fs_ctx, name)) {
887 errno = EINVAL;
888 return -1;
889 }
890
Greg Kurz38771612017-02-26 23:44:46 +0100891 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
892 if (dirfd == -1) {
893 return -1;
894 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530895
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700896 /* Determine the security model */
Greg Kurz38771612017-02-26 23:44:46 +0100897 if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
898 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700899 int fd;
900 ssize_t oldpath_size, write_size;
Greg Kurz38771612017-02-26 23:44:46 +0100901
902 fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
Tobias Schrammb96feb22017-06-29 15:11:50 +0200903 fs_ctx->fmode);
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700904 if (fd == -1) {
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530905 goto out;
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700906 }
907 /* Write the oldpath (target) to the file. */
Harsh Prateek Boraf35bde22011-02-02 10:20:33 +0530908 oldpath_size = strlen(oldpath);
Nikita Ivanov37b0b242022-10-23 12:04:22 +0300909 write_size = RETRY_ON_EINTR(write(fd, (void *)oldpath, oldpath_size));
Greg Kurz38771612017-02-26 23:44:46 +0100910 close_preserve_errno(fd);
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700911
912 if (write_size != oldpath_size) {
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700913 goto err_end;
914 }
Michael Tokarev28cbbdd2023-07-14 14:13:50 +0300915 /* Set client credentials in symlink's xattr */
Greg Kurz38771612017-02-26 23:44:46 +0100916 credp->fc_mode = credp->fc_mode | S_IFLNK;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530917
Greg Kurz38771612017-02-26 23:44:46 +0100918 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
919 err = local_set_xattrat(dirfd, name, credp);
920 } else {
921 err = local_set_mapped_file_attrat(dirfd, name, credp);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530922 }
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530923 if (err == -1) {
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530924 goto err_end;
925 }
Greg Kurz38771612017-02-26 23:44:46 +0100926 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
927 fs_ctx->export_flags & V9FS_SM_NONE) {
928 err = symlinkat(oldpath, dirfd, name);
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700929 if (err) {
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530930 goto out;
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700931 }
Greg Kurz38771612017-02-26 23:44:46 +0100932 err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
933 AT_SYMLINK_NOFOLLOW);
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700934 if (err == -1) {
Aneesh Kumar K.V12848bf2010-09-02 11:09:07 +0530935 /*
936 * If we fail to change ownership and if we are
937 * using security model none. Ignore the error
938 */
Aneesh Kumar K.Vb97400c2011-10-13 13:21:00 +0530939 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
Aneesh Kumar K.V12848bf2010-09-02 11:09:07 +0530940 goto err_end;
Greg Kurz38771612017-02-26 23:44:46 +0100941 } else {
Aneesh Kumar K.V12848bf2010-09-02 11:09:07 +0530942 err = 0;
Greg Kurz38771612017-02-26 23:44:46 +0100943 }
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700944 }
945 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530946 goto out;
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700947
948err_end:
Greg Kurz38771612017-02-26 23:44:46 +0100949 unlinkat_preserve_errno(dirfd, name, 0);
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530950out:
Greg Kurz38771612017-02-26 23:44:46 +0100951 close_preserve_errno(dirfd);
Venkateswararao Jujjuri (JV)879c2812010-06-14 13:34:47 -0700952 return err;
Anthony Liguoric494dd62010-04-29 17:44:59 +0530953}
954
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530955static int local_link(FsContext *ctx, V9fsPath *oldpath,
956 V9fsPath *dirpath, const char *name)
Anthony Liguoric494dd62010-04-29 17:44:59 +0530957{
Greg Kurzad0b46e2017-02-26 23:44:20 +0100958 char *odirpath = g_path_get_dirname(oldpath->data);
959 char *oname = g_path_get_basename(oldpath->data);
960 int ret = -1;
961 int odirfd, ndirfd;
Anthony Liguoric494dd62010-04-29 17:44:59 +0530962
Greg Kurz7a954342017-05-05 14:48:08 +0200963 if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
964 local_is_mapped_file_metadata(ctx, name)) {
965 errno = EINVAL;
Jiajun Chen841b8d02020-01-20 15:11:39 +0100966 goto out;
Greg Kurz7a954342017-05-05 14:48:08 +0200967 }
968
Greg Kurzad0b46e2017-02-26 23:44:20 +0100969 odirfd = local_opendir_nofollow(ctx, odirpath);
970 if (odirfd == -1) {
Greg Kurz6dd4b1f2017-02-26 23:44:11 +0100971 goto out;
972 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +0530973
Greg Kurzad0b46e2017-02-26 23:44:20 +0100974 ndirfd = local_opendir_nofollow(ctx, dirpath->data);
975 if (ndirfd == -1) {
976 close_preserve_errno(odirfd);
977 goto out;
978 }
979
980 ret = linkat(odirfd, oname, ndirfd, name, 0);
981 if (ret < 0) {
982 goto out_close;
983 }
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530984
985 /* now link the virtfs_metadata files */
Greg Kurz6dd4b1f2017-02-26 23:44:11 +0100986 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Greg Kurzad0b46e2017-02-26 23:44:20 +0100987 int omap_dirfd, nmap_dirfd;
Greg Kurz6dd4b1f2017-02-26 23:44:11 +0100988
Bin Meng6ca60cd2022-12-19 18:20:07 +0800989 ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
Greg Kurzad0b46e2017-02-26 23:44:20 +0100990 if (ret < 0 && errno != EEXIST) {
991 goto err_undo_link;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +0530992 }
Greg Kurzad0b46e2017-02-26 23:44:20 +0100993
994 omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
995 if (omap_dirfd == -1) {
996 goto err;
997 }
998
999 nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
1000 if (nmap_dirfd == -1) {
1001 close_preserve_errno(omap_dirfd);
1002 goto err;
1003 }
1004
1005 ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0);
1006 close_preserve_errno(nmap_dirfd);
1007 close_preserve_errno(omap_dirfd);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301008 if (ret < 0 && errno != ENOENT) {
Greg Kurzad0b46e2017-02-26 23:44:20 +01001009 goto err_undo_link;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301010 }
Greg Kurz6dd4b1f2017-02-26 23:44:11 +01001011
Greg Kurzad0b46e2017-02-26 23:44:20 +01001012 ret = 0;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301013 }
Greg Kurzad0b46e2017-02-26 23:44:20 +01001014 goto out_close;
1015
1016err:
1017 ret = -1;
1018err_undo_link:
1019 unlinkat_preserve_errno(ndirfd, name, 0);
1020out_close:
1021 close_preserve_errno(ndirfd);
1022 close_preserve_errno(odirfd);
Greg Kurz6dd4b1f2017-02-26 23:44:11 +01001023out:
Greg Kurzad0b46e2017-02-26 23:44:20 +01001024 g_free(oname);
1025 g_free(odirpath);
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301026 return ret;
Anthony Liguoric494dd62010-04-29 17:44:59 +05301027}
1028
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301029static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301030{
Greg Kurzac125d92017-02-26 23:43:32 +01001031 int fd, ret;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301032
Greg Kurzac125d92017-02-26 23:43:32 +01001033 fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
1034 if (fd == -1) {
1035 return -1;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301036 }
Greg Kurzac125d92017-02-26 23:43:32 +01001037 ret = ftruncate(fd, size);
1038 close_preserve_errno(fd);
Anthony Liguoria9231552010-04-29 17:44:56 +05301039 return ret;
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301040}
1041
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301042static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301043{
Greg Kurzd369f202017-02-26 23:44:37 +01001044 char *dirpath = g_path_get_dirname(fs_path->data);
1045 char *name = g_path_get_basename(fs_path->data);
Chen Gang4fa4ce72014-03-02 01:36:19 +08001046 int ret = -1;
Greg Kurzd369f202017-02-26 23:44:37 +01001047 int dirfd;
1048
1049 dirfd = local_opendir_nofollow(fs_ctx, dirpath);
1050 if (dirfd == -1) {
1051 goto out;
1052 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301053
Sripathi Kodic79ce732010-06-17 18:18:47 +05301054 if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
Aneesh Kumar K.V17b19712011-10-25 12:10:39 +05301055 (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
1056 (fs_ctx->export_flags & V9FS_SM_NONE)) {
Greg Kurzd369f202017-02-26 23:44:37 +01001057 ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
1058 AT_SYMLINK_NOFOLLOW);
Aneesh Kumar K.Vb97400c2011-10-13 13:21:00 +05301059 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
Greg Kurzd369f202017-02-26 23:44:37 +01001060 ret = local_set_xattrat(dirfd, name, credp);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301061 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Greg Kurzd369f202017-02-26 23:44:37 +01001062 ret = local_set_mapped_file_attrat(dirfd, name, credp);
Venkateswararao Jujjuri (JV)f7613be2010-06-14 13:34:43 -07001063 }
Greg Kurzd369f202017-02-26 23:44:37 +01001064
1065 close_preserve_errno(dirfd);
1066out:
1067 g_free(name);
1068 g_free(dirpath);
Chen Gang4fa4ce72014-03-02 01:36:19 +08001069 return ret;
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301070}
1071
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301072static int local_utimensat(FsContext *s, V9fsPath *fs_path,
Hidetoshi Seto38671422010-11-24 11:38:10 +09001073 const struct timespec *buf)
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301074{
Greg Kurza33eda02017-02-26 23:43:17 +01001075 char *dirpath = g_path_get_dirname(fs_path->data);
1076 char *name = g_path_get_basename(fs_path->data);
1077 int dirfd, ret = -1;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301078
Greg Kurza33eda02017-02-26 23:43:17 +01001079 dirfd = local_opendir_nofollow(s, dirpath);
1080 if (dirfd == -1) {
1081 goto out;
1082 }
1083
Bin Meng6ca60cd2022-12-19 18:20:07 +08001084 ret = qemu_utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
Greg Kurza33eda02017-02-26 23:43:17 +01001085 close_preserve_errno(dirfd);
1086out:
1087 g_free(dirpath);
1088 g_free(name);
Chen Gang4fa4ce72014-03-02 01:36:19 +08001089 return ret;
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301090}
1091
Greg Kurzdf4938a2017-02-26 23:43:00 +01001092static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
1093 int flags)
Anthony Liguori5bae1902010-04-29 17:45:01 +05301094{
Daniel Henrique Barboza846cf402020-01-20 15:11:39 +01001095 int ret;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301096
1097 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
Greg Kurzdf4938a2017-02-26 23:43:00 +01001098 int map_dirfd;
1099
Greg Kurz6a87e792017-05-25 10:30:13 +02001100 /* We need to remove the metadata as well:
1101 * - the metadata directory if we're removing a directory
1102 * - the metadata file in the parent's metadata directory
1103 *
1104 * If any of these are missing (ie, ENOENT) then we're probably
1105 * trying to remove something that wasn't created in mapped-file
1106 * mode. We just ignore the error.
1107 */
Greg Kurzdf4938a2017-02-26 23:43:00 +01001108 if (flags == AT_REMOVEDIR) {
1109 int fd;
1110
Greg Kurzb003fc02017-03-06 17:34:01 +01001111 fd = openat_dir(dirfd, name);
Greg Kurzdf4938a2017-02-26 23:43:00 +01001112 if (fd == -1) {
Daniel Henrique Barboza846cf402020-01-20 15:11:39 +01001113 return -1;
Greg Kurzdf4938a2017-02-26 23:43:00 +01001114 }
Bin Meng6ca60cd2022-12-19 18:20:07 +08001115 ret = qemu_unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
Greg Kurzdf4938a2017-02-26 23:43:00 +01001116 close_preserve_errno(fd);
1117 if (ret < 0 && errno != ENOENT) {
Daniel Henrique Barboza846cf402020-01-20 15:11:39 +01001118 return -1;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301119 }
1120 }
Greg Kurzdf4938a2017-02-26 23:43:00 +01001121 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
Greg Kurz6a87e792017-05-25 10:30:13 +02001122 if (map_dirfd != -1) {
Bin Meng6ca60cd2022-12-19 18:20:07 +08001123 ret = qemu_unlinkat(map_dirfd, name, 0);
Greg Kurz6a87e792017-05-25 10:30:13 +02001124 close_preserve_errno(map_dirfd);
1125 if (ret < 0 && errno != ENOENT) {
Daniel Henrique Barboza846cf402020-01-20 15:11:39 +01001126 return -1;
Greg Kurz6a87e792017-05-25 10:30:13 +02001127 }
1128 } else if (errno != ENOENT) {
Daniel Henrique Barboza846cf402020-01-20 15:11:39 +01001129 return -1;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301130 }
1131 }
Chen Gang4fa4ce72014-03-02 01:36:19 +08001132
Bin Meng6ca60cd2022-12-19 18:20:07 +08001133 return qemu_unlinkat(dirfd, name, flags);
Greg Kurzdf4938a2017-02-26 23:43:00 +01001134}
1135
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301136static int local_remove(FsContext *ctx, const char *path)
1137{
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301138 struct stat stbuf;
Greg Kurza0e640a2017-02-26 23:43:08 +01001139 char *dirpath = g_path_get_dirname(path);
1140 char *name = g_path_get_basename(path);
1141 int flags = 0;
1142 int dirfd;
1143 int err = -1;
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301144
Greg Kurza0e640a2017-02-26 23:43:08 +01001145 dirfd = local_opendir_nofollow(ctx, dirpath);
Greg Kurzb7361d42017-03-06 17:34:01 +01001146 if (dirfd == -1) {
Greg Kurza0e640a2017-02-26 23:43:08 +01001147 goto out;
Venkateswararao Jujjuri (JV)faa44e32011-06-01 12:35:14 +05301148 }
Chen Gang4fa4ce72014-03-02 01:36:19 +08001149
Bin Meng6ca60cd2022-12-19 18:20:07 +08001150 if (qemu_fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) {
Greg Kurza0e640a2017-02-26 23:43:08 +01001151 goto err_out;
1152 }
1153
1154 if (S_ISDIR(stbuf.st_mode)) {
1155 flags |= AT_REMOVEDIR;
1156 }
1157
1158 err = local_unlinkat_common(ctx, dirfd, name, flags);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301159err_out:
Greg Kurza0e640a2017-02-26 23:43:08 +01001160 close_preserve_errno(dirfd);
1161out:
1162 g_free(name);
1163 g_free(dirpath);
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301164 return err;
Anthony Liguori5bae1902010-04-29 17:45:01 +05301165}
1166
Aneesh Kumar K.V8b888272011-12-04 22:35:28 +05301167static int local_fsync(FsContext *ctx, int fid_type,
1168 V9fsFidOpenState *fs, int datasync)
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301169{
Aneesh Kumar K.V8b888272011-12-04 22:35:28 +05301170 int fd;
1171
1172 if (fid_type == P9_FID_DIR) {
Greg Kurzf314ea42016-06-06 11:52:34 +02001173 fd = dirfd(fs->dir.stream);
Venkateswararao Jujjuri (JV)49594972010-10-22 10:08:45 -07001174 } else {
Aneesh Kumar K.V8b888272011-12-04 22:35:28 +05301175 fd = fs->fd;
1176 }
1177
1178 if (datasync) {
1179 return qemu_fdatasync(fd);
1180 } else {
1181 return fsync(fd);
Venkateswararao Jujjuri (JV)49594972010-10-22 10:08:45 -07001182 }
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301183}
1184
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301185static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
M. Mohan Kumarbe940c82010-05-10 12:11:03 +05301186{
Greg Kurz31e51d12017-02-26 23:43:25 +01001187 int fd, ret;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301188
Greg Kurz31e51d12017-02-26 23:43:25 +01001189 fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
Greg Kurz23da0142017-03-06 17:34:01 +01001190 if (fd == -1) {
1191 return -1;
1192 }
Greg Kurz31e51d12017-02-26 23:43:25 +01001193 ret = fstatfs(fd, stbuf);
1194 close_preserve_errno(fd);
Chen Gang4fa4ce72014-03-02 01:36:19 +08001195 return ret;
M. Mohan Kumarbe940c82010-05-10 12:11:03 +05301196}
1197
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301198static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
Aneesh Kumar K.Vfa32ef82010-09-02 11:09:06 +05301199 const char *name, void *value, size_t size)
1200{
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301201 char *path = fs_path->data;
1202
Aneesh Kumar K.Vfc221182010-10-18 15:28:16 +05301203 return v9fs_get_xattr(ctx, path, name, value, size);
Aneesh Kumar K.Vfa32ef82010-09-02 11:09:06 +05301204}
1205
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301206static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
Aneesh Kumar K.Vfa32ef82010-09-02 11:09:06 +05301207 void *value, size_t size)
1208{
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301209 char *path = fs_path->data;
1210
Aneesh Kumar K.Vfc221182010-10-18 15:28:16 +05301211 return v9fs_list_xattr(ctx, path, value, size);
Aneesh Kumar K.Vfa32ef82010-09-02 11:09:06 +05301212}
1213
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301214static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
Aneesh Kumar K.V10b468b2010-09-02 11:09:07 +05301215 void *value, size_t size, int flags)
1216{
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301217 char *path = fs_path->data;
1218
Aneesh Kumar K.Vfc221182010-10-18 15:28:16 +05301219 return v9fs_set_xattr(ctx, path, name, value, size, flags);
Aneesh Kumar K.V10b468b2010-09-02 11:09:07 +05301220}
1221
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301222static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1223 const char *name)
Aneesh Kumar K.V9ed3ef22010-08-26 11:15:23 +05301224{
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301225 char *path = fs_path->data;
1226
Aneesh Kumar K.Vfc221182010-10-18 15:28:16 +05301227 return v9fs_remove_xattr(ctx, path, name);
Aneesh Kumar K.V9ed3ef22010-08-26 11:15:23 +05301228}
1229
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301230static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1231 const char *name, V9fsPath *target)
1232{
Greg Kurz7a954342017-05-05 14:48:08 +02001233 if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
1234 local_is_mapped_file_metadata(ctx, name)) {
1235 errno = EINVAL;
1236 return -1;
1237 }
1238
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301239 if (dir_path) {
Greg Kurzf57f5872017-05-25 10:30:14 +02001240 if (!strcmp(name, ".")) {
1241 /* "." relative to "foo/bar" is "foo/bar" */
1242 v9fs_path_copy(target, dir_path);
1243 } else if (!strcmp(name, "..")) {
1244 if (!strcmp(dir_path->data, ".")) {
1245 /* ".." relative to the root is "." */
1246 v9fs_path_sprintf(target, ".");
1247 } else {
1248 char *tmp = g_path_get_dirname(dir_path->data);
1249 /* Symbolic links are resolved by the client. We can assume
1250 * that ".." relative to "foo/bar" is equivalent to "foo"
1251 */
1252 v9fs_path_sprintf(target, "%s", tmp);
1253 g_free(tmp);
1254 }
1255 } else {
1256 assert(!strchr(name, '/'));
1257 v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
1258 }
1259 } else if (!strcmp(name, "/") || !strcmp(name, ".") ||
1260 !strcmp(name, "..")) {
1261 /* This is the root fid */
1262 v9fs_path_sprintf(target, ".");
Greg Kurz9c6b8992017-04-17 10:53:23 +02001263 } else {
Greg Kurzf57f5872017-05-25 10:30:14 +02001264 assert(!strchr(name, '/'));
1265 v9fs_path_sprintf(target, "./%s", name);
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301266 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301267 return 0;
1268}
1269
1270static int local_renameat(FsContext *ctx, V9fsPath *olddir,
1271 const char *old_name, V9fsPath *newdir,
1272 const char *new_name)
1273{
1274 int ret;
Greg Kurz99f2cf42017-02-26 23:43:55 +01001275 int odirfd, ndirfd;
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301276
Greg Kurz7a954342017-05-05 14:48:08 +02001277 if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
1278 (local_is_mapped_file_metadata(ctx, old_name) ||
1279 local_is_mapped_file_metadata(ctx, new_name))) {
1280 errno = EINVAL;
1281 return -1;
1282 }
1283
Greg Kurz99f2cf42017-02-26 23:43:55 +01001284 odirfd = local_opendir_nofollow(ctx, olddir->data);
1285 if (odirfd == -1) {
1286 return -1;
1287 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301288
Greg Kurz99f2cf42017-02-26 23:43:55 +01001289 ndirfd = local_opendir_nofollow(ctx, newdir->data);
1290 if (ndirfd == -1) {
1291 close_preserve_errno(odirfd);
1292 return -1;
1293 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301294
Bin Meng6ca60cd2022-12-19 18:20:07 +08001295 ret = qemu_renameat(odirfd, old_name, ndirfd, new_name);
Greg Kurz99f2cf42017-02-26 23:43:55 +01001296 if (ret < 0) {
1297 goto out;
1298 }
1299
1300 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1301 int omap_dirfd, nmap_dirfd;
1302
Bin Meng6ca60cd2022-12-19 18:20:07 +08001303 ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
Greg Kurz99f2cf42017-02-26 23:43:55 +01001304 if (ret < 0 && errno != EEXIST) {
1305 goto err_undo_rename;
1306 }
1307
Greg Kurz6dd4b1f2017-02-26 23:44:11 +01001308 omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
Greg Kurz99f2cf42017-02-26 23:43:55 +01001309 if (omap_dirfd == -1) {
1310 goto err;
1311 }
1312
Greg Kurz6dd4b1f2017-02-26 23:44:11 +01001313 nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
Greg Kurz99f2cf42017-02-26 23:43:55 +01001314 if (nmap_dirfd == -1) {
1315 close_preserve_errno(omap_dirfd);
1316 goto err;
1317 }
1318
1319 /* rename the .virtfs_metadata files */
Bin Meng6ca60cd2022-12-19 18:20:07 +08001320 ret = qemu_renameat(omap_dirfd, old_name, nmap_dirfd, new_name);
Greg Kurz99f2cf42017-02-26 23:43:55 +01001321 close_preserve_errno(nmap_dirfd);
1322 close_preserve_errno(omap_dirfd);
1323 if (ret < 0 && errno != ENOENT) {
1324 goto err_undo_rename;
1325 }
1326
1327 ret = 0;
1328 }
1329 goto out;
1330
1331err:
1332 ret = -1;
1333err_undo_rename:
1334 renameat_preserve_errno(ndirfd, new_name, odirfd, old_name);
1335out:
1336 close_preserve_errno(ndirfd);
1337 close_preserve_errno(odirfd);
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301338 return ret;
1339}
1340
Greg Kurzd2767ed2017-02-26 23:44:03 +01001341static void v9fs_path_init_dirname(V9fsPath *path, const char *str)
1342{
1343 path->data = g_path_get_dirname(str);
1344 path->size = strlen(path->data) + 1;
1345}
1346
1347static int local_rename(FsContext *ctx, const char *oldpath,
1348 const char *newpath)
1349{
1350 int err;
1351 char *oname = g_path_get_basename(oldpath);
1352 char *nname = g_path_get_basename(newpath);
1353 V9fsPath olddir, newdir;
1354
1355 v9fs_path_init_dirname(&olddir, oldpath);
1356 v9fs_path_init_dirname(&newdir, newpath);
1357
1358 err = local_renameat(ctx, &olddir, oname, &newdir, nname);
1359
1360 v9fs_path_free(&newdir);
1361 v9fs_path_free(&olddir);
1362 g_free(nname);
1363 g_free(oname);
1364
1365 return err;
1366}
1367
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301368static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
1369 const char *name, int flags)
1370{
1371 int ret;
Greg Kurzdf4938a2017-02-26 23:43:00 +01001372 int dirfd;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301373
Greg Kurz7a954342017-05-05 14:48:08 +02001374 if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
1375 local_is_mapped_file_metadata(ctx, name)) {
1376 errno = EINVAL;
1377 return -1;
1378 }
1379
Greg Kurzdf4938a2017-02-26 23:43:00 +01001380 dirfd = local_opendir_nofollow(ctx, dir->data);
1381 if (dirfd == -1) {
1382 return -1;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301383 }
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301384
Greg Kurzdf4938a2017-02-26 23:43:00 +01001385 ret = local_unlinkat_common(ctx, dirfd, name, flags);
1386 close_preserve_errno(dirfd);
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301387 return ret;
1388}
Aneesh Kumar K.V9ed3ef22010-08-26 11:15:23 +05301389
Keno Fischer5b7b2f92018-06-07 12:17:22 +02001390#ifdef FS_IOC_GETVERSION
Harsh Prateek Borae06a7652011-10-12 19:11:25 +05301391static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
1392 mode_t st_mode, uint64_t *st_gen)
1393{
Kirill A. Shutemov0e5fc992014-01-28 17:08:24 +02001394 int err;
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +05301395 V9fsFidOpenState fid_open;
1396
Harsh Prateek Borae06a7652011-10-12 19:11:25 +05301397 /*
1398 * Do not try to open special files like device nodes, fifos etc
1399 * We can get fd for regular files and directories only
1400 */
1401 if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
Kirill A. Shutemov1a9978a2014-01-28 17:08:26 +02001402 errno = ENOTTY;
1403 return -1;
Harsh Prateek Borae06a7652011-10-12 19:11:25 +05301404 }
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +05301405 err = local_open(ctx, path, O_RDONLY, &fid_open);
1406 if (err < 0) {
1407 return err;
Harsh Prateek Borae06a7652011-10-12 19:11:25 +05301408 }
Aneesh Kumar K.Vcc720dd2011-10-25 12:10:40 +05301409 err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
1410 local_close(ctx, &fid_open);
Harsh Prateek Borae06a7652011-10-12 19:11:25 +05301411 return err;
1412}
Keno Fischer5b7b2f92018-06-07 12:17:22 +02001413#endif
Harsh Prateek Borae06a7652011-10-12 19:11:25 +05301414
Keno Fischer5b7b2f92018-06-07 12:17:22 +02001415static int local_ioc_getversion_init(FsContext *ctx, LocalData *data, Error **errp)
Aneesh Kumar K.V0174fe72011-08-02 11:35:54 +05301416{
Greg Kurz00c90bd2017-02-26 23:41:48 +01001417#ifdef FS_IOC_GETVERSION
Keno Fischer5b7b2f92018-06-07 12:17:22 +02001418 struct statfs stbuf;
1419
Greg Kurz00c90bd2017-02-26 23:41:48 +01001420 /*
Michael Tokarev28cbbdd2023-07-14 14:13:50 +03001421 * use ioc_getversion only if the ioctl is defined
Greg Kurz00c90bd2017-02-26 23:41:48 +01001422 */
Greg Kurz0e35a372017-02-26 23:42:10 +01001423 if (fstatfs(data->mountfd, &stbuf) < 0) {
Keno Fischer23062712018-06-07 12:17:21 +02001424 error_setg_errno(errp, errno,
Keno Fischer5b7b2f92018-06-07 12:17:22 +02001425 "failed to stat file system at '%s'", ctx->fs_root);
1426 return -1;
Greg Kurz00c90bd2017-02-26 23:41:48 +01001427 }
1428 switch (stbuf.f_type) {
1429 case EXT2_SUPER_MAGIC:
1430 case BTRFS_SUPER_MAGIC:
1431 case REISERFS_SUPER_MAGIC:
1432 case XFS_SUPER_MAGIC:
1433 ctx->exops.get_st_gen = local_ioc_getversion;
1434 break;
1435 }
1436#endif
Keno Fischer5b7b2f92018-06-07 12:17:22 +02001437 return 0;
1438}
1439
1440static int local_init(FsContext *ctx, Error **errp)
1441{
1442 LocalData *data = g_malloc(sizeof(*data));
1443
1444 data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
1445 if (data->mountfd == -1) {
1446 error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root);
1447 goto err;
1448 }
1449
1450 if (local_ioc_getversion_init(ctx, data, errp) < 0) {
1451 close(data->mountfd);
1452 goto err;
1453 }
Harsh Prateek Borae06a7652011-10-12 19:11:25 +05301454
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301455 if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
1456 ctx->xops = passthrough_xattr_ops;
1457 } else if (ctx->export_flags & V9FS_SM_MAPPED) {
1458 ctx->xops = mapped_xattr_ops;
1459 } else if (ctx->export_flags & V9FS_SM_NONE) {
1460 ctx->xops = none_xattr_ops;
1461 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1462 /*
1463 * xattr operation for mapped-file and passthrough
1464 * remain same.
1465 */
1466 ctx->xops = passthrough_xattr_ops;
1467 }
Aneesh Kumar K.Vc98f1d42011-10-12 20:59:18 +05301468 ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
Greg Kurz00c90bd2017-02-26 23:41:48 +01001469
Greg Kurz0e35a372017-02-26 23:42:10 +01001470 ctx->private = data;
Greg Kurz00c90bd2017-02-26 23:41:48 +01001471 return 0;
Greg Kurz0e35a372017-02-26 23:42:10 +01001472
1473err:
1474 g_free(data);
1475 return -1;
1476}
1477
1478static void local_cleanup(FsContext *ctx)
1479{
1480 LocalData *data = ctx->private;
1481
Greg Kurzc0da0cb2019-10-10 11:36:04 +02001482 if (!data) {
1483 return;
1484 }
1485
Greg Kurz0e35a372017-02-26 23:42:10 +01001486 close(data->mountfd);
1487 g_free(data);
Aneesh Kumar K.V0174fe72011-08-02 11:35:54 +05301488}
1489
Vladimir Sementsov-Ogievskiy4c5ec472019-12-05 20:46:22 +03001490static void error_append_security_model_hint(Error *const *errp)
Greg Kurz91cda4e2018-01-08 11:18:23 +01001491{
1492 error_append_hint(errp, "Valid options are: security_model="
1493 "[passthrough|mapped-xattr|mapped-file|none]\n");
1494}
1495
1496static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301497{
Vladimir Sementsov-Ogievskiy92c45122020-07-07 18:50:35 +02001498 ERRP_GUARD();
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301499 const char *sec_model = qemu_opt_get(opts, "security_model");
1500 const char *path = qemu_opt_get(opts, "path");
Antonios Motakis1a6ed332019-10-10 11:36:05 +02001501 const char *multidevs = qemu_opt_get(opts, "multidevs");
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301502
1503 if (!sec_model) {
Greg Kurz91cda4e2018-01-08 11:18:23 +01001504 error_setg(errp, "security_model property not set");
1505 error_append_security_model_hint(errp);
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301506 return -1;
1507 }
1508
1509 if (!strcmp(sec_model, "passthrough")) {
1510 fse->export_flags |= V9FS_SM_PASSTHROUGH;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301511 } else if (!strcmp(sec_model, "mapped") ||
1512 !strcmp(sec_model, "mapped-xattr")) {
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301513 fse->export_flags |= V9FS_SM_MAPPED;
1514 } else if (!strcmp(sec_model, "none")) {
1515 fse->export_flags |= V9FS_SM_NONE;
Aneesh Kumar K.V2c30dd72012-01-19 12:21:11 +05301516 } else if (!strcmp(sec_model, "mapped-file")) {
1517 fse->export_flags |= V9FS_SM_MAPPED_FILE;
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301518 } else {
Greg Kurz91cda4e2018-01-08 11:18:23 +01001519 error_setg(errp, "invalid security_model property '%s'", sec_model);
1520 error_append_security_model_hint(errp);
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301521 return -1;
1522 }
1523
Antonios Motakis1a6ed332019-10-10 11:36:05 +02001524 if (multidevs) {
1525 if (!strcmp(multidevs, "remap")) {
1526 fse->export_flags &= ~V9FS_FORBID_MULTIDEVS;
1527 fse->export_flags |= V9FS_REMAP_INODES;
1528 } else if (!strcmp(multidevs, "forbid")) {
1529 fse->export_flags &= ~V9FS_REMAP_INODES;
1530 fse->export_flags |= V9FS_FORBID_MULTIDEVS;
1531 } else if (!strcmp(multidevs, "warn")) {
1532 fse->export_flags &= ~V9FS_FORBID_MULTIDEVS;
1533 fse->export_flags &= ~V9FS_REMAP_INODES;
1534 } else {
Vladimir Sementsov-Ogievskiy92c45122020-07-07 18:50:35 +02001535 error_setg(errp, "invalid multidevs property '%s'",
Antonios Motakis1a6ed332019-10-10 11:36:05 +02001536 multidevs);
Vladimir Sementsov-Ogievskiy92c45122020-07-07 18:50:35 +02001537 error_append_hint(errp, "Valid options are: multidevs="
Antonios Motakis1a6ed332019-10-10 11:36:05 +02001538 "[remap|forbid|warn]\n");
Antonios Motakis1a6ed332019-10-10 11:36:05 +02001539 return -1;
1540 }
1541 }
1542
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301543 if (!path) {
Greg Kurz91cda4e2018-01-08 11:18:23 +01001544 error_setg(errp, "path property not set");
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301545 return -1;
1546 }
Pradeep Jagadeeshb8bbdb82017-02-28 10:31:46 +01001547
Vladimir Sementsov-Ogievskiy92c45122020-07-07 18:50:35 +02001548 if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) {
1549 error_prepend(errp, "invalid throttle configuration: ");
Pradeep Jagadeeshb8bbdb82017-02-28 10:31:46 +01001550 return -1;
1551 }
1552
Tobias Schrammb96feb22017-06-29 15:11:50 +02001553 if (fse->export_flags & V9FS_SM_MAPPED ||
1554 fse->export_flags & V9FS_SM_MAPPED_FILE) {
1555 fse->fmode =
1556 qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777;
1557 fse->dmode =
1558 qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777;
1559 } else {
1560 if (qemu_opt_find(opts, "fmode")) {
Greg Kurz91cda4e2018-01-08 11:18:23 +01001561 error_setg(errp, "fmode is only valid for mapped security modes");
Tobias Schrammb96feb22017-06-29 15:11:50 +02001562 return -1;
1563 }
1564 if (qemu_opt_find(opts, "dmode")) {
Greg Kurz91cda4e2018-01-08 11:18:23 +01001565 error_setg(errp, "dmode is only valid for mapped security modes");
Tobias Schrammb96feb22017-06-29 15:11:50 +02001566 return -1;
1567 }
1568 }
1569
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301570 fse->path = g_strdup(path);
1571
1572 return 0;
1573}
1574
Anthony Liguori9f107512010-04-29 17:44:44 +05301575FileOperations local_ops = {
Aneesh Kumar K.V99519f02011-12-14 13:48:59 +05301576 .parse_opts = local_parse_opts,
Aneesh Kumar K.V0174fe72011-08-02 11:35:54 +05301577 .init = local_init,
Greg Kurz0e35a372017-02-26 23:42:10 +01001578 .cleanup = local_cleanup,
Anthony Liguori131dcb22010-04-29 17:44:47 +05301579 .lstat = local_lstat,
Anthony Liguori131dcb22010-04-29 17:44:47 +05301580 .readlink = local_readlink,
1581 .close = local_close,
1582 .closedir = local_closedir,
Anthony Liguoria6568fe2010-04-29 17:44:55 +05301583 .open = local_open,
1584 .opendir = local_opendir,
Anthony Liguoria9231552010-04-29 17:44:56 +05301585 .rewinddir = local_rewinddir,
1586 .telldir = local_telldir,
Greg Kurz635324e2016-06-06 11:52:34 +02001587 .readdir = local_readdir,
Anthony Liguoria9231552010-04-29 17:44:56 +05301588 .seekdir = local_seekdir,
Sanchit Garg56d15a52010-10-08 11:30:16 +05301589 .preadv = local_preadv,
1590 .pwritev = local_pwritev,
Anthony Liguoric494dd62010-04-29 17:44:59 +05301591 .chmod = local_chmod,
1592 .mknod = local_mknod,
Anthony Liguoric494dd62010-04-29 17:44:59 +05301593 .mkdir = local_mkdir,
1594 .fstat = local_fstat,
1595 .open2 = local_open2,
1596 .symlink = local_symlink,
1597 .link = local_link,
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301598 .truncate = local_truncate,
1599 .rename = local_rename,
1600 .chown = local_chown,
M. Mohan Kumar74bc02b2010-06-09 19:14:38 +05301601 .utimensat = local_utimensat,
Anthony Liguori5bae1902010-04-29 17:45:01 +05301602 .remove = local_remove,
Anthony Liguori8cf89e02010-04-29 17:45:00 +05301603 .fsync = local_fsync,
M. Mohan Kumarbe940c82010-05-10 12:11:03 +05301604 .statfs = local_statfs,
Aneesh Kumar K.Vfa32ef82010-09-02 11:09:06 +05301605 .lgetxattr = local_lgetxattr,
1606 .llistxattr = local_llistxattr,
Aneesh Kumar K.V10b468b2010-09-02 11:09:07 +05301607 .lsetxattr = local_lsetxattr,
Aneesh Kumar K.V9ed3ef22010-08-26 11:15:23 +05301608 .lremovexattr = local_lremovexattr,
Aneesh Kumar K.V2289be12011-09-09 15:14:18 +05301609 .name_to_path = local_name_to_path,
1610 .renameat = local_renameat,
1611 .unlinkat = local_unlinkat,
Anthony Liguori9f107512010-04-29 17:44:44 +05301612};