diff --git a/Cargo.lock b/Cargo.lock
index 2e0822984..5578c4d8a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -31,17 +31,6 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
-[[package]]
-name = "aes"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
-dependencies = [
- "cfg-if",
- "cipher",
- "cpufeatures",
-]
-
[[package]]
name = "ahash"
version = "0.8.11"
@@ -461,15 +450,6 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
-[[package]]
-name = "block-buffer"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
-dependencies = [
- "generic-array",
-]
-
[[package]]
name = "block2"
version = "0.5.1"
@@ -697,16 +677,6 @@ dependencies = [
"half",
]
-[[package]]
-name = "cipher"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
-dependencies = [
- "crypto-common",
- "inout",
-]
-
[[package]]
name = "clang-sys"
version = "1.8.1"
@@ -984,15 +954,6 @@ version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
-[[package]]
-name = "cpufeatures"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
-dependencies = [
- "libc",
-]
-
[[package]]
name = "crc32fast"
version = "1.4.2"
@@ -1039,16 +1000,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
-[[package]]
-name = "crypto-common"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
-dependencies = [
- "generic-array",
- "typenum",
-]
-
[[package]]
name = "ctor-lite"
version = "0.1.0"
@@ -1091,17 +1042,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "digest"
-version = "0.10.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
-dependencies = [
- "block-buffer",
- "crypto-common",
- "subtle",
-]
-
[[package]]
name = "dispatch"
version = "0.2.0"
@@ -1568,16 +1508,6 @@ dependencies = [
"num-traits",
]
-[[package]]
-name = "generic-array"
-version = "0.14.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
-
[[package]]
name = "gethostname"
version = "0.4.3"
@@ -1787,15 +1717,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest",
-]
-
[[package]]
name = "home"
version = "0.5.9"
@@ -2251,15 +2172,6 @@ dependencies = [
"hashbrown",
]
-[[package]]
-name = "inout"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
-dependencies = [
- "generic-array",
-]
-
[[package]]
name = "input"
version = "0.9.1"
@@ -3095,19 +3007,6 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
-[[package]]
-name = "pfs"
-version = "0.1.0"
-dependencies = [
- "aes",
- "byteorder",
- "flate2",
- "hmac",
- "sha2",
- "thiserror",
- "xts-mode",
-]
-
[[package]]
name = "pico-args"
version = "0.5.0"
@@ -3592,17 +3491,6 @@ dependencies = [
"unsafe-libyaml",
]
-[[package]]
-name = "sha2"
-version = "0.10.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
[[package]]
name = "shlex"
version = "1.3.0"
@@ -3878,12 +3766,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "subtle"
-version = "2.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
-
[[package]]
name = "svgtypes"
version = "0.15.2"
@@ -4142,12 +4024,6 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e"
-[[package]]
-name = "typenum"
-version = "1.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
-
[[package]]
name = "udev"
version = "0.9.1"
@@ -5075,16 +4951,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
-[[package]]
-name = "xts-mode"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520"
-dependencies = [
- "byteorder",
- "cipher",
-]
-
[[package]]
name = "yoke"
version = "0.7.4"
diff --git a/Cargo.toml b/Cargo.toml
index f6a4f48d7..0fab6d505 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,7 +9,6 @@ members = [
"src/fs",
"src/llt",
"src/obconf",
- "src/pfs",
"src/tls",
]
diff --git a/README.md b/README.md
index 46c69e384..419d1e5f7 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,15 @@ Obliteration is a free and open-source PlayStation 4 kernel rewritten in Rust. O
This project started as a hard-fork from [Kyty](https://github.com/InoriRus/Kyty). Then we decided to rewrite the whole project from scratch by using Kyty and [Uplift](https://github.com/idc/uplift) as a reference to help us getting started with the project.
+Our ultimate goal is to become a permissive free and open-source operating system optimized for gaming that can run on a variety of hardware. The reason we want to built this because:
+
+- Windows is bloated and Microsoft keep pushing too many things into it.
+- Linux is a nightmare for beginners. Its license also making it not an ideal choice for a proprietary hardware.
+- macOS has a limited set of hardware and its price too expensive. You can get a PC with high-end graphic card at the same price.
+- FreeBSD and the others was not designed for gaming. Their goal are either a server or a general desktop.
+
+So we want to take this opportunity to go beyond a PlayStation 4 emulator since we already building an operating system kernel.
+
The project logo and icon was designed by [VocalFan](https://github.com/VocalFan).
## Get a daily build
@@ -24,8 +33,6 @@ We use icons from https://materialdesignicons.com for UI.
## License
-- `src/param`, `src/pfs` and `src/pkg` are licensed under LGPL-3.0.
-- All other source code are licensed under either MIT License or Apache License, Version 2.0; or both.
-- All release binaries are under GPL-3.0.
+All source code are licensed under either MIT License or Apache License, Version 2.0; or both.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Obliteration by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
diff --git a/src/pfs/COPYING b/src/pfs/COPYING
deleted file mode 100644
index 02bbb60bc..000000000
--- a/src/pfs/COPYING
+++ /dev/null
@@ -1,165 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
\ No newline at end of file
diff --git a/src/pfs/Cargo.toml b/src/pfs/Cargo.toml
deleted file mode 100644
index d78a6825e..000000000
--- a/src/pfs/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "pfs"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-aes = "0.8"
-byteorder = "1.4.3"
-flate2 = "1.0"
-hmac = "0.12"
-sha2 = "0.10"
-thiserror = "1.0"
-xts-mode = "0.5"
diff --git a/src/pfs/src/directory/dirent.rs b/src/pfs/src/directory/dirent.rs
deleted file mode 100644
index 84c505dc2..000000000
--- a/src/pfs/src/directory/dirent.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-use byteorder::{ByteOrder, LE};
-use std::error::Error;
-use std::fmt::{Display, Formatter};
-use std::io::Read;
-
-pub(super) struct Dirent {
- ino: usize,
- ty: u32,
- entsize: usize,
- name: Vec,
-}
-
-impl Dirent {
- pub const FILE: u32 = 2;
- pub const DIRECTORY: u32 = 3;
- pub const SELF: u32 = 4;
- pub const PARENT: u32 = 5;
-
- pub fn read(from: &mut F) -> Result {
- // Read static sized fields.
- let mut data: [u8; 16] = [0u8; 16];
-
- from.read_exact(&mut data)?;
-
- let entsize = LE::read_u32(&data[0x0c..]) as usize;
-
- if entsize == 0 {
- return Err(ReadError::EndOfEntry);
- }
-
- let ino = LE::read_u32(&data[0x00..]) as usize;
- let ty = LE::read_u32(&data[0x04..]);
- let namelen = LE::read_u32(&data[0x08..]) as usize;
-
- // Read name.
- let mut name = vec![0; namelen];
-
- from.read_exact(&mut name)?;
-
- Ok(Self {
- ino,
- ty,
- entsize,
- name,
- })
- }
-
- pub fn inode(&self) -> usize {
- self.ino
- }
-
- pub fn ty(&self) -> u32 {
- self.ty
- }
-
- pub fn take_name(&mut self) -> Vec {
- std::mem::take(&mut self.name)
- }
-
- /// This method **MUST** be called before [`take_name`] otherwise the returned value will be incorrect.
- pub fn padding_size(&self) -> usize {
- self.entsize - 16 - self.name.len()
- }
-}
-
-#[derive(Debug)]
-pub enum ReadError {
- IoFailed(std::io::Error),
- TooSmall,
- EndOfEntry,
-}
-
-impl From for ReadError {
- fn from(v: std::io::Error) -> Self {
- if v.kind() == std::io::ErrorKind::UnexpectedEof {
- ReadError::TooSmall
- } else {
- ReadError::IoFailed(v)
- }
- }
-}
-
-impl Error for ReadError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::IoFailed(e) => Some(e),
- _ => None,
- }
- }
-}
-
-impl Display for ReadError {
- fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
- match self {
- Self::IoFailed(_) => f.write_str("I/O failed"),
- Self::TooSmall => f.write_str("data too small"),
- Self::EndOfEntry => f.write_str("end of entry"),
- }
- }
-}
diff --git a/src/pfs/src/directory/mod.rs b/src/pfs/src/directory/mod.rs
deleted file mode 100644
index 1229382f6..000000000
--- a/src/pfs/src/directory/mod.rs
+++ /dev/null
@@ -1,221 +0,0 @@
-use self::dirent::Dirent;
-use crate::file::File;
-use crate::inode::Inode;
-use crate::Pfs;
-use std::collections::HashMap;
-use std::io::SeekFrom;
-use std::ops::DerefMut;
-use std::rc::Rc;
-use thiserror::Error;
-
-pub mod dirent;
-
-/// Represents a directory in the PFS.
-#[derive(Clone)]
-pub struct Directory<'a> {
- pfs: Rc>,
- inode: usize,
-}
-
-impl<'a> Directory<'a> {
- pub(super) fn new(pfs: Rc>, inode: usize) -> Self {
- Self { pfs, inode }
- }
-
- pub fn mode(&self) -> u16 {
- self.inode().mode()
- }
-
- pub fn flags(&self) -> u32 {
- self.inode().flags().value()
- }
-
- pub fn atime(&self) -> u64 {
- self.inode().atime()
- }
-
- pub fn mtime(&self) -> u64 {
- self.inode().mtime()
- }
-
- pub fn ctime(&self) -> u64 {
- self.inode().ctime()
- }
-
- pub fn birthtime(&self) -> u64 {
- self.inode().birthtime()
- }
-
- pub fn mtimensec(&self) -> u32 {
- self.inode().mtimensec()
- }
-
- pub fn atimensec(&self) -> u32 {
- self.inode().atimensec()
- }
-
- pub fn ctimensec(&self) -> u32 {
- self.inode().ctimensec()
- }
-
- pub fn birthnsec(&self) -> u32 {
- self.inode().birthnsec()
- }
-
- pub fn uid(&self) -> u32 {
- self.inode().uid()
- }
-
- pub fn gid(&self) -> u32 {
- self.inode().gid()
- }
-
- pub fn open(&self) -> Result, OpenError> {
- // Load occupied blocks.
- let mut image = self.pfs.image.lock().unwrap();
- let image = image.deref_mut();
- let inode = &self.pfs.inodes[self.inode];
- let blocks = match inode.load_blocks(image.as_mut()) {
- Ok(v) => v,
- Err(e) => return Err(OpenError::LoadBlocksFailed(e)),
- };
-
- // Read all dirents.
- let mut items: HashMap, Item<'a>> = HashMap::new();
- let block_size = image.header().block_size();
- let mut block_data = vec![0; block_size as usize];
-
- for block_num in blocks {
- // Seek to block.
- let offset = (block_num as u64) * (block_size as u64);
-
- match image.seek(SeekFrom::Start(offset)) {
- Ok(v) => {
- if v != offset {
- return Err(OpenError::BlockNotExists(block_num));
- }
- }
- Err(e) => return Err(OpenError::SeekToBlockFailed(block_num, e)),
- }
-
- // Read block data.
- if let Err(e) = image.read_exact(&mut block_data) {
- return Err(OpenError::ReadBlockFailed(block_num, e));
- }
-
- // Read dirents in the block.
- let mut next = block_data.as_slice();
-
- for num in 0.. {
- // Read dirent.
- let mut dirent = match Dirent::read(&mut next) {
- Ok(v) => v,
- Err(e) => match e {
- dirent::ReadError::IoFailed(e) => {
- panic!("Failed to read dirent due to I/O error: {}", e);
- }
- dirent::ReadError::TooSmall | dirent::ReadError::EndOfEntry => break,
- },
- };
-
- // Skip remaining padding.
- next = match next.get(dirent.padding_size()..) {
- Some(v) => v,
- None => {
- return Err(OpenError::InvalidDirent {
- block: block_num,
- dirent: num,
- });
- }
- };
-
- // Check if inode valid.
- let inode = dirent.inode();
-
- if inode >= self.pfs.inodes.len() {
- return Err(OpenError::InvalidInode(inode));
- }
-
- // Construct object.
- let item = match dirent.ty() {
- Dirent::FILE => Item::File(File::new(self.pfs.clone(), inode)),
- Dirent::DIRECTORY => Item::Directory(Directory::new(self.pfs.clone(), inode)),
- Dirent::SELF | Dirent::PARENT => continue,
- _ => {
- return Err(OpenError::UnknownDirent {
- block: block_num,
- dirent: num,
- });
- }
- };
-
- items.insert(dirent.take_name(), item);
- }
- }
-
- Ok(Items { items })
- }
-
- fn inode(&self) -> &Inode {
- &self.pfs.inodes[self.inode]
- }
-}
-
-/// Represents a collection of items in the directory.
-pub struct Items<'a> {
- items: HashMap, Item<'a>>,
-}
-
-impl<'a> Items<'a> {
- pub fn len(&self) -> usize {
- self.items.len()
- }
-
- pub fn get(&self, name: &[u8]) -> Option<&Item<'a>> {
- self.items.get(name)
- }
-
- pub fn take(&mut self, name: &[u8]) -> Option- > {
- self.items.remove(name)
- }
-}
-
-impl<'a> IntoIterator for Items<'a> {
- type Item = (Vec, Item<'a>);
- type IntoIter = std::collections::hash_map::IntoIter, Item<'a>>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.items.into_iter()
- }
-}
-
-/// Represents an item in the directory.
-pub enum Item<'a> {
- Directory(Directory<'a>),
- File(File<'a>),
-}
-
-/// Errors of [`open()`][Directory::open()].
-#[derive(Debug, Error)]
-pub enum OpenError {
- #[error("inode #{0} is not valid")]
- InvalidInode(usize),
-
- #[error("cannot load occupied blocks")]
- LoadBlocksFailed(#[source] crate::inode::LoadBlocksError),
-
- #[error("cannot seek to block #{0}")]
- SeekToBlockFailed(u32, #[source] std::io::Error),
-
- #[error("block #{0} does not exist")]
- BlockNotExists(u32),
-
- #[error("cannot read block #{0}")]
- ReadBlockFailed(u32, #[source] std::io::Error),
-
- #[error("Dirent #{dirent:} in block #{block:} has invalid size")]
- InvalidDirent { block: u32, dirent: usize },
-
- #[error("Dirent #{dirent:} in block #{block:} has unknown type")]
- UnknownDirent { block: u32, dirent: usize },
-}
diff --git a/src/pfs/src/file.rs b/src/pfs/src/file.rs
deleted file mode 100644
index b461de07c..000000000
--- a/src/pfs/src/file.rs
+++ /dev/null
@@ -1,281 +0,0 @@
-use crate::inode::Inode;
-use crate::Pfs;
-use std::cmp::min;
-use std::io::{Error, ErrorKind, Read, Seek, SeekFrom};
-use std::ops::DerefMut;
-use std::rc::Rc;
-
-/// Represents a file in the PFS.
-pub struct File<'a> {
- pfs: Rc>,
- inode: usize,
- occupied_blocks: Vec,
- current_offset: u64,
- current_block: Vec,
-}
-
-impl<'a> File<'a> {
- pub(crate) fn new(pfs: Rc>, inode: usize) -> Self {
- Self {
- pfs,
- inode,
- occupied_blocks: Vec::new(),
- current_offset: 0,
- current_block: Vec::new(),
- }
- }
-
- pub fn mode(&self) -> u16 {
- self.inode().mode()
- }
-
- pub fn flags(&self) -> u32 {
- self.inode().flags().value()
- }
-
- pub fn len(&self) -> u64 {
- self.inode().size()
- }
-
- pub fn decompressed_len(&self) -> u64 {
- self.inode().decompressed_size()
- }
-
- pub fn atime(&self) -> u64 {
- self.inode().atime()
- }
-
- pub fn mtime(&self) -> u64 {
- self.inode().mtime()
- }
-
- pub fn ctime(&self) -> u64 {
- self.inode().ctime()
- }
-
- pub fn birthtime(&self) -> u64 {
- self.inode().birthtime()
- }
-
- pub fn mtimensec(&self) -> u32 {
- self.inode().mtimensec()
- }
-
- pub fn atimensec(&self) -> u32 {
- self.inode().atimensec()
- }
-
- pub fn ctimensec(&self) -> u32 {
- self.inode().ctimensec()
- }
-
- pub fn birthnsec(&self) -> u32 {
- self.inode().birthnsec()
- }
-
- pub fn uid(&self) -> u32 {
- self.inode().uid()
- }
-
- pub fn gid(&self) -> u32 {
- self.inode().gid()
- }
-
- pub fn is_compressed(&self) -> bool {
- self.inode().flags().is_compressed()
- }
-
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- fn inode(&self) -> &Inode {
- &self.pfs.inodes[self.inode]
- }
-}
-
-impl<'a> Clone for File<'a> {
- fn clone(&self) -> Self {
- Self {
- pfs: self.pfs.clone(),
- inode: self.inode,
- occupied_blocks: Vec::new(),
- current_offset: self.current_offset,
- current_block: Vec::new(),
- }
- }
-}
-
-impl<'a> Seek for File<'a> {
- fn seek(&mut self, pos: SeekFrom) -> std::io::Result {
- // Get inode.
- let inode = match self.pfs.inodes.get(self.inode) {
- Some(v) => v,
- None => {
- return Err(Error::new(
- ErrorKind::Other,
- format!("inode #{} does not exist", self.inode),
- ))
- }
- };
-
- // Calculate new offset.
- let offset = match pos {
- SeekFrom::Start(v) => min(inode.size(), v),
- SeekFrom::End(v) => {
- if v >= 0 {
- inode.size()
- } else {
- let v = v.unsigned_abs();
-
- if v > inode.size() {
- return Err(Error::from(ErrorKind::InvalidInput));
- }
-
- inode.size() - v
- }
- }
- SeekFrom::Current(v) => {
- if v >= 0 {
- min(inode.size(), self.current_offset + (v as u64))
- } else {
- let v = v.unsigned_abs();
-
- if v > self.current_offset {
- return Err(Error::from(ErrorKind::InvalidInput));
- }
-
- self.current_offset - v
- }
- }
- };
-
- // Update offset.
- if offset != self.current_offset {
- self.current_offset = offset;
- self.current_block.clear();
- }
-
- Ok(self.current_offset)
- }
-
- fn rewind(&mut self) -> std::io::Result<()> {
- self.current_offset = 0;
- self.current_block.clear();
- Ok(())
- }
-
- fn stream_position(&mut self) -> std::io::Result {
- Ok(self.current_offset)
- }
-}
-
-impl<'a> Read for File<'a> {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result {
- // Get inode.
- let inode = match self.pfs.inodes.get(self.inode) {
- Some(v) => v,
- None => {
- return Err(Error::new(
- ErrorKind::Other,
- format!("inode #{} does not exist", self.inode),
- ))
- }
- };
-
- // Check if we need to do the actual read.
- if buf.is_empty() || self.current_offset == inode.size() {
- return Ok(0);
- }
-
- // Load occupied blocks.
- let mut image = self.pfs.image.lock().unwrap();
- let image = image.deref_mut();
-
- if self.occupied_blocks.is_empty() {
- self.occupied_blocks = match inode.load_blocks(image.as_mut()) {
- Ok(v) => v,
- Err(e) => return Err(Error::new(ErrorKind::Other, e)),
- };
- }
-
- // Copy data.
- let block_size = image.header().block_size();
- let mut copied = 0usize;
-
- loop {
- // Load block for current offset.
- if self.current_block.is_empty() {
- // Get block number.
- let block_index = self.current_offset / (block_size as u64);
- let block_num = match self.occupied_blocks.get(block_index as usize) {
- Some(&v) => v,
- None => {
- break Err(Error::new(
- ErrorKind::Other,
- format!("block #{} is not available", block_index),
- ));
- }
- };
-
- // Check if this is a last block.
- let total = block_index * (block_size as u64) + (block_size as u64);
- let read_amount = if total > inode.size() {
- // Both total and len never be zero.
- (block_size as u64) - (total - inode.size())
- } else {
- block_size as u64
- };
-
- #[allow(clippy::uninit_vec)]
- {
- // calling `set_len()` immediately after reserving a buffer creates uninitialized values
- // Allocate buffer.
- self.current_block.reserve(read_amount as usize);
- unsafe { self.current_block.set_len(read_amount as usize) };
- }
-
- // Seek to block.
- let offset = (block_num as u64) * (block_size as u64);
-
- match image.seek(SeekFrom::Start(offset)) {
- Ok(v) => {
- if v != offset {
- return Err(Error::new(
- ErrorKind::Other,
- format!("block #{} does not exist", block_num),
- ));
- }
- }
- Err(e) => return Err(e),
- }
-
- // Load block data.
- image.read_exact(&mut self.current_block)?;
- }
-
- // Get a window into current block from current offset.
- let offset = self.current_offset % (block_size as u64);
- let src = &self.current_block[(offset as usize)..];
-
- // Copy the window to output buffer.
- let dst = unsafe { buf.as_mut_ptr().add(copied) };
- let amount = min(src.len(), buf.len() - copied) as u32;
-
- unsafe { dst.copy_from_nonoverlapping(src.as_ptr(), amount as usize) };
- copied += amount as usize;
-
- // Advance current offset.
- self.current_offset += amount as u64;
-
- if self.current_offset % (block_size as u64) == 0 {
- self.current_block.clear();
- }
-
- // Check if completed.
- if copied == buf.len() || self.current_offset == inode.size() {
- break Ok(copied);
- }
- }
- }
-}
diff --git a/src/pfs/src/header.rs b/src/pfs/src/header.rs
deleted file mode 100644
index 8afae918a..000000000
--- a/src/pfs/src/header.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-use byteorder::{ByteOrder, LE};
-use std::fmt::{Display, Formatter};
-use std::io::Read;
-use thiserror::Error;
-
-/// Contains PFS header.
-///
-/// See https://www.psdevwiki.com/ps4/PFS#Header.2FSuperblock for some basic information.
-pub(crate) struct Header {
- mode: Mode,
- blocksz: u32,
- ndinode: u64,
- ndinodeblock: u32,
- superroot_ino: u64,
- key_seed: [u8; 16],
-}
-
-impl Header {
- pub(super) fn read(image: &mut I) -> Result {
- // Read the whole header into the buffer.
- let mut hdr = [0u8; 0x380];
-
- if let Err(e) = image.read_exact(&mut hdr) {
- return Err(ReadError::IoFailed(e));
- }
-
- // Check version.
- let version = LE::read_u64(&hdr[0x00..]);
-
- if version != 1 {
- return Err(ReadError::InvalidVersion);
- }
-
- // Check format.
- let format = LE::read_u64(&hdr[0x08..]);
-
- if format != 20130315 {
- return Err(ReadError::InvalidFormat);
- }
-
- // Read fields.
- let mode = Mode(LE::read_u16(&hdr[0x1c..]));
- let blocksz = LE::read_u32(&hdr[0x20..]);
- let ndinode = LE::read_u64(&hdr[0x30..]);
- let ndinodeblock = LE::read_u64(&hdr[0x40..]);
- let superroot_ino = LE::read_u64(&hdr[0x48..]);
- let key_seed = &hdr[0x370..(0x370 + 16)];
-
- // Usually block will be references by u32. Not sure why ndinodeblock is 64-bits. Design
- // flaws?
- if ndinodeblock > (u32::MAX as u64) {
- return Err(ReadError::TooManyInodeBlocks);
- }
-
- Ok(Self {
- mode,
- blocksz,
- ndinode,
- ndinodeblock: ndinodeblock as u32,
- superroot_ino,
- key_seed: key_seed.try_into().unwrap(),
- })
- }
-
- pub fn mode(&self) -> Mode {
- self.mode
- }
-
- pub fn block_size(&self) -> u32 {
- self.blocksz
- }
-
- /// Gets a number of total inodes.
- pub fn inode_count(&self) -> usize {
- self.ndinode as _
- }
-
- /// Gets a number of blocks containing inode (not a number of inode).
- pub fn inode_block_count(&self) -> u32 {
- self.ndinodeblock
- }
-
- pub fn super_root_inode(&self) -> usize {
- self.superroot_ino as _
- }
-
- pub fn key_seed(&self) -> &[u8; 16] {
- &self.key_seed
- }
-}
-
-/// Contains PFS flags.
-#[derive(Clone, Copy)]
-#[repr(transparent)]
-pub(crate) struct Mode(u16);
-
-impl Mode {
- pub fn is_signed(&self) -> bool {
- self.0 & 0x1 != 0
- }
-
- pub fn is_64bits(&self) -> bool {
- self.0 & 0x2 != 0
- }
-
- pub fn is_encrypted(&self) -> bool {
- self.0 & 0x4 != 0
- }
-}
-
-impl Display for Mode {
- fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
- write!(f, "{:x}", self.0)?;
-
- let mut op = false;
- let mut first = true;
- let mut flag = |name: &str| -> std::fmt::Result {
- if !op {
- f.write_str(" (")?;
- op = true;
- }
-
- if !first {
- f.write_str(", ")?;
- }
-
- f.write_str(name)?;
- first = false;
-
- Ok(())
- };
-
- if self.is_signed() {
- flag("signed")?;
- }
-
- if self.is_64bits() {
- flag("64-bits")?;
- }
-
- if self.is_encrypted() {
- flag("encrypted")?;
- }
-
- if op {
- f.write_str(")")?;
- }
-
- Ok(())
- }
-}
-
-/// Errors for [read()][Header::read()].
-#[derive(Debug, Error)]
-pub enum ReadError {
- #[error("cannot read image")]
- IoFailed(#[source] std::io::Error),
-
- #[error("invalid version")]
- InvalidVersion,
-
- #[error("invalid format")]
- InvalidFormat,
-
- #[error("too many blocks for inodes")]
- TooManyInodeBlocks,
-}
diff --git a/src/pfs/src/image.rs b/src/pfs/src/image.rs
deleted file mode 100644
index b84673c4c..000000000
--- a/src/pfs/src/image.rs
+++ /dev/null
@@ -1,255 +0,0 @@
-use crate::header::Header;
-use crate::Image;
-use aes::Aes128;
-use hmac::{Hmac, Mac};
-use sha2::Sha256;
-use std::cmp::min;
-use std::io::{IoSliceMut, Read, Seek, SeekFrom};
-use xts_mode::{get_tweak_default, Xts128};
-
-pub(super) const XTS_BLOCK_SIZE: usize = 0x1000;
-
-/// Gets data key and tweak key.
-pub(super) fn get_xts_keys(ekpfs: &[u8], seed: &[u8; 16]) -> ([u8; 16], [u8; 16]) {
- // Derive key.
- let mut hmac = Hmac::::new_from_slice(ekpfs).unwrap();
- let mut input: Vec = Vec::with_capacity(seed.len() + 4);
-
- input.extend([0x01, 0x00, 0x00, 0x00]);
- input.extend(seed);
-
- hmac.update(input.as_slice());
-
- // Split key.
- let secret = hmac.finalize().into_bytes();
- let mut data_key: [u8; 16] = Default::default();
- let mut tweak_key: [u8; 16] = Default::default();
-
- tweak_key.copy_from_slice(&secret[..16]);
- data_key.copy_from_slice(&secret[16..]);
-
- (data_key, tweak_key)
-}
-
-/// Encapsulate an unencrypted PFS image.
-pub(super) struct Unencrypted {
- image: I,
- header: Header,
-}
-
-impl Unencrypted {
- pub fn new(image: I, header: Header) -> Self {
- Self { image, header }
- }
-}
-
-impl Image for Unencrypted {
- fn header(&self) -> &Header {
- &self.header
- }
-}
-
-impl Seek for Unencrypted {
- fn seek(&mut self, pos: SeekFrom) -> std::io::Result {
- self.image.seek(pos)
- }
-
- fn rewind(&mut self) -> std::io::Result<()> {
- self.image.rewind()
- }
-
- fn stream_position(&mut self) -> std::io::Result {
- self.image.stream_position()
- }
-}
-
-impl Read for Unencrypted {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result {
- self.image.read(buf)
- }
-
- fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> std::io::Result {
- self.image.read_vectored(bufs)
- }
-
- fn read_to_end(&mut self, buf: &mut Vec) -> std::io::Result {
- self.image.read_to_end(buf)
- }
-
- fn read_to_string(&mut self, buf: &mut String) -> std::io::Result {
- self.image.read_to_string(buf)
- }
-
- fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
- self.image.read_exact(buf)
- }
-}
-
-/// Encapsulate an encrypted PFS image.
-pub(super) struct Encrypted {
- image: I,
- len: u64,
- header: Header,
- decryptor: Xts128,
- encrypted_start: usize, // Block index, not offset.
- offset: u64,
- current_block: Vec,
-}
-
-impl Encrypted {
- pub fn new(
- image: I,
- image_len: u64,
- header: Header,
- decryptor: Xts128,
- encrypted_start: usize,
- current_offset: u64,
- ) -> Self {
- Self {
- image,
- len: image_len,
- header,
- decryptor,
- encrypted_start,
- offset: current_offset,
- current_block: Vec::new(),
- }
- }
-}
-
-impl Image for Encrypted {
- fn header(&self) -> &Header {
- &self.header
- }
-}
-
-impl Seek for Encrypted {
- fn seek(&mut self, pos: SeekFrom) -> std::io::Result {
- use std::io::{Error, ErrorKind};
-
- // Calculate the offset.
- let offset = match pos {
- SeekFrom::Start(v) => min(v, self.len),
- SeekFrom::End(v) => {
- if v >= 0 {
- self.len
- } else {
- match self.len.checked_sub(v.unsigned_abs()) {
- Some(v) => v,
- None => return Err(Error::from(ErrorKind::InvalidInput)),
- }
- }
- }
- SeekFrom::Current(v) => {
- if v >= 0 {
- min(self.offset + (v as u64), self.len)
- } else {
- match self.offset.checked_sub(v.unsigned_abs()) {
- Some(v) => v,
- None => return Err(Error::from(ErrorKind::InvalidInput)),
- }
- }
- }
- };
-
- // Update the offset if it is difference.
- if offset != self.offset {
- self.offset = offset;
- self.current_block.clear();
- }
-
- Ok(offset)
- }
-
- fn rewind(&mut self) -> std::io::Result<()> {
- if self.offset != 0 {
- self.offset = 0;
- self.current_block.clear();
- }
-
- Ok(())
- }
-
- fn stream_position(&mut self) -> std::io::Result {
- Ok(self.offset)
- }
-}
-
-impl Read for Encrypted {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result {
- use std::io::{Error, ErrorKind};
-
- // Check if we need to do the actual read.
- if buf.is_empty() || self.offset == self.len {
- return Ok(0);
- }
-
- // Fill output buffer until it is full or EOF.
- let mut copied = 0;
-
- loop {
- // Check if we have remaining data available for current block.
- if self.current_block.is_empty() {
- // Get offset for current block.
- let block = (self.offset as usize) / XTS_BLOCK_SIZE;
- let offset = (block * XTS_BLOCK_SIZE) as u64;
-
- // Seek image file to the target offset.
- match self.image.seek(SeekFrom::Start(offset)) {
- Ok(v) => {
- if v != offset {
- return Err(Error::new(
- ErrorKind::Other,
- format!("unable to seek to offset {}", offset),
- ));
- }
- }
- Err(e) => return Err(e),
- }
-
- // Read the current block.
- self.current_block.reserve(XTS_BLOCK_SIZE);
-
- unsafe {
- let buf = std::slice::from_raw_parts_mut(
- self.current_block.as_mut_ptr(),
- XTS_BLOCK_SIZE,
- );
-
- self.image.read_exact(buf)?;
- self.current_block.set_len(XTS_BLOCK_SIZE);
- }
-
- // Decrypt block.
- if block >= self.encrypted_start {
- let tweak = get_tweak_default(block as _);
-
- self.decryptor
- .decrypt_sector(&mut self.current_block, tweak);
- }
-
- // Discard any data before current offset.
- let offset = (self.offset as usize) % XTS_BLOCK_SIZE;
-
- self.current_block.drain(..offset);
- }
-
- // Copy data to output buffer.
- let amount = min(buf.len() - copied, self.current_block.len());
- let src = self.current_block.drain(..amount);
- let dst = &mut buf[copied..(copied + amount)];
-
- dst.copy_from_slice(src.as_slice());
- copied += amount;
-
- // Advance the offset.
- drop(src);
- self.offset += amount as u64;
-
- // Check if output buffer is filled or EOF.
- if copied == buf.len() || self.offset == self.len {
- break Ok(copied);
- }
- }
- }
-}
diff --git a/src/pfs/src/inode.rs b/src/pfs/src/inode.rs
deleted file mode 100644
index 587d4ed46..000000000
--- a/src/pfs/src/inode.rs
+++ /dev/null
@@ -1,383 +0,0 @@
-use crate::Image;
-use byteorder::{ByteOrder, LE};
-use std::error::Error;
-use std::fmt::{Display, Formatter};
-use std::io::{Read, SeekFrom};
-use thiserror::Error;
-
-/// Contains information for an inode.
-pub(crate) struct Inode {
- index: usize,
- mode: u16,
- flags: InodeFlags,
- size: u64,
- decompressed_size: u64,
- atime: u64,
- mtime: u64,
- ctime: u64,
- birthtime: u64,
- mtimensec: u32,
- atimensec: u32,
- ctimensec: u32,
- birthnsec: u32,
- uid: u32,
- gid: u32,
- blocks: u32,
- direct_blocks: [u32; 12],
- direct_sigs: [Option<[u8; 32]>; 12],
- indirect_blocks: [u32; 5],
- indirect_signs: [Option<[u8; 32]>; 5],
- indirect_reader: fn(&mut &[u8]) -> Option,
-}
-
-impl Inode {
- pub(super) fn from_raw32_unsigned(index: usize, raw: &mut R) -> Result
- where
- R: Read,
- {
- // Read common fields.
- let raw: [u8; 168] = Self::read_raw(raw)?;
- let mut inode = Self::read_common_fields(index, &raw, Self::read_indirect32_unsigned);
-
- // Read block pointers.
- let mut offset = 0x64;
- for i in 0..12 {
- inode.direct_blocks[i] = LE::read_u32(&raw[offset..offset + 4]);
- offset += 4;
- }
-
- for i in 0..5 {
- inode.indirect_blocks[i] = LE::read_u32(&raw[offset..offset + 4]);
- offset += 4;
- }
-
- Ok(inode)
- }
-
- pub(super) fn from_raw32_signed(index: usize, raw: &mut R) -> Result
- where
- R: Read,
- {
- // Read common fields.
- let raw: [u8; 712] = Self::read_raw(raw)?;
- let mut inode = Self::read_common_fields(index, &raw, Self::read_indirect32_signed);
-
- // Read block pointers.
- let mut offset = 0x64;
- for i in 0..12 {
- inode.direct_sigs[i] = Some(raw[offset..offset + 32].try_into().unwrap());
- inode.direct_blocks[i] = LE::read_u32(&raw[offset + 32..offset + 36]);
- offset += 36;
- }
-
- for i in 0..5 {
- inode.indirect_signs[i] = Some(raw[offset..offset + 32].try_into().unwrap());
- inode.indirect_blocks[i] = LE::read_u32(&raw[offset + 32..offset + 36]);
- offset += 36;
- }
-
- Ok(inode)
- }
-
- pub fn mode(&self) -> u16 {
- self.mode
- }
-
- pub fn flags(&self) -> InodeFlags {
- self.flags
- }
-
- pub fn size(&self) -> u64 {
- self.size
- }
-
- pub fn decompressed_size(&self) -> u64 {
- self.decompressed_size
- }
-
- pub fn atime(&self) -> u64 {
- self.atime
- }
-
- pub fn mtime(&self) -> u64 {
- self.mtime
- }
-
- pub fn ctime(&self) -> u64 {
- self.ctime
- }
-
- pub fn birthtime(&self) -> u64 {
- self.birthtime
- }
-
- pub fn mtimensec(&self) -> u32 {
- self.mtimensec
- }
-
- pub fn atimensec(&self) -> u32 {
- self.atimensec
- }
-
- pub fn ctimensec(&self) -> u32 {
- self.ctimensec
- }
-
- pub fn birthnsec(&self) -> u32 {
- self.birthnsec
- }
-
- pub fn uid(&self) -> u32 {
- self.uid
- }
-
- pub fn gid(&self) -> u32 {
- self.gid
- }
-
- pub fn load_blocks(&self, image: &mut dyn Image) -> Result, LoadBlocksError> {
- // Check if inode use contiguous blocks.
- let mut blocks: Vec = Vec::with_capacity(self.blocks as usize);
-
- if blocks.len() == self.blocks as usize {
- // inode with zero block should not be possible but just in case for malformed image.
- return Ok(blocks);
- }
-
- if self.direct_blocks[1] == 0xffffffff {
- let start = self.direct_blocks[0];
-
- for block in start..(start + self.blocks) {
- blocks.push(block);
- }
-
- return Ok(blocks);
- }
-
- // Load direct pointers.
- for i in 0..12 {
- blocks.push(self.direct_blocks[i]);
-
- if blocks.len() == self.blocks as usize {
- return Ok(blocks);
- }
- }
-
- // FIXME: Refactor algorithm to read indirect blocks.
- // Load indirect 0.
- let block_num = self.indirect_blocks[0];
- let block_size = image.header().block_size();
- let offset = (block_num * block_size) as u64;
-
- match image.seek(SeekFrom::Start(offset)) {
- Ok(v) => {
- if v != offset {
- return Err(LoadBlocksError::BlockNotExists(block_num));
- }
- }
- Err(e) => return Err(LoadBlocksError::SeekFailed(block_num, e)),
- }
-
- let mut block0 = vec![0; block_size as usize];
-
- if let Err(e) = image.read_exact(&mut block0) {
- return Err(LoadBlocksError::ReadBlockFailed(block_num, e));
- }
-
- let mut data = block0.as_slice();
-
- while let Some(i) = (self.indirect_reader)(&mut data) {
- blocks.push(i);
-
- if blocks.len() == self.blocks as usize {
- return Ok(blocks);
- }
- }
-
- // Load indirect 1.
- let block_num = self.indirect_blocks[1];
- let offset = (block_num * block_size) as u64;
-
- match image.seek(SeekFrom::Start(offset)) {
- Ok(v) => {
- if v != offset {
- return Err(LoadBlocksError::BlockNotExists(block_num));
- }
- }
- Err(e) => return Err(LoadBlocksError::SeekFailed(block_num, e)),
- }
-
- if let Err(e) = image.read_exact(&mut block0) {
- return Err(LoadBlocksError::ReadBlockFailed(block_num, e));
- }
-
- let mut block1 = vec![0; block_size as usize];
- let mut data0 = block0.as_slice();
-
- while let Some(i) = (self.indirect_reader)(&mut data0) {
- let offset = (i * block_size) as u64;
-
- match image.seek(SeekFrom::Start(offset)) {
- Ok(v) => {
- if v != offset {
- return Err(LoadBlocksError::BlockNotExists(i));
- }
- }
- Err(e) => return Err(LoadBlocksError::SeekFailed(i, e)),
- }
-
- if let Err(e) = image.read_exact(&mut block1) {
- return Err(LoadBlocksError::ReadBlockFailed(i, e));
- }
-
- let mut data1 = block1.as_slice();
-
- while let Some(j) = (self.indirect_reader)(&mut data1) {
- blocks.push(j);
-
- if blocks.len() == self.blocks as usize {
- return Ok(blocks);
- }
- }
- }
-
- panic!(
- "Data of inode #{} was spanned to indirect block #2, which we are not supported yet",
- self.index
- );
- }
-
- fn read_raw(raw: &mut R) -> Result<[u8; L], FromRawError> {
- let mut buf: [u8; L] = [0u8; L];
-
- if let Err(e) = raw.read_exact(&mut buf) {
- return Err(if e.kind() == std::io::ErrorKind::UnexpectedEof {
- FromRawError::TooSmall
- } else {
- FromRawError::IoFailed(e)
- });
- }
-
- Ok(buf)
- }
-
- fn read_common_fields(
- index: usize,
- raw: &[u8],
- indirect_reader: fn(&mut &[u8]) -> Option,
- ) -> Self {
- let mode = LE::read_u16(&raw[0x00..]);
- let flags = InodeFlags(LE::read_u32(&raw[0x04..]));
- let size = LE::read_u64(&raw[0x08..]);
- let decompressed_size = LE::read_u64(&raw[0x10..]);
- let atime = LE::read_u64(&raw[0x18..]);
- let mtime = LE::read_u64(&raw[0x20..]);
- let ctime = LE::read_u64(&raw[0x28..]);
- let birthtime = LE::read_u64(&raw[0x30..]);
- let mtimensec = LE::read_u32(&raw[0x38..]);
- let atimensec = LE::read_u32(&raw[0x3c..]);
- let ctimensec = LE::read_u32(&raw[0x40..]);
- let birthnsec = LE::read_u32(&raw[0x44..]);
- let uid = LE::read_u32(&raw[0x48..]);
- let gid = LE::read_u32(&raw[0x4c..]);
- let blocks = LE::read_u32(&raw[0x60..]);
-
- Self {
- index,
- mode,
- flags,
- size,
- decompressed_size,
- atime,
- mtime,
- ctime,
- birthtime,
- mtimensec,
- atimensec,
- ctimensec,
- birthnsec,
- uid,
- gid,
- blocks,
- direct_blocks: [0; 12],
- direct_sigs: [None; 12],
- indirect_blocks: [0; 5],
- indirect_signs: [None; 5],
- indirect_reader,
- }
- }
-
- fn read_indirect32_unsigned(raw: &mut &[u8]) -> Option {
- let value = match raw.get(..4) {
- Some(v) => LE::read_u32(&v[0x00..]),
- None => return None,
- };
-
- *raw = &raw[4..];
-
- Some(value)
- }
-
- fn read_indirect32_signed(raw: &mut &[u8]) -> Option {
- let value = match raw.get(..36) {
- Some(v) => LE::read_u32(&v[0x20..]),
- None => return None,
- };
-
- *raw = &raw[36..];
-
- Some(value)
- }
-}
-
-/// Flags of the inode.
-#[derive(Clone, Copy)]
-#[repr(transparent)]
-pub(crate) struct InodeFlags(u32);
-
-impl InodeFlags {
- pub fn is_compressed(self) -> bool {
- self.0 & 0x00000001 != 0
- }
-
- pub fn value(self) -> u32 {
- self.0
- }
-}
-
-#[derive(Debug)]
-pub enum FromRawError {
- IoFailed(std::io::Error),
- TooSmall,
-}
-
-impl Error for FromRawError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::IoFailed(e) => Some(e),
- _ => None,
- }
- }
-}
-
-impl Display for FromRawError {
- fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
- match self {
- Self::IoFailed(_) => f.write_str("I/O failed"),
- Self::TooSmall => f.write_str("data too small"),
- }
- }
-}
-
-/// Errors for [`load_blocks()`][Inode::load_blocks()].
-#[derive(Debug, Error)]
-pub enum LoadBlocksError {
- #[error("cannot seek to block #{0}")]
- SeekFailed(u32, #[source] std::io::Error),
-
- #[error("block #{0} does not exist")]
- BlockNotExists(u32),
-
- #[error("cannot read block #{0}")]
- ReadBlockFailed(u32, #[source] std::io::Error),
-}
diff --git a/src/pfs/src/lib.rs b/src/pfs/src/lib.rs
deleted file mode 100644
index b52293cbf..000000000
--- a/src/pfs/src/lib.rs
+++ /dev/null
@@ -1,181 +0,0 @@
-use self::directory::Directory;
-use self::header::Header;
-use self::inode::Inode;
-use aes::cipher::KeyInit;
-use aes::Aes128;
-use std::io::{Read, Seek, SeekFrom};
-use std::rc::Rc;
-use std::sync::Mutex;
-use thiserror::Error;
-use xts_mode::Xts128;
-
-pub mod directory;
-pub mod file;
-pub mod header;
-pub mod image;
-pub mod inode;
-pub mod pfsc;
-
-pub fn open<'a, I>(mut image: I, ekpfs: Option<&[u8]>) -> Result>, OpenError>
-where
- I: Read + Seek + 'a,
-{
- // Read header.
- let header = match Header::read(&mut image) {
- Ok(v) => v,
- Err(e) => return Err(OpenError::ReadHeaderFailed(e)),
- };
-
- // Check if image is supported.
- let mode = header.mode();
-
- if mode.is_64bits() {
- panic!("64-bits inode is not supported yet.");
- }
-
- // Construct reader.
- let block_size = header.block_size();
- let mut image: Box = if mode.is_encrypted() {
- // The super block (block that contain header) never get encrypted.
- if (block_size as usize) < image::XTS_BLOCK_SIZE {
- return Err(OpenError::InvalidBlockSize);
- }
-
- // Get image size.
- let image_len = match image.seek(SeekFrom::End(0)) {
- Ok(v) => v,
- Err(e) => return Err(OpenError::GetImageLengthFailed(e)),
- };
-
- // Setup decryptor.
- let ekpfs = match ekpfs {
- Some(v) => v,
- None => panic!("The image is encrypted but no EKPFS is provided"),
- };
-
- let key_seed = header.key_seed();
- let (data_key, tweak_key) = image::get_xts_keys(ekpfs, key_seed);
- let cipher_1 = Aes128::new((&data_key).into());
- let cipher_2 = Aes128::new((&tweak_key).into());
-
- Box::new(image::Encrypted::new(
- image,
- image_len,
- header,
- Xts128::::new(cipher_1, cipher_2),
- (block_size as usize) / image::XTS_BLOCK_SIZE,
- image_len,
- ))
- } else {
- Box::new(image::Unencrypted::new(image, header))
- };
-
- // Read inode blocks.
- let mut block_data = vec![0; block_size as usize];
- let mut inodes: Vec = Vec::with_capacity(image.header().inode_count());
-
- 'load_block: for block_num in 0..image.header().inode_block_count() {
- // Get the offset for target block. The first inode block always start at second block.
- let offset = (block_size as u64) + (block_num as u64) * (block_size as u64);
-
- // Seek to target block.
- match image.seek(SeekFrom::Start(offset)) {
- Ok(v) => {
- if v != offset {
- return Err(OpenError::InvalidBlock(block_num));
- }
- }
- Err(e) => return Err(OpenError::SeekToBlockFailed(block_num, e)),
- }
-
- // Read the whole block.
- if let Err(e) = image.read_exact(&mut block_data) {
- return Err(OpenError::ReadBlockFailed(block_num, e));
- }
-
- // Read inodes in the block.
- let mut src = block_data.as_slice();
-
- let reader = if mode.is_signed() {
- Inode::from_raw32_signed
- } else {
- Inode::from_raw32_unsigned
- };
-
- while inodes.len() < image.header().inode_count() {
- let inode = match reader(inodes.len(), &mut src) {
- Ok(v) => v,
- Err(e) => match e {
- inode::FromRawError::TooSmall => continue 'load_block,
- inode::FromRawError::IoFailed(e) => {
- panic!("Failed to read inode from a buffer: {}", e);
- }
- },
- };
-
- inodes.push(inode);
- }
-
- break;
- }
-
- // Check if super-root valid
- let super_root = image.header().super_root_inode();
-
- if super_root >= inodes.len() {
- return Err(OpenError::InvalidSuperRoot);
- }
-
- Ok(Rc::new(Pfs {
- image: Mutex::new(image),
- inodes,
- root: super_root,
- }))
-}
-
-/// Represents a loaded PFS.
-pub struct Pfs<'a> {
- image: Mutex>,
- inodes: Vec,
- root: usize,
-}
-
-impl<'a> Pfs<'a> {
- pub fn inodes(&self) -> usize {
- self.inodes.len()
- }
-
- pub fn root(self: &Rc) -> Directory<'a> {
- Directory::new(self.clone(), self.root)
- }
-}
-
-/// Encapsulate a PFS image.
-pub(crate) trait Image: Read + Seek {
- fn header(&self) -> &Header;
-}
-
-/// Errors for [`open()`].
-#[derive(Debug, Error)]
-pub enum OpenError {
- #[error("cannot read header")]
- ReadHeaderFailed(#[source] header::ReadError),
-
- #[error("invalid block size")]
- InvalidBlockSize,
-
- #[error("cannot get the length of image")]
- GetImageLengthFailed(#[source] std::io::Error),
-
- #[error("cannot seek to block #{0}")]
- SeekToBlockFailed(u32, #[source] std::io::Error),
-
- #[error("block #{0} is not valid")]
- InvalidBlock(u32),
-
- #[error("cannot read block #{0}")]
- ReadBlockFailed(u32, #[source] std::io::Error),
-
- #[error("invalid super-root")]
- InvalidSuperRoot,
-}
diff --git a/src/pfs/src/pfsc.rs b/src/pfs/src/pfsc.rs
deleted file mode 100644
index 85a579328..000000000
--- a/src/pfs/src/pfsc.rs
+++ /dev/null
@@ -1,275 +0,0 @@
-use byteorder::{ByteOrder, LE};
-use flate2::FlushDecompress;
-use std::cmp::min;
-use std::error::Error;
-use std::fmt::{Display, Formatter};
-use std::io::{ErrorKind, Read, Seek, SeekFrom};
-
-// FIXME: Refactor the whole implementation of this since a lot of logic does not make sense.
-pub struct Reader {
- file: F,
- block_size: u32,
- original_block_size: u64,
- compressed_blocks: Vec, // Original block number to compressed block offset.
- original_size: u64,
- current_offset: u64,
- current_block: Vec,
-}
-
-impl Reader {
- pub fn open(mut file: F) -> Result {
- // Seek to beginning.
- if let Err(e) = file.rewind() {
- return Err(OpenError::SeekFailed(0, e));
- }
-
- // Check header.
- let mut hdr: [u8; 48] = [0u8; 48];
-
- if let Err(e) = file.read_exact(&mut hdr) {
- return Err(if e.kind() == ErrorKind::UnexpectedEof {
- OpenError::TooSmall
- } else {
- OpenError::IoFailed(e)
- });
- }
-
- let magic = &hdr[0..4];
-
- if &magic != b"PFSC" {
- return Err(OpenError::InvalidMagic);
- }
-
- // Read header.
- let block_size = LE::read_u32(&hdr[0x0c..]); // BlockSz
- let original_block_size = LE::read_u64(&hdr[0x10..]); // BlockSz2
- let block_offsets = LE::read_u64(&hdr[0x18..]); // BlockOffsets
- let original_size = LE::read_u64(&hdr[0x28..]); // DataLength
-
- // Read block offsets.
- if let Err(e) = file.seek(SeekFrom::Start(block_offsets)) {
- return Err(OpenError::SeekFailed(block_offsets, e));
- }
-
- let original_block_count = original_size / original_block_size + 1;
- let mut compressed_blocks: Vec = vec![0; original_block_count as usize];
- let buf = unsafe {
- let (pre, mid, pos) = compressed_blocks.align_to_mut::();
-
- assert!(pre.is_empty());
- assert!(pos.is_empty());
-
- mid
- };
-
- if let Err(e) = file.read_exact(buf) {
- return Err(OpenError::ReadBlockMappingFailed(e));
- }
-
- Ok(Self {
- file,
- block_size,
- original_block_size,
- compressed_blocks,
- original_size,
- current_offset: 0,
- current_block: Vec::new(),
- })
- }
-
- pub fn len(&self) -> u64 {
- self.original_size
- }
-
- pub fn is_empty(&self) -> bool {
- self.original_size == 0
- }
-
- fn read_compressed_block(&mut self, num: u64) -> std::io::Result<()> {
- // Get end offset.
- let end = match self.compressed_blocks.get(num as usize + 1) {
- Some(v) => v,
- None => return Err(std::io::Error::from(ErrorKind::InvalidInput)),
- };
-
- // Get start offset and compressed size.
- let offset = self.compressed_blocks[num as usize];
- let size = end - offset;
-
- #[allow(clippy::uninit_vec)]
- {
- // calling `set_len()` immediately after reserving a buffer creates uninitialized values
- // Allocate buffer.
- self.current_block.reserve(self.block_size as usize);
- unsafe { self.current_block.set_len(self.block_size as usize) };
- }
-
- let buf = self.current_block.as_mut_slice();
-
- // Check if block compressed.
- match size.cmp(&self.original_block_size) {
- std::cmp::Ordering::Less => {
- // Read compressed.
- let mut compressed = vec![0; size as usize];
-
- self.file.seek(SeekFrom::Start(offset))?;
- self.file.read_exact(&mut compressed)?;
-
- // Decompress.
- let mut deflate = flate2::Decompress::new(true);
- let status = match deflate.decompress(&compressed, buf, FlushDecompress::Finish) {
- Ok(v) => v,
- Err(e) => return Err(std::io::Error::new(ErrorKind::Other, e)),
- };
-
- if status != flate2::Status::StreamEnd || deflate.total_out() as usize != buf.len()
- {
- return Err(std::io::Error::new(
- ErrorKind::Other,
- format!("invalid data on block #{}", num),
- ));
- }
- }
- std::cmp::Ordering::Equal => {
- self.file.seek(SeekFrom::Start(offset))?;
- self.file.read_exact(buf)?;
- }
- std::cmp::Ordering::Greater => buf.fill(0),
- }
-
- Ok(())
- }
-}
-
-impl Seek for Reader {
- fn seek(&mut self, pos: SeekFrom) -> std::io::Result {
- use std::io::Error;
-
- // Calculate the new offset.
- let offset = match pos {
- SeekFrom::Start(v) => min(v, self.original_size),
- SeekFrom::End(v) => {
- if v >= 0 {
- self.original_size
- } else {
- match self.original_size.checked_sub(v.unsigned_abs()) {
- Some(v) => v,
- None => return Err(Error::from(ErrorKind::InvalidInput)),
- }
- }
- }
- SeekFrom::Current(v) => {
- if v >= 0 {
- min(self.current_offset + (v as u64), self.original_size)
- } else {
- match self.current_offset.checked_sub(v.unsigned_abs()) {
- Some(v) => v,
- None => return Err(Error::from(ErrorKind::InvalidInput)),
- }
- }
- }
- };
-
- // Check if we need to update the offset.
- if offset != self.current_offset {
- self.current_offset = offset;
- self.current_block.clear();
- }
-
- Ok(offset)
- }
-
- fn rewind(&mut self) -> std::io::Result<()> {
- self.current_offset = 0;
- self.current_block.clear();
- Ok(())
- }
-
- fn stream_position(&mut self) -> std::io::Result {
- Ok(self.current_offset)
- }
-}
-
-impl Read for Reader {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result {
- if buf.is_empty() || self.current_offset == self.original_size {
- return Ok(0);
- }
-
- // Copy data.
- let mut copied = 0usize;
-
- loop {
- // Load block for current offset.
- if self.current_block.is_empty() {
- // Load block data.
- let block_index = self.current_offset / (self.block_size as u64);
-
- self.read_compressed_block(block_index)?;
-
- // Check if this is a last block.
- let total = block_index * (self.block_size as u64) + (self.block_size as u64);
-
- if total > self.original_size {
- let need = (self.block_size as u64) - (total - self.original_size);
- self.current_block.truncate(need as usize);
- };
- }
-
- // Get a window into current block from current offset.
- let offset = self.current_offset % (self.block_size as u64);
- let src = &self.current_block[(offset as usize)..];
-
- // Copy the window to output buffer.
- let dst = unsafe { buf.as_mut_ptr().add(copied) };
- let amount = min(src.len(), buf.len() - copied);
-
- unsafe { dst.copy_from_nonoverlapping(src.as_ptr(), amount) };
- copied += amount;
-
- // Advance current offset.
- self.current_offset += amount as u64;
-
- if self.current_offset % (self.block_size as u64) == 0 {
- self.current_block.clear();
- }
-
- // Check if completed.
- if copied == buf.len() || self.current_offset == self.original_size {
- break Ok(copied);
- }
- }
- }
-}
-
-#[derive(Debug)]
-pub enum OpenError {
- SeekFailed(u64, std::io::Error),
- IoFailed(std::io::Error),
- TooSmall,
- InvalidMagic,
- ReadBlockMappingFailed(std::io::Error),
-}
-
-impl Error for OpenError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::SeekFailed(_, e) => Some(e),
- Self::IoFailed(e) => Some(e),
- Self::ReadBlockMappingFailed(e) => Some(e),
- _ => None,
- }
- }
-}
-
-impl Display for OpenError {
- fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
- match self {
- Self::SeekFailed(p, _) => write!(f, "cannot seek to offset {}", p),
- Self::IoFailed(_) => f.write_str("I/O failed"),
- Self::TooSmall => f.write_str("data too small"),
- Self::InvalidMagic => f.write_str("invalid magic"),
- Self::ReadBlockMappingFailed(_) => f.write_str("cannot read block mapping"),
- }
- }
-}