diff --git a/src/borgstore/backends/posixfs.py b/src/borgstore/backends/posixfs.py index e187222..52dd5af 100644 --- a/src/borgstore/backends/posixfs.py +++ b/src/borgstore/backends/posixfs.py @@ -201,7 +201,7 @@ def info(self, name): self._check_permission(name, "lr") try: st = path.stat() - except FileNotFoundError: + except (FileNotFoundError, NotADirectoryError): return ItemInfo(name=path.name, exists=False, directory=False, size=0) else: is_dir = stat.S_ISDIR(st.st_mode) @@ -219,7 +219,7 @@ def load(self, name, *, size=None, offset=0): if offset != 0: f.seek(offset, os.SEEK_SET if offset >= 0 else os.SEEK_END) return f.read(-1 if size is None else size) - except FileNotFoundError: + except (FileNotFoundError, NotADirectoryError): raise ObjectNotFound(name) from None def _write_to_tempfile(self, path, value, suffix=TMP_SUFFIX, do_fsync=False): @@ -288,7 +288,7 @@ def delete(self, name): if self.quota_limit is not None: size = path.stat().st_size path.unlink() - except FileNotFoundError: + except (FileNotFoundError, NotADirectoryError): raise ObjectNotFound(name) from None if self.quota_limit is not None: self._quota_update(-size) @@ -344,7 +344,7 @@ def hash(self, name: str, algorithm: str = "sha256") -> str: try: with path.open("rb") as f: h = hashing.file_digest(f, algorithm) - except FileNotFoundError: + except (FileNotFoundError, NotADirectoryError): raise ObjectNotFound(name) from None return h.hexdigest() diff --git a/tests/test_backends.py b/tests/test_backends.py index 85b026f..bae530b 100644 --- a/tests/test_backends.py +++ b/tests/test_backends.py @@ -895,6 +895,24 @@ def test_missing_nesting_dir_move(tested_backends, request): backend.move("namespace2/nest2/key2", "namespace2a/nest2a/key2a") +def test_parent_is_an_object(tested_backends, request): + # a name below an object (not below a directory) does not exist, like on Windows, where the OS reports + # such a path as not found (on POSIX, it is ENOTDIR). + if tested_backends == "rclone_backend_created": + # TODO: rclone's local backend answers ENOTDIR with HTTP 500 (not 404), so we get a BackendError. + pytest.skip("rclone does not report a name below an object as not found") + with get_backend_from_fixture(tested_backends, request) as backend: + backend.store("key", b"value") + assert not backend.info("key/child").exists + with pytest.raises(ObjectNotFound): + backend.load("key/child") + with pytest.raises(ObjectNotFound): + backend.hash("key/child") + with pytest.raises(ObjectNotFound): + backend.delete("key/child") + assert backend.load("key") == b"value" + + def test_posixfs_missing_parent_dirs(tmp_path): be = PosixFS(tmp_path / "missing_parent_dir1" / "missing_parent_dir2" / "store") be.create() # this should work, auto-creating the missing parent dir(s)