Skip to main content

dolang_vfs/server/
mod.rs

1use std::sync::{
2    Arc,
3    atomic::{AtomicUsize, Ordering},
4};
5
6#[cfg(unix)]
7use std::{path::Path, time::Duration};
8
9use bytes::{Buf, BytesMut};
10#[cfg(unix)]
11use dolang_rpc::auth::AuthKey;
12use dolang_rpc::{
13    handle::{DefaultHandle, OsHandle},
14    server::CallContext,
15    session::{Cite, Gift, OpaqueGuard, OpaqueResource},
16};
17use dolang_winterop::security::SecDesc;
18#[cfg(unix)]
19use std::os::unix::io::OwnedFd;
20use tokio::io::{self, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
21#[cfg(windows)]
22use tokio::net::windows::named_pipe::NamedPipeClient;
23#[cfg(all(docsrs, not(windows)))]
24struct NamedPipeClient;
25use tokio::sync::{Mutex, watch};
26#[cfg(unix)]
27use tokio::{
28    net::{UnixListener, UnixStream, unix::SocketAddr},
29    sync::mpsc,
30    task::{JoinError, JoinSet},
31    time::timeout,
32};
33
34use crate::{
35    MAX_FILE_READ, STREAM_CHUNK_SIZE, SessionMode, Vfs,
36    directory::ReadDir,
37    error::{Error, ErrorKind, HandoffError, Result},
38    extension::{self, ExtContext},
39    file::XattrEntry,
40    file::{AccessFlags, CopyDest, CopyMode, File, FileLock, FileLockRequest, StreamEntry},
41    metadata::{FsMetadata, Metadata},
42    path,
43    process::{
44        Child, Command, Process, ProcessInfo, Processes, Signal, StartTime, StdioRecv, StdioSend,
45    },
46    protocol::{
47        AccessRequest, AclRequest, CanonicalizeRequest, CopyRequest, CreateDirRequest,
48        ExtensionRequest, ExtensionResponse, FsMetadataRequest, GlobRequest, HardLinkRequest,
49        MetadataRequest, MoveRequest, OpenFlags, OpenHandle, OpenRequest, OpenVfsHandle,
50        PipeResponse, ProcessPage, QueryResponse, ReadDirPage, ReadLinkRequest, RemoveDirRequest,
51        RemoveRequest, RenameRequest, Request, RequestKind, ResponseKind, SecDescRequest,
52        SetAclRequest, SetXattrRequest, SpawnRequest, StdioRecvTarget, StdioSendTarget,
53        StreamsRequest, SymlinkKind, SymlinkRequest, UnixVfsRequest, UpdateMetadataRequest,
54        UpdateSecDescRequest, VfsProtocol, WellKnownPathRequest, WindowsAdminRequest,
55        XattrNamespaceRequest, XattrRequest, XattrsRequest, rpc_builder,
56    },
57    security::{Acl, AclKind},
58    session::{
59        ChildMarker, FileLockMarker, FileMarker, ProcessEnumMarker, ProcessMarker, ReadDirMarker,
60        StdioRecvMarker, StdioSendMarker, VfsMarker,
61    },
62};
63
64#[derive(Clone)]
65struct Connection {
66    server: Arc<ServerState>,
67    mode: SessionMode,
68    drain: Arc<Drain>,
69}
70
71/// Tracks outstanding stdio endpoints so a stop request can drain them.
72///
73/// A stop request must not sever the connection while a peer is still relaying
74/// through a pipe endpoint it obtained from this session: a stdio relay may
75/// outlive the lexical scope of the session it was created in, since pipe
76/// negotiation decides which side of a cross-domain pipeline ends up owning it.
77/// Instead, a stop marks the session as stopping (so no *new* endpoints can be
78/// created) and waits for the endpoints already handed out to be closed.
79struct Drain {
80    /// Outstanding endpoint count in the upper bits, stopping flag in the LSB.
81    state: AtomicUsize,
82    done: watch::Sender<bool>,
83}
84
85impl Drain {
86    fn new() -> Arc<Self> {
87        Arc::new(Self {
88            state: AtomicUsize::new(0),
89            done: watch::channel(false).0,
90        })
91    }
92
93    /// Reserves `count` endpoints, or fails if the session is stopping.
94    ///
95    /// The check and the increment are separate non-atomic steps, which is
96    /// sound because every handler for a connection runs as a future on the
97    /// single serve task: no other handler can observe or modify the state
98    /// between them, since there is no await point in between. A stop landing
99    /// just after a successful reservation is equivalent to one landing just
100    /// before it, and either way the drain waits for those endpoints.
101    fn try_acquire(&self, count: usize) -> bool {
102        if self.state.load(Ordering::Acquire) & 1 != 0 {
103            return false;
104        }
105        self.state.fetch_add(count << 1, Ordering::Relaxed);
106        true
107    }
108
109    /// Returns `count` endpoints, completing the drain if it goes idle.
110    fn release(&self, count: usize) {
111        // The stopping flag plus exactly `count` endpoints means the drain has
112        // just gone idle after a stop was requested.
113        if self.state.fetch_sub(count << 1, Ordering::AcqRel) == (count << 1) | 1 {
114            self.done.send_replace(true);
115        }
116    }
117
118    /// Marks the session as stopping, completing the drain if it is already idle.
119    fn begin_stop(&self) {
120        if self.state.fetch_or(1, Ordering::AcqRel) == 0 {
121            self.done.send_replace(true);
122        }
123    }
124
125    /// Waits for a stop to be requested and all outstanding endpoints to close.
126    async fn wait(&self) {
127        let _ = self.done.subscribe().wait_for(|done| *done).await;
128    }
129}
130
131/// A reserved drain slot, returned when the endpoint holding it dies.
132///
133/// Accounting rides the endpoint's own lifetime rather than any particular
134/// message, so an endpoint reaches the drain exactly once however it ends:
135/// explicitly closed, consumed by a spawn, or released because the peer
136/// dropped the last opaque naming it.
137struct DrainSlot(Arc<Drain>);
138
139impl Drop for DrainSlot {
140    fn drop(&mut self) {
141        self.0.release(1);
142    }
143}
144
145struct RetainedVfs {
146    vfs: Vfs,
147}
148
149impl RetainedVfs {
150    fn plain(vfs: Vfs) -> Self {
151        Self { vfs }
152    }
153}
154
155impl OpaqueResource for RetainedVfs {
156    type Marker = VfsMarker;
157}
158
159/// Reads the whole of `len` bytes at `offset`, or as much as exists.
160///
161/// Deliberately buffers the entire answer instead of streaming it: the response
162/// header goes out first and is the only place a structured error can be
163/// reported, so the read has to have already succeeded by the time it is sent.
164/// `len` is bounded by [`MAX_FILE_READ`] at the call site, which is what makes
165/// buffering it whole affordable.
166///
167/// One [`File::read_at`] may come up short for reasons other than the end
168/// of the file — a nested remote file clamps at one chunk, and a positional read
169/// is permitted to be short in general — so this loops rather than treating the
170/// first short read as the end.
171async fn read_file_range(file: &File, offset: u64, len: usize) -> Result<BytesMut> {
172    let mut buf = BytesMut::with_capacity(len);
173    while buf.len() < len {
174        let at = offset + buf.len() as u64;
175        if file.read_at(&mut buf, at).await? == 0 {
176            break;
177        }
178    }
179    // `read_at` fills the spare capacity, which the allocator may have rounded
180    // up past `len`. Delivering those extra bytes would be a protocol violation
181    // on the peer's side, so trim back to what was asked for.
182    buf.truncate(len);
183    Ok(buf)
184}
185
186/// Accumulates up to one chunk from `trailer`, or `None` at its end.
187///
188/// Bounds how much of a write is held in memory at once: the peer may submit a
189/// trailer far larger than a chunk, and the positional write it feeds wants an
190/// owned buffer.
191async fn next_trailer_chunk(
192    trailer: &mut dolang_rpc::trailer::TrailerRecv,
193) -> Result<Option<BytesMut>> {
194    let mut buf = BytesMut::with_capacity(STREAM_CHUNK_SIZE);
195    // Spare capacity is never zero while this loop runs, so `read_buf` fills
196    // the buffer rather than growing it.
197    while buf.len() < STREAM_CHUNK_SIZE && trailer.read_buf(&mut buf).await? != 0 {}
198    Ok((!buf.is_empty()).then_some(buf))
199}
200
201struct RetainedFile(File);
202
203/// A lock the peer still holds, addressed by its own opaque handle.
204///
205/// Not wrapped in a mutex: releasing is the only thing ever done with one, and
206/// that unregisters the lock first, so the releasing task has it by value and
207/// no other task can still reach it.
208struct RetainedFileLock(FileLock);
209
210impl OpaqueResource for RetainedFileLock {
211    type Marker = FileLockMarker;
212}
213
214struct RetainedReadDir(Mutex<ReadDir>);
215
216impl OpaqueResource for RetainedReadDir {
217    type Marker = ReadDirMarker;
218}
219
220struct RetainedProcesses(Mutex<Processes>);
221
222impl OpaqueResource for RetainedProcesses {
223    type Marker = ProcessEnumMarker;
224}
225
226/// A process handle the peer still holds.
227///
228/// Unlike [`RetainedChild`] this needs no mutex: none of the operations on a
229/// [`Process`] mutate it, so concurrent calls on the same handle are safe. That
230/// matters more than it looks — a `wait` can be outstanding for the life of the
231/// process, and a mutex here would make `terminate` on the same handle
232/// unreachable until it returned.
233struct RetainedProcess(Process);
234
235impl OpaqueResource for RetainedProcess {
236    type Marker = ProcessMarker;
237}
238
239impl OpaqueResource for RetainedFile {
240    type Marker = FileMarker;
241}
242
243struct RetainedStdioSend {
244    stdio: Mutex<StdioSend>,
245    /// Returned to the drain when the endpoint dies, however it ends.
246    _slot: DrainSlot,
247}
248
249impl OpaqueResource for RetainedStdioSend {
250    type Marker = StdioSendMarker;
251}
252
253struct RetainedStdioRecv {
254    stdio: Mutex<StdioRecv>,
255    /// Returned to the drain when the endpoint dies, however it ends.
256    _slot: DrainSlot,
257}
258
259impl OpaqueResource for RetainedStdioRecv {
260    type Marker = StdioRecvMarker;
261}
262
263struct RetainedChild(Mutex<Child>);
264
265impl OpaqueResource for RetainedChild {
266    type Marker = ChildMarker;
267}
268
269struct ServerState {
270    vfs: Vfs,
271    #[cfg(unix)]
272    shutdown_tx: watch::Sender<()>,
273}
274
275/// How long a single connection may take to complete negotiation in
276/// single-session mode before it is dropped. Generous by handshake standards:
277/// the point is only to keep a peer that never speaks from occupying a slot
278/// indefinitely, not to police slow networks.
279#[cfg(unix)]
280const NEGOTIATE_TIMEOUT: Duration = Duration::from_secs(30);
281
282/// How many connections may be negotiating at once in single-session mode.
283/// Bounds what a peer that opens connections without finishing them can tie
284/// up; the listener resumes accepting as attempts drain.
285#[cfg(unix)]
286const MAX_PENDING_CONNECTIONS: usize = 8;
287
288/// Rough payload budget for one page of a process enumeration.
289///
290/// Process records vary by more than an order of magnitude — a kernel thread
291/// carries a short name and nothing else, a build tool's command line can run
292/// to kilobytes — so a fixed entry count would make the page size unpredictable
293/// in exactly the case that matters. Budgeting bytes instead keeps a page's
294/// cost bounded regardless of what is running on the target.
295const PROCESS_PAGE_BYTES: usize = 64 * 1024;
296
297/// Hard entry cap for one page, so a table of uniformly tiny records still
298/// yields to the caller at a sane interval instead of being sent whole.
299const PROCESS_PAGE_ENTRIES: usize = 512;
300
301/// Estimates a record's encoded size for [`PROCESS_PAGE_BYTES`].
302///
303/// Deliberately approximate: it exists to stop one page from being megabytes,
304/// and paying a serialization pass per entry to find out exactly would cost
305/// more than the imprecision does.
306fn process_info_size(info: &ProcessInfo) -> usize {
307    const FIXED: usize = 64;
308    let strings = info.name().len()
309        + info.exe().map_or(0, |path| path.as_str().len())
310        + info.cwd().map_or(0, |path| path.as_str().len())
311        + info
312            .command_line()
313            .map_or(0, |args| args.iter().map(|arg| arg.len() + 1).sum::<usize>())
314        // On a Windows target the command line is carried twice, split and
315        // whole, so it counts twice against the page budget.
316        + info
317            .windows_command_line()
318            .ok()
319            .flatten()
320            .map_or(0, str::len);
321    // Only one of these applies on any given target, and the accessor for the
322    // other reports that rather than returning nothing to measure.
323    let identity = info
324        .identity()
325        .ok()
326        .flatten()
327        .map_or(0, |identity| 16 + identity.groups().len() * 4);
328    // A token carries several SIDs plus one per group, each a variable-length
329    // structure; groups dominate.
330    let token = info
331        .token()
332        .ok()
333        .flatten()
334        .map_or(0, |token| 128 + token.groups().len() * 48);
335    FIXED + strings + identity + token
336}
337
338/// A connection that has completed negotiation, handed from a handler task
339/// back to [`Server::accept_one`].
340#[cfg(unix)]
341struct Negotiated {
342    rpc: dolang_rpc::server::Server<VfsProtocol>,
343    connection: Arc<Connection>,
344}
345
346/// VFS agent server.
347///
348/// Construct a connected server with [`new`](Self::new) or
349/// [`new_split`](Self::new_split) and call [`serve`](Self::serve). On Unix,
350/// `bind` constructs a listener that accepts sessions until a client requests
351/// shutdown.
352pub struct Server {
353    #[cfg(unix)]
354    listener: Option<UnixListener>,
355    rpc: Option<dolang_rpc::server::Server<VfsProtocol>>,
356    mode: SessionMode,
357    shared: Arc<ServerState>,
358    /// Key each accepted connection must prove knowledge of. Held here rather
359    /// than passed to [`bind`](Self::bind) because negotiation happens per
360    /// connection, long after the listener exists.
361    #[cfg(unix)]
362    key: Option<AuthKey>,
363}
364
365impl Server {
366    /// Creates an opaque-only VFS server over a bidirectional byte stream.
367    pub async fn new<T>(stream: T) -> Result<Self>
368    where
369        T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
370    {
371        let rpc = rpc_builder(None).server(stream).await?.bind();
372        Ok(Self {
373            #[cfg(unix)]
374            listener: None,
375            rpc: Some(rpc),
376            mode: SessionMode::Remote,
377            shared: Self::state()?,
378            #[cfg(unix)]
379            key: None,
380        })
381    }
382
383    /// Creates an opaque-only VFS server on separate reader and writer streams.
384    pub async fn new_split<R, W>(reader: R, writer: W) -> Result<Self>
385    where
386        R: AsyncRead + Send + 'static,
387        W: AsyncWrite + Send + 'static,
388    {
389        let rpc = rpc_builder(None).server_split(reader, writer).await?.bind();
390        Ok(Self {
391            #[cfg(unix)]
392            listener: None,
393            rpc: Some(rpc),
394            mode: SessionMode::Remote,
395            shared: Self::state()?,
396            #[cfg(unix)]
397            key: None,
398        })
399    }
400
401    fn state() -> Result<Arc<ServerState>> {
402        #[cfg(unix)]
403        let (shutdown_tx, _) = watch::channel(());
404        Ok(Arc::new(ServerState {
405            vfs: Vfs::direct()?,
406            #[cfg(unix)]
407            shutdown_tx,
408        }))
409    }
410
411    /// Binds a Unix-domain listener for VFS agent connections.
412    #[cfg(unix)]
413    pub async fn bind(path: impl AsRef<Path>) -> Result<Self> {
414        Self::from_listener(UnixListener::bind(path)?, None)
415    }
416
417    /// Binds a Unix-domain listener that requires mutual proof of a pre-shared
418    /// key from every connection.
419    ///
420    /// The socket's permissions cannot distinguish the intended client when
421    /// the peer's uid is not knowable in advance; `key` is what does. A
422    /// connection that fails the check is dropped during negotiation, before
423    /// it can issue any request.
424    #[cfg(unix)]
425    pub async fn bind_with_key(path: impl AsRef<Path>, key: Option<AuthKey>) -> Result<Self> {
426        Self::from_listener(UnixListener::bind(path)?, key)
427    }
428
429    /// Create a server from an existing `UnixListener`.
430    #[cfg(unix)]
431    fn from_listener(listener: UnixListener, key: Option<AuthKey>) -> Result<Self> {
432        Ok(Self {
433            listener: Some(listener),
434            rpc: None,
435            mode: SessionMode::Native,
436            shared: Self::state()?,
437            key,
438        })
439    }
440
441    /// Creates a VFS RPC server on the client end of a connected Windows named pipe.
442    #[cfg(any(windows, docsrs))]
443    #[cfg_attr(docsrs, doc(cfg(windows)))]
444    #[cfg_attr(all(docsrs, not(windows)), allow(private_interfaces))]
445    pub async fn from_named_pipe_client(pipe: NamedPipeClient) -> Result<Self> {
446        #[cfg(windows)]
447        {
448            let rpc = rpc_builder(None)
449                .server_named_pipe_client(pipe)
450                .await?
451                .bind();
452            Ok(Self {
453                #[cfg(unix)]
454                listener: None,
455                rpc: Some(rpc),
456                mode: SessionMode::Native,
457                shared: Self::state()?,
458            })
459        }
460        #[cfg(all(docsrs, not(windows)))]
461        {
462            let _ = pipe;
463            unreachable!()
464        }
465    }
466
467    #[cfg(unix)]
468    fn handle_accept(
469        &self,
470        res: io::Result<(UnixStream, SocketAddr)>,
471        handlers: &mut JoinSet<Result<()>>,
472    ) -> Result<()> {
473        let (stream, _) = res?;
474        let stream = stream.into_std()?;
475        let connection = Arc::new(Connection {
476            server: self.shared.clone(),
477            mode: SessionMode::Native,
478            drain: Drain::new(),
479        });
480        let key = self.key;
481        handlers.spawn(async move {
482            // Negotiation (a real handshake over the wire) happens here,
483            // inside the per-connection task, so a slow or misbehaving peer
484            // can't stall the accept loop from taking new connections. That
485            // includes authentication: an unauthenticated peer fails here and
486            // never reaches `serve_connection`.
487            let rpc = rpc_builder(key).server_unix(stream).await?.bind();
488            let handler = connection.clone();
489            serve_connection(rpc, handler).await
490        });
491        Ok(())
492    }
493
494    /// Accepts connections until a client requests server shutdown.
495    ///
496    /// Each connection runs in an independent handler task. Routine client
497    /// disconnects are ignored; unexpected handler failures are reported to
498    /// standard error.
499    #[cfg(unix)]
500    pub async fn accept(mut self) -> Result<()> {
501        let mut shutdown_rx = self.shared.shutdown_tx.subscribe();
502        let mut handlers = JoinSet::new();
503
504        loop {
505            tokio::select! {
506                res = self.listener.as_ref().unwrap().accept() => {
507                    if let Err(error) = self.handle_accept(res, &mut handlers) {
508                        eprintln!("VFS server failed to accept a connection: {error}");
509                    }
510                }
511                result = handlers.join_next(), if !handlers.is_empty() => {
512                    report_handler_exit(result.unwrap());
513                }
514                _ = shutdown_rx.changed() => {
515                    self.listener.take();
516                    break;
517                }
518            }
519        }
520
521        while let Some(result) = handlers.join_next().await {
522            report_handler_exit(result);
523        }
524        Ok(())
525    }
526
527    /// Accepts connections until one completes negotiation, then serves that
528    /// session alone.
529    ///
530    /// `established` runs once, as soon as some connection has negotiated
531    /// successfully — the point at which the listening socket has done its job
532    /// and the caller can unlink it. Nothing is accepted afterwards.
533    ///
534    /// Connections that fail to negotiate (including failing authentication)
535    /// are dropped and do *not* consume the single slot, so an impostor that
536    /// reaches the socket first cannot deny the intended client its session;
537    /// it can only waste an attempt. Negotiation has a timeout and a fixed
538    /// limit on in-flight attempts, so a peer that connects and then says
539    /// nothing cannot stall or crowd out the real one either.
540    #[cfg(unix)]
541    pub async fn accept_one<F>(mut self, established: F) -> Result<()>
542    where
543        F: FnOnce(),
544    {
545        let mut handlers = JoinSet::new();
546        // Capacity one, and only ever received from once: whichever connection
547        // negotiates first hands its session over and wins. A second one that
548        // finishes in the same instant finds the channel full and is dropped.
549        let (session_tx, mut session_rx) = mpsc::channel::<Negotiated>(1);
550
551        let session = loop {
552            tokio::select! {
553                res = self.listener.as_ref().unwrap().accept(),
554                    if handlers.len() < MAX_PENDING_CONNECTIONS =>
555                {
556                    if let Err(error) = self.handle_accept_one(res, &mut handlers, &session_tx) {
557                        eprintln!("VFS server failed to accept a connection: {error}");
558                    }
559                }
560                result = handlers.join_next(), if !handlers.is_empty() => {
561                    report_handler_exit(result.unwrap());
562                }
563                Some(session) = session_rx.recv() => break session,
564            }
565        };
566
567        // Stop listening before the session runs: the socket has done its job,
568        // and any connection still mid-handshake has already lost the race, so
569        // waiting for it would only delay the session (and, at shutdown, hold
570        // the process open for the length of a negotiation timeout).
571        self.listener.take();
572        handlers.abort_all();
573        while let Some(result) = handlers.join_next().await {
574            if matches!(&result, Err(error) if error.is_cancelled()) {
575                continue;
576            }
577            report_handler_exit(result);
578        }
579        established();
580
581        let Negotiated { rpc, connection } = session;
582        match serve_connection(rpc, connection).await {
583            Ok(()) => Ok(()),
584            Err(error) if orderly_disconnect(&error) => Ok(()),
585            Err(error) => Err(error),
586        }
587    }
588
589    /// Spawns a handler that negotiates and offers the resulting session to
590    /// [`accept_one`](Self::accept_one), which serves it.
591    ///
592    /// The session is handed back rather than served in place so that the
593    /// accept loop can abandon every other in-flight attempt the moment one
594    /// succeeds.
595    #[cfg(unix)]
596    fn handle_accept_one(
597        &self,
598        res: io::Result<(UnixStream, SocketAddr)>,
599        handlers: &mut JoinSet<Result<()>>,
600        session_tx: &mpsc::Sender<Negotiated>,
601    ) -> Result<()> {
602        let (stream, _) = res?;
603        let stream = stream.into_std()?;
604        let connection = Arc::new(Connection {
605            server: self.shared.clone(),
606            mode: SessionMode::Native,
607            drain: Drain::new(),
608        });
609        let key = self.key;
610        let session_tx = session_tx.clone();
611        handlers.spawn(async move {
612            let negotiated = timeout(NEGOTIATE_TIMEOUT, rpc_builder(key).server_unix(stream)).await;
613            let rpc = match negotiated {
614                Ok(result) => result?.bind(),
615                Err(_elapsed) => {
616                    // Not worth reporting: a peer that connects and then says
617                    // nothing is exactly what the timeout is for.
618                    return Ok(());
619                }
620            };
621            // A full channel or a closed receiver both mean another connection
622            // got there first; drop this one.
623            let _ = session_tx.try_send(Negotiated { rpc, connection });
624            Ok(())
625        });
626        Ok(())
627    }
628
629    /// Serves one connected VFS session until it closes or fails.
630    pub async fn serve(mut self) -> Result<()> {
631        let connection = Arc::new(Connection {
632            server: self.shared,
633            mode: self.mode,
634            drain: Drain::new(),
635        });
636        let rpc = self
637            .rpc
638            .take()
639            .expect("server does not own a connected session");
640        match serve_connection(rpc, connection).await {
641            Ok(()) => Ok(()),
642            Err(error) if orderly_disconnect(&error) => Ok(()),
643            Err(error) => Err(error),
644        }
645    }
646}
647
648#[cfg(unix)]
649fn report_handler_exit(result: std::result::Result<Result<()>, JoinError>) {
650    match result {
651        Ok(Ok(())) => {}
652        Ok(Err(error)) if orderly_disconnect(&error) => {}
653        Ok(Err(error)) => eprintln!("VFS connection handler failed: {error}"),
654        Err(error) if error.is_panic() => std::panic::resume_unwind(error.into_panic()),
655        Err(error) => eprintln!("VFS connection handler task failed: {error}"),
656    }
657}
658
659fn orderly_disconnect(error: &Error) -> bool {
660    matches!(
661        error.kind(),
662        ErrorKind::UnexpectedEof
663            | ErrorKind::BrokenPipe
664            | ErrorKind::ConnectionReset
665            | ErrorKind::NotConnected
666    )
667}
668
669async fn serve_connection(
670    rpc: dolang_rpc::server::Server<VfsProtocol>,
671    connection: Arc<Connection>,
672) -> Result<()> {
673    rpc.serve(async move |mut context, Request { vfs, kind }| {
674        let response = if matches!(kind, RequestKind::Stop) {
675            connection.handle_stop(&mut context, vfs).await
676        } else if let Err(error) = connection.select(&context, vfs.clone()) {
677            Err(error)
678        } else {
679            let connection = connection.select(&context, vfs).unwrap();
680            match kind {
681                RequestKind::Spawn(request) => {
682                    connection.handle_spawn_rpc(&mut context, request).await
683                }
684                RequestKind::ChildWait { child } => {
685                    connection.handle_child_wait(&mut context, child).await
686                }
687                RequestKind::ChildTerminate { child } => {
688                    connection.handle_child_terminate(&context, child).await
689                }
690                RequestKind::ChildClose { child } => connection.handle_child_close(&context, child),
691                RequestKind::FileRead { file, offset, len } => {
692                    connection
693                        .handle_file_read(context, file, offset, len)
694                        .await;
695                    return;
696                }
697                RequestKind::StdioRecvRead { stdio, len } => {
698                    connection.handle_stdio_recv_read(context, stdio, len).await;
699                    return;
700                }
701                RequestKind::Stop => unreachable!(),
702                request => connection.handle(&mut context, request).await,
703            }
704        };
705        context.respond(response);
706    })
707    .await?;
708    Ok(())
709}
710
711impl Connection {
712    fn select(
713        &self,
714        context: &CallContext<VfsProtocol>,
715        vfs: Option<Cite<VfsMarker>>,
716    ) -> Result<Self> {
717        let Some(vfs) = vfs else {
718            return Ok(self.clone());
719        };
720        let selected = context
721            .acquire::<RetainedVfs>(vfs.clone())
722            .map_err(|_| Self::invalid_opaque("VFS"))?;
723        Ok(Self {
724            server: Arc::new(ServerState {
725                vfs: selected.vfs.clone(),
726                #[cfg(unix)]
727                shutdown_tx: self.server.shutdown_tx.clone(),
728            }),
729            mode: self.mode,
730            drain: self.drain.clone(),
731        })
732    }
733
734    async fn handle_stop(
735        &self,
736        context: &mut CallContext<VfsProtocol>,
737        vfs: Option<Cite<VfsMarker>>,
738    ) -> Result<ResponseKind> {
739        let Some(vfs) = vfs else {
740            // Stop accepting immediately. Existing sessions have their own
741            // connection tasks and continue draining independently.
742            #[cfg(unix)]
743            let _ = self.server.shutdown_tx.send(());
744            // Reject new stdio endpoints, then keep serving reads, writes and
745            // closes on the ones already handed out until they are all closed.
746            // The rpc serve loop polls request handlers on the same task as it
747            // reads frames, so awaiting here does not stall the connection.
748            self.drain.begin_stop();
749            self.drain.wait().await;
750            context.shutdown();
751            return Ok(ResponseKind::Stop);
752        };
753        let retained = match context.unregister::<RetainedVfs>(vfs) {
754            Ok(Some(retained)) => retained,
755            Ok(None) => {
756                return Err(Error::new(ErrorKind::ResourceBusy, "opaque VFS is in use"));
757            }
758            Err(_) => return Err(Self::invalid_opaque("VFS")),
759        };
760        match retained.vfs.stop().await {
761            Ok(()) => Ok(ResponseKind::Stop),
762            Err(error) => Err(error),
763        }
764    }
765
766    fn unsupported(operation: &str) -> Error {
767        Error::new(
768            ErrorKind::Unsupported,
769            format!("{operation} is not supported by a remote VFS session"),
770        )
771    }
772
773    fn invalid_opaque(kind: &str) -> Error {
774        Error::new(ErrorKind::InvalidInput, format!("invalid opaque {kind}"))
775    }
776
777    async fn handle(
778        &self,
779        context: &mut CallContext<VfsProtocol>,
780        kind: RequestKind,
781    ) -> Result<ResponseKind> {
782        match kind {
783            RequestKind::Query => self.handle_query().await,
784            RequestKind::UserName { uid } => Ok(ResponseKind::UserName(
785                self.server.vfs.user_name(uid).await?,
786            )),
787            RequestKind::UserId { name } => {
788                Ok(ResponseKind::UserId(self.server.vfs.user_id(&name).await?))
789            }
790            RequestKind::GroupName { gid } => Ok(ResponseKind::GroupName(
791                self.server.vfs.group_name(gid).await?,
792            )),
793            RequestKind::GroupId { name } => Ok(ResponseKind::GroupId(
794                self.server.vfs.group_id(&name).await?,
795            )),
796            RequestKind::SidName { sid } => {
797                Ok(ResponseKind::SidName(self.server.vfs.sid_name(&sid).await?))
798            }
799            RequestKind::AccountName { name } => Ok(ResponseKind::AccountName(
800                self.server.vfs.account_name(&name).await?,
801            )),
802            RequestKind::ResolvePrincipalId { input, want } => {
803                Ok(ResponseKind::ResolvePrincipalId(
804                    self.server.vfs.resolve_principal_id(input, want).await?,
805                ))
806            }
807            RequestKind::Which { program, path, cwd } => {
808                self.handle_which(program, path, cwd).await
809            }
810            RequestKind::WellKnownPath(request) => self.handle_well_known_path(request).await,
811            RequestKind::Stop
812            | RequestKind::Spawn(_)
813            | RequestKind::ChildWait { .. }
814            | RequestKind::ChildTerminate { .. }
815            | RequestKind::ChildClose { .. } => unreachable!(),
816            RequestKind::ClearCache => {
817                self.server.vfs.clear_cache().await?;
818                Ok(ResponseKind::ClearCache)
819            }
820            RequestKind::Pipe { buf_size } => Ok(ResponseKind::Pipe(
821                self.handle_pipe(context, buf_size).await?,
822            )),
823            RequestKind::Open(request) => self.handle_open(context, request).await,
824            RequestKind::FileRead { .. } => unreachable!(),
825            RequestKind::FileWrite { file, offset } => Ok(ResponseKind::FileWrite(
826                self.handle_file_write(context, file, offset).await?,
827            )),
828            RequestKind::FileAppend { file } => Ok(ResponseKind::FileAppend(
829                self.handle_file_append(context, file).await?,
830            )),
831            RequestKind::FileSize { file } => Ok(ResponseKind::FileSize(
832                self.handle_file_size(context, file).await?,
833            )),
834            RequestKind::FileSetSize { file, size } => {
835                self.handle_file_set_size(context, file, size).await?;
836                Ok(ResponseKind::FileSetSize)
837            }
838            RequestKind::FileSync { file, data } => {
839                self.handle_file_sync(context, file, data).await?;
840                Ok(ResponseKind::FileSync)
841            }
842            RequestKind::FileCopyData {
843                src,
844                dst,
845                src_offset,
846                target,
847                len,
848                mode,
849            } => Ok(ResponseKind::FileCopyData(
850                self.handle_file_copy_data(context, src, dst, src_offset, target, len, mode)
851                    .await?,
852            )),
853            RequestKind::FileLock { file, request } => {
854                self.handle_file_lock(context, file, request).await
855            }
856            RequestKind::FileUnlock { lock } => self.handle_file_unlock(context, lock).await,
857            RequestKind::FileToStdioSend { file, offset } => Ok(ResponseKind::FileToStdioSend(
858                self.handle_file_to_stdio_send(context, file, offset)
859                    .await?,
860            )),
861            RequestKind::FileToStdioRecv { file, offset } => Ok(ResponseKind::FileToStdioRecv(
862                self.handle_file_to_stdio_recv(context, file, offset)
863                    .await?,
864            )),
865            RequestKind::StdioSendClose { stdio } => {
866                self.close_stdio_send(context, stdio)?;
867                Ok(ResponseKind::StdioSendClose)
868            }
869            RequestKind::StdioSendWrite { stdio } => Ok(ResponseKind::StdioSendWrite(
870                self.handle_stdio_send_write(context, stdio).await?,
871            )),
872            RequestKind::StdioSendClone { stdio } => Ok(ResponseKind::StdioSendClone(
873                self.handle_stdio_send_clone(context, stdio).await?,
874            )),
875            RequestKind::StdioRecvClose { stdio } => {
876                self.close_stdio_recv(context, stdio)?;
877                Ok(ResponseKind::StdioRecvClose)
878            }
879            RequestKind::StdioRecvRead { .. } => unreachable!(),
880            RequestKind::StdioRecvClone { stdio } => Ok(ResponseKind::StdioRecvClone(
881                self.handle_stdio_recv_clone(context, stdio).await?,
882            )),
883            RequestKind::FileMetadata { file } => Ok(ResponseKind::FileMetadata(
884                self.handle_file_metadata(context, file).await?,
885            )),
886            RequestKind::FileFsMetadata { file } => Ok(ResponseKind::FileFsMetadata(
887                self.handle_file_fs_metadata(context, file).await?,
888            )),
889            RequestKind::FileAcl {
890                file,
891                kind,
892                default,
893            } => Ok(ResponseKind::FileAcl(
894                self.handle_file_acl(context, file, kind, default).await?,
895            )),
896            RequestKind::FileSetAcl {
897                file,
898                kind,
899                acl,
900                default,
901            } => {
902                self.handle_file_set_acl(context, file, kind, acl, default)
903                    .await?;
904                Ok(ResponseKind::FileSetAcl)
905            }
906            RequestKind::FileSecDesc { file, mask } => Ok(ResponseKind::FileSecDesc(
907                self.handle_file_sec_desc(context, file, mask).await?,
908            )),
909            RequestKind::FileUpdateSecDesc { file, sec_desc } => {
910                self.handle_file_update_sec_desc(context, file, sec_desc)
911                    .await?;
912                Ok(ResponseKind::FileUpdateSecDesc)
913            }
914            RequestKind::FileXattrs { file, namespace } => Ok(ResponseKind::FileXattrs(
915                self.handle_file_xattrs(context, file, namespace).await?,
916            )),
917            RequestKind::FileXattr {
918                file,
919                name,
920                namespace,
921            } => Ok(ResponseKind::FileXattr(
922                self.handle_file_xattr(context, file, name, namespace)
923                    .await?,
924            )),
925            RequestKind::FileStreams { file } => Ok(ResponseKind::FileStreams(
926                self.handle_file_streams(context, file).await?,
927            )),
928            RequestKind::FileSetXattr {
929                file,
930                name,
931                namespace,
932                value,
933            } => {
934                self.handle_file_set_xattr(context, file, name, namespace, value)
935                    .await?;
936                Ok(ResponseKind::FileSetXattr)
937            }
938            RequestKind::FileRemoveXattr {
939                file,
940                name,
941                namespace,
942            } => {
943                self.handle_file_remove_xattr(context, file, name, namespace)
944                    .await?;
945                Ok(ResponseKind::FileRemoveXattr)
946            }
947            RequestKind::FileClose { file } => self.handle_file_close(context, file).await,
948            RequestKind::UnixVfs(request) => self.handle_unix_vfs(context, request).await,
949            RequestKind::WindowsAdmin(request) => self.handle_windows_admin(context, request).await,
950            RequestKind::ReadDir { path } => self.handle_read_dir(context, path).await,
951            RequestKind::ReadDirNext { read_dir } => {
952                self.handle_read_dir_next(context, read_dir).await
953            }
954            RequestKind::ReadDirClose { read_dir } => self.handle_read_dir_close(context, read_dir),
955            RequestKind::ProcessEnumerate => self.handle_process_enumerate(context).await,
956            RequestKind::ProcessEnumerateNext { processes } => {
957                self.handle_process_enumerate_next(context, processes).await
958            }
959            RequestKind::ProcessEnumerateClose { processes } => {
960                self.handle_process_enumerate_close(context, processes)
961            }
962            RequestKind::ProcessDescribe { pid } => self.handle_process_describe(pid).await,
963            RequestKind::ProcessOpen { pid, start } => {
964                self.handle_process_open(context, pid, start).await
965            }
966            RequestKind::ProcessInfo { process } => {
967                self.handle_process_info(context, process).await
968            }
969            RequestKind::ProcessSignal { process, signal } => {
970                self.handle_process_signal(context, process, signal).await
971            }
972            RequestKind::ProcessTerminate { process } => {
973                self.handle_process_terminate(context, process).await
974            }
975            RequestKind::ProcessKill { process } => {
976                self.handle_process_kill(context, process).await
977            }
978            RequestKind::ProcessWait { process } => {
979                self.handle_process_wait(context, process).await
980            }
981            RequestKind::ProcessClose { process } => self.handle_process_close(context, process),
982            RequestKind::Remove(request) => self.handle_remove(request).await,
983            RequestKind::Metadata(request) => self.handle_metadata(request).await,
984            RequestKind::FsMetadata(request) => self.handle_fs_metadata(request).await,
985            RequestKind::Acl(request) => self.handle_acl(request).await,
986            RequestKind::SetAcl(request) => self.handle_set_acl(request).await,
987            RequestKind::SecDesc(request) => self.handle_sec_desc(request).await,
988            RequestKind::UpdateSecDesc(request) => self.handle_update_sec_desc(request).await,
989            RequestKind::CreateDir(request) => self.handle_create_dir(request).await,
990            RequestKind::RemoveDir(request) => self.handle_remove_dir(request).await,
991            RequestKind::Copy(request) => self.handle_copy(request).await,
992            RequestKind::Rename(request) => self.handle_rename(request).await,
993            RequestKind::Move(request) => self.handle_move(request).await,
994            RequestKind::Symlink(request) => self.handle_symlink(request).await,
995            RequestKind::HardLink(request) => self.handle_hard_link(request).await,
996            RequestKind::SymlinkMetadata(request) => self.handle_symlink_metadata(request).await,
997            RequestKind::UpdateMetadata(request) => self.handle_update_metadata(request).await,
998            RequestKind::Canonicalize(request) => self.handle_canonicalize(request).await,
999            RequestKind::ReadLink(request) => self.handle_read_link(request).await,
1000            RequestKind::Access(request) => self.handle_access(request).await,
1001            RequestKind::Glob(request) => self.handle_glob(request).await,
1002            RequestKind::Xattrs(request) => self.handle_xattrs(request).await,
1003            RequestKind::Xattr(request) => self.handle_xattr(request).await,
1004            RequestKind::SetXattr(request) => self.handle_set_xattr(request).await,
1005            RequestKind::RemoveXattr(request) => self.handle_remove_xattr(request).await,
1006            RequestKind::Streams(request) => self.handle_streams(request).await,
1007            RequestKind::Extension(request) => self.handle_extension(context, request).await,
1008        }
1009    }
1010
1011    async fn handle_extension(
1012        &self,
1013        context: &mut CallContext<VfsProtocol>,
1014        request: ExtensionRequest,
1015    ) -> Result<ResponseKind> {
1016        let ExtensionRequest {
1017            name,
1018            version,
1019            payload,
1020        } = request;
1021        let Some(ext) = extension::lookup(&name, version).filter(|extension| extension.available())
1022        else {
1023            return Err(Self::unsupported(&format!(
1024                "VFS extension {name} v{version}"
1025            )));
1026        };
1027        let mut ctx = ExtContext::remote(context, self.mode == SessionMode::Native);
1028        let payload = ext.dispatch(&mut ctx, payload).await;
1029        Ok(ResponseKind::Extension(ExtensionResponse {
1030            name,
1031            version,
1032            payload,
1033        }))
1034    }
1035
1036    async fn handle_which(
1037        &self,
1038        program: path::PathBuf,
1039        path: Option<String>,
1040        cwd: Option<path::PathBuf>,
1041    ) -> Result<ResponseKind> {
1042        let resolved = self
1043            .server
1044            .vfs
1045            .which(
1046                Into::into(&program),
1047                path.as_deref(),
1048                cwd.as_ref().map(Into::into),
1049            )
1050            .await?;
1051        Ok(ResponseKind::Which(resolved))
1052    }
1053
1054    async fn handle_well_known_path(&self, req: WellKnownPathRequest) -> Result<ResponseKind> {
1055        let path = self
1056            .server
1057            .vfs
1058            .well_known_path(req.key, req.app.as_deref(), &req.env)
1059            .await?;
1060        Ok(ResponseKind::WellKnownPath(path))
1061    }
1062
1063    async fn handle_spawn_rpc(
1064        &self,
1065        context: &mut CallContext<VfsProtocol>,
1066        req: SpawnRequest,
1067    ) -> Result<ResponseKind> {
1068        let mut cmd = self.server.vfs.command(Into::into(&req.program));
1069        for arg in &req.args {
1070            cmd.arg(arg);
1071        }
1072
1073        if let Some(cwd) = &req.cwd {
1074            cmd.current_dir(Into::into(cwd));
1075        }
1076
1077        for (k, v) in &req.env {
1078            match v {
1079                Some(val) => {
1080                    cmd.env(k, val);
1081                }
1082                None => {
1083                    cmd.env_remove(k);
1084                }
1085            };
1086        }
1087        cmd.process_control(req.process_control);
1088        cmd.termination_policy(req.termination_policy);
1089
1090        self.configure_spawn_stdio(context, &mut cmd, req.stdin, req.stdout, req.stderr)
1091            .await?;
1092
1093        let child = cmd.spawn().await?;
1094        Ok(ResponseKind::Spawn(
1095            context.register(RetainedChild(Mutex::new(child))),
1096        ))
1097    }
1098
1099    fn spawn_stdio_recv(
1100        &self,
1101        context: &CallContext<VfsProtocol>,
1102        target: StdioRecvTarget,
1103    ) -> Result<Option<StdioRecv>> {
1104        match target {
1105            StdioRecvTarget::Null => Ok(None),
1106            StdioRecvTarget::Native(handle) => {
1107                if self.mode == SessionMode::Remote {
1108                    return Err(Self::unsupported("native process stdio"));
1109                }
1110                Ok(Some(StdioRecv::from_file(tokio::fs::File::from_std(
1111                    handle.into_inner().into(),
1112                ))))
1113            }
1114            StdioRecvTarget::Opaque(stdio) => {
1115                // Consuming the endpoint hands it to the child, which takes
1116                // its drain slot along with it: once a child owns an endpoint
1117                // the peer is no longer relaying through it, which is the only
1118                // thing the drain protects.
1119                let stdio = context
1120                    .unregister::<RetainedStdioRecv>(stdio)
1121                    .map_err(|_| Self::invalid_opaque("stdio receive"))?;
1122                let Some(stdio) = stdio else {
1123                    return Err(Error::new(
1124                        ErrorKind::ResourceBusy,
1125                        "opaque stdio receive is in use",
1126                    ));
1127                };
1128                Ok(Some(stdio.stdio.into_inner()))
1129            }
1130        }
1131    }
1132
1133    fn spawn_stdio_send(
1134        &self,
1135        context: &CallContext<VfsProtocol>,
1136        target: StdioSendTarget,
1137    ) -> Result<Option<StdioSend>> {
1138        match target {
1139            StdioSendTarget::Null | StdioSendTarget::Stdout => Ok(None),
1140            StdioSendTarget::Native(handle) => {
1141                if self.mode == SessionMode::Remote {
1142                    return Err(Self::unsupported("native process stdio"));
1143                }
1144                Ok(Some(StdioSend::from_file(tokio::fs::File::from_std(
1145                    handle.into_inner().into(),
1146                ))))
1147            }
1148            StdioSendTarget::Opaque(stdio) => {
1149                // Consuming the endpoint hands it to the child, which takes
1150                // its drain slot along with it: once a child owns an endpoint
1151                // the peer is no longer relaying through it, which is the only
1152                // thing the drain protects.
1153                let stdio = context
1154                    .unregister::<RetainedStdioSend>(stdio)
1155                    .map_err(|_| Self::invalid_opaque("stdio send"))?;
1156                let Some(stdio) = stdio else {
1157                    return Err(Error::new(
1158                        ErrorKind::ResourceBusy,
1159                        "opaque stdio send is in use",
1160                    ));
1161                };
1162                Ok(Some(stdio.stdio.into_inner()))
1163            }
1164        }
1165    }
1166
1167    async fn configure_spawn_stdio(
1168        &self,
1169        context: &CallContext<VfsProtocol>,
1170        command: &mut Command<'_>,
1171        stdin: StdioRecvTarget,
1172        stdout: StdioSendTarget,
1173        stderr: StdioSendTarget,
1174    ) -> Result<()> {
1175        let stdin = self.spawn_stdio_recv(context, stdin);
1176        let stdout = self.spawn_stdio_send(context, stdout);
1177        let stderr_to_stdout = matches!(stderr, StdioSendTarget::Stdout);
1178        let stderr = self.spawn_stdio_send(context, stderr);
1179        let (stdin, stdout, stderr) = (stdin?, stdout?, stderr?);
1180
1181        if let Some(stdio) = stdin {
1182            command.stdin(stdio)?;
1183        } else {
1184            command.stdin_null();
1185        }
1186        if let Some(stdio) = stdout {
1187            command.stdout(stdio)?;
1188        } else {
1189            command.stdout_null();
1190        }
1191        if stderr_to_stdout {
1192            command.stderr_to_stdout()?;
1193        } else if let Some(stdio) = stderr {
1194            command.stderr(stdio)?;
1195        } else {
1196            command.stderr_null();
1197        }
1198        Ok(())
1199    }
1200
1201    fn take_child(
1202        &self,
1203        context: &CallContext<VfsProtocol>,
1204        child: Cite<ChildMarker>,
1205    ) -> Result<RetainedChild> {
1206        context
1207            .unregister::<RetainedChild>(child)
1208            .map_err(|_| Error::new(ErrorKind::InvalidInput, "invalid opaque child"))?
1209            .ok_or_else(|| Error::new(ErrorKind::ResourceBusy, "opaque child is in use"))
1210    }
1211
1212    async fn handle_child_wait(
1213        &self,
1214        context: &mut CallContext<VfsProtocol>,
1215        child: Cite<ChildMarker>,
1216    ) -> Result<ResponseKind> {
1217        let child = self.take_child(context, child)?;
1218        let mut child = child.0.into_inner();
1219        let status = match context.cancel_guard(async |_| child.wait().await).await {
1220            Ok(result) => result?,
1221            Err(_) => child
1222                .terminate()
1223                .await?
1224                .ok_or_else(|| Error::other("process was orphaned during cancelled wait"))?,
1225        };
1226        Ok(ResponseKind::ChildWait(status))
1227    }
1228
1229    async fn handle_child_terminate(
1230        &self,
1231        context: &CallContext<VfsProtocol>,
1232        child: Cite<ChildMarker>,
1233    ) -> Result<ResponseKind> {
1234        let child = self.take_child(context, child)?;
1235        let status = child.0.into_inner().terminate().await?;
1236        Ok(ResponseKind::ChildTerminate(status))
1237    }
1238
1239    fn handle_child_close(
1240        &self,
1241        context: &CallContext<VfsProtocol>,
1242        child: Cite<ChildMarker>,
1243    ) -> Result<ResponseKind> {
1244        context
1245            .unregister::<RetainedChild>(child)
1246            .map_err(|_| Error::new(ErrorKind::InvalidInput, "invalid opaque child"))?;
1247        Ok(ResponseKind::ChildClose)
1248    }
1249
1250    async fn handle_query(&self) -> Result<ResponseKind> {
1251        let vfs = &self.server.vfs;
1252        Ok(ResponseKind::Query(QueryResponse {
1253            session: vfs.session(),
1254            pid: vfs.pid(),
1255            env: vfs.env().collect(),
1256            cwd: vfs.cwd().into(),
1257            current_exe: vfs.current_exe().into(),
1258            target: vfs.target().clone(),
1259            security: vfs.security().clone(),
1260            extensions: vfs.extensions().clone(),
1261        }))
1262    }
1263
1264    /// Reserves a drain slot for an endpoint about to be handed to the peer.
1265    ///
1266    /// Only endpoint creation is gated this way. `Spawn` and `Open` stay
1267    /// available while stopping: they create no stdio endpoint of their own,
1268    /// and refusing a spawn could break the very in-flight pipeline stage the
1269    /// drain exists to protect.
1270    fn reserve_stdio(&self) -> Result<DrainSlot> {
1271        if self.drain.try_acquire(1) {
1272            Ok(DrainSlot(self.drain.clone()))
1273        } else {
1274            Err(Error::new(
1275                ErrorKind::NotConnected,
1276                "VFS session is stopping",
1277            ))
1278        }
1279    }
1280
1281    async fn handle_pipe(
1282        &self,
1283        context: &CallContext<VfsProtocol>,
1284        buf_size: Option<usize>,
1285    ) -> Result<PipeResponse> {
1286        let (send, recv) = self.server.vfs.pipe(buf_size).await?;
1287        let send_slot = self.reserve_stdio()?;
1288        let recv_slot = self.reserve_stdio()?;
1289        Ok(PipeResponse {
1290            send: context.register(RetainedStdioSend {
1291                stdio: Mutex::new(send),
1292                _slot: send_slot,
1293            }),
1294            recv: context.register(RetainedStdioRecv {
1295                stdio: Mutex::new(recv),
1296                _slot: recv_slot,
1297            }),
1298        })
1299    }
1300
1301    fn retained_stdio_send(
1302        &self,
1303        context: &CallContext<VfsProtocol>,
1304        stdio: Cite<StdioSendMarker>,
1305    ) -> Result<OpaqueGuard<RetainedStdioSend>> {
1306        context
1307            .acquire::<RetainedStdioSend>(stdio)
1308            .map_err(|_| Self::invalid_opaque("stdio send"))
1309    }
1310
1311    fn retained_stdio_recv(
1312        &self,
1313        context: &CallContext<VfsProtocol>,
1314        stdio: Cite<StdioRecvMarker>,
1315    ) -> Result<OpaqueGuard<RetainedStdioRecv>> {
1316        context
1317            .acquire::<RetainedStdioRecv>(stdio)
1318            .map_err(|_| Self::invalid_opaque("stdio receive"))
1319    }
1320
1321    /// Empties an endpoint's contents, without waiting for the peer to stop
1322    /// naming it.
1323    ///
1324    /// A close that races the endpoint's own read or write leaves the contents
1325    /// alive until that operation finishes, and the registration alive until
1326    /// the peer drops its last reference. Neither is worth reporting: a peer
1327    /// closing while its own I/O is in flight has no flush guarantee to lose,
1328    /// and the drain slot is returned when the endpoint actually dies rather
1329    /// than when this request happens to be served.
1330    fn close_stdio_send(
1331        &self,
1332        context: &CallContext<VfsProtocol>,
1333        stdio: Cite<StdioSendMarker>,
1334    ) -> Result<()> {
1335        context
1336            .unregister::<RetainedStdioSend>(stdio)
1337            .map_err(|_| Self::invalid_opaque("stdio send"))?;
1338        Ok(())
1339    }
1340
1341    fn close_stdio_recv(
1342        &self,
1343        context: &CallContext<VfsProtocol>,
1344        stdio: Cite<StdioRecvMarker>,
1345    ) -> Result<()> {
1346        context
1347            .unregister::<RetainedStdioRecv>(stdio)
1348            .map_err(|_| Self::invalid_opaque("stdio receive"))?;
1349        Ok(())
1350    }
1351
1352    async fn handle_stdio_send_write(
1353        &self,
1354        context: &mut CallContext<VfsProtocol>,
1355        stdio: Cite<StdioSendMarker>,
1356    ) -> Result<usize> {
1357        let stdio = self.retained_stdio_send(context, stdio)?;
1358        let trailer = context.trailer().ok_or_else(|| {
1359            Error::new(
1360                ErrorKind::InvalidInput,
1361                "stdio write request is missing its data trailer",
1362            )
1363        })?;
1364        let mut trailer = io::BufReader::with_capacity(STREAM_CHUNK_SIZE, trailer);
1365        let len = io::copy_buf(&mut trailer, &mut *stdio.stdio.lock().await).await?;
1366        usize::try_from(len).map_err(|_| {
1367            Error::new(
1368                ErrorKind::InvalidData,
1369                "stdio trailer length does not fit in usize",
1370            )
1371        })
1372    }
1373
1374    async fn handle_stdio_send_clone(
1375        &self,
1376        context: &CallContext<VfsProtocol>,
1377        stdio: Cite<StdioSendMarker>,
1378    ) -> Result<Gift<StdioSendMarker>> {
1379        let stdio = self.retained_stdio_send(context, stdio)?;
1380        let clone = stdio.stdio.lock().await.try_clone().await?;
1381        let slot = self.reserve_stdio()?;
1382        Ok(context.register(RetainedStdioSend {
1383            stdio: Mutex::new(clone),
1384            _slot: slot,
1385        }))
1386    }
1387
1388    async fn handle_stdio_recv_read(
1389        &self,
1390        context: CallContext<VfsProtocol>,
1391        stdio: Cite<StdioRecvMarker>,
1392        len: usize,
1393    ) {
1394        let stdio = match self.retained_stdio_recv(&context, stdio) {
1395            Ok(stdio) => stdio,
1396            Err(error) => {
1397                context.respond(Err(error));
1398                return;
1399            }
1400        };
1401        // Unlike `handle_file_read`, this streams: the source is a pipe of
1402        // unknown length, so there is nothing to hold and the answer cannot be
1403        // known before responding. A failure part way through therefore has to
1404        // report itself by abandoning the trailer, which for a byte stream is
1405        // the right shape anyway — it is a teardown, not an operation that
1406        // failed with a particular errno the peer could act on.
1407        let mut send = context.respond_with_trailer(Ok(ResponseKind::StdioRecvRead));
1408        let copied = {
1409            let mut guard = stdio.stdio.lock().await;
1410            let mut source = io::BufReader::with_capacity(
1411                len.clamp(1, STREAM_CHUNK_SIZE),
1412                (&mut *guard).take(len as u64),
1413            );
1414            io::copy_buf(&mut source, &mut send).await.is_ok()
1415        };
1416        // Released before the terminal fragment: the peer may hand the endpoint
1417        // to a child or close it as soon as the trailer ends, and both consume
1418        // the endpoint.
1419        drop(stdio);
1420        if copied {
1421            send.finish();
1422        }
1423    }
1424
1425    async fn handle_stdio_recv_clone(
1426        &self,
1427        context: &CallContext<VfsProtocol>,
1428        stdio: Cite<StdioRecvMarker>,
1429    ) -> Result<Gift<StdioRecvMarker>> {
1430        let stdio = self.retained_stdio_recv(context, stdio)?;
1431        let clone = stdio.stdio.lock().await.try_clone().await?;
1432        let slot = self.reserve_stdio()?;
1433        Ok(context.register(RetainedStdioRecv {
1434            stdio: Mutex::new(clone),
1435            _slot: slot,
1436        }))
1437    }
1438
1439    async fn handle_open(
1440        &self,
1441        context: &CallContext<VfsProtocol>,
1442        req: OpenRequest,
1443    ) -> Result<ResponseKind> {
1444        let mut opts = self.server.vfs.open_options();
1445        opts.read(req.flags.contains(OpenFlags::READ))
1446            .write(req.flags.contains(OpenFlags::WRITE))
1447            .append(req.flags.contains(OpenFlags::APPEND))
1448            .create(req.flags.contains(OpenFlags::CREATE))
1449            .create_new(req.flags.contains(OpenFlags::CREATE_NEW))
1450            .truncate(req.flags.contains(OpenFlags::TRUNCATE))
1451            .no_follow(req.flags.contains(OpenFlags::NO_FOLLOW));
1452
1453        let file = opts.open(Into::into(&req.path)).await?;
1454        let handle = if self.mode == SessionMode::Remote {
1455            OpenHandle::Opaque(context.register(RetainedFile(file)))
1456        } else {
1457            let handle: DefaultHandle = file.try_into_std().await.unwrap().into();
1458            OpenHandle::Native(OsHandle::new(handle))
1459        };
1460        Ok(ResponseKind::Open(handle))
1461    }
1462
1463    fn retained_file(
1464        &self,
1465        context: &CallContext<VfsProtocol>,
1466        file: Cite<FileMarker>,
1467    ) -> Result<OpaqueGuard<RetainedFile>> {
1468        context
1469            .acquire::<RetainedFile>(file)
1470            .map_err(|_| Error::new(ErrorKind::InvalidInput, "invalid opaque file"))
1471    }
1472
1473    /// Takes a file out of the session's registry, for an operation that
1474    /// consumes it.
1475    ///
1476    /// Uses the recovering [`try_unregister`], so a file with operations still
1477    /// in flight stays registered and the peer can retry once they finish;
1478    /// [`handle_file_close`](Self::handle_file_close) deliberately does not,
1479    /// because a close must still take effect after the racing operation ends.
1480    ///
1481    /// [`try_unregister`]: dolang_rpc::server::CallContext::try_unregister
1482    fn take_file(
1483        &self,
1484        context: &CallContext<VfsProtocol>,
1485        file: Cite<FileMarker>,
1486    ) -> Result<RetainedFile> {
1487        match context.try_unregister::<RetainedFile>(file) {
1488            Ok(Some(file)) => Ok(file),
1489            Ok(None) => Err(Error::new(ErrorKind::ResourceBusy, "opaque file is in use")),
1490            Err(_) => Err(Error::new(ErrorKind::InvalidInput, "invalid opaque file")),
1491        }
1492    }
1493
1494    async fn handle_file_read(
1495        &self,
1496        context: CallContext<VfsProtocol>,
1497        file: Cite<FileMarker>,
1498        offset: u64,
1499        len: usize,
1500    ) {
1501        let file = match self.retained_file(&context, file) {
1502            Ok(file) => file,
1503            Err(error) => {
1504                context.respond(Err(error));
1505                return;
1506            }
1507        };
1508        // Read before responding. Once the response header is out the only way
1509        // left to report a failure is to abandon the trailer, which reaches the
1510        // peer as a bare `BrokenPipe` abort with the real error discarded — so
1511        // the read has to have already succeeded or failed by then. Clamping is
1512        // what makes holding the whole answer affordable, and it also stops the
1513        // peer from choosing the size of this allocation.
1514        let data = read_file_range(&file.0, offset, len.min(MAX_FILE_READ)).await;
1515        // Release the file before anything at all goes back. The peer is
1516        // entitled to close the file the moment it sees this read conclude, and
1517        // a reference still held here would make its `FileClose` fail as in
1518        // use. Buffering the answer first is what lets the guard be dropped
1519        // this early — earlier than when the read was streamed, where it had to
1520        // live until the last fragment.
1521        drop(file);
1522        let data = match data {
1523            Ok(data) => data,
1524            Err(error) => {
1525                context.respond(Err(error));
1526                return;
1527            }
1528        };
1529        let mut send = context.respond_with_trailer(Ok(ResponseKind::FileRead));
1530        if send.write_all(&data).await.is_ok() {
1531            send.finish();
1532        }
1533    }
1534
1535    async fn handle_file_write(
1536        &self,
1537        context: &mut CallContext<VfsProtocol>,
1538        file: Cite<FileMarker>,
1539        offset: u64,
1540    ) -> Result<usize> {
1541        let file = self.retained_file(context, file)?;
1542        let mut trailer = Self::write_trailer(context)?;
1543        let mut written = 0usize;
1544        while let Some(chunk) = next_trailer_chunk(&mut trailer).await? {
1545            let mut chunk = chunk.freeze();
1546            while !chunk.is_empty() {
1547                let n = file
1548                    .0
1549                    .write_at(chunk.clone(), offset + written as u64)
1550                    .await?;
1551                if n == 0 {
1552                    return Err(Error::new(
1553                        ErrorKind::WriteZero,
1554                        "file write made no progress",
1555                    ));
1556                }
1557                chunk.advance(n);
1558                written += n;
1559            }
1560        }
1561        Ok(written)
1562    }
1563
1564    async fn handle_file_append(
1565        &self,
1566        context: &mut CallContext<VfsProtocol>,
1567        file: Cite<FileMarker>,
1568    ) -> Result<(usize, u64)> {
1569        let file = self.retained_file(context, file)?;
1570        let mut trailer = Self::write_trailer(context)?;
1571        // The offset of an append is the description's business, not ours. The
1572        // peer cannot know where the data landed either, so report the
1573        // resulting position along with the count.
1574        let mut written = 0usize;
1575        let mut end = file.0.metadata().await?.len;
1576        while let Some(chunk) = next_trailer_chunk(&mut trailer).await? {
1577            let mut chunk = chunk.freeze();
1578            while !chunk.is_empty() {
1579                let (n, position) = file.0.append(chunk.clone()).await?;
1580                if n == 0 {
1581                    return Err(Error::new(
1582                        ErrorKind::WriteZero,
1583                        "file append made no progress",
1584                    ));
1585                }
1586                chunk.advance(n);
1587                written += n;
1588                end = position;
1589            }
1590        }
1591        Ok((written, end))
1592    }
1593
1594    fn write_trailer(
1595        context: &mut CallContext<VfsProtocol>,
1596    ) -> Result<dolang_rpc::trailer::TrailerRecv> {
1597        context.trailer().ok_or_else(|| {
1598            Error::new(
1599                ErrorKind::InvalidInput,
1600                "file write request is missing its data trailer",
1601            )
1602        })
1603    }
1604
1605    async fn handle_file_size(
1606        &self,
1607        context: &CallContext<VfsProtocol>,
1608        file: Cite<FileMarker>,
1609    ) -> Result<u64> {
1610        let file = self.retained_file(context, file)?;
1611        Ok(file.0.metadata().await?.len)
1612    }
1613
1614    async fn handle_file_set_size(
1615        &self,
1616        context: &CallContext<VfsProtocol>,
1617        file: Cite<FileMarker>,
1618        size: u64,
1619    ) -> Result<()> {
1620        let file = self.retained_file(context, file)?;
1621        file.0.set_size(size).await
1622    }
1623
1624    /// Copies bytes between two of this session's files.
1625    ///
1626    /// The only handler that holds two citations at once. Both are acquired
1627    /// for the duration, so a concurrent close of either endpoint reports the
1628    /// file as busy rather than pulling it out from under the bounded copy.
1629    /// Source
1630    /// first, always, so which citation a failure names does not depend on
1631    /// timing.
1632    ///
1633    /// Overlap and identity are not re-checked here: `File::copy_data` does
1634    /// that, and routing through it is what makes a remote copy behave
1635    /// identically to a local one. Cancellation needs no guard either — the
1636    /// serve loop drops this future, and the copy loop's every transfer is a
1637    /// drop point.
1638    #[allow(clippy::too_many_arguments)]
1639    async fn handle_file_copy_data(
1640        &self,
1641        context: &CallContext<VfsProtocol>,
1642        src: Cite<FileMarker>,
1643        dst: Cite<FileMarker>,
1644        src_offset: u64,
1645        target: CopyDest,
1646        len: Option<u64>,
1647        mode: CopyMode,
1648    ) -> Result<crate::file::CopyDataResult> {
1649        let src = self.retained_file(context, src)?;
1650        let dst = self.retained_file(context, dst)?;
1651        src.0.copy_data(&dst.0, src_offset, target, len, mode).await
1652    }
1653
1654    async fn handle_file_sync(
1655        &self,
1656        context: &CallContext<VfsProtocol>,
1657        file: Cite<FileMarker>,
1658        data: bool,
1659    ) -> Result<()> {
1660        let file = self.retained_file(context, file)?;
1661        file.0.sync(data).await
1662    }
1663
1664    async fn handle_file_to_stdio_send(
1665        &self,
1666        context: &CallContext<VfsProtocol>,
1667        file: Cite<FileMarker>,
1668        offset: u64,
1669    ) -> Result<Gift<StdioSendMarker>> {
1670        // Before the file is taken, so that running out of endpoint slots
1671        // leaves the peer's handle alone.
1672        let slot = self.reserve_stdio()?;
1673        let file = self.take_file(context, file)?;
1674        // The peer's cursor is the one that matters, and the descriptor the
1675        // child inherits carries a position of its own, so plant it explicitly
1676        // rather than letting this side's idea of the position decide.
1677        let stdio = file
1678            .0
1679            .into_stdio_send(offset)
1680            .await
1681            .map_err(handoff_error)?;
1682        Ok(context.register(RetainedStdioSend {
1683            stdio: Mutex::new(stdio),
1684            _slot: slot,
1685        }))
1686    }
1687
1688    async fn handle_file_to_stdio_recv(
1689        &self,
1690        context: &CallContext<VfsProtocol>,
1691        file: Cite<FileMarker>,
1692        offset: u64,
1693    ) -> Result<Gift<StdioRecvMarker>> {
1694        let slot = self.reserve_stdio()?;
1695        let file = self.take_file(context, file)?;
1696        // The peer's cursor is the one that matters, and the descriptor the
1697        // child inherits carries a position of its own, so plant it explicitly
1698        // rather than letting this side's idea of the position decide.
1699        let stdio = file
1700            .0
1701            .into_stdio_recv(offset)
1702            .await
1703            .map_err(handoff_error)?;
1704        Ok(context.register(RetainedStdioRecv {
1705            stdio: Mutex::new(stdio),
1706            _slot: slot,
1707        }))
1708    }
1709
1710    async fn handle_file_metadata(
1711        &self,
1712        context: &CallContext<VfsProtocol>,
1713        file: Cite<FileMarker>,
1714    ) -> Result<Metadata> {
1715        let file = self.retained_file(context, file)?;
1716        file.0.metadata().await
1717    }
1718
1719    async fn handle_file_fs_metadata(
1720        &self,
1721        context: &CallContext<VfsProtocol>,
1722        file: Cite<FileMarker>,
1723    ) -> Result<FsMetadata> {
1724        let file = self.retained_file(context, file)?;
1725        file.0.fs_metadata().await
1726    }
1727
1728    async fn handle_file_sec_desc(
1729        &self,
1730        context: &CallContext<VfsProtocol>,
1731        file: Cite<FileMarker>,
1732        mask: dolang_winterop::security::SecInfo,
1733    ) -> Result<SecDesc> {
1734        let file = self.retained_file(context, file)?;
1735        file.0.sec_desc(mask).await
1736    }
1737
1738    async fn handle_file_acl(
1739        &self,
1740        context: &CallContext<VfsProtocol>,
1741        file: Cite<FileMarker>,
1742        kind: AclKind,
1743        default: bool,
1744    ) -> Result<Option<Acl>> {
1745        let file = self.retained_file(context, file)?;
1746        file.0.acl(kind, default).await
1747    }
1748
1749    async fn handle_file_set_acl(
1750        &self,
1751        context: &CallContext<VfsProtocol>,
1752        file: Cite<FileMarker>,
1753        kind: AclKind,
1754        acl: Option<Acl>,
1755        default: bool,
1756    ) -> Result<()> {
1757        let file = self.retained_file(context, file)?;
1758        file.0.set_acl(kind, acl.as_ref(), default).await
1759    }
1760
1761    async fn handle_file_update_sec_desc(
1762        &self,
1763        context: &CallContext<VfsProtocol>,
1764        file: Cite<FileMarker>,
1765        sec_desc: SecDesc,
1766    ) -> Result<()> {
1767        let file = self.retained_file(context, file)?;
1768        file.0.update_sec_desc(&sec_desc).await
1769    }
1770
1771    async fn handle_file_xattrs(
1772        &self,
1773        context: &CallContext<VfsProtocol>,
1774        file: Cite<FileMarker>,
1775        namespace: XattrNamespaceRequest,
1776    ) -> Result<Vec<XattrEntry>> {
1777        let file = self.retained_file(context, file)?;
1778        file.0.xattrs(namespace.as_borrowed()).await
1779    }
1780
1781    async fn handle_file_xattr(
1782        &self,
1783        context: &CallContext<VfsProtocol>,
1784        file: Cite<FileMarker>,
1785        name: String,
1786        namespace: Option<String>,
1787    ) -> Result<Vec<u8>> {
1788        let file = self.retained_file(context, file)?;
1789        file.0.xattr(&name, namespace.as_deref()).await
1790    }
1791
1792    async fn handle_file_streams(
1793        &self,
1794        context: &CallContext<VfsProtocol>,
1795        file: Cite<FileMarker>,
1796    ) -> Result<Vec<StreamEntry>> {
1797        let file = self.retained_file(context, file)?;
1798        file.0.streams().await
1799    }
1800
1801    async fn handle_file_set_xattr(
1802        &self,
1803        context: &CallContext<VfsProtocol>,
1804        file: Cite<FileMarker>,
1805        name: String,
1806        namespace: Option<String>,
1807        value: Vec<u8>,
1808    ) -> Result<()> {
1809        let file = self.retained_file(context, file)?;
1810        file.0.set_xattr(&name, namespace.as_deref(), &value).await
1811    }
1812
1813    async fn handle_file_remove_xattr(
1814        &self,
1815        context: &CallContext<VfsProtocol>,
1816        file: Cite<FileMarker>,
1817        name: String,
1818        namespace: Option<String>,
1819    ) -> Result<()> {
1820        let file = self.retained_file(context, file)?;
1821        file.0.remove_xattr(&name, namespace.as_deref()).await
1822    }
1823
1824    async fn handle_file_lock(
1825        &self,
1826        context: &mut CallContext<VfsProtocol>,
1827        file: Cite<FileMarker>,
1828        request: FileLockRequest,
1829    ) -> Result<ResponseKind> {
1830        let retained = self.retained_file(context, file)?;
1831        let acquired = context
1832            .cancel_guard(async |_context| {
1833                retained
1834                    .0
1835                    .lock(request.range, request.mode, request.behavior)
1836                    .await
1837            })
1838            .await;
1839        let acquired = acquired.map_err(|_| {
1840            Error::new(
1841                ErrorKind::Interrupted,
1842                "file lock acquisition was cancelled",
1843            )
1844        })??;
1845        drop(retained);
1846        // The lock gets an opaque handle of its own rather than an id in a
1847        // table hanging off the file. Nothing needs it to be reachable from the
1848        // file: closing a file releases every lock held on it in band, and a
1849        // lock dropped without an explicit release still releases itself.
1850        Ok(ResponseKind::FileLock(
1851            acquired.map(|lock| context.register(RetainedFileLock(lock))),
1852        ))
1853    }
1854
1855    async fn handle_file_unlock(
1856        &self,
1857        context: &CallContext<VfsProtocol>,
1858        lock: Cite<FileLockMarker>,
1859    ) -> Result<ResponseKind> {
1860        // Releasing consumes the lock, so a handle that is unknown or already
1861        // released is a no-op rather than an error, as it was when the peer
1862        // named locks by id.
1863        let Ok(Some(mut lock)) = context.unregister::<RetainedFileLock>(lock) else {
1864            return Ok(ResponseKind::FileUnlock);
1865        };
1866        lock.0.release().await?;
1867        Ok(ResponseKind::FileUnlock)
1868    }
1869
1870    async fn handle_file_close(
1871        &self,
1872        context: &CallContext<VfsProtocol>,
1873        file: Cite<FileMarker>,
1874    ) -> Result<ResponseKind> {
1875        let retained = self.retained_file(context, file.clone())?;
1876        drop(retained);
1877        match context.unregister::<RetainedFile>(file) {
1878            // `close` releases every lock still held on the file in band, so
1879            // there is nothing to unwind here first.
1880            Ok(Some(file)) => file.0.close().await,
1881            Ok(None) => Err(Error::new(ErrorKind::ResourceBusy, "opaque file is in use")),
1882            Err(_) => Err(Error::new(ErrorKind::InvalidInput, "invalid opaque file")),
1883        }?;
1884        Ok(ResponseKind::FileClose)
1885    }
1886
1887    async fn handle_read_dir(
1888        &self,
1889        context: &CallContext<VfsProtocol>,
1890        path: path::PathBuf,
1891    ) -> Result<ResponseKind> {
1892        let read_dir = self.server.vfs.read_dir(Into::into(&path)).await?;
1893        Ok(ResponseKind::ReadDir(
1894            context.register(RetainedReadDir(Mutex::new(read_dir))),
1895        ))
1896    }
1897
1898    async fn handle_read_dir_next(
1899        &self,
1900        context: &CallContext<VfsProtocol>,
1901        read_dir: Cite<ReadDirMarker>,
1902    ) -> Result<ResponseKind> {
1903        let retained = context
1904            .acquire::<RetainedReadDir>(read_dir.clone())
1905            .map_err(|_| Error::new(ErrorKind::InvalidInput, "invalid opaque directory"))?;
1906        let mut read_dir_guard = retained.0.lock().await;
1907        let mut entries = Vec::with_capacity(64);
1908        let mut done = false;
1909        while entries.len() < 64 {
1910            match read_dir_guard.next_entry().await? {
1911                Some(entry) => entries.push(entry),
1912                None => {
1913                    done = true;
1914                    break;
1915                }
1916            }
1917        }
1918        drop(read_dir_guard);
1919        drop(retained);
1920        if done {
1921            let _ = context.unregister::<RetainedReadDir>(read_dir);
1922        }
1923        Ok(ResponseKind::ReadDirNext(ReadDirPage { entries, done }))
1924    }
1925
1926    fn handle_read_dir_close(
1927        &self,
1928        context: &CallContext<VfsProtocol>,
1929        read_dir: Cite<ReadDirMarker>,
1930    ) -> Result<ResponseKind> {
1931        context
1932            .unregister::<RetainedReadDir>(read_dir)
1933            .map_err(|_| Self::invalid_opaque("directory"))?;
1934        Ok(ResponseKind::ReadDirClose)
1935    }
1936
1937    async fn handle_process_enumerate(
1938        &self,
1939        context: &CallContext<VfsProtocol>,
1940    ) -> Result<ResponseKind> {
1941        let processes = self.server.vfs.processes().await?;
1942        Ok(ResponseKind::ProcessEnumerate(
1943            context.register(RetainedProcesses(Mutex::new(processes))),
1944        ))
1945    }
1946
1947    async fn handle_process_enumerate_next(
1948        &self,
1949        context: &CallContext<VfsProtocol>,
1950        processes: Cite<ProcessEnumMarker>,
1951    ) -> Result<ResponseKind> {
1952        let retained = context
1953            .acquire::<RetainedProcesses>(processes.clone())
1954            .map_err(|_| Self::invalid_opaque("process enumeration"))?;
1955        let mut guard = retained.0.lock().await;
1956        let mut entries: Vec<ProcessInfo> = Vec::new();
1957        let mut budget = PROCESS_PAGE_BYTES;
1958        let mut done = false;
1959        while budget > 0 && entries.len() < PROCESS_PAGE_ENTRIES {
1960            match guard.next_entry().await? {
1961                Some(entry) => {
1962                    budget = budget.saturating_sub(process_info_size(&entry));
1963                    entries.push(entry);
1964                }
1965                None => {
1966                    done = true;
1967                    break;
1968                }
1969            }
1970        }
1971        drop(guard);
1972        drop(retained);
1973        if done {
1974            let _ = context.unregister::<RetainedProcesses>(processes);
1975        }
1976        Ok(ResponseKind::ProcessEnumerateNext(ProcessPage {
1977            entries,
1978            done,
1979        }))
1980    }
1981
1982    fn handle_process_enumerate_close(
1983        &self,
1984        context: &CallContext<VfsProtocol>,
1985        processes: Cite<ProcessEnumMarker>,
1986    ) -> Result<ResponseKind> {
1987        context
1988            .unregister::<RetainedProcesses>(processes)
1989            .map_err(|_| Self::invalid_opaque("process enumeration"))?;
1990        Ok(ResponseKind::ProcessEnumerateClose)
1991    }
1992
1993    async fn handle_process_describe(&self, pid: u32) -> Result<ResponseKind> {
1994        Ok(ResponseKind::ProcessDescribe(
1995            self.server.vfs.describe_process(pid).await?,
1996        ))
1997    }
1998
1999    async fn handle_process_open(
2000        &self,
2001        context: &CallContext<VfsProtocol>,
2002        pid: u32,
2003        start: Option<StartTime>,
2004    ) -> Result<ResponseKind> {
2005        let process = self.server.vfs.open_process_raw(pid, start).await?;
2006        Ok(ResponseKind::ProcessOpen(
2007            context.register(RetainedProcess(process)),
2008        ))
2009    }
2010
2011    async fn handle_process_info(
2012        &self,
2013        context: &CallContext<VfsProtocol>,
2014        process: Cite<ProcessMarker>,
2015    ) -> Result<ResponseKind> {
2016        let retained = context
2017            .acquire::<RetainedProcess>(process)
2018            .map_err(|_| Self::invalid_opaque("process"))?;
2019        Ok(ResponseKind::ProcessInfo(retained.0.info().await?))
2020    }
2021
2022    async fn handle_process_signal(
2023        &self,
2024        context: &CallContext<VfsProtocol>,
2025        process: Cite<ProcessMarker>,
2026        signal: Signal,
2027    ) -> Result<ResponseKind> {
2028        let retained = context
2029            .acquire::<RetainedProcess>(process)
2030            .map_err(|_| Self::invalid_opaque("process"))?;
2031        retained.0.signal(signal).await?;
2032        Ok(ResponseKind::ProcessSignal)
2033    }
2034
2035    async fn handle_process_terminate(
2036        &self,
2037        context: &CallContext<VfsProtocol>,
2038        process: Cite<ProcessMarker>,
2039    ) -> Result<ResponseKind> {
2040        let retained = context
2041            .acquire::<RetainedProcess>(process)
2042            .map_err(|_| Self::invalid_opaque("process"))?;
2043        retained.0.terminate().await?;
2044        Ok(ResponseKind::ProcessTerminate)
2045    }
2046
2047    async fn handle_process_kill(
2048        &self,
2049        context: &CallContext<VfsProtocol>,
2050        process: Cite<ProcessMarker>,
2051    ) -> Result<ResponseKind> {
2052        let retained = context
2053            .acquire::<RetainedProcess>(process)
2054            .map_err(|_| Self::invalid_opaque("process"))?;
2055        retained.0.kill().await?;
2056        Ok(ResponseKind::ProcessKill)
2057    }
2058
2059    async fn handle_process_wait(
2060        &self,
2061        context: &CallContext<VfsProtocol>,
2062        process: Cite<ProcessMarker>,
2063    ) -> Result<ResponseKind> {
2064        let retained = context
2065            .acquire::<RetainedProcess>(process)
2066            .map_err(|_| Self::invalid_opaque("process"))?;
2067        Ok(ResponseKind::ProcessWait(retained.0.wait().await?))
2068    }
2069
2070    fn handle_process_close(
2071        &self,
2072        context: &CallContext<VfsProtocol>,
2073        process: Cite<ProcessMarker>,
2074    ) -> Result<ResponseKind> {
2075        context
2076            .unregister::<RetainedProcess>(process)
2077            .map_err(|_| Self::invalid_opaque("process"))?;
2078        Ok(ResponseKind::ProcessClose)
2079    }
2080
2081    async fn handle_unix_vfs(
2082        &self,
2083        context: &CallContext<VfsProtocol>,
2084        req: UnixVfsRequest,
2085    ) -> Result<ResponseKind> {
2086        #[cfg(unix)]
2087        if self.mode == SessionMode::Native && self.server.vfs.is_direct() {
2088            let handle: OwnedFd = async {
2089                let path = req.path.to_native()?;
2090                let stream = UnixStream::connect(path).await?;
2091                Ok::<OwnedFd, Error>(stream.into_std()?.into())
2092            }
2093            .await?;
2094            return Ok(ResponseKind::UnixVfs(OpenVfsHandle::Native(OsHandle::new(
2095                handle,
2096            ))));
2097        }
2098
2099        let vfs = self
2100            .server
2101            .vfs
2102            .unix_socket(Into::into(&req.path), req.key.as_deref())
2103            .await?;
2104        Ok(ResponseKind::UnixVfs(OpenVfsHandle::Opaque(
2105            context.register(RetainedVfs::plain(vfs)),
2106        )))
2107    }
2108
2109    async fn handle_windows_admin(
2110        &self,
2111        context: &CallContext<VfsProtocol>,
2112        req: WindowsAdminRequest,
2113    ) -> Result<ResponseKind> {
2114        let vfs = self
2115            .server
2116            .vfs
2117            .windows_admin(Into::into(&req.cwd), req.env, req.elevate)
2118            .await?;
2119        Ok(ResponseKind::WindowsAdmin(
2120            context.register(RetainedVfs::plain(vfs)),
2121        ))
2122    }
2123
2124    async fn handle_remove(&self, req: RemoveRequest) -> Result<ResponseKind> {
2125        self.server
2126            .vfs
2127            .remove(Into::into(&req.path), req.all, req.ignore)
2128            .await?;
2129        Ok(ResponseKind::Remove)
2130    }
2131
2132    async fn handle_metadata(&self, req: MetadataRequest) -> Result<ResponseKind> {
2133        let metadata = self.server.vfs.metadata(Into::into(&req.path)).await?;
2134        Ok(ResponseKind::Metadata(metadata))
2135    }
2136
2137    async fn handle_fs_metadata(&self, req: FsMetadataRequest) -> Result<ResponseKind> {
2138        let metadata = self
2139            .server
2140            .vfs
2141            .fs_metadata(Into::into(&req.path), req.follow)
2142            .await?;
2143        Ok(ResponseKind::FsMetadata(metadata))
2144    }
2145
2146    async fn handle_sec_desc(&self, req: SecDescRequest) -> Result<ResponseKind> {
2147        let sec_desc = self
2148            .server
2149            .vfs
2150            .sec_desc(Into::into(&req.path), req.mask, req.follow)
2151            .await?;
2152        Ok(ResponseKind::SecDesc(sec_desc))
2153    }
2154
2155    async fn handle_acl(&self, req: AclRequest) -> Result<ResponseKind> {
2156        let acl = self
2157            .server
2158            .vfs
2159            .acl(Into::into(&req.path), req.kind, req.default, req.follow)
2160            .await?;
2161        Ok(ResponseKind::Acl(acl))
2162    }
2163
2164    async fn handle_set_acl(&self, req: SetAclRequest) -> Result<ResponseKind> {
2165        self.server
2166            .vfs
2167            .set_acl(
2168                Into::into(&req.path),
2169                req.kind,
2170                req.acl.as_ref(),
2171                req.default,
2172                req.follow,
2173            )
2174            .await?;
2175        Ok(ResponseKind::SetAcl)
2176    }
2177
2178    async fn handle_update_sec_desc(&self, req: UpdateSecDescRequest) -> Result<ResponseKind> {
2179        self.server
2180            .vfs
2181            .update_sec_desc(Into::into(&req.path), &req.sec_desc, req.follow)
2182            .await?;
2183        Ok(ResponseKind::UpdateSecDesc)
2184    }
2185
2186    async fn handle_create_dir(&self, req: CreateDirRequest) -> Result<ResponseKind> {
2187        self.server
2188            .vfs
2189            .create_dir(Into::into(&req.path), req.all)
2190            .await?;
2191        Ok(ResponseKind::CreateDir)
2192    }
2193
2194    async fn handle_remove_dir(&self, req: RemoveDirRequest) -> Result<ResponseKind> {
2195        self.server
2196            .vfs
2197            .remove_dir(Into::into(&req.path), req.all, req.ignore)
2198            .await?;
2199        Ok(ResponseKind::RemoveDir)
2200    }
2201
2202    async fn handle_copy(&self, req: CopyRequest) -> Result<ResponseKind> {
2203        self.server
2204            .vfs
2205            .copy(Into::into(&req.from), Into::into(&req.to), req.all)
2206            .await?;
2207        Ok(ResponseKind::Copy)
2208    }
2209
2210    async fn handle_rename(&self, req: RenameRequest) -> Result<ResponseKind> {
2211        self.server
2212            .vfs
2213            .rename(Into::into(&req.from), Into::into(&req.to), req.replace)
2214            .await?;
2215        Ok(ResponseKind::Rename)
2216    }
2217
2218    async fn handle_move(&self, req: MoveRequest) -> Result<ResponseKind> {
2219        self.server
2220            .vfs
2221            .move_(Into::into(&req.from), Into::into(&req.to), req.all)
2222            .await?;
2223        Ok(ResponseKind::Move)
2224    }
2225
2226    async fn handle_symlink(&self, req: SymlinkRequest) -> Result<ResponseKind> {
2227        match req.kind {
2228            SymlinkKind::Infer => {
2229                self.server
2230                    .vfs
2231                    .symlink(
2232                        Into::into(&req.cwd),
2233                        Into::into(&req.src),
2234                        Into::into(&req.dst),
2235                    )
2236                    .await
2237            }
2238            SymlinkKind::Dir => {
2239                self.server
2240                    .vfs
2241                    .symlink_dir(Into::into(&req.src), Into::into(&req.dst))
2242                    .await
2243            }
2244            SymlinkKind::File => {
2245                self.server
2246                    .vfs
2247                    .symlink_file(Into::into(&req.src), Into::into(&req.dst))
2248                    .await
2249            }
2250        }?;
2251        Ok(ResponseKind::Symlink)
2252    }
2253
2254    async fn handle_hard_link(&self, req: HardLinkRequest) -> Result<ResponseKind> {
2255        self.server
2256            .vfs
2257            .hard_link(Into::into(&req.src), Into::into(&req.dst))
2258            .await?;
2259        Ok(ResponseKind::HardLink)
2260    }
2261
2262    async fn handle_symlink_metadata(&self, req: MetadataRequest) -> Result<ResponseKind> {
2263        let metadata = self
2264            .server
2265            .vfs
2266            .symlink_metadata(Into::into(&req.path))
2267            .await?;
2268        Ok(ResponseKind::SymlinkMetadata(metadata))
2269    }
2270
2271    async fn handle_update_metadata(&self, req: UpdateMetadataRequest) -> Result<ResponseKind> {
2272        let paths: Vec<_> = req
2273            .paths
2274            .iter()
2275            .map(|path| path::Path::from(path).to_path_buf())
2276            .collect();
2277        self.server.vfs.update_metadata(&paths, req.patch).await?;
2278        Ok(ResponseKind::UpdateMetadata)
2279    }
2280
2281    async fn handle_canonicalize(&self, req: CanonicalizeRequest) -> Result<ResponseKind> {
2282        let path = self.server.vfs.canonicalize(Into::into(&req.path)).await?;
2283        Ok(ResponseKind::Canonicalize(path))
2284    }
2285
2286    async fn handle_read_link(&self, req: ReadLinkRequest) -> Result<ResponseKind> {
2287        let path = self.server.vfs.read_link(Into::into(&req.path)).await?;
2288        Ok(ResponseKind::ReadLink(path))
2289    }
2290
2291    async fn handle_access(&self, req: AccessRequest) -> Result<ResponseKind> {
2292        let mode = AccessFlags::from_bits(req.mode).unwrap_or(AccessFlags::empty());
2293        self.server.vfs.access(Into::into(&req.path), mode).await?;
2294        Ok(ResponseKind::Access)
2295    }
2296
2297    async fn handle_glob(&self, req: GlobRequest) -> Result<ResponseKind> {
2298        let paths = self
2299            .server
2300            .vfs
2301            .glob(
2302                req.pattern,
2303                Into::into(&req.root),
2304                req.follow_symlinks,
2305                req.max_depth,
2306            )
2307            .await?;
2308        Ok(ResponseKind::Glob(paths))
2309    }
2310
2311    async fn handle_xattrs(&self, req: XattrsRequest) -> Result<ResponseKind> {
2312        let xattrs = self
2313            .server
2314            .vfs
2315            .xattrs(
2316                Into::into(&req.path),
2317                req.namespace.as_borrowed(),
2318                req.follow,
2319            )
2320            .await?;
2321        Ok(ResponseKind::Xattrs(xattrs))
2322    }
2323
2324    async fn handle_xattr(&self, req: XattrRequest) -> Result<ResponseKind> {
2325        let xattr = self
2326            .server
2327            .vfs
2328            .xattr(
2329                Into::into(&req.path),
2330                &req.name,
2331                req.namespace.as_deref(),
2332                req.follow,
2333            )
2334            .await?;
2335        Ok(ResponseKind::Xattr(xattr))
2336    }
2337
2338    async fn handle_set_xattr(&self, req: SetXattrRequest) -> Result<ResponseKind> {
2339        self.server
2340            .vfs
2341            .set_xattr(
2342                Into::into(&req.path),
2343                &req.name,
2344                req.namespace.as_deref(),
2345                &req.value,
2346                req.follow,
2347            )
2348            .await?;
2349        Ok(ResponseKind::SetXattr)
2350    }
2351
2352    async fn handle_remove_xattr(&self, req: XattrRequest) -> Result<ResponseKind> {
2353        self.server
2354            .vfs
2355            .remove_xattr(
2356                Into::into(&req.path),
2357                &req.name,
2358                req.namespace.as_deref(),
2359                req.follow,
2360            )
2361            .await?;
2362        Ok(ResponseKind::RemoveXattr)
2363    }
2364
2365    async fn handle_streams(&self, req: StreamsRequest) -> Result<ResponseKind> {
2366        Ok(ResponseKind::Streams(
2367            self.server
2368                .vfs
2369                .streams(Into::into(&req.path), req.follow)
2370                .await?,
2371        ))
2372    }
2373}
2374
2375/// Reports a handoff that did not happen.
2376///
2377/// The handle it carries back is dropped here rather than restored: the
2378/// registration was already retired to take it, and a fresh one would answer to
2379/// an id the peer does not hold. The peer's handle is dead either way, so the
2380/// file is closed instead of leaked. The one failure this side *can* recover
2381/// from — a busy file — is caught before the file is taken at all, in
2382/// [`take_file`](Session::take_file).
2383fn handoff_error<H>(error: HandoffError<H>) -> Error {
2384    Into::into(error.into_error())
2385}
2386
2387#[cfg(test)]
2388mod tests {
2389    #[cfg(unix)]
2390    use super::report_handler_exit;
2391    use super::{Server, orderly_disconnect};
2392    use crate::{
2393        error::{Error, ErrorKind},
2394        path,
2395        protocol::{
2396            self, OpenFlags, OpenHandle, OpenRequest, Request, RequestKind, ResponseKind,
2397            VfsProtocol,
2398        },
2399    };
2400
2401    fn request(kind: RequestKind) -> Request {
2402        Request { vfs: None, kind }
2403    }
2404
2405    #[tokio::test]
2406    #[cfg(unix)]
2407    #[should_panic(expected = "connection handler panic")]
2408    async fn connection_handler_panics_are_propagated() {
2409        let mut handlers = tokio::task::JoinSet::new();
2410        handlers.spawn(async {
2411            panic!("connection handler panic");
2412            #[allow(unreachable_code)]
2413            Ok::<(), Error>(())
2414        });
2415        report_handler_exit(handlers.join_next().await.unwrap());
2416    }
2417
2418    #[test]
2419    fn orderly_connection_close_is_expected() {
2420        assert!(orderly_disconnect(&Error::new(
2421            ErrorKind::ConnectionReset,
2422            "closed"
2423        )));
2424        assert!(orderly_disconnect(&Error::new(
2425            ErrorKind::BrokenPipe,
2426            "closed"
2427        )));
2428        assert!(!orderly_disconnect(&Error::new(
2429            ErrorKind::InvalidData,
2430            "bad frame"
2431        )));
2432    }
2433
2434    #[tokio::test]
2435    async fn remote_server_replies_without_serializing_a_handle() {
2436        let (client_stream, server_stream) = tokio::io::duplex(4096);
2437        let server =
2438            tokio::spawn(async move { Server::new(server_stream).await.unwrap().serve().await });
2439        let client = protocol::rpc_builder(None)
2440            .client(client_stream)
2441            .await
2442            .unwrap()
2443            .bind::<VfsProtocol>();
2444
2445        let temp = tempfile::NamedTempFile::new().unwrap();
2446        let response = client
2447            .call(request(RequestKind::Open(OpenRequest {
2448                path: path::PathBuf::from_native(temp.path().to_path_buf())
2449                    .unwrap()
2450                    .to_path()
2451                    .into(),
2452                flags: OpenFlags::READ,
2453            })))
2454            .await
2455            .unwrap()
2456            .into_response()
2457            .unwrap();
2458        let ResponseKind::Open(OpenHandle::Opaque(file)) = response else {
2459            panic!("remote open did not return an opaque file");
2460        };
2461        let ResponseKind::FileClose = client
2462            .call(request(RequestKind::FileClose { file: file.cite() }))
2463            .await
2464            .unwrap()
2465            .into_response()
2466            .unwrap()
2467        else {
2468            panic!("file close returned the wrong response");
2469        };
2470        let error = client
2471            .call(request(RequestKind::FileClose { file: file.cite() }))
2472            .await
2473            .unwrap()
2474            .into_response()
2475            .unwrap_err();
2476        assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
2477
2478        let _ = client.call(request(RequestKind::Stop)).await.unwrap();
2479        client.close().await;
2480        server.await.unwrap().unwrap();
2481    }
2482}