-
Notifications
You must be signed in to change notification settings - Fork 157
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
Conversation
Signed-off-by: Alessandro Passaro <alexpax@amazon.co.uk>
mountpoint-s3/src/fs.rs
Outdated
@@ -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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
mountpoint-s3/src/fs.rs
Outdated
@@ -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, |
There was a problem hiding this comment.
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.
mountpoint-s3/src/inode.rs
Outdated
.expect("file should exist"); | ||
|
||
// Inject a key mutation in the Inode. | ||
// SAFETY: Inode and String not accessed concurrently. Mutated string is still valid utf8. |
There was a problem hiding this comment.
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.
mountpoint-s3/src/inode.rs
Outdated
@@ -360,6 +360,10 @@ impl Superblock { | |||
return Err(InodeError::IsDirectory(inode.ino())); | |||
} | |||
|
|||
if !inode.verify_checksum() { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in latest commit.
There was a problem hiding this comment.
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>
Signed-off-by: Alessandro Passaro <alexpax@amazon.co.uk>
mountpoint-s3/src/inode.rs
Outdated
.read() | ||
.unwrap() | ||
.get(&ino) | ||
.cloned() | ||
.ok_or(InodeError::InodeDoesNotExist(ino)) | ||
.ok_or(InodeError::InodeDoesNotExist(ino))?; | ||
inode.verify_checksum()?; |
There was a problem hiding this comment.
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>
} | ||
} | ||
|
||
fn verify_child(&self, expected_parent: InodeNo, expected_name: &str) -> Result<(), InodeError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
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).