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