Repository: diskfs/go-diskfs
Pull request #278 (2025-01-23 19:42:36)

fix: dont include xattr in extended link reading
Got an error when trying to read another squashfs file from ubuntu. Not sure if I should have created an issue first. It was raising [this](https://github.com/diskfs/go-diskfs/blob/master/filesystem/squashfs/xattr.go#L23) error. Traced it down to a symlink reading the wrong part for the xattr.

deitch: Quite the catch. Thank you for tracking it down.

Patch:
diff --git a/filesystem/squashfs/inode.go b/filesystem/squashfs/inode.go
index a08852b..96b3c13 100644
--- a/filesystem/squashfs/inode.go
+++ b/filesystem/squashfs/inode.go
@@ -639,11 +639,12 @@ func parseExtendedSymlink(b []byte) (*extendedSymlink, int, error) {
 	s := &extendedSymlink{
 		links: binary.LittleEndian.Uint32(b[0:4]),
 	}
+	targetSize := int(binary.LittleEndian.Uint32(b[4:8]))
 	// account for the synlink target, plus 4 bytes for the xattr index after it
-	extra = int(binary.LittleEndian.Uint32(b[4:8])) + 4
-	if len(b[target:]) > extra {
-		s.target = string(b[8 : 8+extra])
-		s.xAttrIndex = binary.LittleEndian.Uint32(b[8+extra : 8+extra+4])
+	extra = targetSize + 4
+	if len(b) >= extra+target {
+		s.target = string(b[target : target+targetSize])
+		s.xAttrIndex = binary.LittleEndian.Uint32(b[target+targetSize : target+targetSize+4])
 		extra = 0
 	}
 	return s, extra, nil
diff --git a/filesystem/squashfs/squashfs.go b/filesystem/squashfs/squashfs.go
index f41e84c..3e23c13 100644
--- a/filesystem/squashfs/squashfs.go
+++ b/filesystem/squashfs/squashfs.go
@@ -532,7 +532,7 @@ func (fs *FileSystem) hydrateDirectoryEntries(entries []*directoryEntryRaw) ([]*
 		body, header := in.getBody(), in.getHeader()
 		xattrIndex, has := body.xattrIndex()
 		xattrs := map[string]string{}
-		if has && xattrIndex != noXattrInodeFlag {
+		if has {
 			xattrs, err = fs.xattrs.find(int(xattrIndex))
 			if err != nil {
 				return nil, fmt.Errorf("error reading xattrs for %s: %v", e.name, err)


Test patch:
diff --git a/filesystem/squashfs/inode_internal_test.go b/filesystem/squashfs/inode_internal_test.go
index 5bef3f9..4891003 100644
--- a/filesystem/squashfs/inode_internal_test.go
+++ b/filesystem/squashfs/inode_internal_test.go
@@ -2,6 +2,7 @@ package squashfs
 
 import (
 	"bytes"
+	"encoding/binary"
 	"fmt"
 	"strings"
 	"testing"
@@ -383,13 +384,64 @@ func TestBasicSymlink(t *testing.T) {
 	})
 }
 
-//nolint:unused,revive // keep for future when we implement it and will need t
 func TestExtendedSymlink(t *testing.T) {
-	// when we have more data with which to work
+	s := &extendedSymlink{
+		links:      1,
+		target:     "/a/b/c/d/ef/g/h",
+		xAttrIndex: 46,
+	}
+	b, err := testGetInodeMetabytes()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	inodeB := binary.LittleEndian.AppendUint32(b[testBasicSymlinkStart:testBasicSymlinkEnd], s.xAttrIndex)
+
+	t.Run("toBytes", func(t *testing.T) {
+		b := s.toBytes()
+		if !bytes.Equal(b, inodeB) {
+			t.Errorf("mismatched output, actual then expected")
+			t.Logf("% x", b)
+			t.Logf("% x", inodeB)
+		}
+	})
+	t.Run("Size", func(t *testing.T) {
+		size := s.size()
+		if size != 0 {
+			t.Errorf("mismatched sizes, actual %d expected %d", size, 0)
+		}
+	})
+
+	tests := []struct {
+		b   []byte
+		sym *extendedSymlink
+		ext int
+		err error
+	}{
+		{inodeB, s, 0, nil},
+		{inodeB[:7], nil, 0, fmt.Errorf("received %d bytes instead of expected minimal %d", 7, 8)},
+		{inodeB[:20], &extendedSymlink{links: 1}, 19, nil},
+	}
 
-	// func (i extendedSymlink) toBytes() []byte {
-	// func (i extendedSymlink) size() int64 {
-	// func parseExtendedSymlink(b []byte) (*extendedSymlink, error) {
+	t.Run("parse", func(t *testing.T) {
+		for i, tt := range tests {
+			sym, ext, err := parseExtendedSymlink(tt.b)
+			switch {
+			case (err == nil && tt.err != nil) || (err != nil && tt.err == nil) || (err != nil && tt.err != nil && !strings.HasPrefix(err.Error(), tt.err.Error())):
+				t.Errorf("%d: mismatched error, actual then expected", i)
+				t.Logf("%v", err)
+				t.Logf("%v", tt.err)
+			case tt.ext != ext:
+				t.Errorf("%d: mismatched extra, actual then expected", i)
+				t.Logf("%v", ext)
+				t.Logf("%v", tt.ext)
+			case (sym == nil && tt.sym != nil) || (sym != nil && tt.sym == nil) || (sym != nil && tt.sym != nil && *sym != *tt.sym):
+				t.Errorf("%d: mismatched results, actual then expected", i)
+				t.Logf("%#v", *sym)
+				t.Logf("%#v", *tt.sym)
+			}
+		}
+	})
 }
 
 //nolint:unused,revive // keep for future when we implement it and will need t