From 834cb0d40b55a542e88a75f5e732e7aa19b53d87 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Mon, 5 Jan 2026 04:00:13 +0900 Subject: internal/pkg: override "." for directory checksum This makes the checksum consistent with the final resting state of artifact directories without incurring the cost of an extra pair of chown syscalls. Signed-off-by: Ophestra --- internal/pkg/pkg.go | 70 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 6 deletions(-) (limited to 'internal/pkg/pkg.go') diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go index b880b496..5d5d768b 100644 --- a/internal/pkg/pkg.go +++ b/internal/pkg/pkg.go @@ -376,6 +376,63 @@ func removeAll(pathname *check.Absolute) (chmodErr, removeErr error) { return } +// overrideFileInfo overrides the permission bits of [fs.FileInfo] to 0500 and +// is the concrete type returned by overrideFile.Stat. +type overrideFileInfo struct{ fs.FileInfo } + +// Mode returns [fs.FileMode] with its permission bits set to 0500. +func (fi overrideFileInfo) Mode() fs.FileMode { + return fi.FileInfo.Mode()&(^fs.FileMode(0777)) | 0500 +} + +// Sys returns nil to avoid passing the original permission bits. +func (fi overrideFileInfo) Sys() any { return nil } + +// overrideFile overrides the permission bits of [fs.File] to 0500 and is the +// concrete type returned by dotOverrideFS for calls with "." passed as name. +type overrideFile struct{ fs.File } + +func (f overrideFile) Stat() (fi fs.FileInfo, err error) { + fi, err = f.File.Stat() + if err != nil { + return + } + fi = overrideFileInfo{fi} + return +} + +// dirFS is implemented by the concrete type of the return value of [os.DirFS]. +type dirFS interface { + fs.StatFS + fs.ReadFileFS + fs.ReadDirFS + fs.ReadLinkFS +} + +// dotOverrideFS overrides the permission bits of "." to 0500 to avoid the extra +// system calls to add and remove write bit from the target directory. +type dotOverrideFS struct{ dirFS } + +// Open wraps the underlying [fs.FS] with "." special case. +func (fsys dotOverrideFS) Open(name string) (f fs.File, err error) { + f, err = fsys.dirFS.Open(name) + if err != nil || name != "." { + return + } + f = overrideFile{f} + return +} + +// Stat wraps the underlying [fs.FS] with "." special case. +func (fsys dotOverrideFS) Stat(name string) (fi fs.FileInfo, err error) { + fi, err = fsys.dirFS.Stat(name) + if err != nil || name != "." { + return + } + fi = overrideFileInfo{fi} + return +} + // Cure cures the [Artifact] and returns its pathname and [Checksum]. func (c *Cache) Cure(a Artifact) ( pathname *check.Absolute, @@ -556,13 +613,11 @@ func (c *Cache) Cure(a Artifact) ( return } - // override this before hashing since it will be made read-only after - // the rename anyway so do not let perm bits affect the checksum - if err = os.Chmod(workPathname.String(), 0700); err != nil { - return - } var gotChecksum Checksum - if gotChecksum, err = HashDir(workPathname); err != nil { + if gotChecksum, err = HashFS( + dotOverrideFS{os.DirFS(workPathname.String()).(dirFS)}, + ".", + ); err != nil { return } @@ -583,6 +638,9 @@ func (c *Cache) Cure(a Artifact) ( } } + if err = os.Chmod(workPathname.String(), 0700); err != nil { + return + } c.checksumMu.Lock() if err = os.Rename( workPathname.String(), -- cgit v1.3.1