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

image-rs: Handle gzip whiteouts correctly #687

Merged
merged 2 commits into from
Aug 29, 2024

Conversation

squarti
Copy link
Contributor

@squarti squarti commented Aug 20, 2024

This PR readds whiteout handling on platforms that support xattr

Fixes: #604

@@ -60,6 +130,10 @@ pub async fn unpack<R: AsyncRead + Unpin>(input: R, destination: &Path) -> Resul
.try_into()
.context("GID is too large!")?;

if !convert_whiteout(&file.path()?, file.header(), destination)? {
Copy link
Member

Choose a reason for hiding this comment

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

I noticed that there are some duplicated logic between convert_whiteout and unpack after line 139. Could we do some refactoring upon this commit to avoid this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I will refactor it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Xynnn007 I pushed some changes. Is that what you have in mind?

@bpradipt
Copy link
Member

bpradipt commented Aug 21, 2024

@squarti @Xynnn007 are you aware of any tools that helps to inspect a container image to figure out if it contains gzip whiteouts?
Or any sample image that can be used for testing this PR?

@squarti
Copy link
Contributor Author

squarti commented Aug 21, 2024

@squarti @Xynnn007 are you aware of any tools that helps to inspect a container image to figure out if it contains gzip whiteouts? Or any sample image that can be used for testing this PR?

I am not aware of any tool. I used this image ghcr.io/actions/actions-runner:latest to test it

Copy link
Member

@Xynnn007 Xynnn007 left a comment

Choose a reason for hiding this comment

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

Thanks for the refactoring! Two last comments and other parts looks perfect to me

}

#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
use anyhow::anyhow;
Copy link
Member

Choose a reason for hiding this comment

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

Let's move this

use anyhow::anyhow;

directly inside convert_whiteout


#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
fn convert_whiteout(path: &Path, uid: u32, gid: u32, mode: Option<u32>, destination: &Path) -> Result<bool> {
Ok(true)
Copy link
Member

Choose a reason for hiding this comment

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

Probably we could use a shared convert_whiteout funciton. With cfg-if to firstly judge the platform and return Ok(true) if needed. Thus we do not need two convert_whiteout versions.

@mythi
Copy link
Contributor

mythi commented Aug 22, 2024

@squarti @Xynnn007 are you aware of any tools that helps to inspect a container image to figure out if it contains gzip whiteouts? Or any sample image that can be used for testing this PR?

I am not aware of any tool. I used this image ghcr.io/actions/actions-runner:latest to test it

FWIW, I've built images with whiteouts using the distroless tooling in the past. In case it's useful..

Copy link
Member

@Xynnn007 Xynnn007 left a comment

Choose a reason for hiding this comment

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

One last thing. Sorry I did not make it clear

mode: Option<u32>,
destination: &Path,
) -> Result<bool> {
if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") || cfg!(target_os = "macos") {
Copy link
Member

Choose a reason for hiding this comment

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

We can try to return ASAP to avoid big {..} block

if !cfg!(target_os = "linux") && !cfg!(target_os = "freebsd") && !cfg!(target_os = "macos") {
    return Ok(true)
}

... other logic

Copy link
Member

@Xynnn007 Xynnn007 left a comment

Choose a reason for hiding this comment

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

good. A rebase is needed

bail!("mknod: {:?} error: {:?}", path, io::Error::last_os_error());
}

set_perms_ownerships(&path, ChownType::LChown, uid, gid, mode).await?;
Copy link
Member

Choose a reason for hiding this comment

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

Do we need also to set_file_times() here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. Looking into the golang base implementation, set_file_times is not done.

The purpose of the whiteout file is to hide the original file. Even if we update the file times, it is not going to be visible in the file system.

@squarti squarti force-pushed the main branch 3 times, most recently from 2ad2ced to f65dd6d Compare August 28, 2024 14:47
Copy link
Member

@fitzthum fitzthum left a comment

Choose a reason for hiding this comment

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

Nice. A few comments. I guess this is mainly being ported from the Go code, but I'd like to understand a little better what's happening.

@@ -77,6 +129,11 @@ pub async fn unpack<R: AsyncRead + Unpin>(input: R, destination: &Path) -> Resul
.gid()?
.try_into()
.context("GID is too large!")?;
let mode = file.header().mode().ok();

if unpack_xattrs && !convert_whiteout(&file.path()?, uid, gid, mode, destination).await? {
Copy link
Member

Choose a reason for hiding this comment

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

I guess you might have inherited some of this code, but the logic here seems a bit odd to me. convert_whiteout doesn't necessarily convert a whiteout. It checks if we are pointing to a whiteout file and then converts it if needed. This results in a slightly clunky Result<bool> being returned.

What about making two functions. One called is_whiteout which returns Result<bool> and one called convert_whiteout which just returns a Result<()>

This seem like a bit of messing around but I think it will make this nicer.

original_path.display()
))?;

let ret = unsafe { nix::libc::mknod(path.as_ptr(), nix::libc::S_IFCHR, 0) };
Copy link
Member

Choose a reason for hiding this comment

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

I'm a little confused by this part. Is this a trick to get rid of a file from a lower layer at the same location as the whiteout? It might be good to put a comment here (and maybe also a higher-level one with some reference to a spec) since this is a somewhat unusual operation. Why not just delete the file at that path or am I totally off here?

Copy link
Member

@fitzthum fitzthum left a comment

Choose a reason for hiding this comment

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

This mknod thing is still a bit mysterious to me, but I guess we can live with it.

@squarti
Copy link
Contributor Author

squarti commented Aug 29, 2024

mknod creates a character device with 0/0 device number which hides the file in the lower layer. The lower layer cannot be changed since it is read only.

original_path.display()
))?;

let ret = unsafe { nix::libc::mknod(path.as_ptr(), nix::libc::S_IFCHR, 0) };
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure we use nix here, s.t. https://docs.rs/nix/0.29.0/nix/sys/stat/fn.mknod.html If we can, we will get code w/o unsafe parts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure this is what you are after.

@squarti squarti requested a review from a team as a code owner August 29, 2024 20:36
Signed-off-by: Silenio Quarti <[email protected]>
@fitzthum fitzthum merged commit 02af65a into confidential-containers:main Aug 29, 2024
7 checks passed
@fitzthum
Copy link
Member

Thanks @squarti

Let's keep an eye on image unpacking when we next bump guest-components just in case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

image-rs: Whiteout code causing problems with image pull?
5 participants