| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build darwin || freebsd || linux || netbsd |
| // +build darwin freebsd linux netbsd |
| |
| package unix_test |
| |
| import ( |
| "io/ioutil" |
| "os" |
| "runtime" |
| "strings" |
| "testing" |
| |
| "golang.org/x/sys/unix" |
| ) |
| |
| func TestXattr(t *testing.T) { |
| defer chtmpdir(t)() |
| |
| f := "xattr1" |
| touch(t, f) |
| |
| xattrName := "user.test" |
| xattrDataSet := "gopher" |
| |
| err := unix.Setxattr(f, xattrName, []byte{}, 0) |
| if err == unix.ENOTSUP || err == unix.EOPNOTSUPP { |
| t.Skip("filesystem does not support extended attributes, skipping test") |
| } else if err != nil { |
| t.Fatalf("Setxattr: %v", err) |
| } |
| |
| err = unix.Setxattr(f, xattrName, []byte(xattrDataSet), 0) |
| if err != nil { |
| t.Fatalf("Setxattr: %v", err) |
| } |
| |
| // find size |
| size, err := unix.Listxattr(f, nil) |
| if err != nil { |
| t.Fatalf("Listxattr: %v", err) |
| } |
| |
| if size <= 0 { |
| t.Fatalf("Listxattr returned an empty list of attributes") |
| } |
| |
| buf := make([]byte, size) |
| read, err := unix.Listxattr(f, buf) |
| if err != nil { |
| t.Fatalf("Listxattr: %v", err) |
| } |
| |
| xattrs := stringsFromByteSlice(buf[:read]) |
| |
| xattrWant := xattrName |
| if runtime.GOOS == "freebsd" { |
| // On FreeBSD, the namespace is stored separately from the xattr |
| // name and Listxattr doesn't return the namespace prefix. |
| xattrWant = strings.TrimPrefix(xattrWant, "user.") |
| } |
| found := false |
| for _, name := range xattrs { |
| if name == xattrWant { |
| found = true |
| } |
| } |
| |
| if !found { |
| t.Errorf("Listxattr did not return previously set attribute '%s'", xattrName) |
| } |
| |
| // find size |
| size, err = unix.Getxattr(f, xattrName, nil) |
| if err != nil { |
| t.Fatalf("Getxattr: %v", err) |
| } |
| |
| if size <= 0 { |
| t.Fatalf("Getxattr returned an empty attribute") |
| } |
| |
| xattrDataGet := make([]byte, size) |
| _, err = unix.Getxattr(f, xattrName, xattrDataGet) |
| if err != nil { |
| t.Fatalf("Getxattr: %v", err) |
| } |
| |
| got := string(xattrDataGet) |
| if got != xattrDataSet { |
| t.Errorf("Getxattr: expected attribute value %s, got %s", xattrDataSet, got) |
| } |
| |
| err = unix.Removexattr(f, xattrName) |
| if err != nil { |
| t.Fatalf("Removexattr: %v", err) |
| } |
| |
| n := "nonexistent" |
| err = unix.Lsetxattr(n, xattrName, []byte(xattrDataSet), 0) |
| if err != unix.ENOENT { |
| t.Errorf("Lsetxattr: expected %v on non-existent file, got %v", unix.ENOENT, err) |
| } |
| |
| _, err = unix.Lgetxattr(n, xattrName, nil) |
| if err != unix.ENOENT { |
| t.Errorf("Lgetxattr: %v", err) |
| } |
| |
| s := "symlink1" |
| err = os.Symlink(n, s) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| err = unix.Lsetxattr(s, xattrName, []byte(xattrDataSet), 0) |
| if err != nil { |
| // Linux and Android doen't support xattrs on symlinks according |
| // to xattr(7), so just test that we get the proper error. |
| if (runtime.GOOS != "linux" && runtime.GOOS != "android") || err != unix.EPERM { |
| t.Fatalf("Lsetxattr: %v", err) |
| } |
| } |
| } |
| |
| func TestFdXattr(t *testing.T) { |
| file, err := ioutil.TempFile("", "TestFdXattr") |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer os.Remove(file.Name()) |
| defer file.Close() |
| |
| fd := int(file.Fd()) |
| xattrName := "user.test" |
| xattrDataSet := "gopher" |
| |
| err = unix.Fsetxattr(fd, xattrName, []byte(xattrDataSet), 0) |
| if err == unix.ENOTSUP || err == unix.EOPNOTSUPP { |
| t.Skip("filesystem does not support extended attributes, skipping test") |
| } else if err != nil { |
| t.Fatalf("Fsetxattr: %v", err) |
| } |
| |
| // find size |
| size, err := unix.Flistxattr(fd, nil) |
| if err != nil { |
| t.Fatalf("Flistxattr: %v", err) |
| } |
| |
| if size <= 0 { |
| t.Fatalf("Flistxattr returned an empty list of attributes") |
| } |
| |
| buf := make([]byte, size) |
| read, err := unix.Flistxattr(fd, buf) |
| if err != nil { |
| t.Fatalf("Flistxattr: %v", err) |
| } |
| |
| xattrs := stringsFromByteSlice(buf[:read]) |
| |
| xattrWant := xattrName |
| if runtime.GOOS == "freebsd" { |
| // On FreeBSD, the namespace is stored separately from the xattr |
| // name and Listxattr doesn't return the namespace prefix. |
| xattrWant = strings.TrimPrefix(xattrWant, "user.") |
| } |
| found := false |
| for _, name := range xattrs { |
| if name == xattrWant { |
| found = true |
| } |
| } |
| |
| if !found { |
| t.Errorf("Flistxattr did not return previously set attribute '%s'", xattrName) |
| } |
| |
| // find size |
| size, err = unix.Fgetxattr(fd, xattrName, nil) |
| if err != nil { |
| t.Fatalf("Fgetxattr: %v", err) |
| } |
| |
| if size <= 0 { |
| t.Fatalf("Fgetxattr returned an empty attribute") |
| } |
| |
| xattrDataGet := make([]byte, size) |
| _, err = unix.Fgetxattr(fd, xattrName, xattrDataGet) |
| if err != nil { |
| t.Fatalf("Fgetxattr: %v", err) |
| } |
| |
| got := string(xattrDataGet) |
| if got != xattrDataSet { |
| t.Errorf("Fgetxattr: expected attribute value %s, got %s", xattrDataSet, got) |
| } |
| |
| err = unix.Fremovexattr(fd, xattrName) |
| if err != nil { |
| t.Fatalf("Fremovexattr: %v", err) |
| } |
| } |