diff options
Diffstat (limited to 'internal/pkg/dir.go')
| -rw-r--r-- | internal/pkg/dir.go | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/internal/pkg/dir.go b/internal/pkg/dir.go new file mode 100644 index 00000000..794fdf5e --- /dev/null +++ b/internal/pkg/dir.go @@ -0,0 +1,68 @@ +package pkg + +import ( + "crypto/sha512" + "encoding/gob" + "io" + "io/fs" + "os" + + "hakurei.app/container/check" +) + +// FlatEntry is the representation of a directory entry via [Flatten]. +type FlatEntry struct { + Name string // base name of the file + Mode fs.FileMode // file mode bits + Data []byte // file content or symlink destination +} + +// Flatten writes a deterministic representation of the contents of fsys to w. +// The resulting data can be hashed to produce a deterministic checksum for the +// directory. +func Flatten(fsys fs.FS, root string, w io.Writer) error { + e := gob.NewEncoder(w) + return fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + var fi fs.FileInfo + fi, err = d.Info() + if err != nil { + return err + } + + ent := FlatEntry{ + Name: fi.Name(), + Mode: fi.Mode(), + } + if ent.Mode.IsRegular() { + if ent.Data, err = fs.ReadFile(fsys, path); err != nil { + return err + } + } else if ent.Mode&fs.ModeSymlink != 0 { + var newpath string + if newpath, err = fs.ReadLink(fsys, path); err != nil { + return err + } + ent.Data = []byte(newpath) + } + + return e.Encode(&ent) + }) +} + +// HashFS returns a checksum produced by hashing the result of [Flatten]. +func HashFS(fsys fs.FS, root string) (Checksum, error) { + h := sha512.New384() + if err := Flatten(fsys, root, h); err != nil { + return Checksum{}, err + } + return (Checksum)(h.Sum(nil)), nil +} + +// HashDir returns a checksum produced by hashing the result of [Flatten]. +func HashDir(pathname *check.Absolute) (Checksum, error) { + return HashFS(os.DirFS(pathname.String()), ".") +} |
