Skip to content

Commit 235ff0a

Browse files
committed
fix more fs code idk now spaceable file upload?
1 parent fc4bb84 commit 235ff0a

File tree

2 files changed

+135
-98
lines changed

2 files changed

+135
-98
lines changed

src/auth.rs

-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ pub use authorization::{Matcher, Permission};
160160
use async_trait::async_trait;
161161
use tokio::sync::RwLock;
162162
use tracing::debug;
163-
use tracing::field::debug;
164163

165164
/// S3 Authentication Provider
166165

src/storages/fs.rs

+135-97
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ use crate::path::S3Path;
2323
use crate::storage::S3Storage;
2424
use crate::utils::{crypto, time, Apply};
2525

26-
use std::collections::{HashMap, HashSet, VecDeque};
26+
use std::collections::{HashMap, HashSet};
2727
use std::convert::TryInto;
2828
use std::env;
2929
use std::io::{self, SeekFrom};
30-
use std::path::{Component, Path, PathBuf};
30+
use std::path::{Path, PathBuf};
3131

3232
use futures::io::{AsyncReadExt, AsyncSeekExt, AsyncWrite, AsyncWriteExt, BufWriter};
3333
use futures::stream::{Stream, StreamExt, TryStreamExt};
@@ -47,6 +47,75 @@ pub struct FileSystem {
4747
}
4848

