Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions fsx/fsx.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,41 @@ func ReadFile(path string) ([]byte, error) {

return nil, err
}

func FileExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}

if errors.Is(err, os.ErrNotExist) {
return false, nil
}

if errors.Is(err, os.ErrPermission) {
if isSnapProcess() {
return false, fmt.Errorf("%w: %s", ErrSnapPermission, path)
}

return false, fmt.Errorf("%w: %s", ErrPermission, path)
}

return false, err
}

func WriteFile(path string, data []byte, perm os.FileMode) error {
err := os.WriteFile(path, data, perm)
if err == nil {
return nil
}

if errors.Is(err, os.ErrPermission) {
if isSnapProcess() {
return fmt.Errorf("%w: %s", ErrSnapPermission, path)
}

return fmt.Errorf("%w: %s", ErrPermission, path)
}

return err
}
143 changes: 143 additions & 0 deletions fsx/fsx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,146 @@ func TestReadFile_PermissionDenied_Snap(t *testing.T) {
t.Errorf("expected ErrSnapPermission, got: %v", err)
}
}

func TestFileExists_Exists(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "test.txt")
if err := os.WriteFile(path, []byte("hello"), 0600); err != nil {
t.Fatalf("failed to create test file: %v", err)
}

exists, err := fsx.FileExists(path)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !exists {
t.Error("expected file to exist")
}
}

func TestFileExists_NotExists(t *testing.T) {
exists, err := fsx.FileExists("/nonexistent/path/file.txt")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if exists {
t.Error("expected file to not exist")
}
}

func TestFileExists_PermissionDenied_Generic(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("skipping permission test: running as root")
}

t.Setenv("SNAP", "")
t.Setenv("SNAP_NAME", "")
t.Setenv("SNAP_USER_DATA", "")

dir := t.TempDir()
if err := os.Chmod(dir, 0000); err != nil {
t.Fatalf("failed to chmod dir: %v", err)
}
t.Cleanup(func() { os.Chmod(dir, 0700) })

path := filepath.Join(dir, "file.txt")
_, err := fsx.FileExists(path)
if err == nil {
t.Fatal("expected error, got nil")
}
if !errors.Is(err, fsx.ErrPermission) {
t.Errorf("expected ErrPermission, got: %v", err)
}
}

func TestFileExists_PermissionDenied_Snap(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("skipping permission test: running as root")
}

t.Setenv("SNAP", "/snap/myapp/current")
t.Setenv("SNAP_NAME", "myapp")
t.Setenv("SNAP_USER_DATA", "")

dir := t.TempDir()
if err := os.Chmod(dir, 0000); err != nil {
t.Fatalf("failed to chmod dir: %v", err)
}
t.Cleanup(func() { os.Chmod(dir, 0700) })

path := filepath.Join(dir, "file.txt")
_, err := fsx.FileExists(path)
if err == nil {
t.Fatal("expected error, got nil")
}
if !errors.Is(err, fsx.ErrSnapPermission) {
t.Errorf("expected ErrSnapPermission, got: %v", err)
}
}

func TestWriteFile_Success(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "out.txt")

if err := fsx.WriteFile(path, []byte("world"), 0600); err != nil {
t.Fatalf("unexpected error: %v", err)
}

data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("failed to read back file: %v", err)
}
if string(data) != "world" {
t.Errorf("expected %q, got %q", "world", string(data))
}
}

func TestWriteFile_PermissionDenied_Generic(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("skipping permission test: running as root")
}

t.Setenv("SNAP", "")
t.Setenv("SNAP_NAME", "")
t.Setenv("SNAP_USER_DATA", "")

dir := t.TempDir()
if err := os.Chmod(dir, 0000); err != nil {
t.Fatalf("failed to chmod dir: %v", err)
}
t.Cleanup(func() { os.Chmod(dir, 0700) })

path := filepath.Join(dir, "out.txt")
err := fsx.WriteFile(path, []byte("data"), 0600)
if err == nil {
t.Fatal("expected error, got nil")
}
if !errors.Is(err, fsx.ErrPermission) {
t.Errorf("expected ErrPermission, got: %v", err)
}
}

func TestWriteFile_PermissionDenied_Snap(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("skipping permission test: running as root")
}

t.Setenv("SNAP", "/snap/myapp/current")
t.Setenv("SNAP_NAME", "myapp")
t.Setenv("SNAP_USER_DATA", "")

dir := t.TempDir()
if err := os.Chmod(dir, 0000); err != nil {
t.Fatalf("failed to chmod dir: %v", err)
}
t.Cleanup(func() { os.Chmod(dir, 0700) })

path := filepath.Join(dir, "out.txt")
err := fsx.WriteFile(path, []byte("data"), 0600)
if err == nil {
t.Fatal("expected error, got nil")
}
if !errors.Is(err, fsx.ErrSnapPermission) {
t.Errorf("expected ErrSnapPermission, got: %v", err)
}
}
Loading