]>
Commit | Line | Data |
---|---|---|
9f107513 | 1 | /* |
f00d4f59 | 2 | * 9p Posix callback |
9f107513 AL |
3 | * |
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. | |
11 | * | |
12 | */ | |
873c3213 | 13 | |
fbc04127 | 14 | #include "qemu/osdep.h" |
ebe74f8b | 15 | #include "9p.h" |
996a0d76 | 16 | #include "9p-local.h" |
267ae092 | 17 | #include "9p-xattr.h" |
0e35a378 | 18 | #include "9p-util.h" |
69b15212 | 19 | #include "fsdev/qemu-fsdev.h" /* local_ops */ |
c494dd6f | 20 | #include <arpa/inet.h> |
131dcb25 AL |
21 | #include <pwd.h> |
22 | #include <grp.h> | |
c494dd6f AL |
23 | #include <sys/socket.h> |
24 | #include <sys/un.h> | |
1de7afc9 | 25 | #include "qemu/xattr.h" |
f348b6d1 | 26 | #include "qemu/cutils.h" |
63325b18 | 27 | #include "qemu/error-report.h" |
2c30dd74 | 28 | #include <libgen.h> |
e06a765e HPB |
29 | #include <linux/fs.h> |
30 | #ifdef CONFIG_LINUX_MAGIC_H | |
31 | #include <linux/magic.h> | |
32 | #endif | |
33 | #include <sys/ioctl.h> | |
34 | ||
35 | #ifndef XFS_SUPER_MAGIC | |
36 | #define XFS_SUPER_MAGIC 0x58465342 | |
37 | #endif | |
38 | #ifndef EXT2_SUPER_MAGIC | |
39 | #define EXT2_SUPER_MAGIC 0xEF53 | |
40 | #endif | |
41 | #ifndef REISERFS_SUPER_MAGIC | |
42 | #define REISERFS_SUPER_MAGIC 0x52654973 | |
43 | #endif | |
44 | #ifndef BTRFS_SUPER_MAGIC | |
45 | #define BTRFS_SUPER_MAGIC 0x9123683E | |
46 | #endif | |
131dcb25 | 47 | |
0e35a378 GK |
48 | typedef struct { |
49 | int mountfd; | |
50 | } LocalData; | |
51 | ||
996a0d76 GK |
52 | int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, |
53 | mode_t mode) | |
54 | { | |
55 | LocalData *data = fs_ctx->private; | |
56 | ||
57 | /* All paths are relative to the path data->mountfd points to */ | |
58 | while (*path == '/') { | |
59 | path++; | |
60 | } | |
61 | ||
62 | return relative_openat_nofollow(data->mountfd, path, flags, mode); | |
63 | } | |
64 | ||
65 | int local_opendir_nofollow(FsContext *fs_ctx, const char *path) | |
66 | { | |
67 | return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0); | |
68 | } | |
69 | ||
99f2cf4b GK |
70 | static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd, |
71 | const char *npath) | |
72 | { | |
73 | int serrno = errno; | |
74 | renameat(odirfd, opath, ndirfd, npath); | |
75 | errno = serrno; | |
76 | } | |
77 | ||
ad0b46e6 GK |
78 | static void unlinkat_preserve_errno(int dirfd, const char *path, int flags) |
79 | { | |
80 | int serrno = errno; | |
81 | unlinkat(dirfd, path, flags); | |
82 | errno = serrno; | |
83 | } | |
84 | ||
2c30dd74 AK |
85 | #define VIRTFS_META_DIR ".virtfs_metadata" |
86 | ||
4fa4ce71 | 87 | static char *local_mapped_attr_path(FsContext *ctx, const char *path) |
2c30dd74 | 88 | { |
1b6f85e2 MT |
89 | int dirlen; |
90 | const char *name = strrchr(path, '/'); | |
91 | if (name) { | |
92 | dirlen = name - path; | |
93 | ++name; | |
94 | } else { | |
95 | name = path; | |
96 | dirlen = 0; | |
97 | } | |
98 | return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root, | |
99 | dirlen, path, VIRTFS_META_DIR, name); | |
2c30dd74 AK |
100 | } |
101 | ||
0ceb092e AK |
102 | static FILE *local_fopen(const char *path, const char *mode) |
103 | { | |
104 | int fd, o_mode = 0; | |
105 | FILE *fp; | |
106 | int flags = O_NOFOLLOW; | |
107 | /* | |
108 | * only supports two modes | |
109 | */ | |
110 | if (mode[0] == 'r') { | |
111 | flags |= O_RDONLY; | |
112 | } else if (mode[0] == 'w') { | |
113 | flags |= O_WRONLY | O_TRUNC | O_CREAT; | |
114 | o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; | |
115 | } else { | |
116 | return NULL; | |
117 | } | |
118 | fd = open(path, flags, o_mode); | |
119 | if (fd == -1) { | |
120 | return NULL; | |
121 | } | |
122 | fp = fdopen(fd, mode); | |
123 | if (!fp) { | |
124 | close(fd); | |
125 | } | |
126 | return fp; | |
127 | } | |
128 | ||
f9aef99b GK |
129 | static FILE *local_fopenat(int dirfd, const char *name, const char *mode) |
130 | { | |
131 | int fd, o_mode = 0; | |
132 | FILE *fp; | |
133 | int flags; | |
134 | /* | |
135 | * only supports two modes | |
136 | */ | |
137 | if (mode[0] == 'r') { | |
138 | flags = O_RDONLY; | |
139 | } else if (mode[0] == 'w') { | |
140 | flags = O_WRONLY | O_TRUNC | O_CREAT; | |
141 | o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; | |
142 | } else { | |
143 | return NULL; | |
144 | } | |
145 | fd = openat_file(dirfd, name, flags, o_mode); | |
146 | if (fd == -1) { | |
147 | return NULL; | |
148 | } | |
149 | fp = fdopen(fd, mode); | |
150 | if (!fp) { | |
151 | close(fd); | |
152 | } | |
153 | return fp; | |
154 | } | |
155 | ||
2c30dd74 | 156 | #define ATTR_MAX 100 |
f9aef99b | 157 | static void local_mapped_file_attr(int dirfd, const char *name, |
2c30dd74 AK |
158 | struct stat *stbuf) |
159 | { | |
160 | FILE *fp; | |
161 | char buf[ATTR_MAX]; | |
f9aef99b GK |
162 | int map_dirfd; |
163 | ||
99f2cf4b | 164 | map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); |
f9aef99b GK |
165 | if (map_dirfd == -1) { |
166 | return; | |
167 | } | |
2c30dd74 | 168 | |
f9aef99b GK |
169 | fp = local_fopenat(map_dirfd, name, "r"); |
170 | close_preserve_errno(map_dirfd); | |
2c30dd74 AK |
171 | if (!fp) { |
172 | return; | |
173 | } | |
174 | memset(buf, 0, ATTR_MAX); | |
175 | while (fgets(buf, ATTR_MAX, fp)) { | |
176 | if (!strncmp(buf, "virtfs.uid", 10)) { | |
177 | stbuf->st_uid = atoi(buf+11); | |
178 | } else if (!strncmp(buf, "virtfs.gid", 10)) { | |
179 | stbuf->st_gid = atoi(buf+11); | |
180 | } else if (!strncmp(buf, "virtfs.mode", 11)) { | |
181 | stbuf->st_mode = atoi(buf+12); | |
182 | } else if (!strncmp(buf, "virtfs.rdev", 11)) { | |
183 | stbuf->st_rdev = atoi(buf+12); | |
184 | } | |
185 | memset(buf, 0, ATTR_MAX); | |
186 | } | |
187 | fclose(fp); | |
188 | } | |
189 | ||
2289be19 | 190 | static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) |
131dcb25 | 191 | { |
f9aef99b GK |
192 | int err = -1; |
193 | char *dirpath = g_path_get_dirname(fs_path->data); | |
194 | char *name = g_path_get_basename(fs_path->data); | |
195 | int dirfd; | |
196 | ||
197 | dirfd = local_opendir_nofollow(fs_ctx, dirpath); | |
198 | if (dirfd == -1) { | |
199 | goto out; | |
200 | } | |
2289be19 | 201 | |
f9aef99b | 202 | err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW); |
1237ad76 | 203 | if (err) { |
4fa4ce71 | 204 | goto err_out; |
1237ad76 | 205 | } |
b97400ca | 206 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
1237ad76 VJ |
207 | /* Actual credentials are part of extended attrs */ |
208 | uid_t tmp_uid; | |
209 | gid_t tmp_gid; | |
210 | mode_t tmp_mode; | |
211 | dev_t tmp_dev; | |
f9aef99b GK |
212 | |
213 | if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid, | |
214 | sizeof(uid_t)) > 0) { | |
f8ad4a89 | 215 | stbuf->st_uid = le32_to_cpu(tmp_uid); |
1237ad76 | 216 | } |
f9aef99b GK |
217 | if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid, |
218 | sizeof(gid_t)) > 0) { | |
f8ad4a89 | 219 | stbuf->st_gid = le32_to_cpu(tmp_gid); |
1237ad76 | 220 | } |
f9aef99b GK |
221 | if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode, |
222 | sizeof(mode_t)) > 0) { | |
f8ad4a89 | 223 | stbuf->st_mode = le32_to_cpu(tmp_mode); |
1237ad76 | 224 | } |
f9aef99b GK |
225 | if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev, |
226 | sizeof(dev_t)) > 0) { | |
f8ad4a89 | 227 | stbuf->st_rdev = le64_to_cpu(tmp_dev); |
1237ad76 | 228 | } |
2c30dd74 | 229 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
f9aef99b | 230 | local_mapped_file_attr(dirfd, name, stbuf); |
2c30dd74 | 231 | } |
4fa4ce71 CG |
232 | |
233 | err_out: | |
f9aef99b GK |
234 | close_preserve_errno(dirfd); |
235 | out: | |
236 | g_free(name); | |
237 | g_free(dirpath); | |
2c30dd74 AK |
238 | return err; |
239 | } | |
240 | ||
241 | static int local_create_mapped_attr_dir(FsContext *ctx, const char *path) | |
242 | { | |
243 | int err; | |
4fa4ce71 | 244 | char *attr_dir; |
d3f8e138 | 245 | char *tmp_path = g_strdup(path); |
2c30dd74 | 246 | |
4fa4ce71 | 247 | attr_dir = g_strdup_printf("%s/%s/%s", |
2c30dd74 AK |
248 | ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR); |
249 | ||
250 | err = mkdir(attr_dir, 0700); | |
251 | if (err < 0 && errno == EEXIST) { | |
252 | err = 0; | |
1237ad76 | 253 | } |
4fa4ce71 | 254 | g_free(attr_dir); |
d3f8e138 | 255 | g_free(tmp_path); |
1237ad76 | 256 | return err; |
131dcb25 AL |
257 | } |
258 | ||
2c30dd74 AK |
259 | static int local_set_mapped_file_attr(FsContext *ctx, |
260 | const char *path, FsCred *credp) | |
261 | { | |
262 | FILE *fp; | |
263 | int ret = 0; | |
264 | char buf[ATTR_MAX]; | |
4fa4ce71 | 265 | char *attr_path; |
2c30dd74 AK |
266 | int uid = -1, gid = -1, mode = -1, rdev = -1; |
267 | ||
4fa4ce71 CG |
268 | attr_path = local_mapped_attr_path(ctx, path); |
269 | fp = local_fopen(attr_path, "r"); | |
2c30dd74 AK |
270 | if (!fp) { |
271 | goto create_map_file; | |
272 | } | |
273 | memset(buf, 0, ATTR_MAX); | |
274 | while (fgets(buf, ATTR_MAX, fp)) { | |
275 | if (!strncmp(buf, "virtfs.uid", 10)) { | |
276 | uid = atoi(buf+11); | |
277 | } else if (!strncmp(buf, "virtfs.gid", 10)) { | |
278 | gid = atoi(buf+11); | |
279 | } else if (!strncmp(buf, "virtfs.mode", 11)) { | |
280 | mode = atoi(buf+12); | |
281 | } else if (!strncmp(buf, "virtfs.rdev", 11)) { | |
282 | rdev = atoi(buf+12); | |
283 | } | |
284 | memset(buf, 0, ATTR_MAX); | |
285 | } | |
286 | fclose(fp); | |
287 | goto update_map_file; | |
288 | ||
289 | create_map_file: | |
290 | ret = local_create_mapped_attr_dir(ctx, path); | |
291 | if (ret < 0) { | |
292 | goto err_out; | |
293 | } | |
294 | ||
295 | update_map_file: | |
0ceb092e | 296 | fp = local_fopen(attr_path, "w"); |
2c30dd74 AK |
297 | if (!fp) { |
298 | ret = -1; | |
299 | goto err_out; | |
300 | } | |
301 | ||
302 | if (credp->fc_uid != -1) { | |
303 | uid = credp->fc_uid; | |
304 | } | |
305 | if (credp->fc_gid != -1) { | |
306 | gid = credp->fc_gid; | |
307 | } | |
308 | if (credp->fc_mode != -1) { | |
309 | mode = credp->fc_mode; | |
310 | } | |
311 | if (credp->fc_rdev != -1) { | |
312 | rdev = credp->fc_rdev; | |
313 | } | |
314 | ||
315 | ||
316 | if (uid != -1) { | |
317 | fprintf(fp, "virtfs.uid=%d\n", uid); | |
318 | } | |
319 | if (gid != -1) { | |
320 | fprintf(fp, "virtfs.gid=%d\n", gid); | |
321 | } | |
322 | if (mode != -1) { | |
323 | fprintf(fp, "virtfs.mode=%d\n", mode); | |
324 | } | |
325 | if (rdev != -1) { | |
326 | fprintf(fp, "virtfs.rdev=%d\n", rdev); | |
327 | } | |
328 | fclose(fp); | |
329 | ||
330 | err_out: | |
4fa4ce71 | 331 | g_free(attr_path); |
2c30dd74 AK |
332 | return ret; |
333 | } | |
334 | ||
758e8e38 | 335 | static int local_set_xattr(const char *path, FsCred *credp) |
131dcb25 | 336 | { |
758e8e38 | 337 | int err; |
2289be19 | 338 | |
758e8e38 | 339 | if (credp->fc_uid != -1) { |
f8ad4a89 AK |
340 | uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); |
341 | err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0); | |
758e8e38 VJ |
342 | if (err) { |
343 | return err; | |
344 | } | |
131dcb25 | 345 | } |
758e8e38 | 346 | if (credp->fc_gid != -1) { |
f8ad4a89 AK |
347 | uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); |
348 | err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0); | |
758e8e38 VJ |
349 | if (err) { |
350 | return err; | |
351 | } | |
131dcb25 | 352 | } |
758e8e38 | 353 | if (credp->fc_mode != -1) { |
f8ad4a89 AK |
354 | uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); |
355 | err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0); | |
758e8e38 VJ |
356 | if (err) { |
357 | return err; | |
358 | } | |
131dcb25 | 359 | } |
758e8e38 | 360 | if (credp->fc_rdev != -1) { |
f8ad4a89 AK |
361 | uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); |
362 | err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0); | |
758e8e38 VJ |
363 | if (err) { |
364 | return err; | |
365 | } | |
131dcb25 | 366 | } |
131dcb25 AL |
367 | return 0; |
368 | } | |
369 | ||
4750a96f | 370 | static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, |
2289be19 | 371 | FsCred *credp) |
4750a96f | 372 | { |
4fa4ce71 | 373 | char *buffer; |
2289be19 | 374 | |
4fa4ce71 CG |
375 | buffer = rpath(fs_ctx, path); |
376 | if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) { | |
12848bfc AK |
377 | /* |
378 | * If we fail to change ownership and if we are | |
379 | * using security model none. Ignore the error | |
380 | */ | |
b97400ca | 381 | if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { |
4fa4ce71 | 382 | goto err; |
12848bfc | 383 | } |
4750a96f | 384 | } |
2d40564a | 385 | |
4fa4ce71 CG |
386 | if (chmod(buffer, credp->fc_mode & 07777) < 0) { |
387 | goto err; | |
2d40564a | 388 | } |
4fa4ce71 CG |
389 | |
390 | g_free(buffer); | |
4750a96f | 391 | return 0; |
4fa4ce71 CG |
392 | err: |
393 | g_free(buffer); | |
394 | return -1; | |
4750a96f VJ |
395 | } |
396 | ||
2289be19 AK |
397 | static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, |
398 | char *buf, size_t bufsz) | |
131dcb25 | 399 | { |
879c2813 | 400 | ssize_t tsize = -1; |
2289be19 | 401 | |
2c30dd74 AK |
402 | if ((fs_ctx->export_flags & V9FS_SM_MAPPED) || |
403 | (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) { | |
879c2813 | 404 | int fd; |
bec1e954 GK |
405 | |
406 | fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0); | |
879c2813 VJ |
407 | if (fd == -1) { |
408 | return -1; | |
409 | } | |
410 | do { | |
411 | tsize = read(fd, (void *)buf, bufsz); | |
412 | } while (tsize == -1 && errno == EINTR); | |
bec1e954 | 413 | close_preserve_errno(fd); |
b97400ca AK |
414 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
415 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
bec1e954 GK |
416 | char *dirpath = g_path_get_dirname(fs_path->data); |
417 | char *name = g_path_get_basename(fs_path->data); | |
418 | int dirfd; | |
419 | ||
420 | dirfd = local_opendir_nofollow(fs_ctx, dirpath); | |
421 | if (dirfd == -1) { | |
422 | goto out; | |
423 | } | |
424 | ||
425 | tsize = readlinkat(dirfd, name, buf, bufsz); | |
426 | close_preserve_errno(dirfd); | |
427 | out: | |
428 | g_free(name); | |
429 | g_free(dirpath); | |
879c2813 VJ |
430 | } |
431 | return tsize; | |
131dcb25 AL |
432 | } |
433 | ||
cc720ddb | 434 | static int local_close(FsContext *ctx, V9fsFidOpenState *fs) |
131dcb25 | 435 | { |
cc720ddb | 436 | return close(fs->fd); |
131dcb25 AL |
437 | } |
438 | ||
cc720ddb | 439 | static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) |
131dcb25 | 440 | { |
f314ea4e | 441 | return closedir(fs->dir.stream); |
131dcb25 | 442 | } |
9f107513 | 443 | |
cc720ddb AK |
444 | static int local_open(FsContext *ctx, V9fsPath *fs_path, |
445 | int flags, V9fsFidOpenState *fs) | |
a6568fe2 | 446 | { |
21328e1e | 447 | int fd; |
2289be19 | 448 | |
996a0d76 | 449 | fd = local_open_nofollow(ctx, fs_path->data, flags, 0); |
21328e1e GK |
450 | if (fd == -1) { |
451 | return -1; | |
452 | } | |
453 | fs->fd = fd; | |
cc720ddb | 454 | return fs->fd; |
a6568fe2 AL |
455 | } |
456 | ||
cc720ddb AK |
457 | static int local_opendir(FsContext *ctx, |
458 | V9fsPath *fs_path, V9fsFidOpenState *fs) | |
a6568fe2 | 459 | { |
996a0d76 | 460 | int dirfd; |
21328e1e | 461 | DIR *stream; |
2289be19 | 462 | |
996a0d76 GK |
463 | dirfd = local_opendir_nofollow(ctx, fs_path->data); |
464 | if (dirfd == -1) { | |
465 | return -1; | |
466 | } | |
467 | ||
468 | stream = fdopendir(dirfd); | |
21328e1e | 469 | if (!stream) { |
cc720ddb AK |
470 | return -1; |
471 | } | |
21328e1e | 472 | fs->dir.stream = stream; |
cc720ddb | 473 | return 0; |
a6568fe2 AL |
474 | } |
475 | ||
cc720ddb | 476 | static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) |
a9231555 | 477 | { |
f314ea4e | 478 | rewinddir(fs->dir.stream); |
a9231555 AL |
479 | } |
480 | ||
cc720ddb | 481 | static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) |
a9231555 | 482 | { |
f314ea4e | 483 | return telldir(fs->dir.stream); |
a9231555 AL |
484 | } |
485 | ||
635324e8 | 486 | static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) |
a9231555 | 487 | { |
635324e8 | 488 | struct dirent *entry; |
2c30dd74 AK |
489 | |
490 | again: | |
635324e8 GK |
491 | entry = readdir(fs->dir.stream); |
492 | if (!entry) { | |
493 | return NULL; | |
494 | } | |
495 | ||
840a1bf2 BB |
496 | if (ctx->export_flags & V9FS_SM_MAPPED) { |
497 | entry->d_type = DT_UNKNOWN; | |
498 | } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { | |
635324e8 | 499 | if (!strcmp(entry->d_name, VIRTFS_META_DIR)) { |
2c30dd74 AK |
500 | /* skp the meta data directory */ |
501 | goto again; | |
502 | } | |
840a1bf2 | 503 | entry->d_type = DT_UNKNOWN; |
2c30dd74 | 504 | } |
635324e8 GK |
505 | |
506 | return entry; | |
a9231555 AL |
507 | } |
508 | ||
cc720ddb | 509 | static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) |
a9231555 | 510 | { |
f314ea4e | 511 | seekdir(fs->dir.stream, off); |
a9231555 AL |
512 | } |
513 | ||
cc720ddb AK |
514 | static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, |
515 | const struct iovec *iov, | |
56d15a53 | 516 | int iovcnt, off_t offset) |
a9231555 | 517 | { |
56d15a53 | 518 | #ifdef CONFIG_PREADV |
cc720ddb | 519 | return preadv(fs->fd, iov, iovcnt, offset); |
56d15a53 | 520 | #else |
cc720ddb | 521 | int err = lseek(fs->fd, offset, SEEK_SET); |
56d15a53 SG |
522 | if (err == -1) { |
523 | return err; | |
524 | } else { | |
cc720ddb | 525 | return readv(fs->fd, iov, iovcnt); |
56d15a53 SG |
526 | } |
527 | #endif | |
a9231555 AL |
528 | } |
529 | ||
cc720ddb AK |
530 | static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, |
531 | const struct iovec *iov, | |
2289be19 | 532 | int iovcnt, off_t offset) |
8449360c | 533 | { |
6fe76acc | 534 | ssize_t ret; |
56d15a53 | 535 | #ifdef CONFIG_PREADV |
cc720ddb | 536 | ret = pwritev(fs->fd, iov, iovcnt, offset); |
56d15a53 | 537 | #else |
cc720ddb | 538 | int err = lseek(fs->fd, offset, SEEK_SET); |
56d15a53 SG |
539 | if (err == -1) { |
540 | return err; | |
541 | } else { | |
cc720ddb | 542 | ret = writev(fs->fd, iov, iovcnt); |
56d15a53 SG |
543 | } |
544 | #endif | |
d3ab98e6 AK |
545 | #ifdef CONFIG_SYNC_FILE_RANGE |
546 | if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { | |
547 | /* | |
548 | * Initiate a writeback. This is not a data integrity sync. | |
549 | * We want to ensure that we don't leave dirty pages in the cache | |
550 | * after write when writeout=immediate is sepcified. | |
551 | */ | |
cc720ddb | 552 | sync_file_range(fs->fd, offset, ret, |
d3ab98e6 AK |
553 | SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); |
554 | } | |
555 | #endif | |
556 | return ret; | |
8449360c AL |
557 | } |
558 | ||
2289be19 | 559 | static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) |
c494dd6f | 560 | { |
4fa4ce71 CG |
561 | char *buffer; |
562 | int ret = -1; | |
2289be19 AK |
563 | char *path = fs_path->data; |
564 | ||
b97400ca | 565 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
4fa4ce71 CG |
566 | buffer = rpath(fs_ctx, path); |
567 | ret = local_set_xattr(buffer, credp); | |
568 | g_free(buffer); | |
2c30dd74 AK |
569 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
570 | return local_set_mapped_file_attr(fs_ctx, path, credp); | |
b97400ca AK |
571 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
572 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
4fa4ce71 CG |
573 | buffer = rpath(fs_ctx, path); |
574 | ret = chmod(buffer, credp->fc_mode); | |
575 | g_free(buffer); | |
e95ead32 | 576 | } |
4fa4ce71 | 577 | return ret; |
c494dd6f AL |
578 | } |
579 | ||
2289be19 AK |
580 | static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, |
581 | const char *name, FsCred *credp) | |
c494dd6f | 582 | { |
2289be19 | 583 | char *path; |
1c293312 VJ |
584 | int err = -1; |
585 | int serrno = 0; | |
2289be19 | 586 | V9fsString fullname; |
4ed7b2c3 | 587 | char *buffer = NULL; |
1c293312 | 588 | |
2289be19 AK |
589 | v9fs_string_init(&fullname); |
590 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
591 | path = fullname.data; | |
592 | ||
1c293312 | 593 | /* Determine the security model */ |
b97400ca | 594 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
4fa4ce71 CG |
595 | buffer = rpath(fs_ctx, path); |
596 | err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); | |
1c293312 | 597 | if (err == -1) { |
2289be19 | 598 | goto out; |
1c293312 | 599 | } |
4fa4ce71 | 600 | err = local_set_xattr(buffer, credp); |
1c293312 VJ |
601 | if (err == -1) { |
602 | serrno = errno; | |
603 | goto err_end; | |
604 | } | |
2c30dd74 AK |
605 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
606 | ||
4fa4ce71 CG |
607 | buffer = rpath(fs_ctx, path); |
608 | err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); | |
2c30dd74 AK |
609 | if (err == -1) { |
610 | goto out; | |
611 | } | |
612 | err = local_set_mapped_file_attr(fs_ctx, path, credp); | |
613 | if (err == -1) { | |
614 | serrno = errno; | |
615 | goto err_end; | |
616 | } | |
b97400ca AK |
617 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
618 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
4fa4ce71 CG |
619 | buffer = rpath(fs_ctx, path); |
620 | err = mknod(buffer, credp->fc_mode, credp->fc_rdev); | |
1c293312 | 621 | if (err == -1) { |
2289be19 | 622 | goto out; |
1c293312 VJ |
623 | } |
624 | err = local_post_create_passthrough(fs_ctx, path, credp); | |
625 | if (err == -1) { | |
626 | serrno = errno; | |
627 | goto err_end; | |
628 | } | |
629 | } | |
2289be19 | 630 | goto out; |
1c293312 VJ |
631 | |
632 | err_end: | |
4fa4ce71 | 633 | remove(buffer); |
1c293312 | 634 | errno = serrno; |
2289be19 | 635 | out: |
4ed7b2c3 | 636 | g_free(buffer); |
2289be19 | 637 | v9fs_string_free(&fullname); |
1c293312 | 638 | return err; |
c494dd6f AL |
639 | } |
640 | ||
2289be19 AK |
641 | static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, |
642 | const char *name, FsCred *credp) | |
c494dd6f | 643 | { |
2289be19 | 644 | char *path; |
00ec5c37 VJ |
645 | int err = -1; |
646 | int serrno = 0; | |
2289be19 | 647 | V9fsString fullname; |
4ed7b2c3 | 648 | char *buffer = NULL; |
00ec5c37 | 649 | |
2289be19 AK |
650 | v9fs_string_init(&fullname); |
651 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
652 | path = fullname.data; | |
653 | ||
00ec5c37 | 654 | /* Determine the security model */ |
b97400ca | 655 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
4fa4ce71 CG |
656 | buffer = rpath(fs_ctx, path); |
657 | err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); | |
00ec5c37 | 658 | if (err == -1) { |
2289be19 | 659 | goto out; |
00ec5c37 VJ |
660 | } |
661 | credp->fc_mode = credp->fc_mode|S_IFDIR; | |
4fa4ce71 | 662 | err = local_set_xattr(buffer, credp); |
00ec5c37 VJ |
663 | if (err == -1) { |
664 | serrno = errno; | |
665 | goto err_end; | |
666 | } | |
2c30dd74 | 667 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
4fa4ce71 CG |
668 | buffer = rpath(fs_ctx, path); |
669 | err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); | |
2c30dd74 AK |
670 | if (err == -1) { |
671 | goto out; | |
672 | } | |
673 | credp->fc_mode = credp->fc_mode|S_IFDIR; | |
674 | err = local_set_mapped_file_attr(fs_ctx, path, credp); | |
675 | if (err == -1) { | |
676 | serrno = errno; | |
677 | goto err_end; | |
678 | } | |
b97400ca AK |
679 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
680 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
4fa4ce71 CG |
681 | buffer = rpath(fs_ctx, path); |
682 | err = mkdir(buffer, credp->fc_mode); | |
00ec5c37 | 683 | if (err == -1) { |
2289be19 | 684 | goto out; |
00ec5c37 VJ |
685 | } |
686 | err = local_post_create_passthrough(fs_ctx, path, credp); | |
687 | if (err == -1) { | |
688 | serrno = errno; | |
689 | goto err_end; | |
690 | } | |
691 | } | |
2289be19 | 692 | goto out; |
00ec5c37 VJ |
693 | |
694 | err_end: | |
4fa4ce71 | 695 | remove(buffer); |
00ec5c37 | 696 | errno = serrno; |
2289be19 | 697 | out: |
4ed7b2c3 | 698 | g_free(buffer); |
2289be19 | 699 | v9fs_string_free(&fullname); |
00ec5c37 | 700 | return err; |
c494dd6f AL |
701 | } |
702 | ||
8b888272 | 703 | static int local_fstat(FsContext *fs_ctx, int fid_type, |
cc720ddb | 704 | V9fsFidOpenState *fs, struct stat *stbuf) |
c494dd6f | 705 | { |
8b888272 AK |
706 | int err, fd; |
707 | ||
708 | if (fid_type == P9_FID_DIR) { | |
f314ea4e | 709 | fd = dirfd(fs->dir.stream); |
8b888272 AK |
710 | } else { |
711 | fd = fs->fd; | |
712 | } | |
713 | ||
714 | err = fstat(fd, stbuf); | |
1237ad76 VJ |
715 | if (err) { |
716 | return err; | |
717 | } | |
b97400ca | 718 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
1237ad76 VJ |
719 | /* Actual credentials are part of extended attrs */ |
720 | uid_t tmp_uid; | |
721 | gid_t tmp_gid; | |
722 | mode_t tmp_mode; | |
723 | dev_t tmp_dev; | |
724 | ||
f8ad4a89 AK |
725 | if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { |
726 | stbuf->st_uid = le32_to_cpu(tmp_uid); | |
1237ad76 | 727 | } |
f8ad4a89 AK |
728 | if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { |
729 | stbuf->st_gid = le32_to_cpu(tmp_gid); | |
1237ad76 | 730 | } |
f8ad4a89 AK |
731 | if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) { |
732 | stbuf->st_mode = le32_to_cpu(tmp_mode); | |
1237ad76 | 733 | } |
f8ad4a89 AK |
734 | if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { |
735 | stbuf->st_rdev = le64_to_cpu(tmp_dev); | |
1237ad76 | 736 | } |
2c30dd74 AK |
737 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
738 | errno = EOPNOTSUPP; | |
739 | return -1; | |
1237ad76 VJ |
740 | } |
741 | return err; | |
c494dd6f AL |
742 | } |
743 | ||
2289be19 | 744 | static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, |
cc720ddb | 745 | int flags, FsCred *credp, V9fsFidOpenState *fs) |
c494dd6f | 746 | { |
2289be19 | 747 | char *path; |
4750a96f VJ |
748 | int fd = -1; |
749 | int err = -1; | |
750 | int serrno = 0; | |
2289be19 | 751 | V9fsString fullname; |
4ed7b2c3 | 752 | char *buffer = NULL; |
4750a96f | 753 | |
0ceb092e AK |
754 | /* |
755 | * Mark all the open to not follow symlinks | |
756 | */ | |
757 | flags |= O_NOFOLLOW; | |
758 | ||
2289be19 AK |
759 | v9fs_string_init(&fullname); |
760 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
761 | path = fullname.data; | |
762 | ||
4750a96f | 763 | /* Determine the security model */ |
b97400ca | 764 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
4fa4ce71 CG |
765 | buffer = rpath(fs_ctx, path); |
766 | fd = open(buffer, flags, SM_LOCAL_MODE_BITS); | |
4750a96f | 767 | if (fd == -1) { |
2289be19 AK |
768 | err = fd; |
769 | goto out; | |
4750a96f VJ |
770 | } |
771 | credp->fc_mode = credp->fc_mode|S_IFREG; | |
772 | /* Set cleint credentials in xattr */ | |
4fa4ce71 | 773 | err = local_set_xattr(buffer, credp); |
4750a96f VJ |
774 | if (err == -1) { |
775 | serrno = errno; | |
776 | goto err_end; | |
777 | } | |
2c30dd74 | 778 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
4fa4ce71 CG |
779 | buffer = rpath(fs_ctx, path); |
780 | fd = open(buffer, flags, SM_LOCAL_MODE_BITS); | |
2c30dd74 AK |
781 | if (fd == -1) { |
782 | err = fd; | |
783 | goto out; | |
784 | } | |
785 | credp->fc_mode = credp->fc_mode|S_IFREG; | |
786 | /* Set client credentials in .virtfs_metadata directory files */ | |
787 | err = local_set_mapped_file_attr(fs_ctx, path, credp); | |
788 | if (err == -1) { | |
789 | serrno = errno; | |
790 | goto err_end; | |
791 | } | |
b97400ca AK |
792 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
793 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
4fa4ce71 CG |
794 | buffer = rpath(fs_ctx, path); |
795 | fd = open(buffer, flags, credp->fc_mode); | |
4750a96f | 796 | if (fd == -1) { |
2289be19 AK |
797 | err = fd; |
798 | goto out; | |
4750a96f VJ |
799 | } |
800 | err = local_post_create_passthrough(fs_ctx, path, credp); | |
801 | if (err == -1) { | |
802 | serrno = errno; | |
803 | goto err_end; | |
804 | } | |
805 | } | |
2289be19 | 806 | err = fd; |
cc720ddb | 807 | fs->fd = fd; |
2289be19 | 808 | goto out; |
4750a96f VJ |
809 | |
810 | err_end: | |
811 | close(fd); | |
4fa4ce71 | 812 | remove(buffer); |
4750a96f | 813 | errno = serrno; |
2289be19 | 814 | out: |
4ed7b2c3 | 815 | g_free(buffer); |
2289be19 | 816 | v9fs_string_free(&fullname); |
4750a96f | 817 | return err; |
c494dd6f AL |
818 | } |
819 | ||
758e8e38 | 820 | |
879c2813 | 821 | static int local_symlink(FsContext *fs_ctx, const char *oldpath, |
2289be19 | 822 | V9fsPath *dir_path, const char *name, FsCred *credp) |
c494dd6f | 823 | { |
879c2813 VJ |
824 | int err = -1; |
825 | int serrno = 0; | |
2289be19 AK |
826 | char *newpath; |
827 | V9fsString fullname; | |
4ed7b2c3 | 828 | char *buffer = NULL; |
879c2813 | 829 | |
2289be19 AK |
830 | v9fs_string_init(&fullname); |
831 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
832 | newpath = fullname.data; | |
833 | ||
879c2813 | 834 | /* Determine the security model */ |
b97400ca | 835 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
879c2813 VJ |
836 | int fd; |
837 | ssize_t oldpath_size, write_size; | |
4fa4ce71 CG |
838 | buffer = rpath(fs_ctx, newpath); |
839 | fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); | |
879c2813 | 840 | if (fd == -1) { |
2289be19 AK |
841 | err = fd; |
842 | goto out; | |
879c2813 VJ |
843 | } |
844 | /* Write the oldpath (target) to the file. */ | |
f35bde2f | 845 | oldpath_size = strlen(oldpath); |
879c2813 VJ |
846 | do { |
847 | write_size = write(fd, (void *)oldpath, oldpath_size); | |
848 | } while (write_size == -1 && errno == EINTR); | |
849 | ||
850 | if (write_size != oldpath_size) { | |
851 | serrno = errno; | |
852 | close(fd); | |
853 | err = -1; | |
854 | goto err_end; | |
855 | } | |
856 | close(fd); | |
857 | /* Set cleint credentials in symlink's xattr */ | |
858 | credp->fc_mode = credp->fc_mode|S_IFLNK; | |
4fa4ce71 | 859 | err = local_set_xattr(buffer, credp); |
879c2813 VJ |
860 | if (err == -1) { |
861 | serrno = errno; | |
862 | goto err_end; | |
863 | } | |
2c30dd74 AK |
864 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
865 | int fd; | |
866 | ssize_t oldpath_size, write_size; | |
4fa4ce71 CG |
867 | buffer = rpath(fs_ctx, newpath); |
868 | fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); | |
2c30dd74 AK |
869 | if (fd == -1) { |
870 | err = fd; | |
871 | goto out; | |
872 | } | |
873 | /* Write the oldpath (target) to the file. */ | |
874 | oldpath_size = strlen(oldpath); | |
875 | do { | |
876 | write_size = write(fd, (void *)oldpath, oldpath_size); | |
877 | } while (write_size == -1 && errno == EINTR); | |
878 | ||
879 | if (write_size != oldpath_size) { | |
880 | serrno = errno; | |
881 | close(fd); | |
882 | err = -1; | |
883 | goto err_end; | |
884 | } | |
885 | close(fd); | |
886 | /* Set cleint credentials in symlink's xattr */ | |
887 | credp->fc_mode = credp->fc_mode|S_IFLNK; | |
888 | err = local_set_mapped_file_attr(fs_ctx, newpath, credp); | |
889 | if (err == -1) { | |
890 | serrno = errno; | |
891 | goto err_end; | |
892 | } | |
b97400ca AK |
893 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
894 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
4fa4ce71 CG |
895 | buffer = rpath(fs_ctx, newpath); |
896 | err = symlink(oldpath, buffer); | |
879c2813 | 897 | if (err) { |
2289be19 | 898 | goto out; |
879c2813 | 899 | } |
4fa4ce71 | 900 | err = lchown(buffer, credp->fc_uid, credp->fc_gid); |
879c2813 | 901 | if (err == -1) { |
12848bfc AK |
902 | /* |
903 | * If we fail to change ownership and if we are | |
904 | * using security model none. Ignore the error | |
905 | */ | |
b97400ca | 906 | if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { |
12848bfc AK |
907 | serrno = errno; |
908 | goto err_end; | |
909 | } else | |
910 | err = 0; | |
879c2813 VJ |
911 | } |
912 | } | |
2289be19 | 913 | goto out; |
879c2813 VJ |
914 | |
915 | err_end: | |
4fa4ce71 | 916 | remove(buffer); |
879c2813 | 917 | errno = serrno; |
2289be19 | 918 | out: |
4ed7b2c3 | 919 | g_free(buffer); |
2289be19 | 920 | v9fs_string_free(&fullname); |
879c2813 | 921 | return err; |
c494dd6f AL |
922 | } |
923 | ||
2289be19 AK |
924 | static int local_link(FsContext *ctx, V9fsPath *oldpath, |
925 | V9fsPath *dirpath, const char *name) | |
c494dd6f | 926 | { |
ad0b46e6 GK |
927 | char *odirpath = g_path_get_dirname(oldpath->data); |
928 | char *oname = g_path_get_basename(oldpath->data); | |
929 | int ret = -1; | |
930 | int odirfd, ndirfd; | |
931 | ||
932 | odirfd = local_opendir_nofollow(ctx, odirpath); | |
933 | if (odirfd == -1) { | |
934 | goto out; | |
935 | } | |
c494dd6f | 936 | |
ad0b46e6 GK |
937 | ndirfd = local_opendir_nofollow(ctx, dirpath->data); |
938 | if (ndirfd == -1) { | |
939 | close_preserve_errno(odirfd); | |
940 | goto out; | |
941 | } | |
2289be19 | 942 | |
ad0b46e6 | 943 | ret = linkat(odirfd, oname, ndirfd, name, 0); |
6dd4b1f1 | 944 | if (ret < 0) { |
ad0b46e6 | 945 | goto out_close; |
6dd4b1f1 | 946 | } |
2c30dd74 AK |
947 | |
948 | /* now link the virtfs_metadata files */ | |
6dd4b1f1 | 949 | if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
ad0b46e6 GK |
950 | int omap_dirfd, nmap_dirfd; |
951 | ||
952 | ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); | |
953 | if (ret < 0 && errno != EEXIST) { | |
954 | goto err_undo_link; | |
955 | } | |
956 | ||
957 | omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); | |
958 | if (omap_dirfd == -1) { | |
959 | goto err; | |
960 | } | |
961 | ||
962 | nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); | |
963 | if (nmap_dirfd == -1) { | |
964 | close_preserve_errno(omap_dirfd); | |
965 | goto err; | |
966 | } | |
967 | ||
968 | ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0); | |
969 | close_preserve_errno(nmap_dirfd); | |
970 | close_preserve_errno(omap_dirfd); | |
2c30dd74 | 971 | if (ret < 0 && errno != ENOENT) { |
ad0b46e6 | 972 | goto err_undo_link; |
2c30dd74 | 973 | } |
ad0b46e6 GK |
974 | |
975 | ret = 0; | |
2c30dd74 | 976 | } |
ad0b46e6 | 977 | goto out_close; |
6dd4b1f1 | 978 | |
ad0b46e6 GK |
979 | err: |
980 | ret = -1; | |
981 | err_undo_link: | |
982 | unlinkat_preserve_errno(ndirfd, name, 0); | |
983 | out_close: | |
984 | close_preserve_errno(ndirfd); | |
985 | close_preserve_errno(odirfd); | |
6dd4b1f1 | 986 | out: |
ad0b46e6 GK |
987 | g_free(oname); |
988 | g_free(odirpath); | |
2289be19 | 989 | return ret; |
c494dd6f AL |
990 | } |
991 | ||
2289be19 | 992 | static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) |
8cf89e00 | 993 | { |
ac125d99 | 994 | int fd, ret; |
2289be19 | 995 | |
ac125d99 GK |
996 | fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); |
997 | if (fd == -1) { | |
998 | return -1; | |
999 | } | |
1000 | ret = ftruncate(fd, size); | |
1001 | close_preserve_errno(fd); | |
4fa4ce71 | 1002 | return ret; |
8cf89e00 AL |
1003 | } |
1004 | ||
2289be19 | 1005 | static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) |
8cf89e00 | 1006 | { |
4fa4ce71 CG |
1007 | char *buffer; |
1008 | int ret = -1; | |
2289be19 AK |
1009 | char *path = fs_path->data; |
1010 | ||
c79ce737 | 1011 | if ((credp->fc_uid == -1 && credp->fc_gid == -1) || |
17b1971f AK |
1012 | (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
1013 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
4fa4ce71 CG |
1014 | buffer = rpath(fs_ctx, path); |
1015 | ret = lchown(buffer, credp->fc_uid, credp->fc_gid); | |
1016 | g_free(buffer); | |
b97400ca | 1017 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
4fa4ce71 CG |
1018 | buffer = rpath(fs_ctx, path); |
1019 | ret = local_set_xattr(buffer, credp); | |
1020 | g_free(buffer); | |
2c30dd74 AK |
1021 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
1022 | return local_set_mapped_file_attr(fs_ctx, path, credp); | |
f7613bee | 1023 | } |
4fa4ce71 | 1024 | return ret; |
8cf89e00 AL |
1025 | } |
1026 | ||
2289be19 | 1027 | static int local_utimensat(FsContext *s, V9fsPath *fs_path, |
38671423 | 1028 | const struct timespec *buf) |
8cf89e00 | 1029 | { |
a33eda0d GK |
1030 | char *dirpath = g_path_get_dirname(fs_path->data); |
1031 | char *name = g_path_get_basename(fs_path->data); | |
1032 | int dirfd, ret = -1; | |
2289be19 | 1033 | |
a33eda0d GK |
1034 | dirfd = local_opendir_nofollow(s, dirpath); |
1035 | if (dirfd == -1) { | |
1036 | goto out; | |
1037 | } | |
1038 | ||
1039 | ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW); | |
1040 | close_preserve_errno(dirfd); | |
1041 | out: | |
1042 | g_free(dirpath); | |
1043 | g_free(name); | |
4fa4ce71 | 1044 | return ret; |
8cf89e00 AL |
1045 | } |
1046 | ||
df4938a6 GK |
1047 | static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, |
1048 | int flags) | |
1049 | { | |
1050 | int ret = -1; | |
1051 | ||
1052 | if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { | |
1053 | int map_dirfd; | |
1054 | ||
1055 | if (flags == AT_REMOVEDIR) { | |
1056 | int fd; | |
1057 | ||
1058 | fd = openat(dirfd, name, O_RDONLY | O_DIRECTORY | O_PATH); | |
1059 | if (fd == -1) { | |
1060 | goto err_out; | |
1061 | } | |
1062 | /* | |
1063 | * If directory remove .virtfs_metadata contained in the | |
1064 | * directory | |
1065 | */ | |
1066 | ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR); | |
1067 | close_preserve_errno(fd); | |
1068 | if (ret < 0 && errno != ENOENT) { | |
1069 | /* | |
1070 | * We didn't had the .virtfs_metadata file. May be file created | |
1071 | * in non-mapped mode ?. Ignore ENOENT. | |
1072 | */ | |
1073 | goto err_out; | |
1074 | } | |
1075 | } | |
1076 | /* | |
1077 | * Now remove the name from parent directory | |
1078 | * .virtfs_metadata directory. | |
1079 | */ | |
1080 | map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); | |
1081 | ret = unlinkat(map_dirfd, name, 0); | |
1082 | close_preserve_errno(map_dirfd); | |
1083 | if (ret < 0 && errno != ENOENT) { | |
1084 | /* | |
1085 | * We didn't had the .virtfs_metadata file. May be file created | |
1086 | * in non-mapped mode ?. Ignore ENOENT. | |
1087 | */ | |
1088 | goto err_out; | |
1089 | } | |
1090 | } | |
1091 | ||
1092 | ret = unlinkat(dirfd, name, flags); | |
1093 | err_out: | |
1094 | return ret; | |
1095 | } | |
1096 | ||
5bae1900 AL |
1097 | static int local_remove(FsContext *ctx, const char *path) |
1098 | { | |
2c30dd74 | 1099 | struct stat stbuf; |
a0e640a8 GK |
1100 | char *dirpath = g_path_get_dirname(path); |
1101 | char *name = g_path_get_basename(path); | |
1102 | int flags = 0; | |
1103 | int dirfd; | |
1104 | int err = -1; | |
2c30dd74 | 1105 | |
a0e640a8 GK |
1106 | dirfd = local_opendir_nofollow(ctx, dirpath); |
1107 | if (dirfd) { | |
1108 | goto out; | |
2c30dd74 | 1109 | } |
4fa4ce71 | 1110 | |
a0e640a8 GK |
1111 | if (fstatat(dirfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) { |
1112 | goto err_out; | |
1113 | } | |
1114 | ||
1115 | if (S_ISDIR(stbuf.st_mode)) { | |
1116 | flags |= AT_REMOVEDIR; | |
1117 | } | |
1118 | ||
1119 | err = local_unlinkat_common(ctx, dirfd, name, flags); | |
2c30dd74 | 1120 | err_out: |
a0e640a8 GK |
1121 | close_preserve_errno(dirfd); |
1122 | out: | |
1123 | g_free(name); | |
1124 | g_free(dirpath); | |
2c30dd74 | 1125 | return err; |
5bae1900 AL |
1126 | } |
1127 | ||
8b888272 AK |
1128 | static int local_fsync(FsContext *ctx, int fid_type, |
1129 | V9fsFidOpenState *fs, int datasync) | |
8cf89e00 | 1130 | { |
8b888272 AK |
1131 | int fd; |
1132 | ||
1133 | if (fid_type == P9_FID_DIR) { | |
f314ea4e | 1134 | fd = dirfd(fs->dir.stream); |
8b888272 AK |
1135 | } else { |
1136 | fd = fs->fd; | |
1137 | } | |
1138 | ||
49594973 | 1139 | if (datasync) { |
8b888272 | 1140 | return qemu_fdatasync(fd); |
49594973 | 1141 | } else { |
8b888272 | 1142 | return fsync(fd); |
49594973 | 1143 | } |
8cf89e00 AL |
1144 | } |
1145 | ||
2289be19 | 1146 | static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) |
be940c87 | 1147 | { |
31e51d1c | 1148 | int fd, ret; |
2289be19 | 1149 | |
31e51d1c GK |
1150 | fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0); |
1151 | ret = fstatfs(fd, stbuf); | |
1152 | close_preserve_errno(fd); | |
4fa4ce71 | 1153 | return ret; |
be940c87 MK |
1154 | } |
1155 | ||
2289be19 | 1156 | static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, |
fa32ef88 AK |
1157 | const char *name, void *value, size_t size) |
1158 | { | |
2289be19 AK |
1159 | char *path = fs_path->data; |
1160 | ||
fc22118d | 1161 | return v9fs_get_xattr(ctx, path, name, value, size); |
fa32ef88 AK |
1162 | } |
1163 | ||
2289be19 | 1164 | static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, |
fa32ef88 AK |
1165 | void *value, size_t size) |
1166 | { | |
2289be19 AK |
1167 | char *path = fs_path->data; |
1168 | ||
fc22118d | 1169 | return v9fs_list_xattr(ctx, path, value, size); |
fa32ef88 AK |
1170 | } |
1171 | ||
2289be19 | 1172 | static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, |
10b468bd AK |
1173 | void *value, size_t size, int flags) |
1174 | { | |
2289be19 AK |
1175 | char *path = fs_path->data; |
1176 | ||
fc22118d | 1177 | return v9fs_set_xattr(ctx, path, name, value, size, flags); |
10b468bd AK |
1178 | } |
1179 | ||
2289be19 AK |
1180 | static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, |
1181 | const char *name) | |
9ed3ef26 | 1182 | { |
2289be19 AK |
1183 | char *path = fs_path->data; |
1184 | ||
fc22118d | 1185 | return v9fs_remove_xattr(ctx, path, name); |
9ed3ef26 AK |
1186 | } |
1187 | ||
2289be19 AK |
1188 | static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, |
1189 | const char *name, V9fsPath *target) | |
1190 | { | |
1191 | if (dir_path) { | |
e3e83f2e | 1192 | v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); |
2289be19 | 1193 | } else { |
e3e83f2e | 1194 | v9fs_path_sprintf(target, "%s", name); |
2289be19 | 1195 | } |
2289be19 AK |
1196 | return 0; |
1197 | } | |
1198 | ||
1199 | static int local_renameat(FsContext *ctx, V9fsPath *olddir, | |
1200 | const char *old_name, V9fsPath *newdir, | |
1201 | const char *new_name) | |
1202 | { | |
1203 | int ret; | |
99f2cf4b GK |
1204 | int odirfd, ndirfd; |
1205 | ||
1206 | odirfd = local_opendir_nofollow(ctx, olddir->data); | |
1207 | if (odirfd == -1) { | |
1208 | return -1; | |
1209 | } | |
1210 | ||
1211 | ndirfd = local_opendir_nofollow(ctx, newdir->data); | |
1212 | if (ndirfd == -1) { | |
1213 | close_preserve_errno(odirfd); | |
1214 | return -1; | |
1215 | } | |
1216 | ||
1217 | ret = renameat(odirfd, old_name, ndirfd, new_name); | |
1218 | if (ret < 0) { | |
1219 | goto out; | |
1220 | } | |
2289be19 | 1221 | |
99f2cf4b GK |
1222 | if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { |
1223 | int omap_dirfd, nmap_dirfd; | |
2289be19 | 1224 | |
99f2cf4b GK |
1225 | ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); |
1226 | if (ret < 0 && errno != EEXIST) { | |
1227 | goto err_undo_rename; | |
1228 | } | |
2289be19 | 1229 | |
6dd4b1f1 | 1230 | omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); |
99f2cf4b GK |
1231 | if (omap_dirfd == -1) { |
1232 | goto err; | |
1233 | } | |
1234 | ||
6dd4b1f1 | 1235 | nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); |
99f2cf4b GK |
1236 | if (nmap_dirfd == -1) { |
1237 | close_preserve_errno(omap_dirfd); | |
1238 | goto err; | |
1239 | } | |
1240 | ||
1241 | /* rename the .virtfs_metadata files */ | |
1242 | ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name); | |
1243 | close_preserve_errno(nmap_dirfd); | |
1244 | close_preserve_errno(omap_dirfd); | |
1245 | if (ret < 0 && errno != ENOENT) { | |
1246 | goto err_undo_rename; | |
1247 | } | |
1248 | ||
1249 | ret = 0; | |
1250 | } | |
1251 | goto out; | |
1252 | ||
1253 | err: | |
1254 | ret = -1; | |
1255 | err_undo_rename: | |
1256 | renameat_preserve_errno(ndirfd, new_name, odirfd, old_name); | |
1257 | out: | |
1258 | close_preserve_errno(ndirfd); | |
1259 | close_preserve_errno(odirfd); | |
2289be19 AK |
1260 | return ret; |
1261 | } | |
1262 | ||
d2767ede GK |
1263 | static void v9fs_path_init_dirname(V9fsPath *path, const char *str) |
1264 | { | |
1265 | path->data = g_path_get_dirname(str); | |
1266 | path->size = strlen(path->data) + 1; | |
1267 | } | |
1268 | ||
1269 | static int local_rename(FsContext *ctx, const char *oldpath, | |
1270 | const char *newpath) | |
1271 | { | |
1272 | int err; | |
1273 | char *oname = g_path_get_basename(oldpath); | |
1274 | char *nname = g_path_get_basename(newpath); | |
1275 | V9fsPath olddir, newdir; | |
1276 | ||
1277 | v9fs_path_init_dirname(&olddir, oldpath); | |
1278 | v9fs_path_init_dirname(&newdir, newpath); | |
1279 | ||
1280 | err = local_renameat(ctx, &olddir, oname, &newdir, nname); | |
1281 | ||
1282 | v9fs_path_free(&newdir); | |
1283 | v9fs_path_free(&olddir); | |
1284 | g_free(nname); | |
1285 | g_free(oname); | |
1286 | ||
1287 | return err; | |
1288 | } | |
1289 | ||
2289be19 AK |
1290 | static int local_unlinkat(FsContext *ctx, V9fsPath *dir, |
1291 | const char *name, int flags) | |
1292 | { | |
1293 | int ret; | |
df4938a6 | 1294 | int dirfd; |
2289be19 | 1295 | |
df4938a6 GK |
1296 | dirfd = local_opendir_nofollow(ctx, dir->data); |
1297 | if (dirfd == -1) { | |
1298 | return -1; | |
2c30dd74 | 1299 | } |
2289be19 | 1300 | |
df4938a6 GK |
1301 | ret = local_unlinkat_common(ctx, dirfd, name, flags); |
1302 | close_preserve_errno(dirfd); | |
2289be19 AK |
1303 | return ret; |
1304 | } | |
9ed3ef26 | 1305 | |
e06a765e HPB |
1306 | static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, |
1307 | mode_t st_mode, uint64_t *st_gen) | |
1308 | { | |
ae0f940e | 1309 | #ifdef FS_IOC_GETVERSION |
0e5fc994 | 1310 | int err; |
cc720ddb AK |
1311 | V9fsFidOpenState fid_open; |
1312 | ||
e06a765e HPB |
1313 | /* |
1314 | * Do not try to open special files like device nodes, fifos etc | |
1315 | * We can get fd for regular files and directories only | |
1316 | */ | |
1317 | if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { | |
1a9978a5 KS |
1318 | errno = ENOTTY; |
1319 | return -1; | |
e06a765e | 1320 | } |
cc720ddb AK |
1321 | err = local_open(ctx, path, O_RDONLY, &fid_open); |
1322 | if (err < 0) { | |
1323 | return err; | |
e06a765e | 1324 | } |
cc720ddb AK |
1325 | err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); |
1326 | local_close(ctx, &fid_open); | |
0e5fc994 | 1327 | return err; |
ae0f940e | 1328 | #else |
0e5fc994 KS |
1329 | errno = ENOTTY; |
1330 | return -1; | |
ae0f940e | 1331 | #endif |
e06a765e HPB |
1332 | } |
1333 | ||
0174fe73 AK |
1334 | static int local_init(FsContext *ctx) |
1335 | { | |
e06a765e | 1336 | struct statfs stbuf; |
0e35a378 GK |
1337 | LocalData *data = g_malloc(sizeof(*data)); |
1338 | ||
1339 | data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); | |
1340 | if (data->mountfd == -1) { | |
1341 | goto err; | |
1342 | } | |
e06a765e | 1343 | |
00c90bd1 GK |
1344 | #ifdef FS_IOC_GETVERSION |
1345 | /* | |
1346 | * use ioc_getversion only if the ioctl is definied | |
1347 | */ | |
0e35a378 GK |
1348 | if (fstatfs(data->mountfd, &stbuf) < 0) { |
1349 | close_preserve_errno(data->mountfd); | |
1350 | goto err; | |
00c90bd1 GK |
1351 | } |
1352 | switch (stbuf.f_type) { | |
1353 | case EXT2_SUPER_MAGIC: | |
1354 | case BTRFS_SUPER_MAGIC: | |
1355 | case REISERFS_SUPER_MAGIC: | |
1356 | case XFS_SUPER_MAGIC: | |
1357 | ctx->exops.get_st_gen = local_ioc_getversion; | |
1358 | break; | |
1359 | } | |
1360 | #endif | |
1361 | ||
2c30dd74 AK |
1362 | if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { |
1363 | ctx->xops = passthrough_xattr_ops; | |
1364 | } else if (ctx->export_flags & V9FS_SM_MAPPED) { | |
1365 | ctx->xops = mapped_xattr_ops; | |
1366 | } else if (ctx->export_flags & V9FS_SM_NONE) { | |
1367 | ctx->xops = none_xattr_ops; | |
1368 | } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { | |
1369 | /* | |
1370 | * xattr operation for mapped-file and passthrough | |
1371 | * remain same. | |
1372 | */ | |
1373 | ctx->xops = passthrough_xattr_ops; | |
1374 | } | |
c98f1d4a | 1375 | ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; |
00c90bd1 | 1376 | |
0e35a378 | 1377 | ctx->private = data; |
00c90bd1 | 1378 | return 0; |
0e35a378 GK |
1379 | |
1380 | err: | |
1381 | g_free(data); | |
1382 | return -1; | |
1383 | } | |
1384 | ||
1385 | static void local_cleanup(FsContext *ctx) | |
1386 | { | |
1387 | LocalData *data = ctx->private; | |
1388 | ||
1389 | close(data->mountfd); | |
1390 | g_free(data); | |
0174fe73 AK |
1391 | } |
1392 | ||
99519f0a AK |
1393 | static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) |
1394 | { | |
1395 | const char *sec_model = qemu_opt_get(opts, "security_model"); | |
1396 | const char *path = qemu_opt_get(opts, "path"); | |
1397 | ||
1398 | if (!sec_model) { | |
63325b18 GK |
1399 | error_report("Security model not specified, local fs needs security model"); |
1400 | error_printf("valid options are:" | |
1401 | "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n"); | |
99519f0a AK |
1402 | return -1; |
1403 | } | |
1404 | ||
1405 | if (!strcmp(sec_model, "passthrough")) { | |
1406 | fse->export_flags |= V9FS_SM_PASSTHROUGH; | |
2c30dd74 AK |
1407 | } else if (!strcmp(sec_model, "mapped") || |
1408 | !strcmp(sec_model, "mapped-xattr")) { | |
99519f0a AK |
1409 | fse->export_flags |= V9FS_SM_MAPPED; |
1410 | } else if (!strcmp(sec_model, "none")) { | |
1411 | fse->export_flags |= V9FS_SM_NONE; | |
2c30dd74 AK |
1412 | } else if (!strcmp(sec_model, "mapped-file")) { |
1413 | fse->export_flags |= V9FS_SM_MAPPED_FILE; | |
99519f0a | 1414 | } else { |
63325b18 GK |
1415 | error_report("Invalid security model %s specified", sec_model); |
1416 | error_printf("valid options are:" | |
1417 | "\t[passthrough|mapped-xattr|mapped-file|none]\n"); | |
99519f0a AK |
1418 | return -1; |
1419 | } | |
1420 | ||
1421 | if (!path) { | |
63325b18 | 1422 | error_report("fsdev: No path specified"); |
99519f0a AK |
1423 | return -1; |
1424 | } | |
1425 | fse->path = g_strdup(path); | |
1426 | ||
1427 | return 0; | |
1428 | } | |
1429 | ||
9f107513 | 1430 | FileOperations local_ops = { |
99519f0a | 1431 | .parse_opts = local_parse_opts, |
0174fe73 | 1432 | .init = local_init, |
0e35a378 | 1433 | .cleanup = local_cleanup, |
131dcb25 | 1434 | .lstat = local_lstat, |
131dcb25 AL |
1435 | .readlink = local_readlink, |
1436 | .close = local_close, | |
1437 | .closedir = local_closedir, | |
a6568fe2 AL |
1438 | .open = local_open, |
1439 | .opendir = local_opendir, | |
a9231555 AL |
1440 | .rewinddir = local_rewinddir, |
1441 | .telldir = local_telldir, | |
635324e8 | 1442 | .readdir = local_readdir, |
a9231555 | 1443 | .seekdir = local_seekdir, |
56d15a53 SG |
1444 | .preadv = local_preadv, |
1445 | .pwritev = local_pwritev, | |
c494dd6f AL |
1446 | .chmod = local_chmod, |
1447 | .mknod = local_mknod, | |
c494dd6f AL |
1448 | .mkdir = local_mkdir, |
1449 | .fstat = local_fstat, | |
1450 | .open2 = local_open2, | |
1451 | .symlink = local_symlink, | |
1452 | .link = local_link, | |
8cf89e00 AL |
1453 | .truncate = local_truncate, |
1454 | .rename = local_rename, | |
1455 | .chown = local_chown, | |
74bc02b2 | 1456 | .utimensat = local_utimensat, |
5bae1900 | 1457 | .remove = local_remove, |
8cf89e00 | 1458 | .fsync = local_fsync, |
be940c87 | 1459 | .statfs = local_statfs, |
fa32ef88 AK |
1460 | .lgetxattr = local_lgetxattr, |
1461 | .llistxattr = local_llistxattr, | |
10b468bd | 1462 | .lsetxattr = local_lsetxattr, |
9ed3ef26 | 1463 | .lremovexattr = local_lremovexattr, |
2289be19 AK |
1464 | .name_to_path = local_name_to_path, |
1465 | .renameat = local_renameat, | |
1466 | .unlinkat = local_unlinkat, | |
9f107513 | 1467 | }; |