1mod error;
59mod id;
60mod info;
61mod options;
62
63use log::{error, warn};
64use nuts_backend::{Backend, ReceiveHeader, HEADER_MAX_SIZE};
65use std::io::{self, ErrorKind, Read, Write};
66use std::path::Path;
67use std::{cmp, fs};
68
69pub use error::Error;
70pub use id::Id;
71pub use info::Info;
72pub use options::{CreateOptions, OpenOptions, Settings};
73
74use crate::error::Result;
75
76fn read_block(path: &Path, id: &Id, bsize: u32, buf: &mut [u8]) -> Result<usize> {
77 let path = id.to_pathbuf(path);
78 let mut fh = fs::OpenOptions::new().read(true).open(path)?;
79
80 let len = cmp::min(buf.len(), bsize as usize);
81 let target = &mut buf[..len];
82
83 fh.read_exact(target)?;
84
85 Ok(len)
86}
87
88fn write_block(
89 path: &Path,
90 id: &Id,
91 aquire: bool,
92 header: bool,
93 bsize: u32,
94 buf: &[u8],
95) -> Result<usize> {
96 let path = id.to_pathbuf(path);
97
98 if let Some(dir) = path.parent() {
99 fs::create_dir_all(dir)?;
100 }
101
102 if aquire {
103 if path.exists() {
105 return Err(io::Error::new(
106 ErrorKind::Other,
107 format!("cannot aquire {}, already stored in {}", id, path.display()),
108 )
109 .into());
110 }
111 } else {
112 if !header && !path.is_file() {
116 return Err(io::Error::new(
117 ErrorKind::Other,
118 format!("cannot open {}, no related file {}", id, path.display()),
119 )
120 .into());
121 }
122 }
123
124 let tmp_path = path.with_extension("tmp");
125
126 let mut fh = fs::OpenOptions::new()
127 .write(true)
128 .create_new(true)
129 .open(&tmp_path)?;
130
131 let len = cmp::min(buf.len(), bsize as usize);
132 let pad_len = bsize as usize - len;
133
134 fh.write_all(&buf[..len])?;
135 fh.write_all(&vec![0; pad_len])?;
136 fh.flush()?;
137
138 fs::rename(tmp_path, path)?;
139
140 Ok(len)
141}
142
143fn read_header(path: &Path, buf: &mut [u8]) -> Result<()> {
144 read_block(path, &Id::min(), HEADER_MAX_SIZE as u32, buf).map(|_| ())
145}
146
147fn write_header(path: &Path, bsize: u32, buf: &[u8]) -> Result<()> {
148 write_block(path, &Id::min(), false, true, bsize, buf).map(|_| ())
149}
150
151#[derive(Debug)]
152pub struct DirectoryBackend<P: AsRef<Path>> {
153 bsize: u32,
154 path: P,
155}
156
157impl<P: AsRef<Path>> ReceiveHeader<Self> for DirectoryBackend<P> {
158 fn get_header_bytes(&mut self, bytes: &mut [u8; HEADER_MAX_SIZE]) -> Result<()> {
159 read_header(self.path.as_ref(), bytes)
160 }
161}
162
163impl<P: AsRef<Path>> Backend for DirectoryBackend<P> {
164 type Settings = Settings;
165 type Err = Error;
166 type Id = Id;
167 type Info = Info;
168
169 fn info(&self) -> Result<Info> {
170 Ok(Info { bsize: self.bsize })
171 }
172
173 fn block_size(&self) -> u32 {
174 self.bsize
175 }
176
177 fn aquire(&mut self, buf: &[u8]) -> Result<Self::Id> {
178 const MAX: u8 = 3;
179
180 for n in 0..MAX {
181 let id = Id::generate()?;
182
183 match write_block(self.path.as_ref(), &id, true, false, self.bsize, buf) {
184 Ok(_) => return Ok(id),
185 Err(Error::Io(err)) => {
186 if err.kind() == ErrorKind::AlreadyExists {
187 warn!("Id {} already exists try again ({}/{})", id, n + 1, MAX);
188 } else {
189 return Err(err.into());
190 }
191 }
192 Err(err) => return Err(err),
193 };
194 }
195
196 Err(Error::UniqueId)
197 }
198
199 fn release(&mut self, id: Self::Id) -> Result<()> {
200 let path = id.to_pathbuf(self.path.as_ref());
201
202 Ok(fs::remove_file(path)?)
203 }
204
205 fn read(&mut self, id: &Id, buf: &mut [u8]) -> Result<usize> {
206 read_block(self.path.as_ref(), id, self.bsize, buf)
207 }
208
209 fn write(&mut self, id: &Id, buf: &[u8]) -> Result<usize> {
210 write_block(self.path.as_ref(), id, false, false, self.bsize, buf)
211 }
212
213 fn write_header(&mut self, buf: &[u8; HEADER_MAX_SIZE]) -> Result<()> {
214 write_header(self.path.as_ref(), self.bsize, buf)
215 }
216
217 fn delete(self) {
218 if let Err(err) = fs::remove_dir_all(self.path) {
219 error!("failed to delete backend instance: {}", err);
220 }
221 }
222}