diff options
| author | Fenrir <[email protected]> | 2016-09-26 06:17:50 -0700 |
|---|---|---|
| committer | Fenrir <[email protected]> | 2016-09-26 12:38:27 -0700 |
| commit | e7f87b9d8fe1a2131212107f3df2ab162b9c2ff2 (patch) | |
| tree | 6ffc4c696453526f65641bef45dcfb48c88a8df1 | |
| parent | Add rename function (diff) | |
| download | archived-ctru-rs-e7f87b9d8fe1a2131212107f3df2ab162b9c2ff2.tar.xz archived-ctru-rs-e7f87b9d8fe1a2131212107f3df2ab162b9c2ff2.zip | |
Add read_dir function (directory iteration!)
| -rw-r--r-- | src/services/fs.rs | 110 |
1 files changed, 99 insertions, 11 deletions
diff --git a/src/services/fs.rs b/src/services/fs.rs index a03fe92..fabacfa 100644 --- a/src/services/fs.rs +++ b/src/services/fs.rs @@ -1,9 +1,12 @@ use core::marker::PhantomData; use core::ptr; use core::slice; +use core::mem; +use alloc::arc::Arc; use collections::Vec; -use path::Path; +use path::{Path, PathBuf}; +use ffi::OsString; use libctru::services::fs::*; @@ -65,6 +68,21 @@ pub struct OpenOptions { arch_handle: u64, } +pub struct ReadDir { + handle: Dir, + root: Arc<PathBuf>, +} + +pub struct DirEntry { + root: Arc<PathBuf>, + entry: FS_DirectoryEntry, +} + +struct Dir(u32); + +unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} + impl Fs { pub fn init() -> Result<Fs, i32> { unsafe { @@ -103,11 +121,11 @@ impl Archive { impl File { pub fn open<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<File, i32> { - OpenOptions::new().read(true).archive(arch).open(path) + OpenOptions::new().read(true).archive(arch).open(path.as_ref()) } pub fn create<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<File, i32> { - OpenOptions::new().write(true).create(true).archive(arch).open(path) + OpenOptions::new().write(true).create(true).archive(arch).open(path.as_ref()) } pub fn len(&self) -> Result<u64, i32> { @@ -243,9 +261,44 @@ impl OpenOptions { } } +impl Iterator for ReadDir { + type Item = Result<DirEntry, i32>; + + fn next(&mut self) -> Option<Result<DirEntry, i32>> { + unsafe { + let mut ret = DirEntry { + entry: mem::zeroed(), + root: self.root.clone(), + }; + let mut entries_read = 0; + let entry_count = 1; + let r = FSDIR_Read(self.handle.0, &mut entries_read, entry_count, &mut ret.entry); + + if r < 0 { + return Some(Err(r)) + } + if entries_read != entry_count { + return None + } + Some(Ok(ret)) + } + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.root.join(&self.file_name()) + } + + pub fn file_name(&self) -> OsString { + let filename = truncate_utf16_at_nul(&self.entry.name); + OsString::from_wide(filename) + } +} + pub fn create_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> { unsafe { - let path = to_utf16(path); + let path = to_utf16(path.as_ref()); let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _); let r = FSUSER_CreateDirectory(arch.handle, fs_path, FS_ATTRIBUTE_DIRECTORY); if r < 0 { @@ -258,7 +311,7 @@ pub fn create_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> { pub fn remove_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> { unsafe { - let path = to_utf16(path); + let path = to_utf16(path.as_ref()); let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _); let r = FSUSER_DeleteDirectory(arch.handle, fs_path); if r < 0 { @@ -271,7 +324,7 @@ pub fn remove_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> { pub fn remove_dir_all<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> { unsafe { - let path = to_utf16(path); + let path = to_utf16(path.as_ref()); let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _); let r = FSUSER_DeleteDirectoryRecursively(arch.handle, fs_path); if r < 0 { @@ -282,9 +335,13 @@ pub fn remove_dir_all<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32 } } +pub fn read_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<ReadDir, i32> { + readdir(&arch, path.as_ref()) +} + pub fn remove_file<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> { unsafe { - let path = to_utf16(path); + let path = to_utf16(path.as_ref()); let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _); let r = FSUSER_DeleteFile(arch.handle, fs_path); if r < 0 { @@ -300,8 +357,8 @@ pub fn rename<P, Q>(arch: &Archive, from: P, to: Q) -> Result<(), i32> Q: AsRef<Path> { unsafe { - let from = to_utf16(from); - let to = to_utf16(to); + let from = to_utf16(from.as_ref()); + let to = to_utf16(to.as_ref()); let fs_from = fsMakePath(PathType::UTF16.into(), from.as_ptr() as _); let fs_to = fsMakePath(PathType::UTF16.into(), to.as_ptr() as _); @@ -318,11 +375,34 @@ pub fn rename<P, Q>(arch: &Archive, from: P, to: Q) -> Result<(), i32> } } +fn readdir(arch: &Archive, p: &Path) -> Result<ReadDir, i32> { + unsafe { + let mut handle = 0; + let root = Arc::new(p.to_path_buf()); + let path = to_utf16(p); + let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _); + let r = FSUSER_OpenDirectory(&mut handle, arch.handle, fs_path); + if r < 0 { + Err(r) + } else { + Ok(ReadDir { handle: Dir(handle), root: root }) + } + } +} + // TODO: Determine if interior NULLs are premitted in 3DS file paths -fn to_utf16<S: AsRef<Path>>(path: S) -> Vec<u16> { - path.as_ref().as_os_str().encode_wide().collect::<Vec<_>>() +fn to_utf16(path: &Path) -> Vec<u16> { + path.as_os_str().encode_wide().collect::<Vec<_>>() } +// Adapted from sys/windows/fs.rs in libstd +fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { + match v.iter().position(|c| *c == 0) { + // don't include the 0 + Some(i) => &v[..i], + None => v + } +} // Adapted from sys/common/io.rs in libstd unsafe fn read_to_end_uninitialized(f: &mut File, buf: &mut Vec<u8>) -> Result<usize, i32> { @@ -375,6 +455,14 @@ impl Drop for File { } } +impl Drop for Dir { + fn drop(&mut self) { + unsafe { + FSDIR_Close(self.0); + } + } +} + impl From<PathType> for FS_PathType { fn from(p: PathType) -> Self { use self::PathType::*; |