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