···234234235235 ${lib.optionalString (config.storage == "local") ''
236236 with subtest("Check that all chunks are actually deleted after GC"):
237237- files = server.succeed("find /var/lib/atticd/storage -type f")
237237+ files = server.succeed("find /var/lib/atticd/storage -type f ! -name 'VERSION'")
238238 print(f"Remaining files: {files}")
239239- assert files.strip() == ""
239239+ assert files.strip() == "", "Some files remain after GC: " + files
240240 ''}
241241242242 with subtest("Check that we can include the upload info in the payload"):
+101-6
server/src/storage/local.rs
···11//! Local file storage.
2233+use std::ffi::OsStr;
44+use std::os::unix::ffi::OsStrExt;
55+use std::path::Path;
36use std::path::PathBuf;
4758use async_trait::async_trait;
···3033 pub name: String,
3134}
32353636+async fn read_version(storage_path: &Path) -> ServerResult<u32> {
3737+ let version_path = storage_path.join("VERSION");
3838+ let v = match fs::read_to_string(&version_path).await {
3939+ Ok(version) => version
4040+ .trim()
4141+ .parse()
4242+ .map_err(|_| ErrorKind::StorageError(anyhow::anyhow!("Invalid version file")))?,
4343+ Err(e) if e.kind() == io::ErrorKind::NotFound => 0,
4444+ Err(e) => {
4545+ return Err(ErrorKind::StorageError(anyhow::anyhow!(
4646+ "Failed to read version file: {}",
4747+ e
4848+ ))
4949+ .into());
5050+ }
5151+ };
5252+ Ok(v)
5353+}
5454+5555+async fn write_version(storage_path: &Path, version: u32) -> ServerResult<()> {
5656+ let version_path = storage_path.join("VERSION");
5757+ fs::write(&version_path, format!("{}", version))
5858+ .await
5959+ .map_err(ServerError::storage_error)?;
6060+ Ok(())
6161+}
6262+6363+async fn upgrade_0_to_1(storage_path: &Path) -> ServerResult<()> {
6464+ let mut files = fs::read_dir(storage_path)
6565+ .await
6666+ .map_err(ServerError::storage_error)?;
6767+ // move all files to subdirectory using the first two characters of the filename
6868+ while let Some(file) = files
6969+ .next_entry()
7070+ .await
7171+ .map_err(ServerError::storage_error)?
7272+ {
7373+ if file
7474+ .file_type()
7575+ .await
7676+ .map_err(ServerError::storage_error)?
7777+ .is_file()
7878+ {
7979+ let name = file.file_name();
8080+ let name_bytes = name.as_os_str().as_bytes();
8181+ let parents = storage_path
8282+ .join(OsStr::from_bytes(&name_bytes[0..1]))
8383+ .join(OsStr::from_bytes(&name_bytes[0..2]));
8484+ let new_path = parents.join(name);
8585+ fs::create_dir_all(&parents).await.map_err(|e| {
8686+ ErrorKind::StorageError(anyhow::anyhow!("Failed to create directory {}", e))
8787+ })?;
8888+ fs::rename(&file.path(), &new_path).await.map_err(|e| {
8989+ ErrorKind::StorageError(anyhow::anyhow!(
9090+ "Failed to move file {} to {}: {}",
9191+ file.path().display(),
9292+ new_path.display(),
9393+ e
9494+ ))
9595+ })?;
9696+ }
9797+ }
9898+9999+ Ok(())
100100+}
101101+33102impl LocalBackend {
34103 pub async fn new(config: LocalStorageConfig) -> ServerResult<Self> {
3535- fs::create_dir_all(&config.path)
3636- .await
3737- .map_err(ServerError::storage_error)?;
104104+ fs::create_dir_all(&config.path).await.map_err(|e| {
105105+ ErrorKind::StorageError(anyhow::anyhow!(
106106+ "Failed to create storage directory {}: {}",
107107+ config.path.display(),
108108+ e
109109+ ))
110110+ })?;
111111+112112+ let version = read_version(&config.path).await?;
113113+ if version == 0 {
114114+ upgrade_0_to_1(&config.path).await?;
115115+ }
116116+ write_version(&config.path, 1).await?;
3811739118 Ok(Self { config })
40119 }
4112042121 fn get_path(&self, p: &str) -> PathBuf {
4343- self.config.path.join(p)
122122+ let level1 = &p[0..1];
123123+ let level2 = &p[0..2];
124124+ self.config.path.join(level1).join(level2).join(p)
44125 }
45126}
46127···51132 name: String,
52133 mut stream: &mut (dyn AsyncRead + Unpin + Send),
53134 ) -> ServerResult<RemoteFile> {
5454- let mut file = File::create(self.get_path(&name))
135135+ let path = self.get_path(&name);
136136+ fs::create_dir_all(path.parent().unwrap())
55137 .await
5656- .map_err(ServerError::storage_error)?;
138138+ .map_err(|e| {
139139+ ErrorKind::StorageError(anyhow::anyhow!(
140140+ "Failed to create directory {}: {}",
141141+ path.parent().unwrap().display(),
142142+ e
143143+ ))
144144+ })?;
145145+ let mut file = File::create(self.get_path(&name)).await.map_err(|e| {
146146+ ErrorKind::StorageError(anyhow::anyhow!(
147147+ "Failed to create file {}: {}",
148148+ self.get_path(&name).display(),
149149+ e
150150+ ))
151151+ })?;
5715258153 io::copy(&mut stream, &mut file)
59154 .await