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