-
Notifications
You must be signed in to change notification settings - Fork 238
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
composefs integration in the overlay driver #1646
Changes from all commits
7a5f2be
8bb5a08
a50bb95
4cb91e2
1cc3e7a
6b10c1a
1c76934
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
//go:build !linux || !composefs || !cgo | ||
// +build !linux !composefs !cgo | ||
|
||
package overlay | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
func composeFsSupported() bool { | ||
return false | ||
} | ||
|
||
func generateComposeFsBlob(toc []byte, destFile string) error { | ||
return fmt.Errorf("composefs is not supported") | ||
} | ||
|
||
func mountErofsBlob(blobFile, mountPoint string) error { | ||
return fmt.Errorf("composefs is not supported") | ||
} | ||
|
||
func enableVerityRecursive(path string) error { | ||
return fmt.Errorf("composefs is not supported") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
//go:build linux && composefs && cgo | ||
// +build linux,composefs,cgo | ||
|
||
package overlay | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"sync" | ||
"syscall" | ||
"unsafe" | ||
|
||
"github.com/containers/storage/pkg/loopback" | ||
"github.com/sirupsen/logrus" | ||
"golang.org/x/sys/unix" | ||
) | ||
|
||
var ( | ||
composeFsHelperOnce sync.Once | ||
composeFsHelperPath string | ||
composeFsHelperErr error | ||
) | ||
|
||
func getComposeFsHelper() (string, error) { | ||
composeFsHelperOnce.Do(func() { | ||
composeFsHelperPath, composeFsHelperErr = exec.LookPath("composefs-from-json") | ||
}) | ||
return composeFsHelperPath, composeFsHelperErr | ||
} | ||
|
||
func composeFsSupported() bool { | ||
_, err := getComposeFsHelper() | ||
return err == nil | ||
} | ||
|
||
func enableVerity(description string, fd int) error { | ||
enableArg := unix.FsverityEnableArg{ | ||
Version: 1, | ||
Hash_algorithm: unix.FS_VERITY_HASH_ALG_SHA256, | ||
Block_size: 4096, | ||
} | ||
|
||
_, _, e1 := syscall.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.FS_IOC_ENABLE_VERITY), uintptr(unsafe.Pointer(&enableArg))) | ||
if e1 != 0 && !errors.Is(e1, unix.EEXIST) { | ||
return fmt.Errorf("failed to enable verity for %q: %w", description, e1) | ||
} | ||
return nil | ||
} | ||
|
||
func enableVerityRecursive(path string) error { | ||
walkFn := func(path string, d fs.DirEntry, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
if !d.Type().IsRegular() { | ||
return nil | ||
} | ||
|
||
f, err := os.Open(path) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
|
||
if err := enableVerity(path, int(f.Fd())); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
return filepath.WalkDir(path, walkFn) | ||
} | ||
|
||
func generateComposeFsBlob(toc []byte, destFile string) error { | ||
writerJson, err := getComposeFsHelper() | ||
if err != nil { | ||
return fmt.Errorf("failed to find composefs-from-json: %w", err) | ||
} | ||
|
||
fd, err := unix.Openat(unix.AT_FDCWD, destFile, unix.O_WRONLY|unix.O_CREAT|unix.O_TRUNC|unix.O_EXCL|unix.O_CLOEXEC, 0o644) | ||
if err != nil { | ||
return fmt.Errorf("failed to open output file: %w", err) | ||
} | ||
outFd := os.NewFile(uintptr(fd), "outFd") | ||
|
||
fd, err = unix.Open(fmt.Sprintf("/proc/self/fd/%d", outFd.Fd()), unix.O_RDONLY|unix.O_CLOEXEC, 0) | ||
if err != nil { | ||
outFd.Close() | ||
return fmt.Errorf("failed to dup output file: %w", err) | ||
giuseppe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
newFd := os.NewFile(uintptr(fd), "newFd") | ||
defer newFd.Close() | ||
|
||
err = func() error { | ||
// a scope to close outFd before setting fsverity on the read-only fd. | ||
defer outFd.Close() | ||
|
||
cmd := exec.Command(writerJson, "--format=erofs", "--out=/proc/self/fd/3", "/proc/self/fd/0") | ||
cmd.ExtraFiles = []*os.File{outFd} | ||
cmd.Stderr = os.Stderr | ||
cmd.Stdin = bytes.NewReader(toc) | ||
if err := cmd.Run(); err != nil { | ||
return fmt.Errorf("failed to convert json to erofs: %w", err) | ||
} | ||
return nil | ||
}() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := enableVerity("manifest file", int(newFd.Fd())); err != nil && !errors.Is(err, unix.ENOTSUP) && !errors.Is(err, unix.ENOTTY) { | ||
logrus.Warningf("%s", err) | ||
giuseppe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
return nil | ||
} | ||
|
||
func mountErofsBlob(blobFile, mountPoint string) error { | ||
giuseppe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
loop, err := loopback.AttachLoopDevice(blobFile) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not setting the loopback device read-only. I think we should do that. (composefs does) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it should enable direct-io to avoid caching metadata twice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And, ideally it should pass "noacl" if the composefs header flag that says there are no acls in the file is set (this will improve performance quite a lot). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eh, noacl is in the mount options below, not the loop properties. |
||
if err != nil { | ||
return err | ||
} | ||
defer loop.Close() | ||
|
||
return unix.Mount(loop.Name(), mountPoint, "erofs", unix.MS_RDONLY, "ro") | ||
} |
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 should silently accept EEXIST (fs-verity already enabled for the file).
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 mean, couldn't this happen if a layer file is hard-linked from another layer?
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.
yes, this could happen with hard link deduplication