Nothing to see here, move along
1#![allow(clippy::missing_safety_doc)]
2
3use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter};
4use lancer_vfs_proto::{
5 VFS_STATUS_CORRUPT, VFS_STATUS_CROSS_DEVICE, VFS_STATUS_DIR_NOT_EMPTY, VFS_STATUS_DISK_FULL,
6 VFS_STATUS_FILE_EXISTS, VFS_STATUS_HANDLE_TABLE_FULL, VFS_STATUS_INTEGRITY_ERROR,
7 VFS_STATUS_INVALID_HANDLE, VFS_STATUS_INVALID_OP, VFS_STATUS_IO_ERROR,
8 VFS_STATUS_IS_A_DIRECTORY, VFS_STATUS_NAME_TOO_LONG, VFS_STATUS_NOT_A_DIRECTORY,
9 VFS_STATUS_NOT_A_FILE, VFS_STATUS_NOT_FOUND, VFS_STATUS_OK, VFS_STATUS_PATH_TOO_DEEP,
10 VFS_STATUS_PERMISSION_DENIED, VFS_STATUS_STALE_CAP, VFS_STATUS_SYMLINK_DEPTH,
11 VFS_STATUS_UNSUPPORTED, VfsRequest, VfsResponse,
12};
13
14pub use lancer_vfs_proto::{
15 FsRights, MOUNT_INFO_ENTRY_SIZE, MountInfoEntry, READDIR_ENTRY_SIZE, ReadDirEntry,
16};
17
18use crate::syscall;
19
20pub const VFS_RING_VADDR: u64 = 0x7000_0000;
21pub const VFS_RING_BASE_SLOT: u64 = 11;
22pub const VFS_RING_FRAME_COUNT: u64 = 16;
23pub const CLIENT_NOTIF_SLOT: u8 = 28;
24pub const VFS_NOTIF_SLOT: u8 = 29;
25
26pub const RING_HALF_SIZE: usize = 4096;
27pub const RING_DATA_OFFSET: usize = 8192;
28pub const RING_DATA_PAGES: usize = 14;
29const RING_DATA_SIZE: usize = RING_DATA_PAGES * 4096;
30const REQUEST_SLOT_SIZE: u32 = 48;
31const PUSH_RETRY_LIMIT: u32 = 100_000;
32const POLL_RETRY_LIMIT: u32 = 100_000;
33const MAX_PATH_COMPONENTS: usize = 32;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36#[repr(u8)]
37pub enum FsStatus {
38 NotFound = 1,
39 PermissionDenied = 2,
40 InvalidHandle = 3,
41 HandleTableFull = 4,
42 IoError = 5,
43 DiskFull = 6,
44 NotADirectory = 7,
45 NotAFile = 8,
46 IsADirectory = 9,
47 DirNotEmpty = 10,
48 FileExists = 11,
49 NameTooLong = 12,
50 PathTooDeep = 13,
51 IntegrityError = 14,
52 StaleCap = 15,
53 InvalidOp = 16,
54 SymlinkDepth = 17,
55 Corrupt = 18,
56 Unsupported = 19,
57 CrossDevice = 20,
58 Unknown = 255,
59}
60
61impl FsStatus {
62 pub fn from_u8(v: u8) -> Self {
63 match v {
64 0 => Self::Unknown,
65 VFS_STATUS_NOT_FOUND => Self::NotFound,
66 VFS_STATUS_PERMISSION_DENIED => Self::PermissionDenied,
67 VFS_STATUS_INVALID_HANDLE => Self::InvalidHandle,
68 VFS_STATUS_HANDLE_TABLE_FULL => Self::HandleTableFull,
69 VFS_STATUS_IO_ERROR => Self::IoError,
70 VFS_STATUS_DISK_FULL => Self::DiskFull,
71 VFS_STATUS_NOT_A_DIRECTORY => Self::NotADirectory,
72 VFS_STATUS_NOT_A_FILE => Self::NotAFile,
73 VFS_STATUS_IS_A_DIRECTORY => Self::IsADirectory,
74 VFS_STATUS_DIR_NOT_EMPTY => Self::DirNotEmpty,
75 VFS_STATUS_FILE_EXISTS => Self::FileExists,
76 VFS_STATUS_NAME_TOO_LONG => Self::NameTooLong,
77 VFS_STATUS_PATH_TOO_DEEP => Self::PathTooDeep,
78 VFS_STATUS_INTEGRITY_ERROR => Self::IntegrityError,
79 VFS_STATUS_STALE_CAP => Self::StaleCap,
80 VFS_STATUS_INVALID_OP => Self::InvalidOp,
81 VFS_STATUS_SYMLINK_DEPTH => Self::SymlinkDepth,
82 VFS_STATUS_CORRUPT => Self::Corrupt,
83 VFS_STATUS_UNSUPPORTED => Self::Unsupported,
84 VFS_STATUS_CROSS_DEVICE => Self::CrossDevice,
85 _ => Self::Unknown,
86 }
87 }
88
89 pub fn name(self) -> &'static str {
90 match self {
91 Self::NotFound => "NotFound",
92 Self::PermissionDenied => "PermissionDenied",
93 Self::InvalidHandle => "InvalidHandle",
94 Self::HandleTableFull => "HandleTableFull",
95 Self::IoError => "IoError",
96 Self::DiskFull => "DiskFull",
97 Self::NotADirectory => "NotADirectory",
98 Self::NotAFile => "NotAFile",
99 Self::IsADirectory => "IsADirectory",
100 Self::DirNotEmpty => "DirNotEmpty",
101 Self::FileExists => "FileExists",
102 Self::NameTooLong => "NameTooLong",
103 Self::PathTooDeep => "PathTooDeep",
104 Self::IntegrityError => "IntegrityError",
105 Self::StaleCap => "StaleCap",
106 Self::InvalidOp => "InvalidOp",
107 Self::SymlinkDepth => "SymlinkDepth",
108 Self::Corrupt => "Corrupt",
109 Self::Unsupported => "Unsupported",
110 Self::CrossDevice => "CrossDevice",
111 Self::Unknown => "Unknown",
112 }
113 }
114}
115
116#[derive(Debug, Clone, Copy)]
117pub struct FileStat {
118 pub size: u64,
119 pub inode_type: u8,
120 pub block_count: u64,
121}
122
123impl FileStat {
124 pub fn from_response(val0: u64, val1: u64) -> Self {
125 Self {
126 size: val0,
127 inode_type: (val1 >> 48) as u8,
128 block_count: val1 & 0xFFFF_FFFF_FFFF,
129 }
130 }
131}
132
133pub struct FsClient {
134 request_ring: PacketRingWriter,
135 response_ring: PacketRingReader,
136 data_area: *mut u8,
137 data_area_size: usize,
138 next_tag: u32,
139 vfs_notif_slot: u8,
140 client_notif_slot: u8,
141}
142
143impl FsClient {
144 pub unsafe fn new(ring_vaddr: usize, vfs_notif: u8, client_notif: u8) -> Self {
145 let base = ring_vaddr as *mut u8;
146 let request_ring =
147 unsafe { PacketRingWriter::init(base, RING_HALF_SIZE, REQUEST_SLOT_SIZE) };
148 let response_ring =
149 unsafe { PacketRingReader::attach(base.wrapping_add(RING_HALF_SIZE), RING_HALF_SIZE) };
150 let data_area = (ring_vaddr + RING_DATA_OFFSET) as *mut u8;
151 Self {
152 request_ring,
153 response_ring,
154 data_area,
155 data_area_size: RING_DATA_SIZE,
156 next_tag: 1,
157 vfs_notif_slot: vfs_notif,
158 client_notif_slot: client_notif,
159 }
160 }
161
162 pub unsafe fn reattach(ring_vaddr: usize, vfs_notif: u8, client_notif: u8) -> Self {
163 let base = ring_vaddr as *mut u8;
164 let request_ring = unsafe { PacketRingWriter::attach(base, RING_HALF_SIZE) };
165 let response_ring =
166 unsafe { PacketRingReader::attach(base.wrapping_add(RING_HALF_SIZE), RING_HALF_SIZE) };
167 let data_area = (ring_vaddr + RING_DATA_OFFSET) as *mut u8;
168 Self {
169 request_ring,
170 response_ring,
171 data_area,
172 data_area_size: RING_DATA_SIZE,
173 next_tag: 1,
174 vfs_notif_slot: vfs_notif,
175 client_notif_slot: client_notif,
176 }
177 }
178
179 fn alloc_tag(&mut self) -> u32 {
180 let tag = self.next_tag;
181 self.next_tag = self.next_tag.wrapping_add(1);
182 tag
183 }
184
185 fn write_data(&self, offset: usize, data: &[u8]) {
186 let len = data.len().min(self.data_area_size.saturating_sub(offset));
187 unsafe {
188 core::ptr::copy_nonoverlapping(data.as_ptr(), self.data_area.add(offset), len);
189 }
190 }
191
192 fn read_data(&self, offset: usize, len: usize) -> &[u8] {
193 let actual = len.min(self.data_area_size.saturating_sub(offset));
194 unsafe { core::slice::from_raw_parts(self.data_area.add(offset), actual) }
195 }
196
197 fn send_request(&mut self, req: &VfsRequest) -> Result<VfsResponse, FsStatus> {
198 let data = req.as_bytes();
199 let pushed = (0..PUSH_RETRY_LIMIT).any(|_| self.request_ring.try_push(data));
200 if !pushed {
201 return Err(FsStatus::IoError);
202 }
203 syscall::notify_signal(self.vfs_notif_slot as u64, 1);
204
205 let tag = req.tag;
206 let mut buf = [0u8; 24];
207 (0..POLL_RETRY_LIMIT)
208 .find_map(|_| match self.response_ring.try_pop(&mut buf) {
209 Some(len) if len >= VfsResponse::SIZE => {
210 let resp = VfsResponse::from_bytes(&buf)?;
211 match resp.tag == tag {
212 true => Some(resp),
213 false => None,
214 }
215 }
216 _ => {
217 syscall::notify_wait(self.client_notif_slot as u64);
218 None
219 }
220 })
221 .ok_or(FsStatus::IoError)
222 }
223
224 fn check_response(resp: &VfsResponse) -> Result<(), FsStatus> {
225 match resp.status {
226 VFS_STATUS_OK => Ok(()),
227 s => Err(FsStatus::from_u8(s)),
228 }
229 }
230
231 pub fn open(&mut self, dir_handle: u8, name: &[u8], mode: FsRights) -> Result<u8, FsStatus> {
232 self.write_data(0, name);
233 let tag = self.alloc_tag();
234 let req = VfsRequest::open(tag, dir_handle, mode, 0, name.len() as u64);
235 let resp = self.send_request(&req)?;
236 Self::check_response(&resp)?;
237 Ok(resp.val0 as u8)
238 }
239
240 pub fn close(&mut self, handle: u8) -> Result<(), FsStatus> {
241 let tag = self.alloc_tag();
242 let req = VfsRequest::close(tag, handle);
243 let resp = self.send_request(&req)?;
244 Self::check_response(&resp)
245 }
246
247 pub fn read(&mut self, handle: u8, offset: u64, buf: &mut [u8]) -> Result<usize, FsStatus> {
248 let req_len = buf.len().min(self.data_area_size) as u32;
249 let tag = self.alloc_tag();
250 let req = VfsRequest::read(tag, handle, offset, req_len, 0);
251 let resp = self.send_request(&req)?;
252 Self::check_response(&resp)?;
253 let n = resp.val0 as usize;
254 let src = self.read_data(0, n);
255 let copy_len = src.len().min(buf.len());
256 buf[..copy_len].copy_from_slice(&src[..copy_len]);
257 Ok(copy_len)
258 }
259
260 pub fn write(&mut self, handle: u8, offset: u64, data: &[u8]) -> Result<usize, FsStatus> {
261 let actual_len = data.len().min(self.data_area_size);
262 self.write_data(0, &data[..actual_len]);
263 let tag = self.alloc_tag();
264 let req = VfsRequest::write(tag, handle, offset, actual_len as u32, 0);
265 let resp = self.send_request(&req)?;
266 Self::check_response(&resp)?;
267 Ok(resp.val0 as usize)
268 }
269
270 pub fn stat(&mut self, handle: u8) -> Result<FileStat, FsStatus> {
271 let tag = self.alloc_tag();
272 let req = VfsRequest::stat(tag, handle);
273 let resp = self.send_request(&req)?;
274 Self::check_response(&resp)?;
275 Ok(FileStat::from_response(resp.val0, resp.val1))
276 }
277
278 pub fn mkdir(&mut self, dir_handle: u8, name: &[u8]) -> Result<(), FsStatus> {
279 self.write_data(0, name);
280 let tag = self.alloc_tag();
281 let req = VfsRequest::mkdir(tag, dir_handle, 0, name.len() as u64);
282 let resp = self.send_request(&req)?;
283 Self::check_response(&resp)
284 }
285
286 pub fn unlink(&mut self, dir_handle: u8, name: &[u8]) -> Result<(), FsStatus> {
287 self.write_data(0, name);
288 let tag = self.alloc_tag();
289 let req = VfsRequest::unlink(tag, dir_handle, 0, name.len() as u64);
290 let resp = self.send_request(&req)?;
291 Self::check_response(&resp)
292 }
293
294 pub fn rename(
295 &mut self,
296 src_dir: u8,
297 src_name: &[u8],
298 dst_dir: u8,
299 dst_name: &[u8],
300 ) -> Result<(), FsStatus> {
301 self.write_data(0, src_name);
302 self.write_data(src_name.len(), dst_name);
303 let tag = self.alloc_tag();
304 let req = VfsRequest::rename(
305 tag,
306 src_dir,
307 dst_dir,
308 0,
309 src_name.len() as u32,
310 src_name.len() as u32,
311 dst_name.len() as u32,
312 );
313 let resp = self.send_request(&req)?;
314 Self::check_response(&resp)
315 }
316
317 pub fn readdir(
318 &mut self,
319 dir_handle: u8,
320 cursor: u64,
321 buf: &mut [u8],
322 ) -> Result<(usize, u64), FsStatus> {
323 let tag = self.alloc_tag();
324 let req = VfsRequest::readdir(tag, dir_handle, cursor, 0, buf.len() as u64);
325 let resp = self.send_request(&req)?;
326 Self::check_response(&resp)?;
327 let entry_count = resp.val0 as usize;
328 let next_cursor = resp.val1;
329 let byte_count = entry_count * READDIR_ENTRY_SIZE;
330 let src = self.read_data(0, byte_count);
331 let copy_len = src.len().min(buf.len());
332 buf[..copy_len].copy_from_slice(&src[..copy_len]);
333 let actual_entries = copy_len / READDIR_ENTRY_SIZE;
334 Ok((actual_entries, next_cursor))
335 }
336
337 pub fn truncate(&mut self, handle: u8, new_size: u64) -> Result<(), FsStatus> {
338 let tag = self.alloc_tag();
339 let req = VfsRequest::truncate(tag, handle, new_size);
340 let resp = self.send_request(&req)?;
341 Self::check_response(&resp)
342 }
343
344 pub fn mount_info(&mut self, buf: &mut [u8]) -> Result<usize, FsStatus> {
345 let tag = self.alloc_tag();
346 let req = VfsRequest::mount_info(tag);
347 let resp = self.send_request(&req)?;
348 Self::check_response(&resp)?;
349 let count = resp.val0 as usize;
350 let byte_count = count * MOUNT_INFO_ENTRY_SIZE;
351 let src = self.read_data(0, byte_count);
352 let copy_len = src.len().min(buf.len());
353 buf[..copy_len].copy_from_slice(&src[..copy_len]);
354 Ok(count)
355 }
356
357 pub fn sync(&mut self) -> Result<(), FsStatus> {
358 let tag = self.alloc_tag();
359 let req = VfsRequest::sync(tag);
360 let resp = self.send_request(&req)?;
361 Self::check_response(&resp)
362 }
363
364 pub fn extended_raw(
365 &mut self,
366 sub_opcode: u8,
367 arg0: u64,
368 arg1: u64,
369 ) -> Result<VfsResponse, FsStatus> {
370 let tag = self.alloc_tag();
371 let req = VfsRequest {
372 opcode: lancer_vfs_proto::VfsOpcode::Extended as u8,
373 handle: 0,
374 flags: sub_opcode,
375 _pad: 0,
376 tag,
377 arg0,
378 arg1,
379 arg2: 0,
380 };
381 let resp = self.send_request(&req)?;
382 Self::check_response(&resp)?;
383 Ok(resp)
384 }
385}
386
387pub unsafe fn init() -> FsClient {
388 let map_ok = (0..VFS_RING_FRAME_COUNT).all(|i| {
389 syscall::frame_map(VFS_RING_BASE_SLOT + i, VFS_RING_VADDR + i * 4096, 1) >= 0
390 });
391 match map_ok {
392 true => {}
393 false => {
394 syscall::debug_print("fs::init: failed to map VFS ring frames\n");
395 syscall::exit();
396 }
397 }
398
399 syscall::notify_wait(CLIENT_NOTIF_SLOT as u64);
400
401 unsafe { FsClient::new(VFS_RING_VADDR as usize, VFS_NOTIF_SLOT, CLIENT_NOTIF_SLOT) }
402}
403
404pub fn open_path(client: &mut FsClient, root_handle: u8, path: &[u8]) -> Result<u8, FsStatus> {
405 open_path_with_rights(client, root_handle, path, FsRights::TRAVERSE_INHERITABLE)
406}
407
408pub fn open_path_with_rights(
409 client: &mut FsClient,
410 root_handle: u8,
411 path: &[u8],
412 rights: FsRights,
413) -> Result<u8, FsStatus> {
414 let trimmed = match path.first() {
415 Some(&b'/') => &path[1..],
416 _ => path,
417 };
418 match trimmed.is_empty() {
419 true => client.open(root_handle, b"", rights),
420 false => open_path_components(client, root_handle, trimmed, 0, rights),
421 }
422}
423
424fn open_path_components(
425 client: &mut FsClient,
426 dir_handle: u8,
427 remaining: &[u8],
428 depth: usize,
429 leaf_rights: FsRights,
430) -> Result<u8, FsStatus> {
431 if depth >= MAX_PATH_COMPONENTS {
432 return Err(FsStatus::PathTooDeep);
433 }
434 match remaining.iter().position(|&b| b == b'/') {
435 Some(slash_pos) => {
436 let component = &remaining[..slash_pos];
437 let rest = &remaining[slash_pos + 1..];
438 let intermediate = client.open(dir_handle, component, FsRights::ALL)?;
439 let result = open_path_components(client, intermediate, rest, depth + 1, leaf_rights);
440 let _ = client.close(intermediate);
441 result
442 }
443 None => client.open(dir_handle, remaining, leaf_rights),
444 }
445}
446
447fn strip_trailing_slashes(s: &[u8]) -> &[u8] {
448 match s.last() {
449 Some(&b'/') => strip_trailing_slashes(&s[..s.len() - 1]),
450 _ => s,
451 }
452}
453
454pub fn open_parent<'a>(
455 client: &mut FsClient,
456 root_handle: u8,
457 path: &'a [u8],
458) -> Result<(u8, &'a [u8]), FsStatus> {
459 open_parent_with_rights(client, root_handle, path, FsRights::ALL)
460}
461
462pub fn open_parent_with_rights<'a>(
463 client: &mut FsClient,
464 root_handle: u8,
465 path: &'a [u8],
466 parent_rights: FsRights,
467) -> Result<(u8, &'a [u8]), FsStatus> {
468 let stripped = strip_trailing_slashes(path);
469 let trimmed = match stripped.first() {
470 Some(&b'/') => &stripped[1..],
471 _ => stripped,
472 };
473 match trimmed.is_empty() {
474 true => Err(FsStatus::NotFound),
475 false => match trimmed.iter().rposition(|&b| b == b'/') {
476 Some(last_slash) => {
477 let parent_path = &trimmed[..last_slash];
478 let basename = &trimmed[last_slash + 1..];
479 let parent_handle =
480 open_path_with_rights(client, root_handle, parent_path, parent_rights)?;
481 Ok((parent_handle, basename))
482 }
483 None => {
484 let parent = client.open(root_handle, b"", parent_rights)?;
485 Ok((parent, trimmed))
486 }
487 },
488 }
489}