diff --git a/filesystem/squashfs/directoryentry.go b/filesystem/squashfs/directoryentry.go index b39385f1..760bcf0a 100644 --- a/filesystem/squashfs/directoryentry.go +++ b/filesystem/squashfs/directoryentry.go @@ -1,6 +1,8 @@ package squashfs import ( + "fmt" + "io/fs" "os" "time" ) @@ -129,3 +131,30 @@ func (d *directoryEntry) GID() uint32 { func (d *directoryEntry) Xattrs() map[string]string { return d.xattrs } + +// Readlink returns the destination of the symbolic link if this entry +// is a symbolic link. +// +// If this entry is not a symbolic link then it will return fs.ErrNotExist +func (d *directoryEntry) Readlink() (string, error) { + var target string + body := d.inode.getBody() + //nolint:exhaustive // all other cases fall under default + switch d.inode.inodeType() { + case inodeBasicSymlink: + link, ok := body.(*basicSymlink) + if !ok { + return "", fmt.Errorf("internal error: inode wasn't basic symlink: %T", body) + } + target = link.target + case inodeExtendedSymlink: + link, ok := body.(*extendedSymlink) + if !ok { + return "", fmt.Errorf("internal error: inode wasn't extended symlink: %T", body) + } + target = link.target + default: + return "", fs.ErrNotExist + } + return target, nil +} diff --git a/filesystem/squashfs/squashfs_test.go b/filesystem/squashfs/squashfs_test.go index 4d53c754..75c82099 100644 --- a/filesystem/squashfs/squashfs_test.go +++ b/filesystem/squashfs/squashfs_test.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "fmt" "io" + stdfs "io/fs" "os" "path" "strconv" @@ -357,14 +358,36 @@ func TestSquashfsCheckListing(t *testing.T) { if fi.IsDir() { wantMode |= os.ModeDir } + var wantTarget string switch p { - case "/symlink", "/goodlink", "/emptylink": + case "/goodlink": + wantTarget = "README.md" + wantMode |= os.ModeSymlink + case "/emptylink": + wantTarget = "/a/b/c/d/ef/g/h" wantMode |= os.ModeSymlink } gotMode := fi.Mode() if (gotMode & os.ModeType) != wantMode { t.Errorf("%s: want mode 0o%o got mode 0o%o", p, wantMode, gotMode&os.ModeType) } + fix, ok := fi.Sys().(squashfs.FileStat) + if !ok { + t.Fatal("Wrong type") + } + gotTarget, err := fix.Readlink() + if wantTarget == "" { + if err != stdfs.ErrNotExist { + t.Errorf("%s: ReadLink want error %q got error %q", p, stdfs.ErrNotExist, err) + } + } else { + if err != nil { + t.Errorf("%s: ReadLink returned error: %v", p, err) + } + if wantTarget != gotTarget { + t.Errorf("%s: ReadLink want target %q got target %q", p, wantTarget, gotTarget) + } + } } }