1use std::io::{Read, Seek, Write};
22
23use num_derive::{FromPrimitive, ToPrimitive};
24use num_traits::{FromPrimitive, ToPrimitive};
25use server::{wrapped_scalar, CheckedConn, CheckedPermissions, MessageAllowed};
26use xous::{DropDeallocate, MemoryRange};
27
28pub mod adapter;
29pub mod error;
30mod flags;
31pub mod messages;
32
33pub use error::Error;
34use messages::*;
35
36pub const FILE_BUFFER_SIZE: usize = 64 * 512;
38pub const BLOCK_SIZE: u64 = 512;
39pub const SYSTEM_STATE_ROOT: &str = "state";
40
41#[macro_export]
51macro_rules! use_api {
52 ($fs:path, $server:path) => {
53 mod fs_permissions {
54 use fs::messages::*;
55 pub use $fs as fs;
56 use $server as server;
57 #[derive(Clone, Default, Debug, server::Permissions)]
58 #[server_name = "os/fs"]
59 pub struct FileSystemPermissions;
60 }
61 type FileSystem = fs_permissions::fs::FileSystem<fs_permissions::FileSystemPermissions>;
62 type File = fs_permissions::fs::File<fs_permissions::FileSystemPermissions>;
63 type Dir = fs_permissions::fs::Dir<fs_permissions::FileSystemPermissions>;
64 };
65 () => {
66 fs::use_api!(fs, server);
67 };
68}
69
70#[derive(Debug, Default, Clone)]
76pub struct FileSystem<P: CheckedPermissions> {
77 conn: CheckedConn<P>,
78 read_access_granted: flags::AccessFlags,
79 write_access_granted: flags::AccessFlags,
80}
81
82#[cfg(keyos)]
86pub trait MapFilePermissions: MessageAllowed<MapFileMessage> {}
87#[cfg(keyos)]
88impl<P: MessageAllowed<MapFileMessage>> MapFilePermissions for P {}
89
90#[cfg(not(keyos))]
91pub trait MapFilePermissions:
92 MessageAllowed<OpenFileMessage> + MessageAllowed<CloseFile> + MessageAllowed<ReadFile>
93{
94}
95#[cfg(not(keyos))]
96impl<P> MapFilePermissions for P where
97 P: MessageAllowed<OpenFileMessage> + MessageAllowed<CloseFile> + MessageAllowed<ReadFile>
98{
99}
100
101impl<P: CheckedPermissions> FileSystem<P> {
102 pub fn open_file(
103 &self,
104 path: impl Into<String>,
105 location: Location,
106 flags: OpenFlags,
107 ) -> Result<File<P>, Error>
108 where
109 P: MessageAllowed<OpenFileMessage>,
110 P: MessageAllowed<CloseFile>,
111 {
112 if flags.read {
113 self.ensure_read_access(location)?;
114 }
115 if flags.write {
116 self.ensure_write_access(location)?;
117 }
118 Ok(File {
119 handle: self.conn.send_blocking_archive(OpenFileMessage {
120 path: path.into(),
121 location,
122 flags,
123 })?,
124 work_buf: DropDeallocate::new(
125 xous::map_memory(None, None, FILE_BUFFER_SIZE, xous::MemoryFlags::W)
126 .map_err(|_| Error::FileNotOpen)?,
127 ),
128 conn: self.conn.clone(),
129 })
130 }
131
132 pub fn open_dir(&self, path: impl Into<String>, location: Location) -> Result<Dir<P>, Error>
133 where
134 P: MessageAllowed<OpenDirMessage>,
135 P: MessageAllowed<CloseDir>,
136 {
137 self.ensure_read_access(location)?;
138 Ok(Dir {
139 handle: self.conn.send_blocking_archive(OpenDirMessage { path: path.into(), location })?,
140 conn: self.conn.clone(),
141 })
142 }
143
144 pub fn create_dir(&self, path: impl Into<String>, location: Location) -> Result<Dir<P>, Error>
145 where
146 P: MessageAllowed<CreateDirMessage>,
147 P: MessageAllowed<CloseDir>,
148 {
149 self.ensure_write_access(location)?;
150 Ok(Dir {
151 handle: self.conn.send_blocking_archive(CreateDirMessage { path: path.into(), location })?,
152 conn: self.conn.clone(),
153 })
154 }
155
156 pub fn create_dir_async(
157 &self,
158 path: impl Into<String>,
159 location: Location,
160 ) -> Result<CreateDirMessage, Error>
161 where
162 P: MessageAllowed<CreateDirMessage>,
163 P: MessageAllowed<CloseDir>,
164 {
165 self.ensure_write_access(location)?;
166 Ok(CreateDirMessage { path: path.into(), location })
167 }
168
169 pub fn ensure_parent_dir_exists(&self, path: &str, location: Location) -> Result<(), Error>
170 where
171 P: MessageAllowed<CreateDirMessage>,
172 P: MessageAllowed<CloseDir>,
173 {
174 ensure_parent_dir_exists_impl(|dir| self.create_dir(dir, location).map(|_| ()), path)
175 }
176
177 pub fn remove(&self, path: impl Into<String>, location: Location) -> Result<(), Error>
178 where
179 P: MessageAllowed<Remove>,
180 {
181 let path = path.into();
182 self.ensure_write_access(location)?;
183 self.conn.send_blocking_archive(Remove { path, location })
184 }
185
186 pub fn remove_async(&self, path: impl Into<String>, location: Location) -> Result<Remove, Error>
187 where
188 P: MessageAllowed<Remove>,
189 {
190 let path = path.into();
191 self.ensure_write_access(location)?;
192 Ok(Remove { path, location })
193 }
194
195 pub fn atomic_copy(
203 &self,
204 src: impl Into<String>,
205 dest_dir: impl Into<String>,
206 rename: Option<impl Into<String>>,
207 location: Location,
208 ) -> Result<(), Error>
209 where
210 P: MessageAllowed<AtomicCopy>,
211 {
212 self.ensure_read_access(location)?;
213 self.ensure_write_access(location)?;
214
215 let src = src.into();
216 let dest_dir = dest_dir.into();
217 let rename = rename.map(|s| s.into());
218 self.conn.send_blocking_archive(AtomicCopy { src, dest_dir, rename, location })
219 }
220
221 pub fn metadata(&self, path: impl Into<String>, location: Location) -> Result<Metadata, Error>
222 where
223 P: MessageAllowed<GetMetadata>,
224 {
225 self.ensure_read_access(location)?;
226 self.conn.send_blocking_archive(GetMetadata::Path { path: path.into(), location })
227 }
228
229 pub fn rename(
230 &self,
231 from: impl Into<String>,
232 to: impl Into<String>,
233 location: Location,
234 ) -> Result<(), Error>
235 where
236 P: MessageAllowed<Rename>,
237 {
238 self.ensure_write_access(location)?;
239 self.conn.send_blocking_archive(Rename { from: from.into(), to: to.into(), location })
240 }
241
242 pub fn rename_async(
243 &self,
244 from: impl Into<String>,
245 to: impl Into<String>,
246 location: Location,
247 ) -> Result<Rename, Error>
248 where
249 P: MessageAllowed<Rename>,
250 {
251 self.ensure_write_access(location)?;
252 Ok(Rename { from: from.into(), to: to.into(), location })
253 }
254
255 pub fn map_file(&self, location: Location, path: impl Into<String>) -> Result<xous::MemoryRange, Error>
256 where
257 P: MapFilePermissions,
258 {
259 self.ensure_read_access(location)?;
260
261 #[cfg(keyos)]
262 {
263 let result = self.conn.send_blocking_archive(MapFileMessage { path: path.into(), location })?;
264 Ok(unsafe { xous::MemoryRange::new(result.addr, result.size).unwrap() })
265 }
266
267 #[cfg(not(keyos))]
271 {
272 let mut file = self.open_file(path, location, OpenFlags::READ_ONLY)?;
273 let mut bytes = Vec::new();
274 file.read_to_end(&mut bytes).map_err(|_| Error::Io)?;
275 if bytes.is_empty() {
276 return Err(Error::FileNotFound);
277 }
278
279 let mut buffer = xous::map_memory(None, None, bytes.len(), xous::MemoryFlags::W)
280 .map_err(|_| Error::OutOfMemory)?;
281 buffer.as_slice_mut()[..bytes.len()].copy_from_slice(&bytes);
282 Ok(buffer)
283 }
284 }
285
286 pub fn register_app_resources(
287 &self,
288 app_id: xous::AppId,
289 root: AppResourcesRoot,
290 app_dir: impl Into<String>,
291 ) -> Result<(), Error>
292 where
293 P: MessageAllowed<RegisterAppResources>,
294 {
295 self.conn.send_blocking_archive(RegisterAppResources { app_id, root, app_dir: app_dir.into() })
296 }
297
298 fn ensure_read_access(&self, location: Location) -> Result<(), Error> {
299 if self.read_access_granted.contains(location) {
300 return Ok(());
301 }
302 match location {
303 Location::CommonAssets | Location::AppData | Location::AppResources => return Ok(()),
304 Location::System => self.conn.unchecked().try_send_blocking_scalar(GetSystemReadAccess)?,
305 Location::SystemAppData => {
306 self.conn.unchecked().try_send_blocking_scalar(GetSystemAppDataReadAccess)?
307 }
308 Location::EncryptedRoot => {
309 self.conn.unchecked().try_send_blocking_scalar(GetEncryptedRootReadAccess)?
310 }
311 Location::Usb => self.conn.unchecked().try_send_blocking_scalar(GetUsbReadAccess)?,
312 Location::User => self.conn.unchecked().try_send_blocking_scalar(GetUserReadAccess)?,
313 Location::Airlock => self.conn.unchecked().try_send_blocking_scalar(GetAirlockReadAccess)?,
314 Location::Boot => self.conn.unchecked().try_send_blocking_scalar(GetBootReadAccess)?,
315 };
316 self.read_access_granted.insert(location);
317 Ok(())
318 }
319
320 fn ensure_write_access(&self, location: Location) -> Result<(), Error> {
321 if self.write_access_granted.contains(location) {
322 return Ok(());
323 }
324 match location {
325 Location::CommonAssets | Location::AppResources => return Err(Error::AccessDenied)?,
326 Location::AppData => return Ok(()),
327 Location::System => self.conn.unchecked().try_send_blocking_scalar(GetSystemWriteAccess)?,
328 Location::SystemAppData => {
329 self.conn.unchecked().try_send_blocking_scalar(GetSystemAppDataWriteAccess)?
330 }
331 Location::EncryptedRoot => {
332 self.conn.unchecked().try_send_blocking_scalar(GetEncryptedRootWriteAccess)?
333 }
334 Location::Usb => self.conn.unchecked().try_send_blocking_scalar(GetUsbWriteAccess)?,
335 Location::User => self.conn.unchecked().try_send_blocking_scalar(GetUserWriteAccess)?,
336 Location::Airlock => self.conn.unchecked().try_send_blocking_scalar(GetAirlockWriteAccess)?,
337 Location::Boot => self.conn.unchecked().try_send_blocking_scalar(GetBootWriteAccess)?,
338 };
339 self.write_access_granted.insert(location);
340 Ok(())
341 }
342
343 pub fn read_blocks(
344 &mut self,
345 location: Location,
346 block_index: u32,
347 block_count: usize,
348 buf: MemoryRange,
349 ) -> Result<usize, Error>
350 where
351 P: MessageAllowed<ReadBlocks>,
352 {
353 self.conn.lend_mut(ReadBlocks { buf, block_index, block_count, location })
354 }
355
356 pub fn write_blocks(
357 &mut self,
358 location: Location,
359 block_index: u32,
360 block_count: usize,
361 buf: MemoryRange,
362 ) -> Result<usize, Error>
363 where
364 P: MessageAllowed<WriteBlocks>,
365 {
366 self.conn.lend_mut(WriteBlocks { buf, block_index, block_count, location })
367 }
368
369 pub fn flush(&mut self, location: Location) -> Result<(), Error>
370 where
371 P: MessageAllowed<FlushFs>,
372 {
373 self.conn.try_send_blocking_scalar(FlushFs(location))?
374 }
375
376 pub fn block_count(&self, location: Location) -> Result<usize, Error>
377 where
378 P: MessageAllowed<BlockCount>,
379 {
380 self.conn.try_send_blocking_scalar(BlockCount(location))?
381 }
382
383 pub fn format_encrypted_volume(&self)
384 where
385 P: MessageAllowed<FormatEncryptedVolume>,
386 {
387 self.conn.send_blocking_scalar(FormatEncryptedVolume);
388 }
389
390 pub fn subscribe_filesystem_events<S>(&self, listener: &mut server::ServerContext<S>, location: Location)
391 where
392 S: server::Server + server::ScalarEventHandler<FileSystemEvent>,
393 P: MessageAllowed<SubscribeFilesystemEvent>,
394 {
395 self.conn.subscribe_scalar_infallible(SubscribeFilesystemEvent(location), listener)
396 }
397
398 pub fn wait_for_filesystem(&self, location: Location)
399 where
400 P: 'static,
401 P: MessageAllowed<SubscribeFilesystemEvent>,
402 {
403 server::listen(WaitForFs(self.clone(), location));
404 }
405
406 pub fn mount_airlock(&mut self) -> Result<(), Error>
407 where
408 P: MessageAllowed<MountAirlock>,
409 {
410 self.conn.send_blocking_scalar(MountAirlock(true))
411 }
412
413 pub fn unmount_airlock(&mut self) -> Result<(), Error>
414 where
415 P: MessageAllowed<MountAirlock>,
416 {
417 self.conn.send_blocking_scalar(MountAirlock(false))
418 }
419
420 pub fn format_airlock(&mut self) -> Result<(), Error>
421 where
422 P: MessageAllowed<FormatAirlock>,
423 {
424 self.conn.send_blocking_scalar(FormatAirlock)
425 }
426}
427
428#[derive(Debug)]
429pub struct File<P: CheckedPermissions + MessageAllowed<CloseFile>> {
430 handle: FileHandle,
431 conn: CheckedConn<P>,
432 work_buf: DropDeallocate,
433}
434
435impl<P: CheckedPermissions + MessageAllowed<CloseFile>> File<P> {
436 pub fn truncate(&mut self) -> Result<(), Error>
437 where
438 P: MessageAllowed<TruncateFile>,
439 {
440 self.conn.send_blocking_archive(TruncateFile(self.handle))
441 }
442
443 pub fn set_len(&mut self, len: u64) -> Result<(), Error>
447 where
448 P: MessageAllowed<SetLen>,
449 {
450 self.conn.send_blocking_archive(SetLen { handle: self.handle, len })
451 }
452
453 pub fn metadata(&self) -> Result<Metadata, Error>
454 where
455 P: MessageAllowed<GetMetadata>,
456 {
457 self.conn.send_blocking_archive(GetMetadata::Handle { handle: self.handle })
458 }
459
460 pub fn set_mtime(&mut self, datetime: DateTime) -> Result<(), Error>
461 where
462 P: MessageAllowed<SetMtime>,
463 {
464 self.conn.send_blocking_archive(SetMtime { handle: self.handle, datetime })
465 }
466
467 pub fn async_read(&mut self, read_len: usize) -> AsyncRead { AsyncRead { handle: self.handle, read_len } }
472
473 pub fn async_write(&mut self, buffer: Vec<u8>) -> AsyncWrite {
477 AsyncWrite { handle: self.handle, buffer }
478 }
479
480 pub fn async_copy_block_to(&mut self, to: &mut Self, len: usize) -> AsyncCopyBlock {
485 AsyncCopyBlock { from: self.handle, to: to.handle, len }
486 }
487
488 pub fn copy_block_to(&mut self, to: &mut Self, len: usize) -> Result<usize, Error>
489 where
490 P: MessageAllowed<AsyncCopyBlock>,
491 {
492 self.conn.send_blocking_scalar(AsyncCopyBlock { from: self.handle, to: to.handle, len })
493 }
494
495 pub fn overwrite(&mut self, buf: &[u8]) -> Result<(), Error>
496 where
497 P: MessageAllowed<SeekFile>,
498 P: MessageAllowed<WriteFile>,
499 P: MessageAllowed<TruncateFile>,
500 P: MessageAllowed<Flush>,
501 {
502 self.seek(std::io::SeekFrom::Start(0))?;
503 self.write_all(buf)?;
504 self.truncate()?;
505 Ok(())
506 }
507
508 pub fn copy_to(&mut self, to: &mut Self) -> Result<(), Error>
509 where
510 P: MessageAllowed<SeekFile>,
511 P: MessageAllowed<WriteFile>,
512 P: MessageAllowed<TruncateFile>,
513 P: MessageAllowed<AsyncCopyBlock>,
514 {
515 to.seek(std::io::SeekFrom::Start(0))?;
516 while self.copy_block_to(to, 0x10000)? > 0 {}
517 to.truncate()?;
518 Ok(())
519 }
520}
521
522impl<P: CheckedPermissions + MessageAllowed<CloseFile>> Read for File<P>
523where
524 P: MessageAllowed<ReadFile>,
525{
526 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
527 #[cfg(keyos)]
528 if (buf.as_ptr() as usize) & (xous::keyos::PAGE_SIZE - 1) == 0 && buf.len() >= xous::keyos::PAGE_SIZE
529 {
530 let read_len = (buf.len() & !(xous::keyos::PAGE_SIZE - 1)).min(FILE_BUFFER_SIZE);
531 return Ok(self.conn.lend_mut(ReadFile {
532 buf: unsafe { xous::MemoryRange::new(buf.as_ptr() as usize, read_len).unwrap() },
533 handle: self.handle,
534 read_len,
535 })?);
536 }
537
538 let read_len = buf.len().min(FILE_BUFFER_SIZE);
539
540 let result = self.conn.lend_mut(ReadFile { buf: *self.work_buf, handle: self.handle, read_len })?;
541 buf[..result].copy_from_slice(&self.work_buf.as_slice()[..result]);
542 Ok(result)
543 }
544}
545
546impl<P: CheckedPermissions + MessageAllowed<CloseFile>> Seek for File<P>
547where
548 P: MessageAllowed<SeekFile>,
549{
550 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
551 self.conn.send_blocking_archive(SeekFile { file: self.handle, pos: pos.into() }).map_err(Into::into)
552 }
553}
554
555impl<P: CheckedPermissions + MessageAllowed<CloseFile>> Write for File<P>
556where
557 P: MessageAllowed<WriteFile>,
558 P: MessageAllowed<Flush>,
559{
560 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
561 #[cfg(keyos)]
562 if (buf.as_ptr() as usize) & (xous::keyos::PAGE_SIZE - 1) == 0 && buf.len() >= xous::keyos::PAGE_SIZE
563 {
564 let write_len = (buf.len() & !(xous::keyos::PAGE_SIZE - 1)).min(FILE_BUFFER_SIZE);
565 return Ok(self.conn.lend_mut(WriteFile {
566 buf: unsafe { xous::MemoryRange::new(buf.as_ptr() as usize, write_len).unwrap() },
567 handle: self.handle,
568 write_len,
569 })?);
570 }
571
572 let buf_len = buf.len().min(FILE_BUFFER_SIZE);
573
574 self.work_buf.as_slice_mut()[..buf_len].copy_from_slice(&buf[..buf_len]);
575 let result =
576 self.conn.lend_mut(WriteFile { buf: *self.work_buf, handle: self.handle, write_len: buf_len })?;
577 Ok(result)
578 }
579
580 fn flush(&mut self) -> std::io::Result<()> {
581 self.conn.try_send_blocking_scalar(Flush(self.handle)).map_err(|_| std::io::ErrorKind::Other)??;
582 Ok(())
583 }
584}
585
586impl<P: CheckedPermissions + MessageAllowed<CloseFile>> Drop for File<P> {
587 fn drop(&mut self) {
588 if let Err(e) = self.conn.try_send_blocking_scalar(CloseFile(self.handle)) {
589 log::error!("Failed to close file: {:?}", e);
590 }
591 }
592}
593
594#[derive(Debug)]
595pub struct Dir<P: CheckedPermissions + MessageAllowed<CloseDir>> {
596 handle: DirHandle,
597 conn: CheckedConn<P>,
598}
599
600impl<P: CheckedPermissions + MessageAllowed<CloseDir>> Dir<P> {
601 pub fn next_entry(&self) -> Result<Option<DirEntry>, Error>
602 where
603 P: MessageAllowed<NextEntry>,
604 {
605 self.conn.send_blocking_archive(NextEntry(self.handle))
606 }
607
608 pub fn next_entry_async(&self) -> NextEntry { NextEntry(self.handle) }
609
610 pub fn pick_next_filename(&self, filename: impl Into<String>, pad: Option<usize>) -> Result<String, Error>
611 where
612 P: MessageAllowed<NextEntry>,
613 {
614 let filename: String = filename.into();
615
616 if filename.contains('/') {
618 return Err(Error::InvalidPath);
619 }
620
621 let pad = pad.unwrap_or(3);
622
623 let (basename, ext) = match filename.rsplit_once('.') {
625 Some((base, ext)) => (base, Some(format!(".{}", ext))),
626 None => (filename.as_str(), None),
627 };
628
629 let mut highest = 0u32;
630 let prefix = format!("{}-", basename);
631
632 while let Some(entry) = self.next_entry().ok().flatten() {
633 let name = entry.name;
634
635 let remainder = match name.strip_prefix(&prefix) {
637 Some(r) => r,
638 None => continue,
639 };
640
641 let num = match &ext {
643 Some(e) => match remainder.strip_suffix(e) {
644 Some(n) => n,
645 None => continue,
646 },
647 None => remainder,
648 };
649
650 if let Ok(n) = num.parse::<u32>() {
651 highest = highest.max(n);
652 }
653 }
654
655 let number = highest + 1;
657 Ok(format!("{}-{number:0pad$}{}", basename, ext.clone().unwrap_or_default()))
658 }
659}
660
661impl<P: CheckedPermissions + MessageAllowed<CloseDir>> Drop for Dir<P> {
662 fn drop(&mut self) {
663 if let Err(e) = self.conn.try_send_blocking_scalar(CloseDir(self.handle)) {
664 log::error!("Failed to close dir: {:?}", e);
665 }
666 }
667}
668
669pub struct WaitForFs<P: CheckedPermissions>(pub FileSystem<P>, pub Location);
671
672impl<P: CheckedPermissions> server::ServerMessages for WaitForFs<P> {
673 const NAME: &'static str = "";
674
675 fn messages() -> &'static [server::MessageDef<Self>]
676 where
677 Self: Sized,
678 {
679 &[]
680 }
681}
682
683impl<P: CheckedPermissions> server::Server for WaitForFs<P>
684where
685 P: MessageAllowed<SubscribeFilesystemEvent>,
686{
687 fn on_start(&mut self, context: &mut server::ServerContext<Self>) {
688 self.0.subscribe_filesystem_events(context, self.1);
689 }
690}
691
692impl<P: CheckedPermissions> server::ScalarEventHandler<FileSystemEvent> for WaitForFs<P>
693where
694 P: MessageAllowed<SubscribeFilesystemEvent>,
695{
696 fn handle(
697 &mut self,
698 msg: FileSystemEvent,
699 _sender: xous::PID,
700 context: &mut server::ServerContext<Self>,
701 ) {
702 if msg.location == self.1 && msg.event_type == FileSystemEventType::Mounted {
703 context.shutdown();
704 }
705 }
706}
707
708#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
711pub struct FileHandle(pub u32);
712
713wrapped_scalar!(FileHandle);
714
715impl FileHandle {
716 pub fn new(id: u32, location: Location) -> Self {
717 Self(((location.to_usize().unwrap() as u32) << 24) | (id & 0x00FF_FFFF))
718 }
719
720 pub fn id(self) -> u32 { self.0 & 0x00FF_FFFF }
721
722 pub fn location(self) -> Result<Location, Error> {
723 Location::from_usize((self.0 >> 24) as usize).ok_or(Error::FileNotOpen)
724 }
725}
726
727#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
728pub struct DirHandle(pub u32);
729
730wrapped_scalar!(DirHandle);
731
732impl DirHandle {
733 pub fn new(id: u32, location: Location) -> Self {
734 Self(((location.to_usize().unwrap() as u32) << 24) | (id & 0x00FF_FFFF))
735 }
736
737 pub fn id(self) -> u32 { self.0 & 0x00FF_FFFF }
738
739 pub fn location(self) -> Result<Location, Error> {
740 Location::from_usize((self.0 >> 24) as usize).ok_or(Error::FileNotOpen)
741 }
742}
743
744#[derive(
745 Debug,
746 Clone,
747 Copy,
748 PartialEq,
749 Eq,
750 Hash,
751 rkyv::Archive,
752 rkyv::Serialize,
753 rkyv::Deserialize,
754 FromPrimitive,
755 ToPrimitive,
756)]
757pub enum Location {
758 CommonAssets = 1,
762
763 AppData,
767
768 System,
771
772 EncryptedRoot,
775
776 Boot,
778
779 Usb,
781
782 User,
785
786 Airlock,
788
789 SystemAppData,
792
793 AppResources,
798}
799
800impl server::AsScalar<1> for Location {
801 fn as_scalar(&self) -> [u32; 1] { [self.to_u32().unwrap()] }
802}
803
804impl server::FromScalar<1> for Location {
805 fn from_scalar([value]: [u32; 1]) -> Self { Self::from_u32(value).unwrap_or(Location::AppData) }
806}
807
808#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
809pub struct DirEntry {
810 pub name: String,
811 pub len: u64,
812 pub modified: DateTime,
813 pub is_dir: bool,
814 pub is_file: bool,
815}
816
817#[derive(Debug, Clone, Copy, PartialEq, Eq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
818pub struct OpenFlags {
819 pub read: bool,
820 pub write: bool,
821 pub create: bool,
822}
823
824impl OpenFlags {
825 pub const CREATE: Self = Self { read: true, write: true, create: true };
826 pub const READ_ONLY: Self = Self { read: true, write: false, create: false };
827 pub const READ_WRITE: Self = Self { read: true, write: true, create: false };
828}
829
830#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
831pub enum SeekFrom {
832 Start(u64),
833 End(i64),
834 Current(i64),
835}
836
837impl From<SeekFrom> for std::io::SeekFrom {
838 fn from(from: SeekFrom) -> Self {
839 match from {
840 SeekFrom::Start(offset) => std::io::SeekFrom::Start(offset),
841 SeekFrom::End(offset) => std::io::SeekFrom::End(offset),
842 SeekFrom::Current(offset) => std::io::SeekFrom::Current(offset),
843 }
844 }
845}
846
847impl From<std::io::SeekFrom> for SeekFrom {
848 fn from(from: std::io::SeekFrom) -> Self {
849 match from {
850 std::io::SeekFrom::Start(offset) => SeekFrom::Start(offset),
851 std::io::SeekFrom::End(offset) => SeekFrom::End(offset),
852 std::io::SeekFrom::Current(offset) => SeekFrom::Current(offset),
853 }
854 }
855}
856
857#[derive(Debug, Clone)]
858pub struct FileSystemEvent {
859 pub location: Location,
860 pub event_type: FileSystemEventType,
861}
862
863#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
864pub enum FileSystemEventType {
865 Mounted,
866 Unmounted,
867 Error,
868}
869
870impl server::AsScalar<2> for FileSystemEvent {
871 fn as_scalar(&self) -> [u32; 2] {
872 let [location] = self.location.as_scalar();
873 [location, self.event_type.to_u32().unwrap()]
874 }
875}
876
877impl server::FromScalar<2> for FileSystemEvent {
878 fn from_scalar([location, event_type]: [u32; 2]) -> Self {
879 Self {
880 location: Location::from_scalar([location]),
881 event_type: FileSystemEventType::from_u32(event_type).unwrap_or(FileSystemEventType::Mounted),
882 }
883 }
884}
885
886#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
887pub struct MappedFileInTheirSpace {
888 pub addr: usize,
889 pub size: usize,
890}
891
892#[derive(Debug, Clone, Copy, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
893pub struct Metadata {
894 pub created: DateTime,
895 pub accessed: Date,
896 pub modified: DateTime,
897 pub size: u64,
898 pub is_dir: bool,
899}
900
901#[derive(Debug, Clone, Copy, Eq, PartialEq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
902pub struct Date {
903 pub year: u16,
904 pub month: u16,
905 pub day: u16,
906}
907
908impl Ord for Date {
909 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
910 self.year
911 .cmp(&other.year)
912 .then_with(|| self.month.cmp(&other.month))
913 .then_with(|| self.day.cmp(&other.day))
914 }
915}
916
917impl PartialOrd for Date {
918 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
919}
920
921#[derive(Debug, Clone, Copy, Eq, PartialEq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
922pub struct Time {
923 pub hour: u16,
924 pub min: u16,
925 pub sec: u16,
926 pub millis: u16,
927}
928
929impl Ord for Time {
930 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
931 self.hour
932 .cmp(&other.hour)
933 .then_with(|| self.min.cmp(&other.min))
934 .then_with(|| self.sec.cmp(&other.sec))
935 .then_with(|| self.millis.cmp(&other.millis))
936 }
937}
938
939impl PartialOrd for Time {
940 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
941}
942
943#[derive(Debug, Clone, Copy, Eq, PartialEq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
944pub struct DateTime {
945 pub date: Date,
946 pub time: Time,
947}
948
949impl Ord for DateTime {
950 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
951 self.date.cmp(&other.date).then_with(|| self.time.cmp(&other.time))
952 }
953}
954
955impl PartialOrd for DateTime {
956 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
957}
958
959pub(crate) fn ensure_parent_dir_exists_impl(
960 mut create_dir: impl FnMut(&str) -> Result<(), Error>,
961 path: &str,
962) -> Result<(), Error> {
963 fn recurse(create_dir: &mut impl FnMut(&str) -> Result<(), Error>, path: &str) -> Result<(), Error> {
964 if let Some(parent) = path.rsplit_once('/').map(|(parent, _)| parent) {
965 if !parent.is_empty() {
966 match create_dir(parent) {
967 Ok(_) | Err(Error::FileAlreadyExists) => {}
968 Err(Error::FileNotFound) => {
969 recurse(create_dir, parent)?;
970 match create_dir(parent) {
971 Ok(_) | Err(Error::FileAlreadyExists) => {}
972 Err(e) => return Err(e),
973 }
974 }
975 Err(e) => return Err(e),
976 }
977 }
978 }
979 Ok(())
980 }
981
982 recurse(&mut create_dir, path)
983}