cmd/nardump: support symlinks, add basic test

Signed-off-by: phanirithvij <phanirithvij2000@gmail.com>
This commit is contained in:
phanirithvij 2025-03-27 17:32:25 +05:30 committed by Brad Fitzpatrick
parent 0655dd7b3d
commit ad2b075d4f
2 changed files with 73 additions and 5 deletions

View File

@ -100,14 +100,13 @@ func (nw *narWriter) writeDir(dirPath string) error {
sub := path.Join(dirPath, ent.Name()) sub := path.Join(dirPath, ent.Name())
var err error var err error
switch { switch {
case mode.IsRegular():
err = nw.writeRegular(sub)
case mode.IsDir(): case mode.IsDir():
err = nw.writeDir(sub) err = nw.writeDir(sub)
case mode.IsRegular():
err = nw.writeRegular(sub)
case mode&os.ModeSymlink != 0:
err = nw.writeSymlink(sub)
default: default:
// TODO(bradfitz): symlink, but requires fighting io/fs a bit
// to get at Readlink or the osFS via fs. But for now
// we don't need symlinks because they're not in Go's archive.
return fmt.Errorf("unsupported file type %v at %q", sub, mode) return fmt.Errorf("unsupported file type %v at %q", sub, mode)
} }
if err != nil { if err != nil {
@ -143,6 +142,23 @@ func (nw *narWriter) writeRegular(path string) error {
return nil return nil
} }
func (nw *narWriter) writeSymlink(path string) error {
nw.str("(")
nw.str("type")
nw.str("symlink")
nw.str("target")
// broken symlinks are valid in a nar
// given we do os.chdir(dir) and os.dirfs(".") above
// readlink now resolves relative links even if they are broken
link, err := os.Readlink(path)
if err != nil {
return err
}
nw.str(link)
nw.str(")")
return nil
}
func (nw *narWriter) str(s string) { func (nw *narWriter) str(s string) {
if err := writeString(nw.w, s); err != nil { if err := writeString(nw.w, s); err != nil {
panic(writeNARError{err}) panic(writeNARError{err})

View File

@ -0,0 +1,52 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"crypto/sha256"
"fmt"
"os"
"runtime"
"testing"
)
// setupTmpdir sets up a known golden layout, covering all allowed file/folder types in a nar
func setupTmpdir(t *testing.T) string {
tmpdir := t.TempDir()
pwd, _ := os.Getwd()
os.Chdir(tmpdir)
defer os.Chdir(pwd)
os.MkdirAll("sub/dir", 0755)
os.Symlink("brokenfile", "brokenlink")
os.Symlink("sub/dir", "dirl")
os.Symlink("/abs/nonexistentdir", "dirb")
os.Create("sub/dir/file1")
f, _ := os.Create("file2m")
_ = f.Truncate(2 * 1024 * 1024)
f.Close()
os.Symlink("../file2m", "sub/goodlink")
return tmpdir
}
func TestWriteNar(t *testing.T) {
if runtime.GOOS == "windows" {
// Skip test on Windows as the Nix package manager is not supported on this platform
t.Skip("nix package manager is not available on Windows")
}
dir := setupTmpdir(t)
t.Run("nar", func(t *testing.T) {
// obtained via `nix-store --dump /tmp/... | sha256sum` of the above test dir
expected := "727613a36f41030e93a4abf2649c3ec64a2757ccff364e3f6f7d544eb976e442"
h := sha256.New()
os.Chdir(dir)
err := writeNAR(h, os.DirFS("."))
if err != nil {
t.Fatal(err)
}
hash := fmt.Sprintf("%x", h.Sum(nil))
if expected != hash {
t.Fatal("sha256sum of nar not matched", hash, expected)
}
})
}