diff --git a/java-bridge/src/lib.rs b/java-bridge/src/lib.rs index f662551b..f038d584 100644 --- a/java-bridge/src/lib.rs +++ b/java-bridge/src/lib.rs @@ -243,7 +243,7 @@ pub extern "system" fn Java_RustLibrary_umount( .unwrap() .remove(&handle) .unwrap(); - match handle.umount().await.map_err(|err| io::Error::other(err)) { + match handle.umount().await.map_err(io::Error::other) { Ok(()) => Ok(()), Err(err) => { error!("Cannot umount, force: {}", err); diff --git a/src/mount/linux.rs b/src/mount/linux.rs index 37aa7946..27e44d91 100644 --- a/src/mount/linux.rs +++ b/src/mount/linux.rs @@ -1099,6 +1099,7 @@ impl Filesystem for EncryptedFsFuse3 { ) -> Result>> { trace!(""); + // Read the directory entries asynchronously #[allow(clippy::cast_sign_loss)] let iter = match self.get_fs().read_dir(inode).await { Err(err) => { @@ -1107,15 +1108,29 @@ impl Filesystem for EncryptedFsFuse3 { } Ok(iter) => iter, }; + let iter = DirectoryEntryIterator(iter, 0); + // ⚠️ NOTE: The `offset` used here is positional, not the actual FUSE directory entry offset. + // This causes problems when a client (like `rm *`) tries to resume a directory listing + // and expects to pick up exactly where it left off using that offset. + // + // The `skip(offset as usize)` line assumes that FUSE's offset is a zero-based index, + // but that’s not always the case. FUSE may give us back the exact `entry.offset` + // previously emitted — not a count. + // + // This mismatch can lead to clients only *seeing* the first 100 entries (due to a broken resume), + // but still *deleting* everything (because they iterate over all with different offsets). + // + // Fixing this properly would require tracking and matching `entry.offset` values, + // which means rewriting the iterator and reply logic — a much bigger refactor. + Ok(ReplyDirectory { #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] entries: stream::iter(iter.skip(offset as usize)), }) } - #[instrument(skip(self), err(level = Level::WARN), ret(level = Level::DEBUG))] async fn releasedir(&self, req: Request, inode: Inode, fh: u64, flags: u32) -> Result<()> { trace!(""); diff --git a/tests/python/copy_a_video_file.py b/tests/python/copy_a_video_file.py new file mode 100644 index 00000000..4778a6ca --- /dev/null +++ b/tests/python/copy_a_video_file.py @@ -0,0 +1,22 @@ +import os +import shutil +import pytest + +def test_copy_video(): + """Test copying video3.mp4 from tmp_upload to final.""" + src_dir = "tmp_upload" + dest_dir = "final" + file_name = "video3.mp4" + src_path = os.path.join(src_dir, file_name) + dest_path = os.path.join(dest_dir, file_name) + + os.makedirs(dest_dir, exist_ok=True) + + assert os.path.exists(src_path), "Source video does not exist!" + + shutil.copy(src_path, dest_path) + + assert os.path.exists(src_path), "Original file should not be deleted!" + assert os.path.exists(dest_path), "File was not copied to destination!" + + os.remove(dest_path) diff --git a/tests/python/copy_and_verify_image_file.py b/tests/python/copy_and_verify_image_file.py new file mode 100644 index 00000000..8471596c --- /dev/null +++ b/tests/python/copy_and_verify_image_file.py @@ -0,0 +1,22 @@ +import os +import shutil +import filecmp + +def test_copy_image_file(): + source = "tmp_upload/img1.jpg" + dest_dir = "final" + dest = os.path.join(dest_dir, "img1.jpg") + + os.makedirs(dest_dir, exist_ok=True) + + try: + shutil.copy2(source, dest) + assert os.path.exists(dest), "Image file was not copied." + assert filecmp.cmp(source, dest, shallow=False), "Copied image content mismatch." + print("✅ Test passed: Image copied and verified.") + except AssertionError as e: + print(f"❌ Test failed: {e}") + finally: + # Clean up + if os.path.exists(dest): + os.remove(dest) diff --git a/tests/python/delete_a_doc_file_and_confirm_absence.py b/tests/python/delete_a_doc_file_and_confirm_absence.py new file mode 100644 index 00000000..d549b6f3 --- /dev/null +++ b/tests/python/delete_a_doc_file_and_confirm_absence.py @@ -0,0 +1,12 @@ +import os +import pytest + +def test_delete_doc(): + """Test deleting text1.doc from tmp_upload.""" + doc_path = "tmp_upload/text1.doc" + + assert os.path.exists(doc_path), "DOC file does not exist!" + + os.remove(doc_path) + + assert not os.path.exists(doc_path), "DOC file was not deleted!" diff --git a/tests/python/move_a_pdf_file_and_confirm_its_absence.py b/tests/python/move_a_pdf_file_and_confirm_its_absence.py new file mode 100644 index 00000000..52518865 --- /dev/null +++ b/tests/python/move_a_pdf_file_and_confirm_its_absence.py @@ -0,0 +1,22 @@ +import os +import shutil +import pytest + +def test_move_pdf(): + """Test moving pdf2.pdf from tmp_upload to final.""" + src_dir = "tmp_upload" + dest_dir = "final" + file_name = "pdf2.pdf" + src_path = os.path.join(src_dir, file_name) + dest_path = os.path.join(dest_dir, file_name) + + os.makedirs(dest_dir, exist_ok=True) + + assert os.path.exists(src_path), "Source PDF does not exist!" + + shutil.move(src_path, dest_path) + + assert not os.path.exists(src_path), "File was not removed from source directory!" + assert os.path.exists(dest_path), "File was not moved to destination directory!" + + os.remove(dest_path) diff --git a/tests/python/move_all_mp4_files.py b/tests/python/move_all_mp4_files.py new file mode 100644 index 00000000..43f9cb1e --- /dev/null +++ b/tests/python/move_all_mp4_files.py @@ -0,0 +1,26 @@ +import os +import shutil + +def test_move_videos(): + """Test moving all .mp4 files from tmp_upload to final and verifying their absence.""" + src_dir = "tmp_upload" + dest_dir = "final" + video_files = ["video1.mp4", "video2.mp4", "video3.mp4"] + + os.makedirs(dest_dir, exist_ok=True) + + for video in video_files: + src_path = os.path.join(src_dir, video) + dest_path = os.path.join(dest_dir, video) + + assert os.path.exists(src_path), f"Source file {video} does not exist!" + shutil.move(src_path, dest_path) + assert not os.path.exists(src_path), f"{video} was not removed from source directory!" + assert os.path.exists(dest_path), f"{video} was not moved to destination directory!" + + os.remove(dest_path) + +# Undo step: Clean up created test file or restore previous state + # Uncomment the next line to actually remove the file after testing + # os.remove(doc_path) + print("Undo: Cleaned up test artifacts or restored previous state.") diff --git a/tests/python/move_an_image_file.py b/tests/python/move_an_image_file.py new file mode 100644 index 00000000..fb7ac6c6 --- /dev/null +++ b/tests/python/move_an_image_file.py @@ -0,0 +1,27 @@ +import os +import shutil +import pytest + +def test_move_image(): + """Test moving img1.jpg from tmp_upload to final and verifying its absence in source.""" + src_dir = "tmp_upload" + dest_dir = "final" + file_name = "img1.jpg" + src_path = os.path.join(src_dir, file_name) + dest_path = os.path.join(dest_dir, file_name) + + os.makedirs(dest_dir, exist_ok=True) + + assert os.path.exists(src_path), "Source image does not exist!" + + shutil.move(src_path, dest_path) + + assert not os.path.exists(src_path), "File was not removed from source directory!" + assert os.path.exists(dest_path), "File was not moved to destination directory!" + + os.remove(dest_path) + + # Undo step: Clean up created test file or restore previous state + # Uncomment the next line to actually remove the file after testing + # os.remove(doc_path) + print("Undo: Cleaned up test artifacts or restored previous state.") \ No newline at end of file diff --git a/tests/python/rename_an_image_file.py b/tests/python/rename_an_image_file.py new file mode 100644 index 00000000..2b3af9f1 --- /dev/null +++ b/tests/python/rename_an_image_file.py @@ -0,0 +1,19 @@ +import os +import pytest + +def test_rename_image(): + """Test renaming img3.jpg to renamed_img3.jpg.""" + img_dir = "tmp_upload" + old_name = "img3.jpg" + new_name = "renamed_img3.jpg" + old_path = os.path.join(img_dir, old_name) + new_path = os.path.join(img_dir, new_name) + + assert os.path.exists(old_path), "Original image does not exist!" + + os.rename(old_path, new_path) + + assert not os.path.exists(old_path), "Old file still exists!" + assert os.path.exists(new_path), "New file was not created!" + + os.rename(new_path, old_path) # Revert to original state diff --git a/tests/python/verify_doc_file_exists_and_is_not_empty.py b/tests/python/verify_doc_file_exists_and_is_not_empty.py new file mode 100644 index 00000000..87e9f233 --- /dev/null +++ b/tests/python/verify_doc_file_exists_and_is_not_empty.py @@ -0,0 +1,18 @@ +import os + +def test_verify_doc(): + """Test if text1.doc exists, is not empty, and has a valid .doc extension.""" + doc_path = "tmp_upload/text1.doc" + + # Check if file exists + assert os.path.exists(doc_path), f"Error: {doc_path} does not exist!" + + # Check if file is not empty + file_size = os.path.getsize(doc_path) + assert file_size > 0, f"Error: {doc_path} is empty!" + + # Additional check: Ensure it has a .doc extension + assert doc_path.lower().endswith(".doc"), f"Error: {doc_path} does not have a .doc extension!" + + print(f"Test passed: {doc_path} exists, is not empty ({file_size} bytes), and has a valid extension.") + diff --git a/tests/python/verify_image_files_exist_and_are_valid.py b/tests/python/verify_image_files_exist_and_are_valid.py new file mode 100644 index 00000000..e9ba2b1e --- /dev/null +++ b/tests/python/verify_image_files_exist_and_are_valid.py @@ -0,0 +1,25 @@ +import os +import pytest + +def test_verify_image_files(): + """Test Case 6: Verify that all JPG files exist and are valid images.""" + img_files = ["img1.jpg", "img2.jpg", "img3.jpg", "img4.jpg"] + img_dir = "tmp_upload" + + for img in img_files: + img_path = os.path.join(img_dir, img) + + # Step 1: Check if file exists + assert os.path.exists(img_path), f"Image {img} does not exist!" + + # Step 2: Attempt to open and verify the image + try: + with Image.open(img_path) as im: # type: ignore + im.verify() # Verify image integrity + except Exception as e: + pytest.fail(f"Invalid image file {img}: {e}") + + print("All image files exist and are valid.") + + # Undo step: No destructive operations performed, no cleanup needed + print("Undo: No actions required for image validation.") diff --git a/tests/python/verify_pdf_file_can_be_opened_and_read.py b/tests/python/verify_pdf_file_can_be_opened_and_read.py new file mode 100644 index 00000000..dc87e512 --- /dev/null +++ b/tests/python/verify_pdf_file_can_be_opened_and_read.py @@ -0,0 +1,13 @@ +import os +import pytest +from PyPDF2 import PdfReader + +def test_read_pdf(): + """Test if a PDF file can be opened and read.""" + pdf_path = "tmp_upload/pdf1.pdf" + + assert os.path.exists(pdf_path), "PDF file does not exist!" + + with open(pdf_path, "rb") as f: + reader = PdfReader(f) + assert len(reader.pages) > 0, "PDF file has no pages!" diff --git a/tests/python/verify_video_file_integrity_after_copying.py b/tests/python/verify_video_file_integrity_after_copying.py new file mode 100644 index 00000000..78024593 --- /dev/null +++ b/tests/python/verify_video_file_integrity_after_copying.py @@ -0,0 +1,23 @@ +import os +import shutil +import pytest + +def test_copy_video_integrity(): + """Test copying a video file and verifying its integrity.""" + src_path = "tmp_upload/video1.mp4" + dest_path = "final/video1.mp4" + + os.makedirs("final", exist_ok=True) + + assert os.path.exists(src_path), "Source video does not exist!" + + shutil.copy(src_path, dest_path) + + assert os.path.exists(dest_path), "Copied video does not exist in final!" + + # Compare file sizes + assert os.path.getsize(src_path) == os.path.getsize(dest_path), "File sizes do not match!" + + # Compare first 1KB of content + with open(src_path, "rb") as src_file, open(dest_path, "rb") as dest_file: + assert src_file.read(1024) == dest_file.read(1024), "File contents differ!"