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 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 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 85 #define VIRTFS_META_DIR ".virtfs_metadata" 86 87 static char *local_mapped_attr_path(FsContext *ctx, const char *path) 88 { 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); 100 } 101 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 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 156 #define ATTR_MAX 100 157 static void local_mapped_file_attr(int dirfd, const char *name, 158 struct stat *stbuf) 159 { 160 FILE *fp; 161 char buf[ATTR_MAX]; 162 int map_dirfd; 163 164 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); 165 if (map_dirfd == -1) { 166 return; 167 } 168 169 fp = local_fopenat(map_dirfd, name, "r"); 170 close_preserve_errno(map_dirfd); 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 190 static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) 191 { 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 } 201 202 err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW); 203 if (err) { 204 goto err_out; 205 } 206 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 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; 212 213 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid, 214 sizeof(uid_t)) > 0) { 215 stbuf->st_uid = le32_to_cpu(tmp_uid); 216 } 217 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid, 218 sizeof(gid_t)) > 0) { 219 stbuf->st_gid = le32_to_cpu(tmp_gid); 220 } 221 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode, 222 sizeof(mode_t)) > 0) { 223 stbuf->st_mode = le32_to_cpu(tmp_mode); 224 } 225 if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev, 226 sizeof(dev_t)) > 0) { 227 stbuf->st_rdev = le64_to_cpu(tmp_dev); 228 } 229 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 230 local_mapped_file_attr(dirfd, name, stbuf); 231 } 232 233 err_out: 234 close_preserve_errno(dirfd); 235 out: 236 g_free(name); 237 g_free(dirpath); 238 return err; 239 } 240 241 static int local_create_mapped_attr_dir(FsContext *ctx, const char *path) 242 { 243 int err; 244 char *attr_dir; 245 char *tmp_path = g_strdup(path); 246 247 attr_dir = g_strdup_printf("%s/%s/%s", 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; 253 } 254 g_free(attr_dir); 255 g_free(tmp_path); 256 return err; 257 } 258 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]; 265 char *attr_path; 266 int uid = -1, gid = -1, mode = -1, rdev = -1; 267 268 attr_path = local_mapped_attr_path(ctx, path); 269 fp = local_fopen(attr_path, "r"); 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: 296 fp = local_fopen(attr_path, "w"); 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: 331 g_free(attr_path); 332 return ret; 333 } 334 335 static int local_set_xattr(const char *path, FsCred *credp) 336 { 337 int err; 338 339 if (credp->fc_uid != -1) { 340 uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); 341 err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0); 342 if (err) { 343 return err; 344 } 345 } 346 if (credp->fc_gid != -1) { 347 uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); 348 err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0); 349 if (err) { 350 return err; 351 } 352 } 353 if (credp->fc_mode != -1) { 354 uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); 355 err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0); 356 if (err) { 357 return err; 358 } 359 } 360 if (credp->fc_rdev != -1) { 361 uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); 362 err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0); 363 if (err) { 364 return err; 365 } 366 } 367 return 0; 368 } 369 370 static int local_set_mapped_file_attrat(int dirfd, const char *name, 371 FsCred *credp) 372 { 373 FILE *fp; 374 int ret; 375 char buf[ATTR_MAX]; 376 int uid = -1, gid = -1, mode = -1, rdev = -1; 377 int map_dirfd; 378 379 ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700); 380 if (ret < 0 && errno != EEXIST) { 381 return -1; 382 } 383 384 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); 385 if (map_dirfd == -1) { 386 return -1; 387 } 388 389 fp = local_fopenat(map_dirfd, name, "r"); 390 if (!fp) { 391 if (errno == ENOENT) { 392 goto update_map_file; 393 } else { 394 close_preserve_errno(map_dirfd); 395 return -1; 396 } 397 } 398 memset(buf, 0, ATTR_MAX); 399 while (fgets(buf, ATTR_MAX, fp)) { 400 if (!strncmp(buf, "virtfs.uid", 10)) { 401 uid = atoi(buf + 11); 402 } else if (!strncmp(buf, "virtfs.gid", 10)) { 403 gid = atoi(buf + 11); 404 } else if (!strncmp(buf, "virtfs.mode", 11)) { 405 mode = atoi(buf + 12); 406 } else if (!strncmp(buf, "virtfs.rdev", 11)) { 407 rdev = atoi(buf + 12); 408 } 409 memset(buf, 0, ATTR_MAX); 410 } 411 fclose(fp); 412 413 update_map_file: 414 fp = local_fopenat(map_dirfd, name, "w"); 415 close_preserve_errno(map_dirfd); 416 if (!fp) { 417 return -1; 418 } 419 420 if (credp->fc_uid != -1) { 421 uid = credp->fc_uid; 422 } 423 if (credp->fc_gid != -1) { 424 gid = credp->fc_gid; 425 } 426 if (credp->fc_mode != -1) { 427 mode = credp->fc_mode; 428 } 429 if (credp->fc_rdev != -1) { 430 rdev = credp->fc_rdev; 431 } 432 433 if (uid != -1) { 434 fprintf(fp, "virtfs.uid=%d\n", uid); 435 } 436 if (gid != -1) { 437 fprintf(fp, "virtfs.gid=%d\n", gid); 438 } 439 if (mode != -1) { 440 fprintf(fp, "virtfs.mode=%d\n", mode); 441 } 442 if (rdev != -1) { 443 fprintf(fp, "virtfs.rdev=%d\n", rdev); 444 } 445 fclose(fp); 446 447 return 0; 448 } 449 450 static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode) 451 { 452 int fd, ret; 453 454 /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW). 455 * Unfortunately, the linux kernel doesn't implement it yet. As an 456 * alternative, let's open the file and use fchmod() instead. This 457 * may fail depending on the permissions of the file, but it is the 458 * best we can do to avoid TOCTTOU. We first try to open read-only 459 * in case name points to a directory. If that fails, we try write-only 460 * in case name doesn't point to a directory. 461 */ 462 fd = openat_file(dirfd, name, O_RDONLY, 0); 463 if (fd == -1) { 464 /* In case the file is writable-only and isn't a directory. */ 465 if (errno == EACCES) { 466 fd = openat_file(dirfd, name, O_WRONLY, 0); 467 } 468 if (fd == -1 && errno == EISDIR) { 469 errno = EACCES; 470 } 471 } 472 if (fd == -1) { 473 return -1; 474 } 475 ret = fchmod(fd, mode); 476 close_preserve_errno(fd); 477 return ret; 478 } 479 480 static int local_set_xattrat(int dirfd, const char *path, FsCred *credp) 481 { 482 int err; 483 484 if (credp->fc_uid != -1) { 485 uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); 486 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.uid", &tmp_uid, 487 sizeof(uid_t), 0); 488 if (err) { 489 return err; 490 } 491 } 492 if (credp->fc_gid != -1) { 493 uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); 494 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.gid", &tmp_gid, 495 sizeof(gid_t), 0); 496 if (err) { 497 return err; 498 } 499 } 500 if (credp->fc_mode != -1) { 501 uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); 502 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.mode", &tmp_mode, 503 sizeof(mode_t), 0); 504 if (err) { 505 return err; 506 } 507 } 508 if (credp->fc_rdev != -1) { 509 uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); 510 err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.rdev", &tmp_rdev, 511 sizeof(dev_t), 0); 512 if (err) { 513 return err; 514 } 515 } 516 return 0; 517 } 518 519 static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, 520 FsCred *credp) 521 { 522 char *buffer; 523 524 buffer = rpath(fs_ctx, path); 525 if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) { 526 /* 527 * If we fail to change ownership and if we are 528 * using security model none. Ignore the error 529 */ 530 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { 531 goto err; 532 } 533 } 534 535 if (chmod(buffer, credp->fc_mode & 07777) < 0) { 536 goto err; 537 } 538 539 g_free(buffer); 540 return 0; 541 err: 542 g_free(buffer); 543 return -1; 544 } 545 546 static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd, 547 const char *name, FsCred *credp) 548 { 549 if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid, 550 AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH) < 0) { 551 /* 552 * If we fail to change ownership and if we are 553 * using security model none. Ignore the error 554 */ 555 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { 556 return -1; 557 } 558 } 559 560 return fchmodat_nofollow(dirfd, name, credp->fc_mode & 07777); 561 } 562 563 static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, 564 char *buf, size_t bufsz) 565 { 566 ssize_t tsize = -1; 567 568 if ((fs_ctx->export_flags & V9FS_SM_MAPPED) || 569 (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) { 570 int fd; 571 572 fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0); 573 if (fd == -1) { 574 return -1; 575 } 576 do { 577 tsize = read(fd, (void *)buf, bufsz); 578 } while (tsize == -1 && errno == EINTR); 579 close_preserve_errno(fd); 580 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || 581 (fs_ctx->export_flags & V9FS_SM_NONE)) { 582 char *dirpath = g_path_get_dirname(fs_path->data); 583 char *name = g_path_get_basename(fs_path->data); 584 int dirfd; 585 586 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 587 if (dirfd == -1) { 588 goto out; 589 } 590 591 tsize = readlinkat(dirfd, name, buf, bufsz); 592 close_preserve_errno(dirfd); 593 out: 594 g_free(name); 595 g_free(dirpath); 596 } 597 return tsize; 598 } 599 600 static int local_close(FsContext *ctx, V9fsFidOpenState *fs) 601 { 602 return close(fs->fd); 603 } 604 605 static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) 606 { 607 return closedir(fs->dir.stream); 608 } 609 610 static int local_open(FsContext *ctx, V9fsPath *fs_path, 611 int flags, V9fsFidOpenState *fs) 612 { 613 int fd; 614 615 fd = local_open_nofollow(ctx, fs_path->data, flags, 0); 616 if (fd == -1) { 617 return -1; 618 } 619 fs->fd = fd; 620 return fs->fd; 621 } 622 623 static int local_opendir(FsContext *ctx, 624 V9fsPath *fs_path, V9fsFidOpenState *fs) 625 { 626 int dirfd; 627 DIR *stream; 628 629 dirfd = local_opendir_nofollow(ctx, fs_path->data); 630 if (dirfd == -1) { 631 return -1; 632 } 633 634 stream = fdopendir(dirfd); 635 if (!stream) { 636 return -1; 637 } 638 fs->dir.stream = stream; 639 return 0; 640 } 641 642 static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) 643 { 644 rewinddir(fs->dir.stream); 645 } 646 647 static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) 648 { 649 return telldir(fs->dir.stream); 650 } 651 652 static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) 653 { 654 struct dirent *entry; 655 656 again: 657 entry = readdir(fs->dir.stream); 658 if (!entry) { 659 return NULL; 660 } 661 662 if (ctx->export_flags & V9FS_SM_MAPPED) { 663 entry->d_type = DT_UNKNOWN; 664 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 665 if (!strcmp(entry->d_name, VIRTFS_META_DIR)) { 666 /* skp the meta data directory */ 667 goto again; 668 } 669 entry->d_type = DT_UNKNOWN; 670 } 671 672 return entry; 673 } 674 675 static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) 676 { 677 seekdir(fs->dir.stream, off); 678 } 679 680 static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, 681 const struct iovec *iov, 682 int iovcnt, off_t offset) 683 { 684 #ifdef CONFIG_PREADV 685 return preadv(fs->fd, iov, iovcnt, offset); 686 #else 687 int err = lseek(fs->fd, offset, SEEK_SET); 688 if (err == -1) { 689 return err; 690 } else { 691 return readv(fs->fd, iov, iovcnt); 692 } 693 #endif 694 } 695 696 static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, 697 const struct iovec *iov, 698 int iovcnt, off_t offset) 699 { 700 ssize_t ret; 701 #ifdef CONFIG_PREADV 702 ret = pwritev(fs->fd, iov, iovcnt, offset); 703 #else 704 int err = lseek(fs->fd, offset, SEEK_SET); 705 if (err == -1) { 706 return err; 707 } else { 708 ret = writev(fs->fd, iov, iovcnt); 709 } 710 #endif 711 #ifdef CONFIG_SYNC_FILE_RANGE 712 if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { 713 /* 714 * Initiate a writeback. This is not a data integrity sync. 715 * We want to ensure that we don't leave dirty pages in the cache 716 * after write when writeout=immediate is sepcified. 717 */ 718 sync_file_range(fs->fd, offset, ret, 719 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); 720 } 721 #endif 722 return ret; 723 } 724 725 static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) 726 { 727 char *dirpath = g_path_get_dirname(fs_path->data); 728 char *name = g_path_get_basename(fs_path->data); 729 int ret = -1; 730 int dirfd; 731 732 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 733 if (dirfd == -1) { 734 goto out; 735 } 736 737 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 738 ret = local_set_xattrat(dirfd, name, credp); 739 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 740 ret = local_set_mapped_file_attrat(dirfd, name, credp); 741 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 742 fs_ctx->export_flags & V9FS_SM_NONE) { 743 ret = fchmodat_nofollow(dirfd, name, credp->fc_mode); 744 } 745 close_preserve_errno(dirfd); 746 747 out: 748 g_free(dirpath); 749 g_free(name); 750 return ret; 751 } 752 753 static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, 754 const char *name, FsCred *credp) 755 { 756 int err = -1; 757 int dirfd; 758 759 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 760 if (dirfd == -1) { 761 return -1; 762 } 763 764 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 765 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 766 err = mknodat(dirfd, name, SM_LOCAL_MODE_BITS | S_IFREG, 0); 767 if (err == -1) { 768 goto out; 769 } 770 771 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 772 err = local_set_xattrat(dirfd, name, credp); 773 } else { 774 err = local_set_mapped_file_attrat(dirfd, name, credp); 775 } 776 if (err == -1) { 777 goto err_end; 778 } 779 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 780 fs_ctx->export_flags & V9FS_SM_NONE) { 781 err = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); 782 if (err == -1) { 783 goto out; 784 } 785 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp); 786 if (err == -1) { 787 goto err_end; 788 } 789 } 790 goto out; 791 792 err_end: 793 unlinkat_preserve_errno(dirfd, name, 0); 794 out: 795 close_preserve_errno(dirfd); 796 return err; 797 } 798 799 static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, 800 const char *name, FsCred *credp) 801 { 802 int err = -1; 803 int dirfd; 804 805 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 806 if (dirfd == -1) { 807 return -1; 808 } 809 810 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 811 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 812 err = mkdirat(dirfd, name, SM_LOCAL_DIR_MODE_BITS); 813 if (err == -1) { 814 goto out; 815 } 816 credp->fc_mode = credp->fc_mode | S_IFDIR; 817 818 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 819 err = local_set_xattrat(dirfd, name, credp); 820 } else { 821 err = local_set_mapped_file_attrat(dirfd, name, credp); 822 } 823 if (err == -1) { 824 goto err_end; 825 } 826 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 827 fs_ctx->export_flags & V9FS_SM_NONE) { 828 err = mkdirat(dirfd, name, credp->fc_mode); 829 if (err == -1) { 830 goto out; 831 } 832 err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp); 833 if (err == -1) { 834 goto err_end; 835 } 836 } 837 goto out; 838 839 err_end: 840 unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR); 841 out: 842 close_preserve_errno(dirfd); 843 return err; 844 } 845 846 static int local_fstat(FsContext *fs_ctx, int fid_type, 847 V9fsFidOpenState *fs, struct stat *stbuf) 848 { 849 int err, fd; 850 851 if (fid_type == P9_FID_DIR) { 852 fd = dirfd(fs->dir.stream); 853 } else { 854 fd = fs->fd; 855 } 856 857 err = fstat(fd, stbuf); 858 if (err) { 859 return err; 860 } 861 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 862 /* Actual credentials are part of extended attrs */ 863 uid_t tmp_uid; 864 gid_t tmp_gid; 865 mode_t tmp_mode; 866 dev_t tmp_dev; 867 868 if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { 869 stbuf->st_uid = le32_to_cpu(tmp_uid); 870 } 871 if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { 872 stbuf->st_gid = le32_to_cpu(tmp_gid); 873 } 874 if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) { 875 stbuf->st_mode = le32_to_cpu(tmp_mode); 876 } 877 if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { 878 stbuf->st_rdev = le64_to_cpu(tmp_dev); 879 } 880 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 881 errno = EOPNOTSUPP; 882 return -1; 883 } 884 return err; 885 } 886 887 static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, 888 int flags, FsCred *credp, V9fsFidOpenState *fs) 889 { 890 char *path; 891 int fd = -1; 892 int err = -1; 893 int serrno = 0; 894 V9fsString fullname; 895 char *buffer = NULL; 896 897 /* 898 * Mark all the open to not follow symlinks 899 */ 900 flags |= O_NOFOLLOW; 901 902 v9fs_string_init(&fullname); 903 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); 904 path = fullname.data; 905 906 /* Determine the security model */ 907 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 908 buffer = rpath(fs_ctx, path); 909 fd = open(buffer, flags, SM_LOCAL_MODE_BITS); 910 if (fd == -1) { 911 err = fd; 912 goto out; 913 } 914 credp->fc_mode = credp->fc_mode|S_IFREG; 915 /* Set cleint credentials in xattr */ 916 err = local_set_xattr(buffer, credp); 917 if (err == -1) { 918 serrno = errno; 919 goto err_end; 920 } 921 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 922 buffer = rpath(fs_ctx, path); 923 fd = open(buffer, flags, SM_LOCAL_MODE_BITS); 924 if (fd == -1) { 925 err = fd; 926 goto out; 927 } 928 credp->fc_mode = credp->fc_mode|S_IFREG; 929 /* Set client credentials in .virtfs_metadata directory files */ 930 err = local_set_mapped_file_attr(fs_ctx, path, credp); 931 if (err == -1) { 932 serrno = errno; 933 goto err_end; 934 } 935 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || 936 (fs_ctx->export_flags & V9FS_SM_NONE)) { 937 buffer = rpath(fs_ctx, path); 938 fd = open(buffer, flags, credp->fc_mode); 939 if (fd == -1) { 940 err = fd; 941 goto out; 942 } 943 err = local_post_create_passthrough(fs_ctx, path, credp); 944 if (err == -1) { 945 serrno = errno; 946 goto err_end; 947 } 948 } 949 err = fd; 950 fs->fd = fd; 951 goto out; 952 953 err_end: 954 close(fd); 955 remove(buffer); 956 errno = serrno; 957 out: 958 g_free(buffer); 959 v9fs_string_free(&fullname); 960 return err; 961 } 962 963 964 static int local_symlink(FsContext *fs_ctx, const char *oldpath, 965 V9fsPath *dir_path, const char *name, FsCred *credp) 966 { 967 int err = -1; 968 int dirfd; 969 970 dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); 971 if (dirfd == -1) { 972 return -1; 973 } 974 975 /* Determine the security model */ 976 if (fs_ctx->export_flags & V9FS_SM_MAPPED || 977 fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 978 int fd; 979 ssize_t oldpath_size, write_size; 980 981 fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR, 982 SM_LOCAL_MODE_BITS); 983 if (fd == -1) { 984 goto out; 985 } 986 /* Write the oldpath (target) to the file. */ 987 oldpath_size = strlen(oldpath); 988 do { 989 write_size = write(fd, (void *)oldpath, oldpath_size); 990 } while (write_size == -1 && errno == EINTR); 991 close_preserve_errno(fd); 992 993 if (write_size != oldpath_size) { 994 goto err_end; 995 } 996 /* Set cleint credentials in symlink's xattr */ 997 credp->fc_mode = credp->fc_mode | S_IFLNK; 998 999 if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 1000 err = local_set_xattrat(dirfd, name, credp); 1001 } else { 1002 err = local_set_mapped_file_attrat(dirfd, name, credp); 1003 } 1004 if (err == -1) { 1005 goto err_end; 1006 } 1007 } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || 1008 fs_ctx->export_flags & V9FS_SM_NONE) { 1009 err = symlinkat(oldpath, dirfd, name); 1010 if (err) { 1011 goto out; 1012 } 1013 err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid, 1014 AT_SYMLINK_NOFOLLOW); 1015 if (err == -1) { 1016 /* 1017 * If we fail to change ownership and if we are 1018 * using security model none. Ignore the error 1019 */ 1020 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { 1021 goto err_end; 1022 } else { 1023 err = 0; 1024 } 1025 } 1026 } 1027 goto out; 1028 1029 err_end: 1030 unlinkat_preserve_errno(dirfd, name, 0); 1031 out: 1032 close_preserve_errno(dirfd); 1033 return err; 1034 } 1035 1036 static int local_link(FsContext *ctx, V9fsPath *oldpath, 1037 V9fsPath *dirpath, const char *name) 1038 { 1039 char *odirpath = g_path_get_dirname(oldpath->data); 1040 char *oname = g_path_get_basename(oldpath->data); 1041 int ret = -1; 1042 int odirfd, ndirfd; 1043 1044 odirfd = local_opendir_nofollow(ctx, odirpath); 1045 if (odirfd == -1) { 1046 goto out; 1047 } 1048 1049 ndirfd = local_opendir_nofollow(ctx, dirpath->data); 1050 if (ndirfd == -1) { 1051 close_preserve_errno(odirfd); 1052 goto out; 1053 } 1054 1055 ret = linkat(odirfd, oname, ndirfd, name, 0); 1056 if (ret < 0) { 1057 goto out_close; 1058 } 1059 1060 /* now link the virtfs_metadata files */ 1061 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1062 int omap_dirfd, nmap_dirfd; 1063 1064 ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); 1065 if (ret < 0 && errno != EEXIST) { 1066 goto err_undo_link; 1067 } 1068 1069 omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); 1070 if (omap_dirfd == -1) { 1071 goto err; 1072 } 1073 1074 nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); 1075 if (nmap_dirfd == -1) { 1076 close_preserve_errno(omap_dirfd); 1077 goto err; 1078 } 1079 1080 ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0); 1081 close_preserve_errno(nmap_dirfd); 1082 close_preserve_errno(omap_dirfd); 1083 if (ret < 0 && errno != ENOENT) { 1084 goto err_undo_link; 1085 } 1086 1087 ret = 0; 1088 } 1089 goto out_close; 1090 1091 err: 1092 ret = -1; 1093 err_undo_link: 1094 unlinkat_preserve_errno(ndirfd, name, 0); 1095 out_close: 1096 close_preserve_errno(ndirfd); 1097 close_preserve_errno(odirfd); 1098 out: 1099 g_free(oname); 1100 g_free(odirpath); 1101 return ret; 1102 } 1103 1104 static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) 1105 { 1106 int fd, ret; 1107 1108 fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); 1109 if (fd == -1) { 1110 return -1; 1111 } 1112 ret = ftruncate(fd, size); 1113 close_preserve_errno(fd); 1114 return ret; 1115 } 1116 1117 static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) 1118 { 1119 char *dirpath = g_path_get_dirname(fs_path->data); 1120 char *name = g_path_get_basename(fs_path->data); 1121 int ret = -1; 1122 int dirfd; 1123 1124 dirfd = local_opendir_nofollow(fs_ctx, dirpath); 1125 if (dirfd == -1) { 1126 goto out; 1127 } 1128 1129 if ((credp->fc_uid == -1 && credp->fc_gid == -1) || 1130 (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || 1131 (fs_ctx->export_flags & V9FS_SM_NONE)) { 1132 ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid, 1133 AT_SYMLINK_NOFOLLOW); 1134 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { 1135 ret = local_set_xattrat(dirfd, name, credp); 1136 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1137 ret = local_set_mapped_file_attrat(dirfd, name, credp); 1138 } 1139 1140 close_preserve_errno(dirfd); 1141 out: 1142 g_free(name); 1143 g_free(dirpath); 1144 return ret; 1145 } 1146 1147 static int local_utimensat(FsContext *s, V9fsPath *fs_path, 1148 const struct timespec *buf) 1149 { 1150 char *dirpath = g_path_get_dirname(fs_path->data); 1151 char *name = g_path_get_basename(fs_path->data); 1152 int dirfd, ret = -1; 1153 1154 dirfd = local_opendir_nofollow(s, dirpath); 1155 if (dirfd == -1) { 1156 goto out; 1157 } 1158 1159 ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW); 1160 close_preserve_errno(dirfd); 1161 out: 1162 g_free(dirpath); 1163 g_free(name); 1164 return ret; 1165 } 1166 1167 static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, 1168 int flags) 1169 { 1170 int ret = -1; 1171 1172 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1173 int map_dirfd; 1174 1175 if (flags == AT_REMOVEDIR) { 1176 int fd; 1177 1178 fd = openat(dirfd, name, O_RDONLY | O_DIRECTORY | O_PATH); 1179 if (fd == -1) { 1180 goto err_out; 1181 } 1182 /* 1183 * If directory remove .virtfs_metadata contained in the 1184 * directory 1185 */ 1186 ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR); 1187 close_preserve_errno(fd); 1188 if (ret < 0 && errno != ENOENT) { 1189 /* 1190 * We didn't had the .virtfs_metadata file. May be file created 1191 * in non-mapped mode ?. Ignore ENOENT. 1192 */ 1193 goto err_out; 1194 } 1195 } 1196 /* 1197 * Now remove the name from parent directory 1198 * .virtfs_metadata directory. 1199 */ 1200 map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); 1201 ret = unlinkat(map_dirfd, name, 0); 1202 close_preserve_errno(map_dirfd); 1203 if (ret < 0 && errno != ENOENT) { 1204 /* 1205 * We didn't had the .virtfs_metadata file. May be file created 1206 * in non-mapped mode ?. Ignore ENOENT. 1207 */ 1208 goto err_out; 1209 } 1210 } 1211 1212 ret = unlinkat(dirfd, name, flags); 1213 err_out: 1214 return ret; 1215 } 1216 1217 static int local_remove(FsContext *ctx, const char *path) 1218 { 1219 struct stat stbuf; 1220 char *dirpath = g_path_get_dirname(path); 1221 char *name = g_path_get_basename(path); 1222 int flags = 0; 1223 int dirfd; 1224 int err = -1; 1225 1226 dirfd = local_opendir_nofollow(ctx, dirpath); 1227 if (dirfd) { 1228 goto out; 1229 } 1230 1231 if (fstatat(dirfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) { 1232 goto err_out; 1233 } 1234 1235 if (S_ISDIR(stbuf.st_mode)) { 1236 flags |= AT_REMOVEDIR; 1237 } 1238 1239 err = local_unlinkat_common(ctx, dirfd, name, flags); 1240 err_out: 1241 close_preserve_errno(dirfd); 1242 out: 1243 g_free(name); 1244 g_free(dirpath); 1245 return err; 1246 } 1247 1248 static int local_fsync(FsContext *ctx, int fid_type, 1249 V9fsFidOpenState *fs, int datasync) 1250 { 1251 int fd; 1252 1253 if (fid_type == P9_FID_DIR) { 1254 fd = dirfd(fs->dir.stream); 1255 } else { 1256 fd = fs->fd; 1257 } 1258 1259 if (datasync) { 1260 return qemu_fdatasync(fd); 1261 } else { 1262 return fsync(fd); 1263 } 1264 } 1265 1266 static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) 1267 { 1268 int fd, ret; 1269 1270 fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0); 1271 ret = fstatfs(fd, stbuf); 1272 close_preserve_errno(fd); 1273 return ret; 1274 } 1275 1276 static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, 1277 const char *name, void *value, size_t size) 1278 { 1279 char *path = fs_path->data; 1280 1281 return v9fs_get_xattr(ctx, path, name, value, size); 1282 } 1283 1284 static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, 1285 void *value, size_t size) 1286 { 1287 char *path = fs_path->data; 1288 1289 return v9fs_list_xattr(ctx, path, value, size); 1290 } 1291 1292 static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, 1293 void *value, size_t size, int flags) 1294 { 1295 char *path = fs_path->data; 1296 1297 return v9fs_set_xattr(ctx, path, name, value, size, flags); 1298 } 1299 1300 static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, 1301 const char *name) 1302 { 1303 char *path = fs_path->data; 1304 1305 return v9fs_remove_xattr(ctx, path, name); 1306 } 1307 1308 static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, 1309 const char *name, V9fsPath *target) 1310 { 1311 if (dir_path) { 1312 v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); 1313 } else { 1314 v9fs_path_sprintf(target, "%s", name); 1315 } 1316 return 0; 1317 } 1318 1319 static int local_renameat(FsContext *ctx, V9fsPath *olddir, 1320 const char *old_name, V9fsPath *newdir, 1321 const char *new_name) 1322 { 1323 int ret; 1324 int odirfd, ndirfd; 1325 1326 odirfd = local_opendir_nofollow(ctx, olddir->data); 1327 if (odirfd == -1) { 1328 return -1; 1329 } 1330 1331 ndirfd = local_opendir_nofollow(ctx, newdir->data); 1332 if (ndirfd == -1) { 1333 close_preserve_errno(odirfd); 1334 return -1; 1335 } 1336 1337 ret = renameat(odirfd, old_name, ndirfd, new_name); 1338 if (ret < 0) { 1339 goto out; 1340 } 1341 1342 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1343 int omap_dirfd, nmap_dirfd; 1344 1345 ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); 1346 if (ret < 0 && errno != EEXIST) { 1347 goto err_undo_rename; 1348 } 1349 1350 omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR); 1351 if (omap_dirfd == -1) { 1352 goto err; 1353 } 1354 1355 nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR); 1356 if (nmap_dirfd == -1) { 1357 close_preserve_errno(omap_dirfd); 1358 goto err; 1359 } 1360 1361 /* rename the .virtfs_metadata files */ 1362 ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name); 1363 close_preserve_errno(nmap_dirfd); 1364 close_preserve_errno(omap_dirfd); 1365 if (ret < 0 && errno != ENOENT) { 1366 goto err_undo_rename; 1367 } 1368 1369 ret = 0; 1370 } 1371 goto out; 1372 1373 err: 1374 ret = -1; 1375 err_undo_rename: 1376 renameat_preserve_errno(ndirfd, new_name, odirfd, old_name); 1377 out: 1378 close_preserve_errno(ndirfd); 1379 close_preserve_errno(odirfd); 1380 return ret; 1381 } 1382 1383 static void v9fs_path_init_dirname(V9fsPath *path, const char *str) 1384 { 1385 path->data = g_path_get_dirname(str); 1386 path->size = strlen(path->data) + 1; 1387 } 1388 1389 static int local_rename(FsContext *ctx, const char *oldpath, 1390 const char *newpath) 1391 { 1392 int err; 1393 char *oname = g_path_get_basename(oldpath); 1394 char *nname = g_path_get_basename(newpath); 1395 V9fsPath olddir, newdir; 1396 1397 v9fs_path_init_dirname(&olddir, oldpath); 1398 v9fs_path_init_dirname(&newdir, newpath); 1399 1400 err = local_renameat(ctx, &olddir, oname, &newdir, nname); 1401 1402 v9fs_path_free(&newdir); 1403 v9fs_path_free(&olddir); 1404 g_free(nname); 1405 g_free(oname); 1406 1407 return err; 1408 } 1409 1410 static int local_unlinkat(FsContext *ctx, V9fsPath *dir, 1411 const char *name, int flags) 1412 { 1413 int ret; 1414 int dirfd; 1415 1416 dirfd = local_opendir_nofollow(ctx, dir->data); 1417 if (dirfd == -1) { 1418 return -1; 1419 } 1420 1421 ret = local_unlinkat_common(ctx, dirfd, name, flags); 1422 close_preserve_errno(dirfd); 1423 return ret; 1424 } 1425 1426 static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, 1427 mode_t st_mode, uint64_t *st_gen) 1428 { 1429 #ifdef FS_IOC_GETVERSION 1430 int err; 1431 V9fsFidOpenState fid_open; 1432 1433 /* 1434 * Do not try to open special files like device nodes, fifos etc 1435 * We can get fd for regular files and directories only 1436 */ 1437 if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { 1438 errno = ENOTTY; 1439 return -1; 1440 } 1441 err = local_open(ctx, path, O_RDONLY, &fid_open); 1442 if (err < 0) { 1443 return err; 1444 } 1445 err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); 1446 local_close(ctx, &fid_open); 1447 return err; 1448 #else 1449 errno = ENOTTY; 1450 return -1; 1451 #endif 1452 } 1453 1454 static int local_init(FsContext *ctx) 1455 { 1456 struct statfs stbuf; 1457 LocalData *data = g_malloc(sizeof(*data)); 1458 1459 data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); 1460 if (data->mountfd == -1) { 1461 goto err; 1462 } 1463 1464 #ifdef FS_IOC_GETVERSION 1465 /* 1466 * use ioc_getversion only if the ioctl is definied 1467 */ 1468 if (fstatfs(data->mountfd, &stbuf) < 0) { 1469 close_preserve_errno(data->mountfd); 1470 goto err; 1471 } 1472 switch (stbuf.f_type) { 1473 case EXT2_SUPER_MAGIC: 1474 case BTRFS_SUPER_MAGIC: 1475 case REISERFS_SUPER_MAGIC: 1476 case XFS_SUPER_MAGIC: 1477 ctx->exops.get_st_gen = local_ioc_getversion; 1478 break; 1479 } 1480 #endif 1481 1482 if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { 1483 ctx->xops = passthrough_xattr_ops; 1484 } else if (ctx->export_flags & V9FS_SM_MAPPED) { 1485 ctx->xops = mapped_xattr_ops; 1486 } else if (ctx->export_flags & V9FS_SM_NONE) { 1487 ctx->xops = none_xattr_ops; 1488 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { 1489 /* 1490 * xattr operation for mapped-file and passthrough 1491 * remain same. 1492 */ 1493 ctx->xops = passthrough_xattr_ops; 1494 } 1495 ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; 1496 1497 ctx->private = data; 1498 return 0; 1499 1500 err: 1501 g_free(data); 1502 return -1; 1503 } 1504 1505 static void local_cleanup(FsContext *ctx) 1506 { 1507 LocalData *data = ctx->private; 1508 1509 close(data->mountfd); 1510 g_free(data); 1511 } 1512 1513 static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) 1514 { 1515 const char *sec_model = qemu_opt_get(opts, "security_model"); 1516 const char *path = qemu_opt_get(opts, "path"); 1517 1518 if (!sec_model) { 1519 error_report("Security model not specified, local fs needs security model"); 1520 error_printf("valid options are:" 1521 "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n"); 1522 return -1; 1523 } 1524 1525 if (!strcmp(sec_model, "passthrough")) { 1526 fse->export_flags |= V9FS_SM_PASSTHROUGH; 1527 } else if (!strcmp(sec_model, "mapped") || 1528 !strcmp(sec_model, "mapped-xattr")) { 1529 fse->export_flags |= V9FS_SM_MAPPED; 1530 } else if (!strcmp(sec_model, "none")) { 1531 fse->export_flags |= V9FS_SM_NONE; 1532 } else if (!strcmp(sec_model, "mapped-file")) { 1533 fse->export_flags |= V9FS_SM_MAPPED_FILE; 1534 } else { 1535 error_report("Invalid security model %s specified", sec_model); 1536 error_printf("valid options are:" 1537 "\t[passthrough|mapped-xattr|mapped-file|none]\n"); 1538 return -1; 1539 } 1540 1541 if (!path) { 1542 error_report("fsdev: No path specified"); 1543 return -1; 1544 } 1545 fse->path = g_strdup(path); 1546 1547 return 0; 1548 } 1549 1550 FileOperations local_ops = { 1551 .parse_opts = local_parse_opts, 1552 .init = local_init, 1553 .cleanup = local_cleanup, 1554 .lstat = local_lstat, 1555 .readlink = local_readlink, 1556 .close = local_close, 1557 .closedir = local_closedir, 1558 .open = local_open, 1559 .opendir = local_opendir, 1560 .rewinddir = local_rewinddir, 1561 .telldir = local_telldir, 1562 .readdir = local_readdir, 1563 .seekdir = local_seekdir, 1564 .preadv = local_preadv, 1565 .pwritev = local_pwritev, 1566 .chmod = local_chmod, 1567 .mknod = local_mknod, 1568 .mkdir = local_mkdir, 1569 .fstat = local_fstat, 1570 .open2 = local_open2, 1571 .symlink = local_symlink, 1572 .link = local_link, 1573 .truncate = local_truncate, 1574 .rename = local_rename, 1575 .chown = local_chown, 1576 .utimensat = local_utimensat, 1577 .remove = local_remove, 1578 .fsync = local_fsync, 1579 .statfs = local_statfs, 1580 .lgetxattr = local_lgetxattr, 1581 .llistxattr = local_llistxattr, 1582 .lsetxattr = local_lsetxattr, 1583 .lremovexattr = local_lremovexattr, 1584 .name_to_path = local_name_to_path, 1585 .renameat = local_renameat, 1586 .unlinkat = local_unlinkat, 1587 }; 1588