Skip to main content

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

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