New upstream version 0.4.8
Mathias Gibbens
1 year, 7 months ago
0 | name: build | |
1 | on: [push] | |
2 | ||
3 | jobs: | |
4 | build: | |
5 | strategy: | |
6 | matrix: | |
7 | go-version: [1.15, 1.16, 1.17, 1.18] | |
8 | os: [ubuntu-latest, macos-latest] | |
9 | runs-on: ${{ matrix.os }} | |
10 | steps: | |
11 | - name: Set up Go | |
12 | uses: actions/setup-go@v2 | |
13 | with: | |
14 | go-version: ${{ matrix.go-version }} | |
15 | ||
16 | - name: Checkout code | |
17 | uses: actions/checkout@v2 | |
18 | ||
19 | - name: Build | |
20 | run: | | |
21 | GOOS=freebsd go build | |
22 | GOOS=windows go build | |
23 | go build -v . | |
24 | ||
25 | - name: Test | |
26 | run: | | |
27 | go vet | |
28 | go test -v -race -coverprofile=coverage.txt -covermode=atomic | |
29 | ||
30 | - name: After success | |
31 | run: | | |
32 | bash <(curl -s https://codecov.io/bash) | |
33 |
0 | #!/bin/sh -e | |
1 | ||
2 | echo "Building for Linux..." | |
3 | GOOS=linux go build | |
4 | ||
5 | echo "Building for Darwin..." | |
6 | GOOS=darwin go build | |
7 | ||
8 | echo "Building for FreeBSD..." | |
9 | GOOS=freebsd go build | |
10 | ||
11 | echo "Building for Windows...(dummy)" | |
12 | GOOS=windows go build | |
13 | ||
14 | echo "Running tests..." | |
15 | go vet | |
16 | go test -v -race -coverprofile=coverage.txt -covermode=atomic |
0 | language: go | |
1 | sudo: false | |
2 | ||
3 | go: | |
4 | - "1.15.x" | |
5 | - "1.14.x" | |
6 | ||
7 | os: | |
8 | - linux | |
9 | - osx | |
10 | - windows | |
11 | ||
12 | before_install: | |
13 | - export GO111MODULE=on | |
14 | - go version | |
15 | - go get golang.org/x/tools/cmd/goimports | |
16 | ||
17 | install: | |
18 | - go build | |
19 | ||
20 | script: | |
21 | - ./.travis.sh | |
22 | # goimports on windows gives false positives | |
23 | - if [[ "${TRAVIS_OS_NAME}" != "windows" ]]; then diff <(goimports -d .) <(printf ""); fi | |
24 | ||
25 | after_success: | |
26 | - bash <(curl -s https://codecov.io/bash) |
0 | 0 | [![GoDoc](https://godoc.org/github.com/pkg/xattr?status.svg)](http://godoc.org/github.com/pkg/xattr) |
1 | 1 | [![Go Report Card](https://goreportcard.com/badge/github.com/pkg/xattr)](https://goreportcard.com/report/github.com/pkg/xattr) |
2 | [![Build Status](https://travis-ci.org/pkg/xattr.svg?branch=master)](https://travis-ci.org/pkg/xattr) | |
3 | [![Version](https://badge.fury.io/gh/pkg%2Fxattr.svg)](https://github.com/pkg/xattr/releases) | |
2 | [![Build Status](https://github.com/pkg/xattr/workflows/build/badge.svg)](https://github.com/pkg/xattr/actions?query=workflow%3Abuild) | |
4 | 3 | [![Codecov](https://codecov.io/gh/pkg/xattr/branch/master/graph/badge.svg)](https://codecov.io/gh/pkg/xattr) |
5 | 4 | |
6 | 5 | xattr |
7 | 6 | ===== |
8 | Extended attribute support for Go (linux + darwin + freebsd + netbsd). | |
7 | Extended attribute support for Go (linux + darwin + freebsd + netbsd + solaris). | |
9 | 8 | |
10 | 9 | "Extended attributes are name:value pairs associated permanently with files and directories, similar to the environment strings associated with a process. An attribute may be defined or undefined. If it is defined, its value may be empty or non-empty." [See more...](https://en.wikipedia.org/wiki/Extended_file_attributes) |
11 | 10 |
1 | 1 | |
2 | 2 | go 1.14 |
3 | 3 | |
4 | require golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 | |
4 | require golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f |
0 | 0 | golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck= |
1 | 1 | golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
2 | golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw= | |
3 | golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
28 | 28 | Err error |
29 | 29 | } |
30 | 30 | |
31 | func (e *Error) Error() string { | |
32 | return e.Op + " " + e.Path + " " + e.Name + ": " + e.Err.Error() | |
31 | func (e *Error) Unwrap() error { return e.Err } | |
32 | ||
33 | func (e *Error) Error() (errstr string) { | |
34 | if e.Op != "" { | |
35 | errstr += e.Op | |
36 | } | |
37 | if e.Path != "" { | |
38 | if errstr != "" { | |
39 | errstr += " " | |
40 | } | |
41 | errstr += e.Path | |
42 | } | |
43 | if e.Name != "" { | |
44 | if errstr != "" { | |
45 | errstr += " " | |
46 | } | |
47 | errstr += e.Name | |
48 | } | |
49 | if e.Err != nil { | |
50 | if errstr != "" { | |
51 | errstr += ": " | |
52 | } | |
53 | errstr += e.Err.Error() | |
54 | } | |
55 | return | |
33 | 56 | } |
34 | 57 | |
35 | 58 | // Get retrieves extended attribute data associated with path. It will follow |
84 | 107 | // truncated, and we retry with a bigger buffer. Contrary to documentation, |
85 | 108 | // MacOS never seems to return ERANGE! |
86 | 109 | // To keep the code simple, we always check both conditions, and sometimes |
87 | // double the buffer size without it being strictly neccessary. | |
110 | // double the buffer size without it being strictly necessary. | |
88 | 111 | if err == syscall.ERANGE || read == size { |
89 | 112 | // The buffer was too small. Try again. |
90 | 113 | size <<= 1 |
0 | // +build linux darwin | |
0 | //go:build linux || darwin || solaris | |
1 | // +build linux darwin solaris | |
1 | 2 | |
2 | 3 | package xattr |
3 | 4 |
0 | //go:build linux | |
0 | 1 | // +build linux |
1 | 2 | |
2 | 3 | package xattr |
3 | 4 | |
4 | 5 | import ( |
5 | "errors" | |
6 | 6 | "os" |
7 | 7 | "syscall" |
8 | 8 | |
30 | 30 | func ignoringEINTR(fn func() error) (err error) { |
31 | 31 | for { |
32 | 32 | err = fn() |
33 | if !errors.Is(err, unix.EINTR) { | |
33 | if err != unix.EINTR { | |
34 | 34 | break |
35 | 35 | } |
36 | 36 | } |
0 | //go:build solaris | |
1 | // +build solaris | |
2 | ||
3 | package xattr | |
4 | ||
5 | import ( | |
6 | "os" | |
7 | "syscall" | |
8 | ||
9 | "golang.org/x/sys/unix" | |
10 | ) | |
11 | ||
12 | const ( | |
13 | // XATTR_SUPPORTED will be true if the current platform is supported | |
14 | XATTR_SUPPORTED = true | |
15 | ||
16 | XATTR_CREATE = 0x1 | |
17 | XATTR_REPLACE = 0x2 | |
18 | ||
19 | // ENOATTR is not exported by the syscall package on Linux, because it is | |
20 | // an alias for ENODATA. We export it here so it is available on all | |
21 | // our supported platforms. | |
22 | ENOATTR = syscall.ENODATA | |
23 | ) | |
24 | ||
25 | func getxattr(path string, name string, data []byte) (int, error) { | |
26 | f, err := os.OpenFile(path, os.O_RDONLY, 0) | |
27 | if err != nil { | |
28 | return 0, err | |
29 | } | |
30 | defer func() { | |
31 | _ = f.Close() | |
32 | }() | |
33 | return fgetxattr(f, name, data) | |
34 | } | |
35 | ||
36 | func lgetxattr(path string, name string, data []byte) (int, error) { | |
37 | return 0, unix.ENOTSUP | |
38 | } | |
39 | ||
40 | func fgetxattr(f *os.File, name string, data []byte) (int, error) { | |
41 | fd, err := unix.Openat(int(f.Fd()), name, unix.O_RDONLY|unix.O_XATTR, 0) | |
42 | if err != nil { | |
43 | return 0, err | |
44 | } | |
45 | defer func() { | |
46 | _ = unix.Close(fd) | |
47 | }() | |
48 | return unix.Read(fd, data) | |
49 | } | |
50 | ||
51 | func setxattr(path string, name string, data []byte, flags int) error { | |
52 | f, err := os.OpenFile(path, os.O_RDONLY, 0) | |
53 | if err != nil { | |
54 | return err | |
55 | } | |
56 | err = fsetxattr(f, name, data, flags) | |
57 | if err != nil { | |
58 | _ = f.Close() | |
59 | return err | |
60 | } | |
61 | return f.Close() | |
62 | } | |
63 | ||
64 | func lsetxattr(path string, name string, data []byte, flags int) error { | |
65 | return unix.ENOTSUP | |
66 | } | |
67 | ||
68 | func fsetxattr(f *os.File, name string, data []byte, flags int) error { | |
69 | mode := unix.O_WRONLY | unix.O_XATTR | |
70 | if flags&XATTR_REPLACE != 0 { | |
71 | mode |= unix.O_TRUNC | |
72 | } else if flags&XATTR_CREATE != 0 { | |
73 | mode |= unix.O_CREAT | unix.O_EXCL | |
74 | } else { | |
75 | mode |= unix.O_CREAT | unix.O_TRUNC | |
76 | } | |
77 | fd, err := unix.Openat(int(f.Fd()), name, mode, 0666) | |
78 | if err != nil { | |
79 | return err | |
80 | } | |
81 | if _, err = unix.Write(fd, data); err != nil { | |
82 | _ = unix.Close(fd) | |
83 | return err | |
84 | } | |
85 | return unix.Close(fd) | |
86 | } | |
87 | ||
88 | func removexattr(path string, name string) error { | |
89 | fd, err := unix.Open(path, unix.O_RDONLY|unix.O_XATTR, 0) | |
90 | if err != nil { | |
91 | return err | |
92 | } | |
93 | f := os.NewFile(uintptr(fd), path) | |
94 | defer func() { | |
95 | _ = f.Close() | |
96 | }() | |
97 | return fremovexattr(f, name) | |
98 | } | |
99 | ||
100 | func lremovexattr(path string, name string) error { | |
101 | return unix.ENOTSUP | |
102 | } | |
103 | ||
104 | func fremovexattr(f *os.File, name string) error { | |
105 | fd, err := unix.Openat(int(f.Fd()), ".", unix.O_XATTR, 0) | |
106 | if err != nil { | |
107 | return err | |
108 | } | |
109 | defer func() { | |
110 | _ = unix.Close(fd) | |
111 | }() | |
112 | return unix.Unlinkat(fd, name, 0) | |
113 | } | |
114 | ||
115 | func listxattr(path string, data []byte) (int, error) { | |
116 | f, err := os.OpenFile(path, os.O_RDONLY, 0) | |
117 | if err != nil { | |
118 | return 0, err | |
119 | } | |
120 | defer func() { | |
121 | _ = f.Close() | |
122 | }() | |
123 | return flistxattr(f, data) | |
124 | } | |
125 | ||
126 | func llistxattr(path string, data []byte) (int, error) { | |
127 | return 0, unix.ENOTSUP | |
128 | } | |
129 | ||
130 | func flistxattr(f *os.File, data []byte) (int, error) { | |
131 | fd, err := unix.Openat(int(f.Fd()), ".", unix.O_RDONLY|unix.O_XATTR, 0) | |
132 | if err != nil { | |
133 | return 0, unix.ENOTSUP | |
134 | } | |
135 | xf := os.NewFile(uintptr(fd), f.Name()) | |
136 | defer func() { | |
137 | _ = xf.Close() | |
138 | }() | |
139 | names, err := xf.Readdirnames(-1) | |
140 | if err != nil { | |
141 | return 0, err | |
142 | } | |
143 | var buf []byte | |
144 | for _, name := range names { | |
145 | buf = append(buf, append([]byte(name), '\000')...) | |
146 | } | |
147 | if data == nil { | |
148 | return len(buf), nil | |
149 | } | |
150 | return copy(data, buf), nil | |
151 | } | |
152 | ||
153 | // stringsFromByteSlice converts a sequence of attributes to a []string. | |
154 | // On Darwin and Linux, each entry is a NULL-terminated string. | |
155 | func stringsFromByteSlice(buf []byte) (result []string) { | |
156 | offset := 0 | |
157 | for index, b := range buf { | |
158 | if b == 0 { | |
159 | result = append(result, string(buf[offset:index])) | |
160 | offset = index + 1 | |
161 | } | |
162 | } | |
163 | return | |
164 | } |
0 | // +build linux darwin freebsd netbsd | |
0 | //go:build linux || darwin || freebsd || netbsd || solaris | |
1 | // +build linux darwin freebsd netbsd solaris | |
1 | 2 | |
2 | 3 | package xattr |
3 | 4 | |
146 | 147 | // Test that Get/LGet, Set/LSet etc operate as expected on symlinks. The |
147 | 148 | // functions should behave differently when operating on a symlink. |
148 | 149 | func TestSymlink(t *testing.T) { |
150 | if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" { | |
151 | t.Skipf("extended attributes aren't supported for symlinks on %s", runtime.GOOS) | |
152 | } | |
149 | 153 | dir, err := ioutil.TempDir("", "") |
150 | 154 | if err != nil { |
151 | 155 | t.Fatal(err) |
280 | 284 | if !ok { |
281 | 285 | log.Panicf("cannot unpack err=%#v", err) |
282 | 286 | } |
283 | return err2.Err.(syscall.Errno) | |
287 | err3, ok := err2.Err.(syscall.Errno) | |
288 | if !ok { | |
289 | log.Panicf("cannot unpack err2=%#v", err2) | |
290 | } | |
291 | return err3 | |
284 | 292 | } |
285 | 293 | |
286 | 294 | // wrappers to adapt "F" variants to the test |
0 | // +build !linux,!freebsd,!netbsd,!darwin | |
0 | //go:build !linux && !freebsd && !netbsd && !darwin && !solaris | |
1 | // +build !linux,!freebsd,!netbsd,!darwin,!solaris | |
1 | 2 | |
2 | 3 | package xattr |
3 | 4 | |
4 | 5 | import ( |
5 | 6 | "os" |
7 | "syscall" | |
8 | ) | |
9 | ||
10 | const ( | |
11 | // We need to use the default for non supported operating systems | |
12 | ENOATTR = syscall.ENODATA | |
6 | 13 | ) |
7 | 14 | |
8 | 15 | // XATTR_SUPPORTED will be true if the current platform is supported |