1// +build linux darwin
2
3/*
4   Copyright The containerd Authors.
5
6   Licensed under the Apache License, Version 2.0 (the "License");
7   you may not use this file except in compliance with the License.
8   You may obtain a copy of the License at
9
10       http://www.apache.org/licenses/LICENSE-2.0
11
12   Unless required by applicable law or agreed to in writing, software
13   distributed under the License is distributed on an "AS IS" BASIS,
14   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   See the License for the specific language governing permissions and
16   limitations under the License.
17*/
18
19package sysx
20
21import (
22	"bytes"
23	"syscall"
24
25	"golang.org/x/sys/unix"
26)
27
28// Listxattr calls syscall listxattr and reads all content
29// and returns a string array
30func Listxattr(path string) ([]string, error) {
31	return listxattrAll(path, unix.Listxattr)
32}
33
34// Removexattr calls syscall removexattr
35func Removexattr(path string, attr string) (err error) {
36	return unix.Removexattr(path, attr)
37}
38
39// Setxattr calls syscall setxattr
40func Setxattr(path string, attr string, data []byte, flags int) (err error) {
41	return unix.Setxattr(path, attr, data, flags)
42}
43
44// Getxattr calls syscall getxattr
45func Getxattr(path, attr string) ([]byte, error) {
46	return getxattrAll(path, attr, unix.Getxattr)
47}
48
49// LListxattr lists xattrs, not following symlinks
50func LListxattr(path string) ([]string, error) {
51	return listxattrAll(path, unix.Llistxattr)
52}
53
54// LRemovexattr removes an xattr, not following symlinks
55func LRemovexattr(path string, attr string) (err error) {
56	return unix.Lremovexattr(path, attr)
57}
58
59// LSetxattr sets an xattr, not following symlinks
60func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
61	return unix.Lsetxattr(path, attr, data, flags)
62}
63
64// LGetxattr gets an xattr, not following symlinks
65func LGetxattr(path, attr string) ([]byte, error) {
66	return getxattrAll(path, attr, unix.Lgetxattr)
67}
68
69const defaultXattrBufferSize = 5
70
71type listxattrFunc func(path string, dest []byte) (int, error)
72
73func listxattrAll(path string, listFunc listxattrFunc) ([]string, error) {
74	var p []byte // nil on first execution
75
76	for {
77		n, err := listFunc(path, p) // first call gets buffer size.
78		if err != nil {
79			return nil, err
80		}
81
82		if n > len(p) {
83			p = make([]byte, n)
84			continue
85		}
86
87		p = p[:n]
88
89		ps := bytes.Split(bytes.TrimSuffix(p, []byte{0}), []byte{0})
90		var entries []string
91		for _, p := range ps {
92			s := string(p)
93			if s != "" {
94				entries = append(entries, s)
95			}
96		}
97
98		return entries, nil
99	}
100}
101
102type getxattrFunc func(string, string, []byte) (int, error)
103
104func getxattrAll(path, attr string, getFunc getxattrFunc) ([]byte, error) {
105	p := make([]byte, defaultXattrBufferSize)
106	for {
107		n, err := getFunc(path, attr, p)
108		if err != nil {
109			if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERANGE {
110				p = make([]byte, len(p)*2) // this can't be ideal.
111				continue                   // try again!
112			}
113
114			return nil, err
115		}
116
117		// realloc to correct size and repeat
118		if n > len(p) {
119			p = make([]byte, n)
120			continue
121		}
122
123		return p[:n], nil
124	}
125}
126