1pub mod fs;
2use self::fs::{Directory, File, FileMut};
3
4mod io;
5
6use std::io::{Cursor, Error as IoError, ErrorKind as IoErrorKind, Result as IoResult};
7use std::marker::PhantomData;
8use std::path::Path;
9use std::{fs as stdfs, io as stdio};
10
11use pk2::block_chain::PackBlock;
12use pk2::blowfish::Blowfish;
13use pk2::chain_index::{ChainIndex, ChainIndexParser};
14use pk2::entry::PackEntry;
15use pk2::header::PackHeader;
16use pk2::{ChainOffset, StreamOffset};
17
18pub struct ReadOnly<B>(pub B);
20impl<B: stdio::Read> stdio::Read for ReadOnly<B> {
21 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
22 self.0.read(buf)
23 }
24}
25impl<B: stdio::Seek> stdio::Seek for ReadOnly<B> {
26 fn seek(&mut self, pos: stdio::SeekFrom) -> IoResult<u64> {
27 self.0.seek(pos)
28 }
29}
30
31pub trait Lock<T> {
33 fn new(b: T) -> Self;
35 fn into_inner(self) -> T;
37 fn with_lock<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
39}
40
41pub trait LockChoice {
44 type Lock<T>: Lock<T>;
46 fn new_locked<T>(t: T) -> Self::Lock<T> {
48 Self::Lock::new(t)
49 }
50}
51
52macro_rules! gen_type_aliases {
53 ($lock:ident) => {
54 pub type Pk2<Buffer = std::fs::File> = crate::Pk2<Buffer, $lock>;
55
56 pub type File<'pk2, Buffer = std::fs::File> = crate::fs::File<'pk2, Buffer, $lock>;
57 pub type FileMut<'pk2, Buffer = std::fs::File> = crate::fs::FileMut<'pk2, Buffer, $lock>;
58 pub type DirEntry<'pk2, Buffer = std::fs::File> = crate::fs::DirEntry<'pk2, Buffer, $lock>;
59 pub type Directory<'pk2, Buffer = std::fs::File> =
60 crate::fs::Directory<'pk2, Buffer, $lock>;
61 pub mod readonly {
63 pub type Pk2<Buffer = std::fs::File> = super::Pk2<crate::ReadOnly<Buffer>>;
64
65 pub type File<'pk2, Buffer = std::fs::File> =
66 super::File<'pk2, crate::ReadOnly<Buffer>>;
67 pub type FileMut<'pk2, Buffer = std::fs::File> =
68 super::FileMut<'pk2, crate::ReadOnly<Buffer>>;
69 pub type DirEntry<'pk2, Buffer = std::fs::File> =
70 super::DirEntry<'pk2, crate::ReadOnly<Buffer>>;
71 pub type Directory<'pk2, Buffer = std::fs::File> =
72 super::Directory<'pk2, crate::ReadOnly<Buffer>>;
73 }
74 };
75}
76
77pub use self::sync::Lock as SyncLock;
78pub mod sync {
79 use std::sync::Mutex;
80
81 pub enum Lock {}
83 impl super::LockChoice for Lock {
84 type Lock<T> = Mutex<T>;
85 }
86
87 gen_type_aliases! {
88 Lock
89 }
90
91 impl<T> super::Lock<T> for Mutex<T> {
92 fn new(b: T) -> Self {
93 Mutex::new(b)
94 }
95 fn into_inner(self) -> T {
96 self.into_inner().unwrap()
97 }
98 fn with_lock<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
99 f(&mut self.lock().unwrap())
100 }
101 }
102}
103
104pub use self::unsync::Lock as UnsyncLock;
105pub mod unsync {
106 use std::cell::RefCell;
107
108 pub enum Lock {}
110 impl super::LockChoice for Lock {
111 type Lock<T> = RefCell<T>;
112 }
113
114 gen_type_aliases! {
115 Lock
116 }
117
118 impl<T> super::Lock<T> for RefCell<T> {
119 fn new(b: T) -> Self {
120 RefCell::new(b)
121 }
122 fn into_inner(self) -> T {
123 self.into_inner()
124 }
125 fn with_lock<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
126 f(&mut self.borrow_mut())
127 }
128 }
129}
130
131use IoResult as OpenResult;
132
133pub struct Pk2<Buffer, L: LockChoice> {
135 stream: <L as LockChoice>::Lock<Buffer>,
136 blowfish: Option<Box<Blowfish>>,
137 chain_index: ChainIndex,
138 유령: PhantomData<Buffer>,
139}
140
141impl<L: LockChoice> Pk2<stdfs::File, L> {
142 pub fn create_new<P: AsRef<Path>, K: AsRef<[u8]>>(path: P, key: K) -> OpenResult<Self> {
144 let file = stdfs::OpenOptions::new()
145 .create_new(true)
146 .write(true)
147 .read(true)
148 .open(path.as_ref())?;
149 Self::_create_impl(file, key)
150 }
151
152 pub fn open<P: AsRef<Path>, K: AsRef<[u8]>>(path: P, key: K) -> OpenResult<Self> {
157 let file = stdfs::OpenOptions::new().write(true).read(true).open(path)?;
158 Self::_open_in_impl(file, key)
159 }
160}
161
162impl<L: LockChoice> Pk2<ReadOnly<stdfs::File>, L> {
163 pub fn open_readonly<P: AsRef<Path>, K: AsRef<[u8]>>(path: P, key: K) -> OpenResult<Self> {
168 let file = stdfs::OpenOptions::new().read(true).open(path)?;
169 Self::_open_in_impl(ReadOnly(file), key)
170 }
171}
172
173impl<L: LockChoice> Pk2<Cursor<Vec<u8>>, L> {
174 pub fn create_new_in_memory<K: AsRef<[u8]>>(key: K) -> Result<Self, pk2::blowfish::InvalidKey> {
176 Self::_create_impl(Cursor::new(Vec::with_capacity(4096)), key).map_err(|_| {
177 pk2::blowfish::InvalidKey
179 })
180 }
181}
182
183impl<L: LockChoice> From<Pk2<Cursor<Vec<u8>>, L>> for Vec<u8> {
184 fn from(pk2: Pk2<Cursor<Vec<u8>>, L>) -> Self {
185 pk2.stream.into_inner().into_inner()
186 }
187}
188
189impl<B, L> Pk2<B, L>
190where
191 B: stdio::Read + stdio::Seek,
192 L: LockChoice,
193{
194 pub fn chain_index(&self) -> &ChainIndex {
196 &self.chain_index
197 }
198
199 pub fn open_in<K: AsRef<[u8]>>(mut stream: B, key: K) -> OpenResult<Self> {
204 stream.seek(stdio::SeekFrom::Start(0))?;
205 Self::_open_in_impl(stream, key)
206 }
207
208 fn _open_in_impl<K: AsRef<[u8]>>(mut stream: B, key: K) -> OpenResult<Self> {
209 let mut buffer = [0; PackHeader::PACK_HEADER_LEN];
210 stream.read_exact(&mut buffer)?;
211 let header = PackHeader::parse(&buffer);
212 header.validate_sig().map_err(|e| IoError::new(IoErrorKind::InvalidData, e))?;
213 let blowfish = if header.encrypted {
214 let bf = Blowfish::new(key.as_ref())
215 .map_err(|e| IoError::new(IoErrorKind::InvalidInput, e))?;
216 header.verify(&bf).map_err(|e| IoError::new(IoErrorKind::InvalidInput, e))?;
217 Some(Box::new(bf))
218 } else {
219 None
220 };
221 let chain_index = ChainIndex::read_sync(&mut stream, blowfish.as_deref())?;
222
223 Ok(Pk2 {
224 stream: <L as LockChoice>::Lock::new(stream),
225 blowfish,
226 chain_index,
227 유령: PhantomData,
228 })
229 }
230}
231
232impl<B, L> Pk2<B, L>
233where
234 B: stdio::Read + stdio::Write + stdio::Seek,
235 L: LockChoice,
236{
237 pub fn create_new_in<K: AsRef<[u8]>>(mut stream: B, key: K) -> OpenResult<Self> {
238 stream.seek(stdio::SeekFrom::Start(0))?;
239 Self::_create_impl(stream, key)
240 }
241
242 fn _create_impl<K: AsRef<[u8]>>(mut stream: B, key: K) -> OpenResult<Self> {
243 let (header, blowfish) = if key.as_ref().is_empty() {
244 (PackHeader::default(), None)
245 } else {
246 let bf = Blowfish::new(key.as_ref())
247 .map_err(|e| IoError::new(IoErrorKind::InvalidInput, e))?;
248 (PackHeader::new_encrypted(&bf), Some(Box::new(bf)))
249 };
250
251 let mut out = [0; PackHeader::PACK_HEADER_LEN];
252 header.write_into(&mut out);
253 stream.write_all(&out)?;
254 let mut block = PackBlock::default();
255 block[0] = PackEntry::new_directory(".", ChainIndex::PK2_ROOT_CHAIN_OFFSET, None);
256 crate::io::write_block(
257 blowfish.as_deref(),
258 &mut stream,
259 ChainIndex::PK2_ROOT_BLOCK_OFFSET,
260 &block,
261 )?;
262
263 let bf = blowfish.as_deref();
264 let mut chain_index = ChainIndex::default();
265 let mut fsm = ChainIndexParser::new(
266 &mut chain_index,
267 vec![(ChainIndex::PK2_ROOT_CHAIN_OFFSET, ChainIndex::PK2_ROOT_BLOCK_OFFSET)],
268 );
269 let mut buffer = [0; PackBlock::PK2_FILE_BLOCK_SIZE];
270 while let Some(offset) = fsm.wants_read_at() {
271 stream.seek(std::io::SeekFrom::Start(offset.0.get()))?;
272 stream.read_exact(&mut buffer)?;
273 if let Some(bf) = bf {
274 bf.decrypt(&mut buffer);
275 }
276 fsm.progress(&buffer).map_err(|e| {
277 std::io::Error::new(
278 std::io::ErrorKind::InvalidData,
279 format!("Failed to parse block at offset {}: {}", offset.0, e),
280 )
281 })?;
282 }
283
284 Ok(Pk2 { stream: L::new_locked(stream), blowfish, chain_index, 유령: PhantomData })
285 }
286}
287
288impl<L: LockChoice, B> Pk2<B, L> {
289 fn root_resolve_path_to_entry_and_parent<P: AsRef<str>>(
290 &self,
291 path: P,
292 ) -> OpenResult<Option<(ChainOffset, usize, &PackEntry)>> {
293 let path = check_root(path.as_ref())?;
294 if path.is_empty() {
295 return Ok(None);
296 }
297 self.chain_index
298 .resolve_path_to_entry_and_parent(ChainIndex::PK2_ROOT_CHAIN_OFFSET, path)
299 .map(Some)
300 .map_err(|e| IoError::new(IoErrorKind::InvalidData, e))
301 }
302
303 fn root_resolve_path_to_entry_and_parent_mut<P: AsRef<str>>(
304 &mut self,
305 path: P,
306 ) -> OpenResult<Option<(ChainOffset, usize, &mut PackEntry)>> {
307 let path = check_root(path.as_ref())?;
308 if path.is_empty() {
309 return Ok(None);
310 }
311 self.chain_index
312 .resolve_path_to_entry_and_parent_mut(ChainIndex::PK2_ROOT_CHAIN_OFFSET, path)
313 .map(Some)
314 .map_err(|e| IoError::new(IoErrorKind::InvalidData, e))
315 }
316
317 fn is_file(entry: &PackEntry) -> OpenResult<()> {
318 match entry.is_file() {
319 true => Ok(()),
320 false => Err(IoError::new(IoErrorKind::InvalidData, "Expected a file entry")),
321 }
322 }
323
324 fn is_dir(entry: &PackEntry) -> OpenResult<()> {
325 match entry.is_directory() {
326 true => Ok(()),
327 false => Err(IoError::new(IoErrorKind::InvalidData, "Expected a directory entry")),
328 }
329 }
330}
331
332impl<B, L: LockChoice> Pk2<B, L> {
333 pub fn open_file<P: AsRef<str>>(&self, path: P) -> OpenResult<File<'_, B, L>> {
334 let (chain, entry_idx, entry) = self
335 .root_resolve_path_to_entry_and_parent(path)?
336 .ok_or_else(|| IoError::new(IoErrorKind::InvalidData, "Expected a file entry"))?;
337 Self::is_file(entry)?;
338 Ok(File::new(self, chain, entry_idx))
339 }
340
341 pub fn open_directory<P: AsRef<str>>(&self, path: P) -> OpenResult<Directory<'_, B, L>> {
342 match self.root_resolve_path_to_entry_and_parent(path)? {
343 Some((chain, entry_idx, entry)) => {
344 Self::is_dir(entry)?;
345 Ok(Directory::new(self, Some(chain), entry_idx))
346 }
347 None => Ok(Directory::new(self, None, 0)),
348 }
349 }
350
351 pub fn open_root_dir(&self) -> Directory<'_, B, L> {
352 Directory::new(self, None, 0)
353 }
354
355 pub fn for_each_file(
359 &self,
360 base: impl AsRef<str>,
361 cb: impl FnMut(&Path, File<'_, B, L>) -> OpenResult<()>,
362 ) -> OpenResult<()> {
363 self.open_directory(base)?.for_each_file(cb)
364 }
365}
366
367impl<B, L> Pk2<B, L>
368where
369 B: stdio::Read + stdio::Seek,
370 L: LockChoice,
371{
372 pub fn read<P: AsRef<str>>(&self, path: P) -> OpenResult<Vec<u8>> {
373 let mut file = self.open_file(path)?;
374 let mut buf = Vec::with_capacity(file.size() as usize);
375 stdio::Read::read_to_end(&mut file, &mut buf)?;
376 Ok(buf)
377 }
378}
379
380impl<B, L> Pk2<B, L>
381where
382 B: stdio::Read + stdio::Write + stdio::Seek,
383 L: LockChoice,
384{
385 pub fn open_file_mut<P: AsRef<str>>(&mut self, path: P) -> OpenResult<FileMut<'_, B, L>> {
386 let (chain, entry_idx, entry) = self
387 .root_resolve_path_to_entry_and_parent(path)?
388 .ok_or_else(|| IoError::new(IoErrorKind::InvalidData, "Expected a file entry"))?;
389 Self::is_file(entry)?;
390 Ok(FileMut::new(self, chain, entry_idx))
391 }
392
393 pub fn delete_file<P: AsRef<str>>(&mut self, path: P) -> OpenResult<()> {
396 let (chain_index, entry_idx, entry) = self
397 .root_resolve_path_to_entry_and_parent_mut(path)?
398 .ok_or_else(|| IoError::new(IoErrorKind::InvalidData, "Expected a file entry"))?;
399 Self::is_file(entry)?;
400 entry.clear();
401
402 self.stream.with_lock(|stream| {
403 crate::io::write_chain_entry(
404 self.blowfish.as_deref(),
405 stream,
406 self.chain_index.get(chain_index).unwrap(),
407 entry_idx,
408 )
409 })?;
410 Ok(())
411 }
412
413 pub fn create_file<P: AsRef<str>>(&mut self, path: P) -> OpenResult<FileMut<'_, B, L>> {
414 let path = check_root(path.as_ref())?;
415 let (chain, entry_idx, file_name) = self.stream.with_lock(|stream| {
416 Self::create_entry_at(
417 &mut self.chain_index,
418 self.blowfish.as_deref(),
419 stream,
420 ChainIndex::PK2_ROOT_CHAIN_OFFSET,
421 path,
422 )
423 })?;
424 let entry = self.chain_index.get_entry_mut(chain, entry_idx).unwrap();
425 *entry = PackEntry::new_file(
427 file_name,
428 StreamOffset(ChainIndex::PK2_ROOT_BLOCK_OFFSET.0),
429 0,
430 entry.next_block(),
431 );
432 Ok(FileMut::new(self, chain, entry_idx))
433 }
434
435 pub fn create_file_truncate<P: AsRef<str>>(
441 &mut self,
442 path: P,
443 ) -> OpenResult<FileMut<'_, B, L>> {
444 let path_str = path.as_ref();
445 match self.root_resolve_path_to_entry_and_parent(path_str) {
447 Ok(Some((chain, entry_idx, entry))) if entry.is_file() => {
448 Ok(FileMut::new_truncated(self, chain, entry_idx))
450 }
451 Ok(Some(_)) => {
452 Err(IoError::new(IoErrorKind::InvalidData, "Expected a file entry"))
454 }
455 Ok(None) | Err(_) => {
456 self.create_file(path_str)
458 }
459 }
460 }
461
462 fn create_entry_at<'p>(
467 chain_index: &mut ChainIndex,
468 blowfish: Option<&Blowfish>,
469 mut stream: &mut B,
470 chain: ChainOffset,
471 path: &'p str,
472 ) -> OpenResult<(ChainOffset, usize, &'p str)> {
473 use crate::io::{allocate_empty_block, allocate_new_block_chain, write_chain_entry};
474 let (mut current_chain_index, mut components) = chain_index
475 .validate_dir_path_until(chain, path)
476 .map_err(|e| IoError::new(IoErrorKind::InvalidInput, e))?
477 .ok_or_else(|| IoError::from(IoErrorKind::AlreadyExists))?;
478
479 while let Some(component) = components.next() {
480 let current_chain = chain_index
481 .get_mut(current_chain_index)
482 .ok_or_else(|| IoError::from(IoErrorKind::InvalidInput))?;
483
484 if current_chain.entries().any(|e| e.name_eq_ignore_ascii_case(component)) {
486 return Err(IoErrorKind::AlreadyExists.into());
487 }
488
489 let empty_pos = current_chain.entries().position(PackEntry::is_empty);
490 let chain_entry_idx = if let Some(idx) = empty_pos {
491 idx
492 } else {
493 let (offset, block) = allocate_empty_block(blowfish, &mut stream)?;
495 let chain_entry_idx = current_chain.num_entries();
496 current_chain.push_and_link(offset, block);
497 write_chain_entry(blowfish, &mut stream, current_chain, chain_entry_idx - 1)?;
498 chain_entry_idx
499 };
500 if components.peek().is_some() {
503 let block_chain = allocate_new_block_chain(
504 blowfish,
505 &mut stream,
506 current_chain,
507 component,
508 chain_entry_idx,
509 )?;
510 current_chain_index = block_chain.chain_index();
511 chain_index.insert(current_chain_index, block_chain);
512 } else {
513 return Ok((current_chain.chain_index(), chain_entry_idx, component));
514 }
515 }
516 Err(IoErrorKind::AlreadyExists.into())
517 }
518}
519
520fn check_root(path: &str) -> OpenResult<&str> {
521 path.strip_prefix("/").ok_or_else(|| IoError::new(IoErrorKind::InvalidInput, "invalid path"))
522}