1use std::fs::File;
19use std::path::PathBuf;
20use std::sync::Arc;
21
22use log::debug;
23
24use super::FS_SCHEME;
25use super::config::FsConfig;
26use super::core::*;
27use super::deleter::FsDeleter;
28use super::reader::*;
29use opendal_core::raw::*;
30use opendal_core::*;
31
32#[doc = include_str!("docs.md")]
34#[derive(Debug, Default)]
35pub struct FsBuilder {
36 pub(super) config: FsConfig,
37}
38
39impl FsBuilder {
40 pub fn root(mut self, root: &str) -> Self {
42 self.config.root = if root.is_empty() {
43 None
44 } else {
45 Some(root.to_string())
46 };
47
48 self
49 }
50
51 pub fn atomic_write_dir(mut self, dir: &str) -> Self {
58 if !dir.is_empty() {
59 self.config.atomic_write_dir = Some(dir.to_string());
60 }
61
62 self
63 }
64}
65
66impl Builder for FsBuilder {
67 type Config = FsConfig;
68
69 fn build(self) -> Result<impl Service> {
70 debug!("backend build started: {:?}", self);
71
72 let root = match self.config.root.map(PathBuf::from) {
73 Some(root) => Ok(root),
74 None => Err(Error::new(
75 ErrorKind::ConfigInvalid,
76 "root is not specified",
77 )),
78 }?;
79 debug!("backend use root {}", root.to_string_lossy());
80
81 if let Err(e) = std::fs::metadata(&root)
83 && e.kind() == std::io::ErrorKind::NotFound
84 {
85 std::fs::create_dir_all(&root).map_err(|e| {
86 Error::new(ErrorKind::Unexpected, "create root dir failed")
87 .with_operation("Builder::build")
88 .with_context("root", root.to_string_lossy())
89 .set_source(e)
90 })?;
91 }
92
93 let atomic_write_dir = self.config.atomic_write_dir.map(PathBuf::from);
94
95 if let Some(d) = &atomic_write_dir
97 && let Err(e) = std::fs::metadata(d)
98 && e.kind() == std::io::ErrorKind::NotFound
99 {
100 std::fs::create_dir_all(d).map_err(|e| {
101 Error::new(ErrorKind::Unexpected, "create atomic write dir failed")
102 .with_operation("Builder::build")
103 .with_context("atomic_write_dir", d.to_string_lossy())
104 .set_source(e)
105 })?;
106 }
107
108 let root = root.canonicalize().map_err(|e| {
111 Error::new(
112 ErrorKind::Unexpected,
113 "canonicalize of root directory failed",
114 )
115 .set_source(e)
116 })?;
117
118 let atomic_write_dir = atomic_write_dir
121 .map(|p| {
122 p.canonicalize().map(Some).map_err(|e| {
123 Error::new(
124 ErrorKind::Unexpected,
125 "canonicalize of atomic_write_dir directory failed",
126 )
127 .with_operation("Builder::build")
128 .with_context("root", root.to_string_lossy())
129 .set_source(e)
130 })
131 })
132 .unwrap_or(Ok(None))?;
133
134 Ok(FsBackend {
135 core: Arc::new(FsCore {
136 info: ServiceInfo::new(FS_SCHEME, root.to_string_lossy(), ""),
137 capability: Capability {
138 stat: true,
139
140 read: true,
141
142 write: true,
143 write_can_empty: true,
144 write_can_append: true,
145 write_can_multi: true,
146 write_with_if_not_exists: true,
147 #[cfg(unix)]
148 write_with_user_metadata: true,
149
150 create_dir: true,
151 delete: true,
152 delete_with_recursive: true,
153
154 list: true,
155
156 copy: true,
157 rename: true,
158
159 shared: true,
160
161 ..Default::default()
162 },
163 root,
164 atomic_write_dir,
165 buf_pool: oio::PooledBuf::new(16).with_initial_capacity(256 * 1024),
166 }),
167 })
168 }
169}
170
171#[derive(Debug, Clone)]
173pub struct FsBackend {
174 pub(crate) core: Arc<FsCore>,
175}
176
177impl Service for FsBackend {
178 type Reader = oio::PositionReader<FsReader>;
179 type Writer = FsLazyWriter;
180 type Lister = FsLazyLister;
181 type Deleter = oio::OneShotDeleter<FsDeleter>;
182 type Copier = oio::OneShotCopier;
183 type Composer = ();
184
185 fn info(&self) -> ServiceInfo {
186 self.core.info.clone()
187 }
188
189 fn capability(&self) -> Capability {
190 self.core.capability
191 }
192
193 async fn create_dir(
194 &self,
195 _ctx: &OperationContext,
196 path: &str,
197 _: OpCreateDir,
198 ) -> Result<RpCreateDir> {
199 self.core.fs_create_dir(path).await?;
200 Ok(RpCreateDir::default())
201 }
202
203 async fn stat(&self, _ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
204 let m = self.core.fs_stat(path).await?;
205 Ok(RpStat::new(m))
206 }
207
208 fn read(&self, _ctx: &OperationContext, path: &str, _: OpRead) -> Result<Self::Reader> {
209 Ok(oio::PositionReader::new(FsReader::new(
210 self.core.clone(),
211 path,
212 )))
213 }
214
215 fn write(&self, ctx: &OperationContext, path: &str, op: OpWrite) -> Result<Self::Writer> {
216 Ok(FsLazyWriter::new(
217 self.core.clone(),
218 ctx.executor().clone(),
219 path,
220 op,
221 ))
222 }
223
224 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
225 Ok(oio::OneShotDeleter::new(FsDeleter::new(self.core.clone())))
226 }
227
228 fn list(&self, _ctx: &OperationContext, path: &str, _: OpList) -> Result<Self::Lister> {
229 Ok(FsLazyLister::new(self.core.clone(), path))
230 }
231
232 fn copy(
233 &self,
234 _ctx: &OperationContext,
235 from: &str,
236 to: &str,
237 _args: OpCopy,
238 ) -> Result<Self::Copier> {
239 let core = self.core.clone();
240 let from = from.to_string();
241 let to = to.to_string();
242 Ok(oio::OneShotCopier::new(async move {
243 let size = core.fs_copy(&from, &to).await?;
244 let metadata = MetadataBuilder::file(size);
245 Ok(metadata.build())
246 }))
247 }
248
249 async fn rename(
250 &self,
251 _ctx: &OperationContext,
252 from: &str,
253 to: &str,
254 _args: OpRename,
255 ) -> Result<RpRename> {
256 self.core.fs_rename(from, to).await?;
257 Ok(RpRename::default())
258 }
259
260 async fn presign(
261 &self,
262 _ctx: &OperationContext,
263 _path: &str,
264 _args: OpPresign,
265 ) -> Result<RpPresign> {
266 Err(Error::new(
267 ErrorKind::Unsupported,
268 "operation is not supported",
269 ))
270 }
271}
272
273#[cfg(windows)]
274pub(crate) fn read_at(f: &File, buf: &mut [u8], offset: u64) -> Result<usize> {
275 use std::os::windows::fs::FileExt;
276 f.seek_read(buf, offset).map_err(new_std_io_error)
277}
278
279#[cfg(unix)]
280pub(crate) fn read_at(f: &File, buf: &mut [u8], offset: u64) -> Result<usize> {
281 use std::os::unix::fs::FileExt;
282 f.read_at(buf, offset).map_err(new_std_io_error)
283}