From 06c372051dabd79c55793c48492412fc1c77c076 Mon Sep 17 00:00:00 2001 From: joshjennings98 Date: Fri, 1 Aug 2025 15:09:59 +0100 Subject: [PATCH 1/3] IsFile should return true for special files --- utils/filesystem/files.go | 14 ++++++++++- utils/filesystem/files_test.go | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/utils/filesystem/files.go b/utils/filesystem/files.go index e8a7787cf6..6fb414fbc0 100644 --- a/utils/filesystem/files.go +++ b/utils/filesystem/files.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "io" + "io/fs" "os" "os/user" "path/filepath" @@ -777,7 +778,7 @@ func (fs *VFS) IsFile(path string) (result bool, err error) { if err != nil { return } - result = IsRegularFile(fi) + result = IsRegularFile(fi) || IsSpecialFile(fi) return } @@ -788,6 +789,17 @@ func IsRegularFile(fi os.FileInfo) bool { return fi.Mode().IsRegular() } +func IsSpecialFile(fi os.FileInfo) bool { + if fi == nil { + return false + } + mode := fi.Mode() + isSocket := mode&fs.ModeSocket != 0 + isDevice := mode&fs.ModeDevice != 0 + isNamedPipe := mode&fs.ModeNamedPipe != 0 + return isSocket || isDevice || isNamedPipe +} + func (fs *VFS) IsLink(path string) (result bool, err error) { err = fs.checkWhetherUnderlyingResourceIsClosed() if err != nil { diff --git a/utils/filesystem/files_test.go b/utils/filesystem/files_test.go index cd5c92ff44..d036512691 100644 --- a/utils/filesystem/files_test.go +++ b/utils/filesystem/files_test.go @@ -9,6 +9,7 @@ import ( "context" "fmt" "io" + "net" "os" "path/filepath" "reflect" @@ -22,6 +23,7 @@ import ( "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/sys/unix" "github.com/ARM-software/golang-utils/utils/collection" "github.com/ARM-software/golang-utils/utils/commonerrors" @@ -546,6 +548,50 @@ func TestConvertPaths(t *testing.T) { } } +func TestIsFile(t *testing.T) { + if platform.IsWindows() { + t.Skip("Windows doesn't have features such as named pipes or unix sockets") + } + for _, fsType := range FileSystemTypes { + t.Run(fmt.Sprint(fsType), func(t *testing.T) { + fs := NewFs(fsType) + + if fsType == InMemoryFS { + t.Skip("In-memory file system won't have hardware devices or special files") + } + + b, err := fs.IsFile("/dev/null") + require.NoError(t, err) + assert.True(t, b) + + tmpDir := t.TempDir() + + fifoPath := filepath.Join(tmpDir, faker.Word()) + require.NoError(t, err) + defer func() { _ = fs.Rm(fifoPath) }() + err = unix.Mkfifo(fifoPath, 0666) + require.NoError(t, err) + b, err = fs.IsFile(fifoPath) + require.NoError(t, err) + assert.True(t, b) + err = fs.Rm(fifoPath) + require.NoError(t, err) + + socketPath := filepath.Join(tmpDir, faker.Word()) + require.NoError(t, err) + defer func() { _ = fs.Rm(socketPath) }() + l, err := net.Listen("unix", socketPath) + require.NoError(t, err) + defer func() { _ = l.Close() }() + b, err = fs.IsFile(socketPath) + require.NoError(t, err) + assert.True(t, b) + err = fs.Rm(socketPath) + require.NoError(t, err) + }) + } +} + func TestLink(t *testing.T) { if platform.IsWindows() { fmt.Println("In order to run TestLink on Windows, Developer mode must be enabled: https://github.com/golang/go/pull/24307") From e0f9084f2d29e9f6d195ff195b2381c27e0e1934 Mon Sep 17 00:00:00 2001 From: joshjennings98 Date: Fri, 1 Aug 2025 15:11:04 +0100 Subject: [PATCH 2/3] :bug: `filsystem` IsFile should return true for special unix files not just regular files --- changes/20250801151058.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/20250801151058.bugfix diff --git a/changes/20250801151058.bugfix b/changes/20250801151058.bugfix new file mode 100644 index 0000000000..d17d19e1b8 --- /dev/null +++ b/changes/20250801151058.bugfix @@ -0,0 +1 @@ +:bug: `filsystem` IsFile should return true for special unix files not just regular files From 9458e60e150962662681ca558052bbbc204e6bf9 Mon Sep 17 00:00:00 2001 From: joshjennings98 Date: Fri, 1 Aug 2025 15:29:08 +0100 Subject: [PATCH 3/3] review --- changes/20250801151058.bugfix | 2 +- utils/filesystem/files.go | 3 +- utils/filesystem/files_test.go | 67 ++++++++++++++++++++-------------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/changes/20250801151058.bugfix b/changes/20250801151058.bugfix index d17d19e1b8..b2a6baf5e6 100644 --- a/changes/20250801151058.bugfix +++ b/changes/20250801151058.bugfix @@ -1 +1 @@ -:bug: `filsystem` IsFile should return true for special unix files not just regular files +:bug: `filesystem` IsFile should return true for special unix files not just regular files diff --git a/utils/filesystem/files.go b/utils/filesystem/files.go index 6fb414fbc0..074c1cf60f 100644 --- a/utils/filesystem/files.go +++ b/utils/filesystem/files.go @@ -778,7 +778,7 @@ func (fs *VFS) IsFile(path string) (result bool, err error) { if err != nil { return } - result = IsRegularFile(fi) || IsSpecialFile(fi) + result = IsRegularFile(fi) || IsSpecialFile(fi) // We want to treat special files as files (otherwise the only distinction is a directory which special files are not) return } @@ -789,6 +789,7 @@ func IsRegularFile(fi os.FileInfo) bool { return fi.Mode().IsRegular() } +// IsSpecialFile returns true for files that are not regular files. This includes: unix socket files, device files, and named pipes func IsSpecialFile(fi os.FileInfo) bool { if fi == nil { return false diff --git a/utils/filesystem/files_test.go b/utils/filesystem/files_test.go index d036512691..f3e320a095 100644 --- a/utils/filesystem/files_test.go +++ b/utils/filesystem/files_test.go @@ -556,38 +556,49 @@ func TestIsFile(t *testing.T) { t.Run(fmt.Sprint(fsType), func(t *testing.T) { fs := NewFs(fsType) - if fsType == InMemoryFS { - t.Skip("In-memory file system won't have hardware devices or special files") - } + tmpDir := t.TempDir() - b, err := fs.IsFile("/dev/null") - require.NoError(t, err) - assert.True(t, b) + t.Run("normal file", func(t *testing.T) { + filePath := filepath.Join(tmpDir, faker.Word()) + err := fs.Touch(filePath) + require.NoError(t, err) + b, err := fs.IsFile(filePath) + require.NoError(t, err) + assert.True(t, b) + }) - tmpDir := t.TempDir() + t.Run("special file", func(t *testing.T) { + if fsType == InMemoryFS { + t.Skip("In-memory file system won't have hardware devices or special files") + } - fifoPath := filepath.Join(tmpDir, faker.Word()) - require.NoError(t, err) - defer func() { _ = fs.Rm(fifoPath) }() - err = unix.Mkfifo(fifoPath, 0666) - require.NoError(t, err) - b, err = fs.IsFile(fifoPath) - require.NoError(t, err) - assert.True(t, b) - err = fs.Rm(fifoPath) - require.NoError(t, err) + b, err := fs.IsFile("/dev/null") + require.NoError(t, err) + assert.True(t, b) - socketPath := filepath.Join(tmpDir, faker.Word()) - require.NoError(t, err) - defer func() { _ = fs.Rm(socketPath) }() - l, err := net.Listen("unix", socketPath) - require.NoError(t, err) - defer func() { _ = l.Close() }() - b, err = fs.IsFile(socketPath) - require.NoError(t, err) - assert.True(t, b) - err = fs.Rm(socketPath) - require.NoError(t, err) + fifoPath := filepath.Join(tmpDir, faker.Word()) + require.NoError(t, err) + defer func() { _ = fs.Rm(fifoPath) }() + err = unix.Mkfifo(fifoPath, 0666) + require.NoError(t, err) + b, err = fs.IsFile(fifoPath) + require.NoError(t, err) + assert.True(t, b) + err = fs.Rm(fifoPath) + require.NoError(t, err) + + socketPath := filepath.Join(tmpDir, faker.Word()) + require.NoError(t, err) + defer func() { _ = fs.Rm(socketPath) }() + l, err := net.Listen("unix", socketPath) + require.NoError(t, err) + defer func() { _ = l.Close() }() + b, err = fs.IsFile(socketPath) + require.NoError(t, err) + assert.True(t, b) + err = fs.Rm(socketPath) + require.NoError(t, err) + }) }) } }