master
cpp 926 lines 23 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 drvfs.c
8
9 Abstract:
10
11 This file contains DrvFs function definitions.
12
13 --*/
14
15 #include "common.h"
16 #include <sys/mount.h>
17 #include <sys/stat.h>
18 #include <stdarg.h>
19 #include <mountutilcpp.h>
20 #include "util.h"
21 #include "drvfs.h"
22 #include "config.h"
23 #include "message.h"
24 #include <cassert>
25 #include <filesystem>
26 #include <mutex>
27 #include <optional>
28 #include <thread>
29
30 using namespace std::chrono_literals;
31
32 #define PLAN9_CASE_OPTION "case="
33 #define PLAN9_CASE_OPTION_DIR PLAN9_CASE_OPTION "dir"
34 #define PLAN9_CASE_OPTION_FORCE PLAN9_CASE_OPTION "force"
35 #define PLAN9_CASE_OPTION_OFF PLAN9_CASE_OPTION "off"
36 #define PLAN9_SYMLINK_ROOT_OPTION "symlinkroot="
37 #define PLAN9_UNC_PREFIX_LENGTH (2)
38
39 #define VIRTIOFS_TAG_DIR "/run/wsl/virtiofs"
40
41 #define LOG_STDERR(_errno) fprintf(stderr, "mount: %s\n", strerror(_errno))
42
43 constexpr int c_exitCodeInvalidUsage = 1;
44 constexpr int c_exitCodeMountFail = 32;
45
46 int MountFilesystem(const char* FsType, const char* Source, const char* Target, const char* Options, int* ExitCode = nullptr);
47
48 int MountWithRetry(const char* Source, const char* Target, const char* FsType, const char* Options, int* ExitCode = nullptr);
49
50 std::optional<VirtioFsMountRoot> ParseAggregateVirtioFsMountRoot(std::string_view Tag, std::string_view Root)
51 {
52 if ((Tag != LX_INIT_DRVFS_VIRTIO_TAG && Tag != LX_INIT_DRVFS_ADMIN_VIRTIO_TAG) || Root.size() < 2 || Root.front() != '/')
53 {
54 return {};
55 }
56
57 Root.remove_prefix(1);
58 const auto separator = Root.find('/');
59 const auto childName = Root.substr(0, separator);
60 if (!wsl::shared::string::ToGuid(childName))
61 {
62 return {};
63 }
64
65 const auto subPath = separator == std::string_view::npos ? std::string_view{"/"} : Root.substr(separator);
66 return VirtioFsMountRoot{childName, subPath};
67 }
68
69 namespace {
70 std::mutex g_virtiofsDeviceMutex;
71
72 bool IsMountPoint(const std::string& Path)
73 {
74 struct stat self = {};
75 struct stat parent = {};
76 if (stat(Path.c_str(), &self) != 0)
77 {
78 return false;
79 }
80
81 const auto parentPath = Path + "/..";
82 if (stat(parentPath.c_str(), &parent) != 0)
83 {
84 return false;
85 }
86
87 return self.st_dev != parent.st_dev;
88 }
89 } // namespace
90
91 int MountVirtioFsChild(const char* Tag, const char* ChildName, const char* Target, const char* Options, int* ExitCode, std::string_view SubPath)
92 {
93 if ((strcmp(Tag, LX_INIT_DRVFS_VIRTIO_TAG) != 0 && strcmp(Tag, LX_INIT_DRVFS_ADMIN_VIRTIO_TAG) != 0) ||
94 !wsl::shared::string::ToGuid(ChildName) || SubPath.empty() || SubPath.front() != '/')
95 {
96 errno = EINVAL;
97 return -1;
98 }
99
100 const auto rootTarget = std::format("{}/{}", VIRTIOFS_MOUNT_DIR, Tag);
101 const auto childSource = std::format("{}/{}{}", rootTarget, ChildName, SubPath == "/" ? std::string_view{} : SubPath);
102 {
103 std::lock_guard<std::mutex> lock(g_virtiofsDeviceMutex);
104 if (!IsMountPoint(rootTarget))
105 {
106 if (UtilMkdirPath(rootTarget.c_str(), 0755) < 0)
107 {
108 return -1;
109 }
110
111 if (MountWithRetry(Tag, rootTarget.c_str(), VIRTIO_FS_TYPE, "", ExitCode) < 0 && !IsMountPoint(rootTarget))
112 {
113 return -1;
114 }
115 }
116 }
117
118 if (UtilMkdirPath(Target, 0755) < 0)
119 {
120 return -1;
121 }
122
123 constexpr int c_maxAttempts = 5;
124 int lastError = 0;
125 for (int attempt = 0; attempt < c_maxAttempts; ++attempt)
126 {
127 if (mount(childSource.c_str(), Target, nullptr, MS_BIND, nullptr) == 0)
128 {
129 lastError = 0;
130 break;
131 }
132
133 lastError = errno;
134 if (lastError != ENOENT)
135 {
136 break;
137 }
138
139 std::this_thread::sleep_for(std::chrono::milliseconds{50});
140 }
141
142 if (lastError != 0)
143 {
144 errno = lastError;
145 if (ExitCode != nullptr)
146 {
147 *ExitCode = c_exitCodeMountFail;
148 }
149
150 return -1;
151 }
152
153 const auto parsed = mountutil::MountParseFlags(Options ? Options : "");
154 auto bindFlags = parsed.MountFlags;
155 if ((bindFlags & (MS_NOATIME | MS_STRICTATIME)) == 0)
156 {
157 bindFlags |= MS_RELATIME;
158 }
159
160 if (mount(nullptr, Target, nullptr, MS_BIND | MS_REMOUNT | bindFlags, parsed.StringOptions.c_str()) < 0)
161 {
162 const auto savedError = errno;
163 // Remove the successful bind before MountVirtioFs falls back to Plan 9.
164 umount2(Target, MNT_DETACH);
165 errno = savedError;
166 if (ExitCode != nullptr)
167 {
168 *ExitCode = c_exitCodeMountFail;
169 }
170
171 return -1;
172 }
173
174 if (ExitCode != nullptr)
175 {
176 *ExitCode = 0;
177 }
178
179 return 0;
180 }
181
182 void SaveVirtiofsTagMapping(const char* Tag, const char* Source)
183
184 /*++
185
186 Routine Description:
187
188 This routine creates a symlink in VIRTIOFS_TAG_DIR that maps a virtiofs tag
189 to its Windows mount source path. This allows QueryVirtiofsMountSource to
190 resolve tags without talking to the service.
191
192 Arguments:
193
194 Tag - Supplies the virtiofs tag.
195
196 Source - Supplies the Windows path the tag refers to.
197
198 Return Value:
199
200 None.
201
202 --*/
203
204 {
205 //
206 // Validate the tag is a GUID to prevent path traversal.
207 //
208
209 const auto Guid = wsl::shared::string::ToGuid(Tag);
210 if (!Guid)
211 {
212 LOG_WARNING("Invalid virtiofs tag {}", Tag);
213 return;
214 }
215
216 //
217 // Canonicalize path separators to backslashes before persisting.
218 //
219
220 std::string CanonicalSource{Source};
221 UtilCanonicalisePathSeparator(CanonicalSource, PATH_SEP_NT);
222
223 UtilMkdirPath(VIRTIOFS_TAG_DIR, 0755);
224
225 auto LinkPath = std::format("{}/{}", VIRTIOFS_TAG_DIR, Tag);
226
227 //
228 // Remove any existing symlink for this tag before creating a new one.
229 //
230
231 unlink(LinkPath.c_str());
232 if (symlink(CanonicalSource.c_str(), LinkPath.c_str()) < 0)
233 {
234 LOG_WARNING("Failed to create virtiofs tag symlink {} -> {}: {}", LinkPath, CanonicalSource, errno);
235 }
236 }
237
238 std::pair<std::string, std::string> ConvertDrvfsMountOptionsToPlan9(std::string_view Options, const wsl::linux::WslDistributionConfig& Config)
239
240 /*++
241
242 Routine Description:
243
244 This routine converts each applicable DrvFs mount option into a 9p mount option and splits
245 non-DrvFs options to a different list.
246
247 Arguments:
248
249 Options - Supplies the DrvFs mount options.
250
251 Config - Supplies the distribution configuration.
252
253 Return Value:
254
255 A pair representing the 9p mount options, and standard mount options.
256
257 --*/
258
259 {
260 using wsl::shared::string::StartsWith;
261
262 std::string Plan9Options{};
263 std::string StandardOptions{};
264 while (!Options.empty())
265 {
266 auto Option = UtilStringNextToken(Options, ",");
267 if ((Option == "metadata") || (StartsWith(Option, PLAN9_CASE_OPTION)) || (StartsWith(Option, "uid=")) ||
268 (StartsWith(Option, "gid=")) || (StartsWith(Option, "umask=")) || (StartsWith(Option, "dmask=")) ||
269 (StartsWith(Option, "fmask=")) || (StartsWith(Option, PLAN9_SYMLINK_ROOT_OPTION)))
270 {
271 if (Option == PLAN9_CASE_OPTION_FORCE)
272 {
273 LOG_WARNING("{} not supported, using {}", PLAN9_CASE_OPTION_FORCE, PLAN9_CASE_OPTION_DIR);
274 Option = PLAN9_CASE_OPTION_DIR;
275 }
276
277 Plan9Options += ';';
278 Plan9Options += Option;
279 }
280 else if (StartsWith(Option, "fallback="))
281 {
282 LOG_WARNING("{} not supported, ignoring...", Option);
283 }
284 else
285 {
286 StandardOptions += Option;
287 StandardOptions += ',';
288 }
289 }
290
291 Plan9Options += ";" PLAN9_SYMLINK_ROOT_OPTION;
292 Plan9Options += Config.DrvFsPrefix;
293 return {std::move(Plan9Options), std::move(StandardOptions)};
294 }
295
296 bool IsDrvfsElevated(void)
297
298 /*++
299
300 Routine Description:
301
302 This routine determines whether drvfs mounts should use the elevated server.
303
304 Arguments:
305
306 None.
307
308 Return Value:
309
310 True if this process should use the elevated server; otherwise, false.
311
312 --*/
313
314 {
315 const char* envFlag = getenv(WSL_DRVFS_ELEVATED_ENV);
316 if (envFlag != nullptr)
317 {
318 if (strcmp(envFlag, "0") == 0)
319 {
320 return false;
321 }
322 else if (strcmp(envFlag, "1") == 0)
323 {
324 return true;
325 }
326
327 LOG_ERROR("Unexpected value for {}: '{}'", WSL_DRVFS_ELEVATED_ENV, envFlag);
328 }
329
330 //
331 // Establish a connection to the interop server. If the connection cannot
332 // be established, use the non-admin DrvFs port.
333 //
334
335 wsl::shared::SocketChannel channel{UtilConnectToInteropServer(), "InteropClientDrvfs"};
336 if (channel.Socket() < 0)
337 {
338 return false;
339 }
340
341 //
342 // Query the interop server for which port to use.
343 //
344
345 MESSAGE_HEADER QueryPortMessage{};
346 QueryPortMessage.MessageType = LxInitMessageQueryDrvfsElevated;
347 QueryPortMessage.MessageSize = sizeof(QueryPortMessage);
348
349 auto transaction = channel.StartTransaction();
350 transaction.Send(QueryPortMessage);
351 return transaction.Receive<RESULT_MESSAGE<bool>>().Result;
352 }
353
354 int MountFilesystem(const char* FsType, const char* Source, const char* Target, const char* Options, int* ExitCode)
355
356 /*++
357
358 Routine Description:
359
360 This routine will perform a mount using the /bin/mount binary.
361
362 Arguments:
363
364 FsType - Supplies the file system type.
365
366 Source - Supplies the mount source.
367
368 Target - Supplies the mount target.
369
370 Options - Supplies the mount options.
371
372 ExitCode - Supplies an optional pointer that receives the exit code.
373
374 Return Value:
375
376 0 on success, -1 on failure.
377
378 --*/
379
380 {
381 const char* const Argv[] = {
382 MOUNT_COMMAND, MOUNT_INTERNAL_ONLY_ARG, MOUNT_TYPES_ARG, FsType, Source, Target, MOUNT_OPTIONS_ARG, Options, nullptr};
383
384 int Status = 0;
385 const int Result = UtilCreateProcessAndWait(Argv[0], Argv, &Status);
386
387 //
388 // If the mount process failed, make sure its exit code is propagated. If it terminated
389 // abnormally or could not be launched, just return failure.
390 //
391
392 if (ExitCode != nullptr)
393 {
394 if (Result < 0)
395 {
396 if (WIFEXITED(Status) && Status != 0)
397 {
398 *ExitCode = WEXITSTATUS(Status);
399 }
400 else
401 {
402 *ExitCode = c_exitCodeMountFail;
403 }
404 }
405 else
406 {
407 *ExitCode = 0;
408 }
409 }
410
411 return Result;
412 }
413
414 int MountWithRetry(const char* Source, const char* Target, const char* FsType, const char* Options, int* ExitCode)
415
416 /*++
417
418 Routine Description:
419
420 This routine performs a mount with retry logic for DrvFs filesystems.
421
422 Arguments:
423
424 Source - Supplies the mount source.
425
426 Target - Supplies the mount target.
427
428 FsType - Supplies the filesystem type.
429
430 Options - Supplies the mount options.
431
432 ExitCode - Supplies an optional pointer that receives the exit code.
433
434 Return Value:
435
436 0 on success, -1 on failure.
437
438 --*/
439
440 try
441 {
442 //
443 // Verify the target directory exists before mounting.
444 //
445
446 int Result = access(Target, F_OK);
447 if (Result < 0)
448 {
449 LOG_STDERR(errno);
450 }
451 else
452 {
453 auto Parsed = mountutil::MountParseFlags(Options);
454 Result = UtilMount(Source, Target, FsType, Parsed.MountFlags, Parsed.StringOptions.c_str(), std::chrono::seconds{2});
455 }
456
457 if (ExitCode)
458 {
459 *ExitCode = Result < 0 ? c_exitCodeMountFail : 0;
460 }
461
462 return Result;
463 }
464 CATCH_RETURN_ERRNO()
465
466 int MountDrvfs(const char* Source, const char* Target, const char* Options, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config, int* ExitCode)
467
468 /*++
469
470 Routine Description:
471
472 This routine will perform a DrvFs mount.
473
474 Arguments:
475
476 Source - Supplies the mount source.
477
478 Target - Supplies the mount target.
479
480 Options - Supplies the mount options.
481
482 Admin - Supplies an optional boolean to specify if the admin or non-admin share should be used.
483
484 ExitCode - Supplies an optional pointer that receives the exit code.
485
486 Return Value:
487
488 0 on success, -1 on failure.
489
490 --*/
491
492 try
493 {
494 if (!UtilIsUtilityVm())
495 {
496 return MountFilesystem(DRVFS_FS_TYPE, Source, Target, Options, ExitCode);
497 }
498 else if (WSL_USE_VIRTIO_FS())
499 {
500 return MountVirtioFs(Source, Target, Options, Admin, Config, ExitCode);
501 }
502
503 return MountPlan9(Source, Target, Options, Admin, Config, ExitCode);
504 }
505 CATCH_RETURN_ERRNO()
506
507 int MountDrvfsEntry(int Argc, char* Argv[])
508
509 /*++
510
511 Routine Description:
512
513 This routine is the entrypoint for mount.drvfs.
514
515 Arguments:
516
517 Argc - Supplies the argument count.
518
519 Argv - Supplies the command line arguments.
520
521 Return Value:
522
523 0 on success, -1 on failure.
524
525 --*/
526
527 {
528 if (Argc < 3)
529 {
530 LOG_STDERR(EINVAL);
531 return c_exitCodeInvalidUsage;
532 }
533
534 //
535 // Handle mount options if provided.
536 //
537
538 auto* Options = "";
539 if (Argc > 4)
540 {
541 Options = Argv[4];
542 }
543
544 int ExitCode = c_exitCodeMountFail;
545 MountDrvfs(Argv[1], Argv[2], Options, {}, wsl::linux::WslDistributionConfig{CONFIG_FILE}, &ExitCode);
546 return ExitCode;
547 }
548
549 int MountPlan9Share(const char* Source, const char* Target, const char* Options, bool Admin, int* ExitCode)
550
551 /*++
552
553 Routine Description:
554
555 This routine will perform a plan 9 mount using the /bin/mount binary.
556
557 Arguments:
558
559 Source - Supplies the mount source.
560
561 Target - Supplies the mount target.
562
563 Options - Supplies the mount options.
564
565 Admin - Supplies a boolean specifying if the admin share should be used.
566
567 ExitCode - Supplies an optional pointer that receives the exit code.
568
569 Return Value:
570
571 0 on success, -1 on failure.
572
573 --*/
574
575 {
576 std::string MountOptions;
577 if (WSL_USE_VIRTIO_9P())
578 {
579 Source = Admin ? LX_INIT_DRVFS_ADMIN_VIRTIO_TAG : LX_INIT_DRVFS_VIRTIO_TAG;
580 MountOptions = std::format("msize=262144,trans=virtio,{}", Options);
581 return MountWithRetry(Source, Target, PLAN9_FS_TYPE, MountOptions.c_str(), ExitCode);
582 }
583 else
584 {
585 auto Port = Admin ? LX_INIT_UTILITY_VM_PLAN9_DRVFS_ADMIN_PORT : LX_INIT_UTILITY_VM_PLAN9_DRVFS_PORT;
586 wil::unique_fd Fd{UtilConnectVsock(Port, false, LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE)};
587 if (!Fd)
588 {
589 return -1;
590 }
591
592 MountOptions =
593 std::format("msize={},trans=fd,rfdno={},wfdno={},{}", LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE, Fd.get(), Fd.get(), Options);
594
595 return MountFilesystem(PLAN9_FS_TYPE, Source, Target, MountOptions.c_str(), ExitCode);
596 }
597 }
598
599 int MountPlan9(const char* Source, const char* Target, const char* Options, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config, int* ExitCode)
600
601 /*++
602
603 Routine Description:
604
605 This routine will perform a DrvFs mount using Plan9.
606
607 Arguments:
608
609 Source - Supplies the mount source.
610
611 Target - Supplies the mount target.
612
613 Options - Supplies the mount options.
614
615 Admin - Supplies an optional boolean to specify if the admin or non-admin share should be used.
616
617 Config - Supplies the distribution configuration.
618
619 ExitCode - Supplies an optional pointer that receives the exit code.
620
621 Return Value:
622
623 0 on success, -1 on failure.
624
625 --*/
626
627 try
628 {
629 //
630 // Check if the path is a UNC path.
631 //
632
633 const char* Plan9Source;
634 std::string UncSource;
635 if ((strlen(Source) >= PLAN9_UNC_PREFIX_LENGTH) && ((Source[0] == '/') || (Source[0] == '\\')) &&
636 ((Source[1] == '/') || (Source[1] == '\\')))
637 {
638 UncSource = PLAN9_UNC_TRANSLATED_PREFIX;
639 UncSource += &Source[PLAN9_UNC_PREFIX_LENGTH];
640 Plan9Source = UncSource.c_str();
641 }
642 else
643 {
644 Plan9Source = Source;
645 }
646
647 //
648 // Check whether to use the elevated or regular 9p server.
649 //
650
651 bool Elevated = Admin.has_value() ? Admin.value() : IsDrvfsElevated();
652
653 //
654 // Initialize mount options.
655 //
656
657 auto Plan9Options = std::format("{};path={}", PLAN9_ANAME_DRVFS, Plan9Source);
658
659 //
660 // N.B. The cache option is added to the start of this so if the user
661 // specifies one explicitly, it will override the default.
662 //
663
664 std::string MountOptions = "cache=mmap,";
665 auto ParsedOptions = ConvertDrvfsMountOptionsToPlan9(Options ? Options : "", Config);
666 Plan9Options += ParsedOptions.first;
667 MountOptions += ParsedOptions.second;
668
669 //
670 // Append the 9p mount options to the end of the other mount options and perform the mount operation.
671 //
672
673 MountOptions += Plan9Options;
674 if (MountPlan9Share(Source, Target, MountOptions.c_str(), Elevated, ExitCode) < 0)
675 {
676 return -1;
677 }
678
679 return 0;
680 }
681 CATCH_RETURN_ERRNO()
682
683 int MountVirtioFs(const char* Source, const char* Target, const char* Options, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config, int* ExitCode)
684
685 /*++
686
687 Routine Description:
688
689 This routine mounts a virtiofs share. The DrvFs mount options are converted into 9p mount options
690 which are used to determine behavior when the device is added to the host.
691
692 Arguments:
693
694 Source - Supplies the mount source.
695
696 Target - Supplies the mount target.
697
698 Options - Supplies DrvFs mount options to translate into Plan9 mount
699 options.
700
701 Admin - Supplies an optional boolean to specify if the admin or non-admin server should be used.
702
703 ExitCode - Supplies an optional pointer that receives the exit code.
704
705 Return Value:
706
707 0 on success, -1 on failure.
708
709 --*/
710
711 try
712 {
713 assert(WSL_USE_VIRTIO_FS());
714
715 //
716 // Check whether to use the elevated or non-elevated virtiofs server.
717 //
718
719 if (!Admin.has_value())
720 {
721 Admin = IsDrvfsElevated();
722 }
723
724 //
725 // Convert the DrvFs mount options.
726 //
727 // N.B. Since virtiofs does not allow passing mount options, the 9p mount options are used to specify share
728 // behavior when creating virtiofs shares on the host.
729 //
730
731 auto [Plan9Options, MountOptions] = ConvertDrvfsMountOptionsToPlan9(Options ? Options : "", Config);
732 const auto ParsedOptions = mountutil::MountParseFlags(MountOptions);
733 if ((ParsedOptions.MountFlags & MS_RDONLY) != 0)
734 {
735 Plan9Options += ";ro";
736 }
737
738 //
739 // Construct a request to add a virtiofs share.
740 //
741
742 wsl::shared::MessageWriter<LX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE> AddShare(LxInitMessageAddVirtioFsDevice);
743 AddShare->Admin = Admin.value();
744 AddShare.WriteString(AddShare->PathOffset, Source);
745 AddShare.WriteString(AddShare->OptionsOffset, Plan9Options);
746
747 //
748 // Connect to the wsl service to add the virtiofs share. If adding the share fails, fallback to mounting using Plan9.
749 //
750
751 wsl::shared::SocketChannel Channel{UtilConnectVsock(LX_INIT_UTILITY_VM_VIRTIOFS_PORT, true), "VirtoFs"};
752 if (Channel.Socket() < 0)
753 {
754 return -1;
755 }
756
757 gsl::span<gsl::byte> ResponseSpan;
758 const auto& Response = Channel.Transaction<LX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE>(AddShare.Span(), &ResponseSpan);
759 if (Response.Result != 0)
760 {
761 LOG_WARNING("Add virtiofs share for {} failed {}, falling back to Plan9", Source, Response.Result);
762 return MountPlan9(Source, Target, Options, Admin, Config, ExitCode);
763 }
764
765 //
766 // Perform the mount operation.
767 //
768
769 auto* Tag = wsl::shared::string::FromSpan(ResponseSpan, Response.TagOffset);
770 auto* ChildName = wsl::shared::string::FromSpan(ResponseSpan, Response.ChildNameOffset);
771 auto* ResponseSource = wsl::shared::string::FromSpan(ResponseSpan, Response.SourceOffset);
772 const char* MappingName = Tag;
773 if (*ChildName == '\0')
774 {
775 THROW_LAST_ERROR_IF(MountWithRetry(Tag, Target, VIRTIO_FS_TYPE, MountOptions.c_str(), ExitCode) < 0);
776 }
777 else if (MountVirtioFsChild(Tag, ChildName, Target, MountOptions.c_str(), ExitCode) < 0)
778 {
779 const auto childError = errno;
780 LOG_WARNING("Mounting virtiofs child for {} failed {}, falling back to Plan9", Source, childError);
781
782 return MountPlan9(Source, Target, Options, Admin, Config, ExitCode);
783 }
784 else
785 {
786 MappingName = ChildName;
787 }
788
789 //
790 // Save the tag mapping.
791 //
792 // N.B. Use the source path from the response since the service canonicalizes it.
793 //
794
795 SaveVirtiofsTagMapping(MappingName, ResponseSource);
796
797 return 0;
798 }
799 CATCH_RETURN_ERRNO()
800
801 int RemountVirtioFs(const char* Tag, const char* Target, const char* Options, bool Admin, std::string_view SubPath)
802
803 /*++
804
805 Routine Description:
806
807 This routine translates DrvFs mount options into Plan9 mount options and
808 mounts the share.
809
810 Arguments:
811
812 Tag - Supplies the virtiofs tag to remount.
813
814 Target - Supplies the mount target.
815
816 Options - Supplies mount options.
817
818 Admin - Supplies a boolean to specify if the admin or non-admin server should be used.
819
820 Return Value:
821
822 0 on success, -1 on failure.
823
824 --*/
825
826 try
827 {
828 assert(WSL_USE_VIRTIO_FS());
829
830 wsl::shared::MessageWriter<LX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE> RemountShare(LxInitMessageRemountVirtioFsDevice);
831 RemountShare->Admin = Admin;
832 RemountShare.WriteString(RemountShare->TagOffset, Tag);
833
834 //
835 // Connect to the host and send the remount request.
836 //
837
838 wsl::shared::SocketChannel Channel{UtilConnectVsock(LX_INIT_UTILITY_VM_VIRTIOFS_PORT, true), "RemountVirtioFs"};
839 if (Channel.Socket() < 0)
840 {
841 return -1;
842 }
843
844 gsl::span<gsl::byte> ResponseSpan;
845 const auto& Response = Channel.Transaction<LX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE>(RemountShare.Span(), &ResponseSpan);
846 if (Response.Result != 0)
847 {
848 LOG_ERROR("Remount virtiofs share for {} failed {}", Tag, Response.Result);
849 return -1;
850 }
851
852 auto* NewTag = wsl::shared::string::FromSpan(ResponseSpan, Response.TagOffset);
853 auto* ChildName = wsl::shared::string::FromSpan(ResponseSpan, Response.ChildNameOffset);
854 auto* Source = wsl::shared::string::FromSpan(ResponseSpan, Response.SourceOffset);
855 const char* MappingName = NewTag;
856 if (*ChildName == '\0')
857 {
858 THROW_LAST_ERROR_IF(MountWithRetry(NewTag, Target, VIRTIO_FS_TYPE, Options) < 0);
859 }
860 else
861 {
862 THROW_LAST_ERROR_IF(MountVirtioFsChild(NewTag, ChildName, Target, Options, nullptr, SubPath) < 0);
863 MappingName = ChildName;
864 }
865
866 SaveVirtiofsTagMapping(MappingName, Source);
867
868 return 0;
869 }
870 CATCH_RETURN_ERRNO()
871
872 std::string QueryVirtiofsMountSource(const char* Tag, const char* Root)
873
874 /*++
875
876 Routine Description:
877
878 This routine takes a virtiofs tag and determines the Windows path it refers to
879 by reading the symlink created during mount.
880
881 Arguments:
882
883 Tag - Supplies the virtiofs tag to query.
884
885 Root - Optionally supplies the mountinfo root for an aggregate child.
886
887 Return Value:
888
889 The mount source, an empty string on failure.
890
891 --*/
892
893 try
894 {
895 if (!WSL_USE_VIRTIO_FS())
896 {
897 return {};
898 }
899
900 std::string mappingName{Tag};
901 if (Root != nullptr)
902 {
903 if (const auto mountRoot = ParseAggregateVirtioFsMountRoot(Tag, Root))
904 {
905 mappingName = mountRoot->ChildName;
906 }
907 }
908
909 const auto Guid = wsl::shared::string::ToGuid(mappingName);
910 if (!Guid)
911 {
912 return {};
913 }
914
915 //
916 // Read the symlink that maps this tag to its Windows source path.
917 //
918
919 auto LinkPath = std::format("{}/{}", VIRTIOFS_TAG_DIR, mappingName);
920 return std::filesystem::read_symlink(LinkPath).string();
921 }
922 catch (...)
923 {
924 LOG_CAUGHT_EXCEPTION();
925 return {};
926 }