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