Import upstream version 0.4.6
Debian Janitor
2 years 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] | |
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 |
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) Error() (errstr string) { | |
32 | if e.Op != "" { | |
33 | errstr += e.Op | |
34 | } | |
35 | if e.Path != "" { | |
36 | if errstr != "" { | |
37 | errstr += " " | |
38 | } | |
39 | errstr += e.Path | |
40 | } | |
41 | if e.Name != "" { | |
42 | if errstr != "" { | |
43 | errstr += " " | |
44 | } | |
45 | errstr += e.Name | |
46 | } | |
47 | if e.Err != nil { | |
48 | if errstr != "" { | |
49 | errstr += ": " | |
50 | } | |
51 | errstr += e.Err.Error() | |
52 | } | |
53 | return | |
33 | 54 | } |
34 | 55 | |
35 | 56 | // Get retrieves extended attribute data associated with path. It will follow |
84 | 105 | // truncated, and we retry with a bigger buffer. Contrary to documentation, |
85 | 106 | // MacOS never seems to return ERANGE! |
86 | 107 | // To keep the code simple, we always check both conditions, and sometimes |
87 | // double the buffer size without it being strictly neccessary. | |
108 | // double the buffer size without it being strictly necessary. | |
88 | 109 | if err == syscall.ERANGE || read == size { |
89 | 110 | // The buffer was too small. Try again. |
90 | 111 | size <<= 1 |
2 | 2 | package xattr |
3 | 3 | |
4 | 4 | import ( |
5 | "errors" | |
6 | 5 | "os" |
7 | 6 | "syscall" |
8 | 7 | |
30 | 29 | func ignoringEINTR(fn func() error) (err error) { |
31 | 30 | for { |
32 | 31 | err = fn() |
33 | if !errors.Is(err, unix.EINTR) { | |
32 | if err != unix.EINTR { | |
34 | 33 | break |
35 | 34 | } |
36 | 35 | } |
0 | // +build solaris | |
1 | ||
2 | package xattr | |
3 | ||
4 | import ( | |
5 | "os" | |
6 | "syscall" | |
7 | ||
8 | "golang.org/x/sys/unix" | |
9 | ) | |
10 | ||
11 | const ( | |
12 | // XATTR_SUPPORTED will be true if the current platform is supported | |
13 | XATTR_SUPPORTED = true | |
14 | ||
15 | XATTR_CREATE = 0x1 | |
16 | XATTR_REPLACE = 0x2 | |
17 | ||
18 | // ENOATTR is not exported by the syscall package on Linux, because it is | |
19 | // an alias for ENODATA. We export it here so it is available on all | |
20 | // our supported platforms. | |
21 | ENOATTR = syscall.ENODATA | |
22 | ) | |
23 | ||
24 | func getxattr(path string, name string, data []byte) (int, error) { | |
25 | f, err := os.OpenFile(path, os.O_RDONLY, 0) | |
26 | if err != nil { | |
27 | return 0, err | |
28 | } | |
29 | defer func() { | |
30 | _ = f.Close() | |
31 | }() | |
32 | return fgetxattr(f, name, data) | |
33 | } | |
34 | ||
35 | func lgetxattr(path string, name string, data []byte) (int, error) { | |
36 | return 0, unix.ENOTSUP | |
37 | } | |
38 | ||
39 | func fgetxattr(f *os.File, name string, data []byte) (int, error) { | |
40 | fd, err := unix.Openat(int(f.Fd()), name, unix.O_RDONLY|unix.O_XATTR, 0) | |
41 | if err != nil { | |
42 | return 0, err | |
43 | } | |
44 | defer func() { | |
45 | _ = unix.Close(fd) | |
46 | }() | |
47 | return unix.Read(fd, data) | |
48 | } | |
49 | ||
50 | func setxattr(path string, name string, data []byte, flags int) error { | |
51 | f, err := os.OpenFile(path, os.O_RDONLY, 0) | |
52 | if err != nil { | |
53 | return err | |
54 | } | |
55 | err = fsetxattr(f, name, data, flags) | |
56 | if err != nil { | |
57 | _ = f.Close() | |
58 | return err | |
59 | } | |
60 | return f.Close() | |
61 | } | |
62 | ||
63 | func lsetxattr(path string, name string, data []byte, flags int) error { | |
64 | return unix.ENOTSUP | |
65 | } | |
66 | ||
67 | func fsetxattr(f *os.File, name string, data []byte, flags int) error { | |
68 | mode := unix.O_WRONLY | unix.O_XATTR | |
69 | if flags&XATTR_REPLACE != 0 { | |
70 | mode |= unix.O_TRUNC | |
71 | } else if flags&XATTR_CREATE != 0 { | |
72 | mode |= unix.O_CREAT | unix.O_EXCL | |
73 | } else { | |
74 | mode |= unix.O_CREAT | unix.O_TRUNC | |
75 | } | |
76 | fd, err := unix.Openat(int(f.Fd()), name, mode, 0666) | |
77 | if err != nil { | |
78 | return err | |
79 | } | |
80 | if _, err = unix.Write(fd, data); err != nil { | |
81 | _ = unix.Close(fd) | |
82 | return err | |
83 | } | |
84 | return unix.Close(fd) | |
85 | } | |
86 | ||
87 | func removexattr(path string, name string) error { | |
88 | fd, err := unix.Open(path, unix.O_RDONLY|unix.O_XATTR, 0) | |
89 | if err != nil { | |
90 | return err | |
91 | } | |
92 | f := os.NewFile(uintptr(fd), path); | |
93 | defer func() { | |
94 | _ = f.Close() | |
95 | }() | |
96 | return fremovexattr(f, name) | |
97 | } | |
98 | ||
99 | func lremovexattr(path string, name string) error { | |
100 | return unix.ENOTSUP | |
101 | } | |
102 | ||
103 | func fremovexattr(f *os.File, name string) error { | |
104 | fd, err := unix.Openat(int(f.Fd()), ".", unix.O_XATTR, 0) | |
105 | if err != nil { | |
106 | return err | |
107 | } | |
108 | defer func() { | |
109 | _ = unix.Close(fd) | |
110 | }() | |
111 | return unix.Unlinkat(fd, name, 0) | |
112 | } | |
113 | ||
114 | func listxattr(path string, data []byte) (int, error) { | |
115 | f, err := os.OpenFile(path, os.O_RDONLY, 0) | |
116 | if err != nil { | |
117 | return 0, err | |
118 | } | |
119 | defer func() { | |
120 | _ = f.Close() | |
121 | }() | |
122 | return flistxattr(f, data) | |
123 | } | |
124 | ||
125 | func llistxattr(path string, data []byte) (int, error) { | |
126 | return 0, unix.ENOTSUP | |
127 | } | |
128 | ||
129 | func flistxattr(f *os.File, data []byte) (int, error) { | |
130 | fd, err := unix.Openat(int(f.Fd()), ".", unix.O_RDONLY|unix.O_XATTR, 0) | |
131 | if err != nil { | |
132 | return 0, err | |
133 | } | |
134 | xf := os.NewFile(uintptr(fd), f.Name()) | |
135 | defer func() { | |
136 | _ = xf.Close() | |
137 | }() | |
138 | names, err := xf.Readdirnames(-1) | |
139 | if err != nil { | |
140 | return 0, err | |
141 | } | |
142 | var buf []byte | |
143 | for _, name := range names { | |
144 | buf = append(buf, append([]byte(name), '\000')...) | |
145 | } | |
146 | if data == nil { | |
147 | return len(buf), nil | |
148 | } | |
149 | return copy(data, buf), nil | |
150 | } | |
151 | ||
152 | // stringsFromByteSlice converts a sequence of attributes to a []string. | |
153 | // On Darwin and Linux, each entry is a NULL-terminated string. | |
154 | func stringsFromByteSlice(buf []byte) (result []string) { | |
155 | offset := 0 | |
156 | for index, b := range buf { | |
157 | if b == 0 { | |
158 | result = append(result, string(buf[offset:index])) | |
159 | offset = index + 1 | |
160 | } | |
161 | } | |
162 | return | |
163 | } |
0 | // +build linux darwin freebsd netbsd | |
0 | // +build linux darwin freebsd netbsd solaris | |
1 | 1 | |
2 | 2 | package xattr |
3 | 3 | |
146 | 146 | // Test that Get/LGet, Set/LSet etc operate as expected on symlinks. The |
147 | 147 | // functions should behave differently when operating on a symlink. |
148 | 148 | func TestSymlink(t *testing.T) { |
149 | if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" { | |
150 | t.Skipf("extended attributes aren't supported for symlinks on %s", runtime.GOOS) | |
151 | } | |
149 | 152 | dir, err := ioutil.TempDir("", "") |
150 | 153 | if err != nil { |
151 | 154 | t.Fatal(err) |
280 | 283 | if !ok { |
281 | 284 | log.Panicf("cannot unpack err=%#v", err) |
282 | 285 | } |
283 | return err2.Err.(syscall.Errno) | |
286 | err3, ok := err2.Err.(syscall.Errno) | |
287 | if !ok { | |
288 | log.Panicf("cannot unpack err2=%#v", err2) | |
289 | } | |
290 | return err3 | |
284 | 291 | } |
285 | 292 | |
286 | 293 | // 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 |