Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maintain metadata checksums and verify on unlink #388

Merged
merged 4 commits into from
Jul 20, 2023

Conversation

passaro
Copy link
Contributor

@passaro passaro commented Jul 18, 2023

Description of change

Adds a checksum to each inode computed on the inode number and the full key. On unlink, verifies the checksum before deleting the object from the bucket.

Does this change impact existing behavior?

No impact on the happy path. Unlink could fail if inode metadata gets corrupted.


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and I agree to the terms of the Developer Certificate of Origin (DCO).

Signed-off-by: Alessandro Passaro <alexpax@amazon.co.uk>
@passaro passaro temporarily deployed to PR integration tests July 18, 2023 14:08 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 18, 2023 14:08 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 18, 2023 14:08 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 18, 2023 14:08 — with GitHub Actions Inactive
@@ -804,6 +804,7 @@ impl From<InodeError> for i32 {
InodeError::CannotRemoveRemoteDirectory(_) => libc::EPERM,
InodeError::DirectoryNotEmpty(_) => libc::ENOTEMPTY,
InodeError::UnlinkNotPermittedWhileWriting(_) => libc::EPERM,
InodeError::CorruptedMetadata(_, _) => libc::EPERM,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if there is a more fitting error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe EIO is slightly more appropriate, but it's pretty niche either way.

@passaro passaro marked this pull request as ready for review July 18, 2023 14:44
Copy link
Member

@jamesbornholt jamesbornholt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat! Just a couple of suggestions.

@@ -804,6 +804,7 @@ impl From<InodeError> for i32 {
InodeError::CannotRemoveRemoteDirectory(_) => libc::EPERM,
InodeError::DirectoryNotEmpty(_) => libc::ENOTEMPTY,
InodeError::UnlinkNotPermittedWhileWriting(_) => libc::EPERM,
InodeError::CorruptedMetadata(_, _) => libc::EPERM,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe EIO is slightly more appropriate, but it's pretty niche either way.

.expect("file should exist");

// Inject a key mutation in the Inode.
// SAFETY: Inode and String not accessed concurrently. Mutated string is still valid utf8.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually undefined behavior and so not safe (mutating data to which a shared reference exists is always UB). The risk here is low in practice — the way this would go wrong is if an extremely smart compiler previously cached the full_key in a register somewhere, which would be OK because it's a shared immutable field, and so didn't observe this mutation to the key the next time it read it. But in theory the compiler is free to do whatever it wants with the entire program when it sees this.

As an alternative, maybe you could manually construct a new Inode struct here as a copy of the existing one, and then overwrite it in the right places (the superblock and the parent of this inode)? It's a lot more coupled to the internals, but maybe unavoidable.

@@ -360,6 +360,10 @@ impl Superblock {
return Err(InodeError::IsDirectory(inode.ino()));
}

if !inode.verify_checksum() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just make self.inner.get do this check every time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe. And/or in self.inner.lookup (or it would miss unlink for example)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in latest commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe instead we should verify the checksum when we get the full key?

Signed-off-by: Alessandro Passaro <alexpax@amazon.co.uk>
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 11:06 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 11:06 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 11:07 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 11:07 — with GitHub Actions Inactive
Signed-off-by: Alessandro Passaro <alexpax@amazon.co.uk>
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 14:07 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 14:07 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 14:07 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 14:07 — with GitHub Actions Inactive
@passaro passaro added the performance PRs to run benchmarks on label Jul 19, 2023
@passaro passaro temporarily deployed to PR benchmarks July 19, 2023 14:08 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR benchmarks July 19, 2023 14:08 — with GitHub Actions Inactive
.read()
.unwrap()
.get(&ino)
.cloned()
.ok_or(InodeError::InodeDoesNotExist(ino))
.ok_or(InodeError::InodeDoesNotExist(ino))?;
inode.verify_checksum()?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also check that inode.ino() == ino here to be safe.

Signed-off-by: Alessandro Passaro <alexpax@amazon.co.uk>
@passaro passaro temporarily deployed to PR benchmarks July 19, 2023 22:20 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 22:20 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 22:20 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 22:20 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR benchmarks July 19, 2023 22:20 — with GitHub Actions Inactive
@passaro passaro temporarily deployed to PR integration tests July 19, 2023 22:20 — with GitHub Actions Inactive
}
}

fn verify_child(&self, expected_parent: InodeNo, expected_name: &str) -> Result<(), InodeError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@jamesbornholt jamesbornholt merged commit 5364fcc into awslabs:main Jul 20, 2023
17 checks passed
@passaro passaro deleted the metadata-checksums branch July 20, 2023 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance PRs to run benchmarks on
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants