2 use std
::path
::{Path, PathBuf}
;
5 use crypto
::digest
::Digest
;
6 use crypto
::sha2
::Sha512Trunc256
;
9 use std
::fs
::{File, OpenOptions}
;
10 use nix
::fcntl
::{flock, FlockArg}
;
11 use std
::os
::unix
::io
::AsRawFd
;
13 pub struct ChunkStore
{
16 hasher
: Sha512Trunc256
,
21 const HEX_CHARS
: &'
static [u8; 16] = b
"0123456789abcdef";
23 fn u256_to_hex(digest
: &[u8; 32]) -> String
{
25 let mut buf
= Vec
::<u8>::with_capacity(64);
28 buf
.push(HEX_CHARS
[(digest
[i
] >> 4) as usize]);
29 buf
.push(HEX_CHARS
[(digest
[i
] & 0xf) as usize]);
32 unsafe { String::from_utf8_unchecked(buf) }
35 fn u256_to_prefix(digest
: &[u8; 32]) -> PathBuf
{
37 let mut buf
= Vec
::<u8>::with_capacity(3+1+2+1);
39 buf
.push(HEX_CHARS
[(digest
[0] as usize) >> 4]);
40 buf
.push(HEX_CHARS
[(digest
[0] as usize) &0xf]);
41 buf
.push(HEX_CHARS
[(digest
[1] as usize) >> 4]);
44 buf
.push(HEX_CHARS
[(digest
[1] as usize) & 0xf]);
45 buf
.push(HEX_CHARS
[(digest
[2] as usize) >> 4]);
48 let path
= unsafe { String::from_utf8_unchecked(buf)}
;
55 fn chunk_dir
<P
: AsRef
<Path
>>(path
: P
) -> PathBuf
{
57 let mut chunk_dir
: PathBuf
= PathBuf
::from(path
.as_ref());
58 chunk_dir
.push(".chunks");
63 pub fn create
<P
: Into
<PathBuf
>>(path
: P
) -> Result
<Self, Error
> {
65 let base
: PathBuf
= path
.into();
66 let chunk_dir
= Self::chunk_dir(&base
);
68 if let Err(err
) = std
::fs
::create_dir(&base
) {
69 bail
!("unable to create chunk store {:?} - {}", base
, err
);
72 if let Err(err
) = std
::fs
::create_dir(&chunk_dir
) {
73 bail
!("unable to create chunk store subdir {:?} - {}", chunk_dir
, err
);
78 let mut l1path
= base
.clone();
79 l1path
.push(format
!("{:03x}",i
));
80 if let Err(err
) = std
::fs
::create_dir(&l1path
) {
81 bail
!("unable to create chunk subdir {:?} - {}", l1path
, err
);
88 pub fn open
<P
: Into
<PathBuf
>>(path
: P
) -> Result
<Self, Error
> {
90 let base
: PathBuf
= path
.into();
91 let chunk_dir
= Self::chunk_dir(&base
);
93 let metadata
= match std
::fs
::metadata(&chunk_dir
) {
95 Err(err
) => bail
!("unable to open chunk store {:?} - {}", chunk_dir
, err
),
98 let mut lockfile_path
= base
.clone();
99 lockfile_path
.push(".lock");
101 let lockfile
= match OpenOptions
::new()
104 .open(&lockfile_path
) {
106 Err(err
) => bail
!("unable to open chunk store lock file {:?} - {}",
110 let fd
= lockfile
.as_raw_fd();
112 let now
= std
::time
::SystemTime
::now();
114 let mut print_msg
= true;
116 match flock(fd
, FlockArg
::LockExclusiveNonblock
) {
121 eprintln
!("trying to aquire lock...");
126 match now
.elapsed() {
128 if elapsed
.as_secs() >= timeout
{
129 bail
!("unable to aquire chunk store lock {:?} - got timeout",
134 bail
!("unable to aquire chunk store lock {:?} - clock problems - {}",
138 std
::thread
::sleep_ms(100);
144 hasher
: Sha512Trunc256
::new(),
146 mutex
: Mutex
::new(false)
150 pub fn insert_chunk(&mut self, chunk
: &[u8]) -> Result
<([u8; 32]), Error
> {
153 self.hasher
.input(chunk
);
155 let mut digest
= [0u8; 32];
156 self.hasher
.result(&mut digest
);
157 println
!("DIGEST {}", u256_to_hex(&digest
));
159 let mut chunk_path
= self.base
.clone();
160 let prefix
= u256_to_prefix(&digest
);
161 chunk_path
.push(&prefix
);
162 let digest_str
= u256_to_hex(&digest
);
163 chunk_path
.push(&digest_str
);
165 let lock
= self.mutex
.lock();
167 if let Ok(metadata
) = std
::fs
::metadata(&chunk_path
) {
168 if metadata
.is_file() {
171 bail
!("Got unexpected file type for chunk {}", digest_str
);
175 let mut chunk_dir
= self.base
.clone();
176 chunk_dir
.push(&prefix
);
178 if let Err(_
) = std
::fs
::create_dir(&chunk_dir
) { /* ignore */ }
180 let mut tmp_path
= chunk_path
.clone();
181 tmp_path
.set_extension("tmp");
182 let mut f
= std
::fs
::File
::create(&tmp_path
)?
;
185 if let Err(err
) = std
::fs
::rename(&tmp_path
, &chunk_path
) {
186 if let Err(_
) = std
::fs
::remove_file(&tmp_path
) { /* ignore */ }
187 bail
!("Atomic rename failed for chunk {} - {}", digest_str
, err
);
190 println
!("PATH {:?}", chunk_path
);
201 fn test_chunk_store1() {
203 if let Err(_e
) = std
::fs
::remove_dir_all(".testdir") { /* ignore */ }
205 let chunk_store
= ChunkStore
::open(".testdir");
206 assert
!(chunk_store
.is_err());
208 let mut chunk_store
= ChunkStore
::create(".testdir").unwrap();
209 chunk_store
.insert_chunk(&[0u8, 1u8]).unwrap();
210 chunk_store
.insert_chunk(&[0u8, 1u8]).unwrap();
213 let chunk_store
= ChunkStore
::create(".testdir");
214 assert
!(chunk_store
.is_err());