1use super::error::Error;
4use crate::auth::UserDetail;
5use crate::storage::ErrorKind;
6use async_trait::async_trait;
7use chrono::{
8 Datelike,
9 prelude::{DateTime, Utc},
10};
11use md5::{Digest, Md5};
12use std::{
13 fmt::{self, Debug, Formatter, Write},
14 io,
15 path::Path,
16 result,
17 time::SystemTime,
18};
19use tokio::io::AsyncReadExt;
20
21pub const FEATURE_RESTART: u32 = 0b0000_0001;
24pub const FEATURE_SITEMD5: u32 = 0b0000_0010;
26
27pub type Result<T> = result::Result<T, Error>;
29
30pub trait Metadata {
32 fn len(&self) -> u64;
34
35 fn is_empty(&self) -> bool {
37 self.len() == 0
38 }
39
40 fn is_dir(&self) -> bool;
42
43 fn is_file(&self) -> bool;
45
46 fn is_symlink(&self) -> bool;
48
49 fn modified(&self) -> Result<SystemTime>;
51
52 fn gid(&self) -> u32;
54
55 fn uid(&self) -> u32;
57
58 fn links(&self) -> u64 {
60 1
61 }
62
63 fn permissions(&self) -> Permissions {
66 Permissions(0o7755)
67 }
68
69 fn readlink(&self) -> Option<&Path> {
71 None
72 }
73}
74
75pub struct Permissions(pub u32);
77
78const PERM_READ: u32 = 0b100100100;
79const PERM_WRITE: u32 = 0b010010010;
80const PERM_EXEC: u32 = 0b001001001;
81const PERM_USER: u32 = 0b111000000;
82const PERM_GROUP: u32 = 0b000111000;
83const PERM_OTHERS: u32 = 0b000000111;
84
85impl std::fmt::Display for Permissions {
86 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
87 f.write_char(if self.0 & PERM_USER & PERM_READ > 0 { 'r' } else { '-' })?;
88 f.write_char(if self.0 & PERM_USER & PERM_WRITE > 0 { 'w' } else { '-' })?;
89 f.write_char(if self.0 & PERM_USER & PERM_EXEC > 0 { 'x' } else { '-' })?;
90 f.write_char(if self.0 & PERM_GROUP & PERM_READ > 0 { 'r' } else { '-' })?;
91 f.write_char(if self.0 & PERM_GROUP & PERM_WRITE > 0 { 'w' } else { '-' })?;
92 f.write_char(if self.0 & PERM_GROUP & PERM_EXEC > 0 { 'x' } else { '-' })?;
93 f.write_char(if self.0 & PERM_OTHERS & PERM_READ > 0 { 'r' } else { '-' })?;
94 f.write_char(if self.0 & PERM_OTHERS & PERM_WRITE > 0 { 'w' } else { '-' })?;
95 f.write_char(if self.0 & PERM_OTHERS & PERM_EXEC > 0 { 'x' } else { '-' })?;
96 Ok(())
97 }
98}
99
100#[derive(Clone)]
104pub struct Fileinfo<P, M>
105where
106 P: AsRef<Path>,
107 M: Metadata,
108{
109 pub path: P,
111 pub metadata: M,
113}
114
115impl<P, M> std::fmt::Display for Fileinfo<P, M>
116where
117 P: AsRef<Path>,
118 M: Metadata,
119{
120 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
121 let modified: String = self
122 .metadata
123 .modified()
124 .map(|modified| {
125 let modified = DateTime::<Utc>::from(modified);
126 let now = Utc::now();
127 if modified.year() == now.year() {
128 modified.format("%b %d %H:%M").to_string()
129 } else {
130 modified.format("%b %d %Y").to_string()
131 }
132 })
133 .unwrap_or_else(|_| "--- -- --:--".to_string());
134 let basename = self.path.as_ref().components().next_back();
135 let path = match basename {
136 Some(v) => v.as_os_str().to_string_lossy(),
137 None => {
138 return Err(std::fmt::Error);
139 }
140 };
141 let perms = format!("{}", self.metadata.permissions());
142 let link_target = if self.metadata.is_symlink() {
143 match self.metadata.readlink() {
144 Some(t) => format!(" -> {}", t.display()),
145 None => {
146 "".to_string()
148 }
149 }
150 } else {
151 "".to_string()
152 };
153 write!(
154 f,
155 "{filetype}{permissions} {links:>12} {owner:>12} {group:>12} {size:#14} {modified:>12} {path}{link_target}",
156 filetype = if self.metadata.is_dir() {
157 "d"
158 } else if self.metadata.is_symlink() {
159 "l"
160 } else {
161 "-"
162 },
163 permissions = perms,
164 links = self.metadata.links(),
165 owner = self.metadata.uid(),
166 group = self.metadata.gid(),
167 size = self.metadata.len(),
168 modified = modified,
169 path = path,
170 )
171 }
172}
173
174#[async_trait]
179pub trait StorageBackend<User: UserDetail>: Send + Sync + Debug {
180 type Metadata: Metadata + Sync + Send;
182
183 fn enter(&mut self, _user_detail: &User) -> io::Result<()> {
188 Ok(())
189 }
190
191 fn name(&self) -> &str {
193 std::any::type_name::<Self>()
194 }
195
196 fn supported_features(&self) -> u32 {
199 0
200 }
201
202 async fn metadata<P: AsRef<Path> + Send + Debug>(&self, user: &User, path: P) -> Result<Self::Metadata>;
206
207 async fn md5<P: AsRef<Path> + Send + Debug>(&self, user: &User, path: P) -> Result<String>
219 where
220 P: AsRef<Path> + Send + Debug,
221 {
222 let mut md5sum = Md5::new();
223 let mut reader = self.get(user, path, 0).await?;
224 let mut buffer = vec![0_u8; 1024 * 1024 * 10];
225
226 while let Ok(n) = reader.read(&mut buffer[..]).await {
227 if n == 0 {
228 break;
229 }
230 md5sum.update(&buffer[0..n]);
231 }
232
233 Ok(format!("{:x}", md5sum.finalize()))
234 }
235
236 async fn list<P: AsRef<Path> + Send + Debug>(&self, user: &User, path: P) -> Result<Vec<Fileinfo<std::path::PathBuf, Self::Metadata>>>
238 where
239 <Self as StorageBackend<User>>::Metadata: Metadata;
240
241 #[allow(clippy::type_complexity)]
243 #[tracing_attributes::instrument]
244 async fn list_fmt<P>(&self, user: &User, path: P) -> std::result::Result<std::io::Cursor<Vec<u8>>, Error>
245 where
246 P: AsRef<Path> + Send + Debug,
247 Self::Metadata: Metadata + 'static,
248 {
249 let list = self.list(user, path).await?;
250
251 let buffer = list.iter().fold(String::new(), |mut buf, fi| {
252 let _ = write!(buf, "{}\r\n", fi);
253 buf
254 });
255
256 let file_infos: Vec<u8> = buffer.into_bytes();
257
258 Ok(std::io::Cursor::new(file_infos))
259 }
260
261 #[tracing_attributes::instrument]
263 async fn list_vec<P>(&self, user: &User, path: P) -> std::result::Result<Vec<String>, Error>
264 where
265 P: AsRef<Path> + Send + Debug,
266 Self::Metadata: Metadata + 'static,
267 {
268 let inlist = self.list(user, path).await?;
269 let out = inlist.iter().map(|fi| fi.to_string()).collect::<Vec<String>>();
270
271 Ok(out)
272 }
273
274 #[allow(clippy::type_complexity)]
277 #[tracing_attributes::instrument]
278 async fn nlst<P>(&self, user: &User, path: P) -> std::result::Result<std::io::Cursor<Vec<u8>>, std::io::Error>
279 where
280 P: AsRef<Path> + Send + Debug,
281 Self::Metadata: Metadata + 'static,
282 {
283 let list = self.list(user, path).await.map_err(|_| std::io::Error::from(std::io::ErrorKind::Other))?;
284
285 let buffer = list.iter().fold(String::new(), |mut buf, fi| {
286 let _ = write!(
287 buf,
288 "{}\r\n",
289 fi.path.file_name().unwrap_or_else(|| std::ffi::OsStr::new("")).to_str().unwrap_or("")
290 );
291 buf
292 });
293
294 let file_infos: Vec<u8> = buffer.into_bytes();
295
296 Ok(std::io::Cursor::new(file_infos))
297 }
298
299 async fn get_into<'a, P, W: ?Sized>(&self, user: &User, path: P, start_pos: u64, output: &'a mut W) -> Result<u64>
304 where
305 W: tokio::io::AsyncWrite + Unpin + Sync + Send,
306 P: AsRef<Path> + Send + Debug,
307 {
308 let mut reader = self.get(user, path, start_pos).await?;
309 Ok(tokio::io::copy(&mut reader, output).await.map_err(Error::from)?)
310 }
311
312 async fn get<P: AsRef<Path> + Send + Debug>(&self, user: &User, path: P, start_pos: u64) -> Result<Box<dyn tokio::io::AsyncRead + Send + Sync + Unpin>>;
317
318 async fn put<P: AsRef<Path> + Send + Debug, R: tokio::io::AsyncRead + Send + Sync + Unpin + 'static>(
320 &self,
321 user: &User,
322 input: R,
323 path: P,
324 start_pos: u64,
325 ) -> Result<u64>;
326
327 async fn del<P: AsRef<Path> + Send + Debug>(&self, user: &User, path: P) -> Result<()>;
329
330 async fn mkd<P: AsRef<Path> + Send + Debug>(&self, user: &User, path: P) -> Result<()>;
332
333 async fn rename<P: AsRef<Path> + Send + Debug>(&self, user: &User, from: P, to: P) -> Result<()>;
335
336 async fn rmd<P: AsRef<Path> + Send + Debug>(&self, user: &User, path: P) -> Result<()>;
338
339 async fn cwd<P: AsRef<Path> + Send + Debug>(&self, user: &User, path: P) -> Result<()>;
341}
342
343impl From<std::io::Error> for Error {
347 fn from(err: std::io::Error) -> Self {
348 let kind = err.kind();
349 let raw_os_error = err.raw_os_error();
350 match (kind, raw_os_error) {
351 (std::io::ErrorKind::NotFound, _) => Error::new(ErrorKind::PermanentFileNotAvailable, err),
352 (std::io::ErrorKind::AlreadyExists, _) => Error::new(ErrorKind::PermanentFileNotAvailable, err),
354 (std::io::ErrorKind::PermissionDenied, _) => Error::new(ErrorKind::PermissionDenied, err),
355 #[cfg(unix)]
360 (_, Some(libc::ENOTEMPTY)) => Error::new(ErrorKind::PermanentDirectoryNotEmpty, err),
361 #[cfg(unix)]
363 (_, Some(libc::ENOTDIR)) => Error::new(ErrorKind::PermanentDirectoryNotAvailable, err),
364 #[cfg(unix)]
366 (_, Some(libc::EISDIR) | Some(libc::EFBIG) | Some(libc::ESPIPE) | Some(libc::ENAMETOOLONG) | Some(libc::ELOOP)) => {
367 Error::new(ErrorKind::PermanentFileNotAvailable, err)
368 }
369 #[cfg(unix)]
371 (_, Some(libc::ENOSPC)) => Error::new(ErrorKind::InsufficientStorageSpaceError, err),
372 #[cfg(unix)]
374 (_, Some(libc::EROFS)) => Error::new(ErrorKind::PermissionDenied, err),
375 (std::io::ErrorKind::ConnectionReset, _) => Error::new(ErrorKind::ConnectionClosed, err),
377 (std::io::ErrorKind::BrokenPipe, _) => Error::new(ErrorKind::ConnectionClosed, err),
379 (std::io::ErrorKind::ConnectionAborted, _) => Error::new(ErrorKind::ConnectionClosed, err),
381 _ => Error::new(ErrorKind::LocalError, err),
383 }
384 }
385}