Skip to main content

Mountain/IPC/WindServiceHandlers/FileSystem/Native/
FileOpenFd.rs

1//! `file:open` - open a file and return an integer file descriptor.
2//!
3//! VS Code's `DiskFileSystemProvider.open(resource, opts)` uses fd-based
4//! access for large binary files and write operations. The fd table is a
5//! process-global `HashMap<u32, File>` guarded by a `Mutex`. FDs start
6//! from 1 and increment with each successful open.
7//!
8//! Arguments\[0\] = resource URI or path string
9//! Arguments\[1\] = options: `{ create?: boolean, unlock?: boolean }`
10//!
11//! Returns: integer fd number, or 0 on error (VS Code ignores the error
12//! for read-only opens and falls back to the full-read path).
13
14use std::{
15	collections::HashMap,
16	sync::{
17		Mutex,
18		OnceLock,
19		atomic::{AtomicU32, Ordering},
20	},
21};
22
23use serde_json::{Value, json};
24use tokio::fs::File;
25
26use crate::{IPC::WindServiceHandlers::Utilities::PathExtraction::Fn as extract_path_from_arg, dev_log};
27
28static NEXT_FD:AtomicU32 = AtomicU32::new(1);
29
30pub struct FdTable {
31	pub Files:Mutex<HashMap<u32, File>>,
32}
33
34static FD_TABLE:OnceLock<FdTable> = OnceLock::new();
35
36pub(crate) fn GetFdTable() -> &'static FdTable { FD_TABLE.get_or_init(|| FdTable { Files:Mutex::new(HashMap::new()) }) }
37
38pub async fn Fn(Arguments:Vec<Value>) -> Result<Value, String> {
39	let ResourceArg = Arguments.first().ok_or("file:open: missing resource")?;
40
41	let Path = extract_path_from_arg(ResourceArg)?;
42
43	let Opts = Arguments.get(1).and_then(Value::as_object);
44
45	let Create = Opts.and_then(|O| O.get("create")).and_then(Value::as_bool).unwrap_or(false);
46
47	let Truncate = Opts.and_then(|O| O.get("truncate")).and_then(Value::as_bool).unwrap_or(false);
48
49	let F = if Create {
50		let mut OpenOpts = tokio::fs::OpenOptions::new();
51
52		OpenOpts.write(true).create(true);
53
54		if Truncate {
55			OpenOpts.truncate(true);
56		}
57
58		OpenOpts
59			.open(&Path)
60			.await
61			.map_err(|E| format!("file:open create '{}': {}", Path, E))?
62	} else {
63		tokio::fs::File::open(&Path)
64			.await
65			.map_err(|E| format!("file:open '{}': {}", Path, E))?
66	};
67
68	let Fd = NEXT_FD.fetch_add(1, Ordering::Relaxed);
69
70	if let Ok(mut Table) = GetFdTable().Files.lock() {
71		Table.insert(Fd, F);
72	}
73
74	dev_log!("vfs", "file:open fd={} path={} create={}", Fd, Path, Create);
75
76	Ok(json!(Fd))
77}