1#![deny(warnings)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#[cfg(test)]
13mod test_temp;
14
15use dolang_winterop::security::{SecDesc, Sid};
16use extension::VfsExtension;
17use futures::io::{AsyncRead, AsyncWrite};
18use std::collections::HashMap;
19mod client;
21mod direct;
23pub mod directory;
25pub mod error;
27pub mod extension;
28pub mod file;
29mod macos_acl;
30pub mod metadata;
31mod nfs4_acl;
32pub mod path;
33mod posix_acl;
34pub mod process;
36mod protocol;
37pub mod security;
38pub mod server;
40mod session;
41pub mod target;
42#[cfg(windows)]
43mod windows;
44
45const STREAM_CHUNK_SIZE: usize = 512 * 1024;
56
57const MAX_FILE_READ: usize = STREAM_CHUNK_SIZE;
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq)]
73pub(crate) enum SessionMode {
74 Native,
75 Remote,
76}
77
78#[derive(Clone)]
84enum VfsInner {
85 Client(client::Client),
86 Direct(direct::Direct),
87}
88
89#[derive(Clone)]
90pub struct Vfs {
91 inner: VfsInner,
92}
93
94impl Vfs {
95 fn from_client(client: client::Client) -> Self {
96 Self {
97 inner: VfsInner::Client(client),
98 }
99 }
100
101 fn from_direct(direct: direct::Direct) -> Self {
102 Self {
103 inner: VfsInner::Direct(direct),
104 }
105 }
106
107 pub fn direct() -> error::Result<Self> {
109 direct::Direct::new().map(Self::from_direct)
110 }
111
112 pub async fn new<T>(stream: T) -> error::Result<Self>
117 where
118 T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
119 {
120 client::Client::new(stream).await.map(Self::from_client)
121 }
122
123 pub async fn new_split<R, W>(reader: R, writer: W) -> error::Result<Self>
127 where
128 R: AsyncRead + Send + 'static,
129 W: AsyncWrite + Send + 'static,
130 {
131 client::Client::new_split(reader, writer)
132 .await
133 .map(Self::from_client)
134 }
135
136 #[cfg(unix)]
140 pub async fn connect(path: impl AsRef<std::path::Path>) -> error::Result<Self> {
141 client::Client::connect(path).await.map(Self::from_client)
142 }
143
144 #[cfg(unix)]
151 pub async fn connect_with_key(
152 path: impl AsRef<std::path::Path>,
153 key: Option<dolang_rpc::auth::AuthKey>,
154 ) -> error::Result<Self> {
155 client::Client::connect_with_key(path, key)
156 .await
157 .map(Self::from_client)
158 }
159
160 #[cfg(unix)]
164 pub async fn from_stream(stream: tokio::net::UnixStream) -> error::Result<Self> {
165 client::Client::from_stream(stream)
166 .await
167 .map(Self::from_client)
168 }
169
170 #[cfg(unix)]
175 pub async fn from_owned_fd(value: std::os::fd::OwnedFd) -> error::Result<Self> {
176 client::Client::from_owned_fd(value)
177 .await
178 .map(Self::from_client)
179 }
180
181 #[cfg(unix)]
184 pub async fn from_owned_fd_with_key(
185 value: std::os::fd::OwnedFd,
186 key: Option<dolang_rpc::auth::AuthKey>,
187 ) -> error::Result<Self> {
188 client::Client::from_owned_fd_with_key(value, key)
189 .await
190 .map(Self::from_client)
191 }
192
193 #[cfg(windows)]
200 pub async unsafe fn from_named_pipe_server(
201 pipe: tokio::net::windows::named_pipe::NamedPipeServer,
202 server_process: std::os::windows::io::OwnedHandle,
203 ) -> error::Result<Self> {
204 unsafe { client::Client::from_named_pipe_server(pipe, server_process) }
205 .await
206 .map(Self::from_client)
207 }
208
209 pub fn is_direct(&self) -> bool {
211 matches!(&self.inner, VfsInner::Direct(_))
212 }
213
214 pub async fn stop(self) -> error::Result<()> {
228 match self.inner {
229 VfsInner::Client(client) => client.stop().await,
230 VfsInner::Direct(_) => Ok(()),
231 }
232 }
233
234 pub async fn close(self) {
241 match self.inner {
242 VfsInner::Client(client) => client.close().await,
243 VfsInner::Direct(_) => {}
244 }
245 }
246
247 pub async fn abort(self) {
249 match self.inner {
250 VfsInner::Client(client) => client.abort().await,
251 VfsInner::Direct(_) => {}
252 }
253 }
254
255 pub async fn call_extension<T: VfsExtension>(
258 &self,
259 request: T::Request,
260 ) -> error::Result<T::Response> {
261 match &self.inner {
262 VfsInner::Client(client) => client.call_extension::<T>(request).await,
263 VfsInner::Direct(direct) => direct.call_extension::<T>(request).await,
264 }
265 }
266
267 pub fn env(&self) -> Box<dyn Iterator<Item = (String, String)> + '_> {
269 match &self.inner {
270 VfsInner::Client(client) => client.env(),
271 VfsInner::Direct(direct) => direct.env(),
272 }
273 }
274
275 pub fn cwd(&self) -> path::Path<'_> {
277 match &self.inner {
278 VfsInner::Client(vfs) => vfs.cwd(),
279 VfsInner::Direct(vfs) => vfs.cwd(),
280 }
281 }
282
283 pub fn current_exe(&self) -> path::Path<'_> {
285 match &self.inner {
286 VfsInner::Client(vfs) => vfs.current_exe(),
287 VfsInner::Direct(vfs) => vfs.current_exe(),
288 }
289 }
290
291 pub fn target(&self) -> &target::TargetInfo {
293 match &self.inner {
294 VfsInner::Client(vfs) => vfs.target(),
295 VfsInner::Direct(vfs) => vfs.target(),
296 }
297 }
298
299 pub fn security(&self) -> &security::SecurityInfo {
301 match &self.inner {
302 VfsInner::Client(vfs) => vfs.security(),
303 VfsInner::Direct(vfs) => vfs.security(),
304 }
305 }
306
307 pub fn session(&self) -> uuid::Uuid {
315 match &self.inner {
316 VfsInner::Client(vfs) => vfs.session(),
317 VfsInner::Direct(vfs) => vfs.session(),
318 }
319 }
320
321 pub fn pid(&self) -> u32 {
329 match &self.inner {
330 VfsInner::Client(vfs) => vfs.pid(),
331 VfsInner::Direct(vfs) => vfs.pid(),
332 }
333 }
334
335 pub async fn processes(&self) -> error::Result<process::Processes> {
342 match &self.inner {
343 VfsInner::Client(client) => client.processes().await,
344 VfsInner::Direct(direct) => direct.processes().await,
345 }
346 }
347
348 pub async fn describe_process(&self, pid: u32) -> error::Result<process::ProcessInfo> {
361 match &self.inner {
362 VfsInner::Client(client) => client.describe_process(pid).await,
363 VfsInner::Direct(direct) => direct.describe_process(pid).await,
364 }
365 }
366
367 pub async fn open_process_info(
372 &self,
373 info: &process::ProcessInfo,
374 ) -> error::Result<process::Process> {
375 if info.session() != self.session() {
376 return Err(error::Error::new(
377 error::ErrorKind::InvalidInput,
378 "process record was captured from a different target",
379 ));
380 }
381 self.open_process_raw(info.pid(), Some(info.start_time()))
382 .await
383 }
384
385 pub async fn open_process(&self, pid: u32) -> error::Result<process::Process> {
393 self.open_process_raw(pid, None).await
394 }
395
396 pub(crate) async fn open_process_raw(
397 &self,
398 pid: u32,
399 start: Option<process::StartTime>,
400 ) -> error::Result<process::Process> {
401 match &self.inner {
402 VfsInner::Client(client) => client.open_process(pid, start).await,
403 VfsInner::Direct(direct) => direct.open_process(pid, start).await,
404 }
405 }
406
407 pub fn extensions(&self) -> &extension::ExtensionSet {
409 match &self.inner {
410 VfsInner::Client(vfs) => vfs.extensions(),
411 VfsInner::Direct(vfs) => vfs.extensions(),
412 }
413 }
414
415 pub fn open_options(&self) -> file::OpenOptions<'_> {
417 match &self.inner {
418 VfsInner::Client(client) => file::OpenOptions::client(client.open_options()),
419 VfsInner::Direct(direct) => file::OpenOptions::direct(direct.open_options()),
420 }
421 }
422
423 pub fn command(&self, program: path::Path<'_>) -> process::Command<'_> {
425 process::Command::new(self, program)
426 }
427
428 pub async fn unix_socket(
435 &self,
436 path: path::Path<'_>,
437 key: Option<&[u8]>,
438 ) -> error::Result<Vfs> {
439 match &self.inner {
440 VfsInner::Client(client) => client.unix_socket(path, key).await,
441 VfsInner::Direct(direct) => direct.unix_socket(path, key).await,
442 }
443 }
444
445 pub async fn windows_admin(
447 &self,
448 cwd: path::Path<'_>,
449 env: HashMap<String, Option<String>>,
450 elevate: bool,
451 ) -> error::Result<Vfs> {
452 match &self.inner {
453 VfsInner::Client(client) => client.windows_admin(cwd, env, elevate).await,
454 VfsInner::Direct(direct) => direct.windows_admin(cwd, env, elevate).await,
455 }
456 }
457
458 pub async fn pipe(
463 &self,
464 buf_size: Option<usize>,
465 ) -> error::Result<(process::PipeSend, process::PipeRecv)> {
466 match &self.inner {
467 VfsInner::Client(client) => client.pipe(buf_size).await,
468 VfsInner::Direct(direct) => direct.pipe(buf_size).await,
469 }
470 }
471
472 pub async fn user_name(&self, uid: u32) -> error::Result<String> {
474 match &self.inner {
475 VfsInner::Client(client) => client.user_name(uid).await,
476 VfsInner::Direct(direct) => direct.user_name(uid).await,
477 }
478 }
479
480 pub async fn user_id(&self, name: &str) -> error::Result<u32> {
482 match &self.inner {
483 VfsInner::Client(client) => client.user_id(name).await,
484 VfsInner::Direct(direct) => direct.user_id(name).await,
485 }
486 }
487
488 pub async fn group_name(&self, gid: u32) -> error::Result<String> {
490 match &self.inner {
491 VfsInner::Client(client) => client.group_name(gid).await,
492 VfsInner::Direct(direct) => direct.group_name(gid).await,
493 }
494 }
495
496 pub async fn group_id(&self, name: &str) -> error::Result<u32> {
498 match &self.inner {
499 VfsInner::Client(client) => client.group_id(name).await,
500 VfsInner::Direct(direct) => direct.group_id(name).await,
501 }
502 }
503
504 pub async fn sid_name(&self, sid: &Sid) -> error::Result<security::SidName> {
506 match &self.inner {
507 VfsInner::Client(client) => client.sid_name(sid).await,
508 VfsInner::Direct(direct) => direct.sid_name(sid).await,
509 }
510 }
511
512 pub async fn account_name(&self, name: &str) -> error::Result<security::SidName> {
514 match &self.inner {
515 VfsInner::Client(client) => client.account_name(name).await,
516 VfsInner::Direct(direct) => direct.account_name(name).await,
517 }
518 }
519
520 pub async fn resolve_principal_id(
523 &self,
524 input: security::PrincipalId,
525 want: security::PrincipalIdKind,
526 ) -> error::Result<security::PrincipalId> {
527 match &self.inner {
528 VfsInner::Client(client) => client.resolve_principal_id(input, want).await,
529 VfsInner::Direct(direct) => direct.resolve_principal_id(input, want).await,
530 }
531 }
532
533 pub async fn read_dir(&self, path: path::Path<'_>) -> error::Result<directory::ReadDir> {
535 match &self.inner {
536 VfsInner::Client(client) => client.read_dir(path).await,
537 VfsInner::Direct(direct) => direct.read_dir(path).await,
538 }
539 }
540
541 pub async fn which(
543 &self,
544 program: path::Path<'_>,
545 path: Option<&str>,
546 cwd: Option<path::Path<'_>>,
547 ) -> error::Result<Option<path::PathBuf>> {
548 match &self.inner {
549 VfsInner::Client(client) => client.which(program, path, cwd).await,
550 VfsInner::Direct(direct) => direct.which(program, path, cwd).await,
551 }
552 }
553
554 pub async fn well_known_path(
559 &self,
560 key: path::WellKnownPath,
561 app: Option<&str>,
562 env: &HashMap<String, Option<String>>,
563 ) -> error::Result<Option<path::PathBuf>> {
564 match &self.inner {
565 VfsInner::Client(client) => client.well_known_path(key, app, env).await,
566 VfsInner::Direct(direct) => direct.well_known_path(key, app, env).await,
567 }
568 }
569
570 pub async fn clear_cache(&self) -> error::Result<()> {
572 match &self.inner {
573 VfsInner::Client(client) => client.clear_cache().await,
574 VfsInner::Direct(direct) => direct.clear_cache().await,
575 }
576 }
577
578 pub async fn xattrs(
580 &self,
581 path: path::Path<'_>,
582 namespace: file::XattrNamespace<'_>,
583 follow: bool,
584 ) -> error::Result<Vec<file::XattrEntry>> {
585 match &self.inner {
586 VfsInner::Client(client) => client.xattrs(path, namespace, follow).await,
587 VfsInner::Direct(direct) => direct.xattrs(path, namespace, follow).await,
588 }
589 }
590
591 pub async fn streams(
593 &self,
594 path: path::Path<'_>,
595 follow: bool,
596 ) -> error::Result<Vec<file::StreamEntry>> {
597 match &self.inner {
598 VfsInner::Client(client) => client.streams(path, follow).await,
599 VfsInner::Direct(direct) => direct.streams(path, follow).await,
600 }
601 }
602
603 pub async fn xattr(
605 &self,
606 path: path::Path<'_>,
607 name: &str,
608 namespace: Option<&str>,
609 follow: bool,
610 ) -> error::Result<Vec<u8>> {
611 match &self.inner {
612 VfsInner::Client(client) => client.xattr(path, name, namespace, follow).await,
613 VfsInner::Direct(direct) => direct.xattr(path, name, namespace, follow).await,
614 }
615 }
616
617 pub async fn set_xattr(
619 &self,
620 path: path::Path<'_>,
621 name: &str,
622 namespace: Option<&str>,
623 value: &[u8],
624 follow: bool,
625 ) -> error::Result<()> {
626 match &self.inner {
627 VfsInner::Client(client) => {
628 client.set_xattr(path, name, namespace, value, follow).await
629 }
630 VfsInner::Direct(direct) => {
631 direct.set_xattr(path, name, namespace, value, follow).await
632 }
633 }
634 }
635
636 pub async fn remove_xattr(
638 &self,
639 path: path::Path<'_>,
640 name: &str,
641 namespace: Option<&str>,
642 follow: bool,
643 ) -> error::Result<()> {
644 match &self.inner {
645 VfsInner::Client(client) => client.remove_xattr(path, name, namespace, follow).await,
646 VfsInner::Direct(direct) => direct.remove_xattr(path, name, namespace, follow).await,
647 }
648 }
649
650 pub async fn remove(&self, path: path::Path<'_>, all: bool, ignore: bool) -> error::Result<()> {
652 match &self.inner {
653 VfsInner::Client(client) => client.remove(path, all, ignore).await,
654 VfsInner::Direct(direct) => direct.remove(path, all, ignore).await,
655 }
656 }
657
658 pub async fn metadata(&self, path: path::Path<'_>) -> error::Result<metadata::Metadata> {
660 match &self.inner {
661 VfsInner::Client(client) => client.metadata(path).await,
662 VfsInner::Direct(direct) => direct.metadata(path).await,
663 }
664 }
665
666 pub async fn fs_metadata(
668 &self,
669 path: path::Path<'_>,
670 follow: bool,
671 ) -> error::Result<metadata::FsMetadata> {
672 match &self.inner {
673 VfsInner::Client(client) => client.fs_metadata(path, follow).await,
674 VfsInner::Direct(direct) => direct.fs_metadata(path, follow).await,
675 }
676 }
677
678 pub async fn acl(
681 &self,
682 path: path::Path<'_>,
683 kind: security::AclKind,
684 default: bool,
685 follow: bool,
686 ) -> error::Result<Option<security::Acl>> {
687 match &self.inner {
688 VfsInner::Client(client) => client.acl(path, kind, default, follow).await,
689 VfsInner::Direct(direct) => direct.acl(path, kind, default, follow).await,
690 }
691 }
692
693 pub async fn set_acl(
696 &self,
697 path: path::Path<'_>,
698 kind: security::AclKind,
699 acl: Option<&security::Acl>,
700 default: bool,
701 follow: bool,
702 ) -> error::Result<()> {
703 match &self.inner {
704 VfsInner::Client(client) => client.set_acl(path, kind, acl, default, follow).await,
705 VfsInner::Direct(direct) => direct.set_acl(path, kind, acl, default, follow).await,
706 }
707 }
708
709 pub async fn sec_desc(
711 &self,
712 path: path::Path<'_>,
713 mask: dolang_winterop::security::SecInfo,
714 follow: bool,
715 ) -> error::Result<SecDesc> {
716 match &self.inner {
717 VfsInner::Client(client) => client.sec_desc(path, mask, follow).await,
718 VfsInner::Direct(direct) => direct.sec_desc(path, mask, follow).await,
719 }
720 }
721
722 pub async fn update_sec_desc(
724 &self,
725 path: path::Path<'_>,
726 sec_desc: &SecDesc,
727 follow: bool,
728 ) -> error::Result<()> {
729 match &self.inner {
730 VfsInner::Client(client) => client.update_sec_desc(path, sec_desc, follow).await,
731 VfsInner::Direct(direct) => direct.update_sec_desc(path, sec_desc, follow).await,
732 }
733 }
734
735 pub async fn create_dir(&self, path: path::Path<'_>, all: bool) -> error::Result<()> {
737 match &self.inner {
738 VfsInner::Client(client) => client.create_dir(path, all).await,
739 VfsInner::Direct(direct) => direct.create_dir(path, all).await,
740 }
741 }
742
743 pub async fn remove_dir(
745 &self,
746 path: path::Path<'_>,
747 all: bool,
748 ignore: bool,
749 ) -> error::Result<()> {
750 match &self.inner {
751 VfsInner::Client(client) => client.remove_dir(path, all, ignore).await,
752 VfsInner::Direct(direct) => direct.remove_dir(path, all, ignore).await,
753 }
754 }
755
756 pub async fn copy(
758 &self,
759 from: path::Path<'_>,
760 to: path::Path<'_>,
761 all: bool,
762 ) -> error::Result<()> {
763 match &self.inner {
764 VfsInner::Client(client) => client.copy(from, to, all).await,
765 VfsInner::Direct(direct) => direct.copy(from, to, all).await,
766 }
767 }
768
769 pub async fn rename(
771 &self,
772 from: path::Path<'_>,
773 to: path::Path<'_>,
774 replace: bool,
775 ) -> error::Result<()> {
776 match &self.inner {
777 VfsInner::Client(client) => client.rename(from, to, replace).await,
778 VfsInner::Direct(direct) => direct.rename(from, to, replace).await,
779 }
780 }
781
782 pub async fn move_(
784 &self,
785 from: path::Path<'_>,
786 to: path::Path<'_>,
787 all: bool,
788 ) -> error::Result<()> {
789 match &self.inner {
790 VfsInner::Client(client) => client.move_(from, to, all).await,
791 VfsInner::Direct(direct) => direct.move_(from, to, all).await,
792 }
793 }
794
795 pub async fn symlink(
797 &self,
798 cwd: path::Path<'_>,
799 src: path::Path<'_>,
800 dst: path::Path<'_>,
801 ) -> error::Result<()> {
802 match &self.inner {
803 VfsInner::Client(client) => client.symlink(cwd, src, dst).await,
804 VfsInner::Direct(direct) => direct.symlink(cwd, src, dst).await,
805 }
806 }
807
808 pub async fn hard_link(&self, src: path::Path<'_>, dst: path::Path<'_>) -> error::Result<()> {
810 match &self.inner {
811 VfsInner::Client(client) => client.hard_link(src, dst).await,
812 VfsInner::Direct(direct) => direct.hard_link(src, dst).await,
813 }
814 }
815
816 pub async fn symlink_dir(&self, src: path::Path<'_>, dst: path::Path<'_>) -> error::Result<()> {
818 match &self.inner {
819 VfsInner::Client(client) => client.symlink_dir(src, dst).await,
820 VfsInner::Direct(direct) => direct.symlink_dir(src, dst).await,
821 }
822 }
823
824 pub async fn symlink_file(
826 &self,
827 src: path::Path<'_>,
828 dst: path::Path<'_>,
829 ) -> error::Result<()> {
830 match &self.inner {
831 VfsInner::Client(client) => client.symlink_file(src, dst).await,
832 VfsInner::Direct(direct) => direct.symlink_file(src, dst).await,
833 }
834 }
835
836 pub async fn symlink_metadata(
838 &self,
839 path: path::Path<'_>,
840 ) -> error::Result<metadata::Metadata> {
841 match &self.inner {
842 VfsInner::Client(client) => client.symlink_metadata(path).await,
843 VfsInner::Direct(direct) => direct.symlink_metadata(path).await,
844 }
845 }
846
847 pub async fn update_metadata(
849 &self,
850 paths: &[path::PathBuf],
851 patch: metadata::MetadataPatch,
852 ) -> error::Result<()> {
853 match &self.inner {
854 VfsInner::Client(client) => client.update_metadata(paths, patch).await,
855 VfsInner::Direct(direct) => direct.update_metadata(paths, patch).await,
856 }
857 }
858
859 pub async fn canonicalize(&self, path: path::Path<'_>) -> error::Result<path::PathBuf> {
861 match &self.inner {
862 VfsInner::Client(client) => client.canonicalize(path).await,
863 VfsInner::Direct(direct) => direct.canonicalize(path).await,
864 }
865 }
866
867 pub async fn read_link(&self, path: path::Path<'_>) -> error::Result<path::PathBuf> {
869 match &self.inner {
870 VfsInner::Client(client) => client.read_link(path).await,
871 VfsInner::Direct(direct) => direct.read_link(path).await,
872 }
873 }
874
875 pub async fn access(&self, path: path::Path<'_>, mode: file::AccessFlags) -> error::Result<()> {
877 match &self.inner {
878 VfsInner::Client(client) => client.access(path, mode).await,
879 VfsInner::Direct(direct) => direct.access(path, mode).await,
880 }
881 }
882
883 pub async fn glob(
885 &self,
886 pattern: impl Into<String>,
887 root: path::Path<'_>,
888 follow_symlinks: bool,
889 max_depth: Option<usize>,
890 ) -> error::Result<Vec<path::PathBuf>> {
891 let pattern = pattern.into();
892
893 match &self.inner {
894 VfsInner::Client(client) => {
895 client.glob(pattern, root, follow_symlinks, max_depth).await
896 }
897 VfsInner::Direct(direct) => {
898 direct.glob(pattern, root, follow_symlinks, max_depth).await
899 }
900 }
901 }
902}
903
904#[cfg(test)]
905mod tests;