4949
impl FileSystem {
50+
fn url_encode_list_results(
51+
objects: Vec<Object>,
52+
common_prefixes: Vec<String>,
53+
) -> (Vec<Object>, Vec<String>) {
54+
fn encode_with_slash(s: &str) -> String {
55+
s.split('/')
56+
.map(|part| urlencoding::encode(part).into_owned())
57+
.collect::<Vec<_>>()
58+
.join("/")
59+
}
60+
61+
(
62+
objects
63+
.into_iter()
64+
.map(|mut obj| {
65+
if let Some(key) = obj.key.as_mut() {
66+
*key = encode_with_slash(key);
67+
}
68+
obj
69+
})
70+
.collect(),
71+
common_prefixes
72+
.into_iter()
73+
.map(|p| encode_with_slash(&p))
74+
.collect(),
75+
)
76+
}
77+
78+
async fn abstract_list_objects(
79+
&self,
80+
bucket: &str,
81+
prefix: &Option<String>,
82+
delimiter: &Option<String>,
83+
max_keys: i64,
84+
) -> io::Result<(Vec<Object>, Vec<String>)> {
85+
let bucket_path = self.get_bucket_path(bucket)?;
86+
debug!("Bucket path: {:?}", bucket_path);
87+
88+
let (search_dir, prefix_filter) =
89+
if delimiter.is_none() || prefix.as_deref().unwrap_or("").is_empty() {
90+
(PathBuf::new(), String::new())
91+
} else {
92+
self.parse_prefix_and_delimiter(prefix, delimiter)
93+
};
94+
let search_path = bucket_path.join(&search_dir);
95+
debug!("Search path: {:?}", search_path);
96+
97+
if !search_path.is_dir() {
98+
return Ok((Vec::new(), Vec::new()));
99+
}
100+
101+
let (mut objects, common_prefixes) = self
102+
.list_contents(
103+
delimiter.is_some() && prefix.as_deref().map_or(false, |p| !p.is_empty()),
104+
&bucket_path,
105+
&search_path,
106+
&prefix_filter,
107+
delimiter,
108+
max_keys,
109+
)
110+
.await?;
111+
112+
objects.sort_by(|a, b| a.key.cmp(&b.key));
113+
let mut common_prefixes: Vec<_> = common_prefixes.into_iter().collect();
114+
common_prefixes.sort();
115+
116+
Ok((objects, common_prefixes))
117+
}
118+
50119
/// Constructs a file system storage located at `root`
51120
/// # Errors
52121
/// Returns an `Err` if current working directory is invalid or `root` doesn't exist
@@ -57,12 +126,42 @@ impl FileSystem {
57126

58127
/// resolve object path under the virtual root
59128
fn get_object_path(&self, bucket: &str, key: &str) -> io::Result<PathBuf> {
60-
let dir = Path::new(&bucket);
61-
let file_path = Path::new(&key);
62-
let ans = dir.join(file_path).absolutize_virtually(&self.root)?.into();
129+
let bucket_path = self.get_bucket_path(bucket)?;
130+
let file_path = Path::new(key);
131+
let ans = bucket_path
132+
.join(file_path)
133+
.absolutize_virtually(&self.root)?
134+
.into();
63135
Ok(ans)
64136
}
65137

138+
/// Wrap get_object_path with URL decoding and filename sanitization
139+
fn get_unsanitized_object_path(&self, bucket: &str, key: &str) -> io::Result<PathBuf> {
140+
let decoded_key =
141+
urlencoding::decode(key).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
142+
143+
// Sanitize filename
144+
let sanitized_key = decoded_key
145+
.chars()
146+
.map(|c| {
147+
if c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_' || c == '/' {
148+
c
149+
} else {
150+
'_'
151+
}
152+
})
153+
.collect::<String>();
154+
155+
// if sanitized_key.split('/').any(|component| component == "..") {
156+
// return Err(io::Error::new(
157+
// io::ErrorKind::InvalidInput,
158+
// "Invalid path: contains '..' sequence",
159+
// ));
160+
// }
161+
162+
self.get_object_path(bucket, &sanitized_key)
163+
}
164+
66165
/// resolve bucket path under the virtual root
67166
fn get_bucket_path(&self, bucket: &str) -> io::Result<PathBuf> {
68167
let dir = Path::new(&bucket);
@@ -377,8 +476,20 @@ impl S3Storage for FileSystem {
377476
AmzCopySource::Bucket { bucket, key } => (bucket, key),
378477
};
379478

380-
let src_path = trace_try!(self.get_object_path(bucket, key));
479+
let src_path = trace_try!(self.get_unsanitized_object_path(bucket, key));
480+
debug!(
481+
"CopyObject: src_path = {}, bucket = {}, key = {}",
482+
src_path.display(),
483+
bucket,
484+
key,
485+
);
381486
let dst_path = trace_try!(self.get_object_path(&input.bucket, &input.key));
487+
debug!(
488+
"CopyObject: dst_path = {}, input.bucket = {}, input.key = {}",
489+
dst_path.display(),
490+
input.bucket,
491+
input.key
492+
);
382493

383494
let file_metadata = trace_try!(async_fs::metadata(&src_path).await);
384495
let last_modified = time::to_rfc3339(trace_try!(file_metadata.modified()));
@@ -661,57 +772,21 @@ impl S3Storage for FileSystem {
661772
&self,
662773
input: ListObjectsRequest,
663774
) -> S3StorageResult<ListObjectsOutput, ListObjectsError> {
664-
let bucket_path = trace_try!(self.get_bucket_path(&input.bucket));
665-
debug!("Bucket path: {:?}", bucket_path);
666-
667-
let (search_dir, prefix_filter) =
668-
if input.delimiter.is_none() || input.prefix.as_deref().unwrap_or("").is_empty() {
669-
(PathBuf::new(), String::new())
670-
} else {
671-
self.parse_prefix_and_delimiter(&input.prefix, &input.delimiter)
672-
};
673-
let search_path = bucket_path.join(&search_dir);
674-
debug!("Search path: {:?}", search_path);
675-
676-
if !search_path.is_dir() {
677-
return Ok(ListObjectsOutput {
678-
name: Some(input.bucket),
679-
prefix: input.prefix,
680-
delimiter: input.delimiter,
681-
encoding_type: input.encoding_type,
682-
..Default::default()
683-
});
684-
}
685-
686-
let (mut objects, common_prefixes) = trace_try!(
687-
self.list_contents(
688-
input.delimiter.is_some()
689-
&& input.prefix.as_deref().map_or(false, |p| !p.is_empty()),
690-
&bucket_path,
691-
&search_path,
692-
&prefix_filter,
775+
let (objects, common_prefixes) = trace_try!(
776+
self.abstract_list_objects(
777+
&input.bucket,
778+
&input.prefix,
693779
&input.delimiter,
694780
input.max_keys.unwrap_or(1000i64),
695781
)
696782
.await
697783
);
698784

699-
objects.sort_by(|a, b| a.key.cmp(&b.key));
700-
let mut common_prefixes: Vec<_> = common_prefixes.into_iter().collect();
701-
common_prefixes.sort();
702-
703-
// URL encode object keys and common prefixes if encoding type is specified
704-
if input.encoding_type.as_deref() == Some("url") {
705-
for object in objects.iter_mut() {
706-
if let Some(key) = object.key.as_mut() {
707-
*key = urlencoding::encode(key).into_owned();
708-
}
709-
}
710-
common_prefixes = common_prefixes
711-
.into_iter()
712-
.map(|p| urlencoding::encode(&p).into_owned())
713-
.collect();
714-
}
785+
let (objects, common_prefixes) = if input.encoding_type.as_deref() == Some("url") {
786+
Self::url_encode_list_results(objects, common_prefixes)
787+
} else {
788+
(objects, common_prefixes)
789+
};
715790

716791
Ok(ListObjectsOutput {
717792
contents: Some(objects),
@@ -741,61 +816,24 @@ impl S3Storage for FileSystem {
741816
&self,
742817
input: ListObjectsV2Request,
743818
) -> S3StorageResult<ListObjectsV2Output, ListObjectsV2Error> {
744-
let bucket_path = trace_try!(self.get_bucket_path(&input.bucket));
745-
debug!("Bucket path: {:?}", bucket_path);
746-
747-
let (search_dir, prefix_filter) =
748-
if input.delimiter.is_none() || input.prefix.as_deref().unwrap_or("").is_empty() {
749-
(PathBuf::new(), String::new())
750-
} else {
751-
self.parse_prefix_and_delimiter(&input.prefix, &input.delimiter)
752-
};
753-
let search_path = bucket_path.join(&search_dir);
754-
debug!("Search path: {:?}", search_path);
755-
756-
if !search_path.is_dir() {
757-
return Ok(ListObjectsV2Output {
758-
name: Some(input.bucket),
759-
prefix: input.prefix,
760-
delimiter: input.delimiter,
761-
key_count: Some(0),
762-
encoding_type: input.encoding_type.clone(),
763-
..Default::default()
764-
});
765-
}
766-
767-
let (mut objects, common_prefixes) = trace_try!(
768-
self.list_contents(
769-
input.delimiter.is_some()
770-
&& input.prefix.as_deref().map_or(false, |p| !p.is_empty()),
771-
&bucket_path,
772-
&search_path,
773-
&prefix_filter,
819+
let (objects, common_prefixes) = trace_try!(
820+
self.abstract_list_objects(
821+
&input.bucket,
822+
&input.prefix,
774823
&input.delimiter,
775824
input.max_keys.unwrap_or(1000i64),
776825
)
777826
.await
778827
);
779828

780-
objects.sort_by(|a, b| a.key.cmp(&b.key));
781-
let mut common_prefixes: Vec<_> = common_prefixes.into_iter().collect();
782-
common_prefixes.sort();
783-
784829
let object_count = objects.len();
785830
let common_prefix_count = common_prefixes.len();
786831

787-
// URL encode object keys if encoding type is specified
788-
if input.encoding_type.as_deref() == Some("url") {
789-
for object in objects.iter_mut() {
790-
if let Some(key) = object.key.as_mut() {
791-
*key = urlencoding::encode(key).into_owned();
792-
}
793-
}
794-
common_prefixes = common_prefixes
795-
.into_iter()
796-
.map(|p| urlencoding::encode(&p).into_owned())
797-
.collect();
798-
}
832+
let (objects, common_prefixes) = if input.encoding_type.as_deref() == Some("url") {
833+
Self::url_encode_list_results(objects, common_prefixes)
834+
} else {
835+
(objects, common_prefixes)
836+
};
799837

800838
Ok(ListObjectsV2Output {
801839
contents: Some(objects),

0 commit comments

Comments
 (0)