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