golang.org/x/sys@v0.9.0/unix/xattr_bsd.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build freebsd || netbsd
     6  // +build freebsd netbsd
     7  
     8  package unix
     9  
    10  import (
    11  	"strings"
    12  	"unsafe"
    13  )
    14  
    15  // Derive extattr namespace and attribute name
    16  
    17  func xattrnamespace(fullattr string) (ns int, attr string, err error) {
    18  	s := strings.IndexByte(fullattr, '.')
    19  	if s == -1 {
    20  		return -1, "", ENOATTR
    21  	}
    22  
    23  	namespace := fullattr[0:s]
    24  	attr = fullattr[s+1:]
    25  
    26  	switch namespace {
    27  	case "user":
    28  		return EXTATTR_NAMESPACE_USER, attr, nil
    29  	case "system":
    30  		return EXTATTR_NAMESPACE_SYSTEM, attr, nil
    31  	default:
    32  		return -1, "", ENOATTR
    33  	}
    34  }
    35  
    36  func initxattrdest(dest []byte, idx int) (d unsafe.Pointer) {
    37  	if len(dest) > idx {
    38  		return unsafe.Pointer(&dest[idx])
    39  	}
    40  	if dest != nil {
    41  		// extattr_get_file and extattr_list_file treat NULL differently from
    42  		// a non-NULL pointer of length zero. Preserve the property of nilness,
    43  		// even if we can't use dest directly.
    44  		return unsafe.Pointer(&_zero)
    45  	}
    46  	return nil
    47  }
    48  
    49  // FreeBSD and NetBSD implement their own syscalls to handle extended attributes
    50  
    51  func Getxattr(file string, attr string, dest []byte) (sz int, err error) {
    52  	d := initxattrdest(dest, 0)
    53  	destsize := len(dest)
    54  
    55  	nsid, a, err := xattrnamespace(attr)
    56  	if err != nil {
    57  		return -1, err
    58  	}
    59  
    60  	return ExtattrGetFile(file, nsid, a, uintptr(d), destsize)
    61  }
    62  
    63  func Fgetxattr(fd int, attr string, dest []byte) (sz int, err error) {
    64  	d := initxattrdest(dest, 0)
    65  	destsize := len(dest)
    66  
    67  	nsid, a, err := xattrnamespace(attr)
    68  	if err != nil {
    69  		return -1, err
    70  	}
    71  
    72  	return ExtattrGetFd(fd, nsid, a, uintptr(d), destsize)
    73  }
    74  
    75  func Lgetxattr(link string, attr string, dest []byte) (sz int, err error) {
    76  	d := initxattrdest(dest, 0)
    77  	destsize := len(dest)
    78  
    79  	nsid, a, err := xattrnamespace(attr)
    80  	if err != nil {
    81  		return -1, err
    82  	}
    83  
    84  	return ExtattrGetLink(link, nsid, a, uintptr(d), destsize)
    85  }
    86  
    87  // flags are unused on FreeBSD
    88  
    89  func Fsetxattr(fd int, attr string, data []byte, flags int) (err error) {
    90  	var d unsafe.Pointer
    91  	if len(data) > 0 {
    92  		d = unsafe.Pointer(&data[0])
    93  	}
    94  	datasiz := len(data)
    95  
    96  	nsid, a, err := xattrnamespace(attr)
    97  	if err != nil {
    98  		return
    99  	}
   100  
   101  	_, err = ExtattrSetFd(fd, nsid, a, uintptr(d), datasiz)
   102  	return
   103  }
   104  
   105  func Setxattr(file string, attr string, data []byte, flags int) (err error) {
   106  	var d unsafe.Pointer
   107  	if len(data) > 0 {
   108  		d = unsafe.Pointer(&data[0])
   109  	}
   110  	datasiz := len(data)
   111  
   112  	nsid, a, err := xattrnamespace(attr)
   113  	if err != nil {
   114  		return
   115  	}
   116  
   117  	_, err = ExtattrSetFile(file, nsid, a, uintptr(d), datasiz)
   118  	return
   119  }
   120  
   121  func Lsetxattr(link string, attr string, data []byte, flags int) (err error) {
   122  	var d unsafe.Pointer
   123  	if len(data) > 0 {
   124  		d = unsafe.Pointer(&data[0])
   125  	}
   126  	datasiz := len(data)
   127  
   128  	nsid, a, err := xattrnamespace(attr)
   129  	if err != nil {
   130  		return
   131  	}
   132  
   133  	_, err = ExtattrSetLink(link, nsid, a, uintptr(d), datasiz)
   134  	return
   135  }
   136  
   137  func Removexattr(file string, attr string) (err error) {
   138  	nsid, a, err := xattrnamespace(attr)
   139  	if err != nil {
   140  		return
   141  	}
   142  
   143  	err = ExtattrDeleteFile(file, nsid, a)
   144  	return
   145  }
   146  
   147  func Fremovexattr(fd int, attr string) (err error) {
   148  	nsid, a, err := xattrnamespace(attr)
   149  	if err != nil {
   150  		return
   151  	}
   152  
   153  	err = ExtattrDeleteFd(fd, nsid, a)
   154  	return
   155  }
   156  
   157  func Lremovexattr(link string, attr string) (err error) {
   158  	nsid, a, err := xattrnamespace(attr)
   159  	if err != nil {
   160  		return
   161  	}
   162  
   163  	err = ExtattrDeleteLink(link, nsid, a)
   164  	return
   165  }
   166  
   167  func Listxattr(file string, dest []byte) (sz int, err error) {
   168  	destsiz := len(dest)
   169  
   170  	// FreeBSD won't allow you to list xattrs from multiple namespaces
   171  	s, pos := 0, 0
   172  	for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
   173  		stmp, e := ListxattrNS(file, nsid, dest[pos:])
   174  
   175  		/* Errors accessing system attrs are ignored so that
   176  		 * we can implement the Linux-like behavior of omitting errors that
   177  		 * we don't have read permissions on
   178  		 *
   179  		 * Linux will still error if we ask for user attributes on a file that
   180  		 * we don't have read permissions on, so don't ignore those errors
   181  		 */
   182  		if e != nil {
   183  			if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
   184  				continue
   185  			}
   186  			return s, e
   187  		}
   188  
   189  		s += stmp
   190  		pos = s
   191  		if pos > destsiz {
   192  			pos = destsiz
   193  		}
   194  	}
   195  
   196  	return s, nil
   197  }
   198  
   199  func ListxattrNS(file string, nsid int, dest []byte) (sz int, err error) {
   200  	d := initxattrdest(dest, 0)
   201  	destsiz := len(dest)
   202  
   203  	s, e := ExtattrListFile(file, nsid, uintptr(d), destsiz)
   204  	if e != nil {
   205  		return 0, err
   206  	}
   207  
   208  	return s, nil
   209  }
   210  
   211  func Flistxattr(fd int, dest []byte) (sz int, err error) {
   212  	destsiz := len(dest)
   213  
   214  	s, pos := 0, 0
   215  	for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
   216  		stmp, e := FlistxattrNS(fd, nsid, dest[pos:])
   217  
   218  		if e != nil {
   219  			if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
   220  				continue
   221  			}
   222  			return s, e
   223  		}
   224  
   225  		s += stmp
   226  		pos = s
   227  		if pos > destsiz {
   228  			pos = destsiz
   229  		}
   230  	}
   231  
   232  	return s, nil
   233  }
   234  
   235  func FlistxattrNS(fd int, nsid int, dest []byte) (sz int, err error) {
   236  	d := initxattrdest(dest, 0)
   237  	destsiz := len(dest)
   238  
   239  	s, e := ExtattrListFd(fd, nsid, uintptr(d), destsiz)
   240  	if e != nil {
   241  		return 0, err
   242  	}
   243  
   244  	return s, nil
   245  }
   246  
   247  func Llistxattr(link string, dest []byte) (sz int, err error) {
   248  	destsiz := len(dest)
   249  
   250  	s, pos := 0, 0
   251  	for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
   252  		stmp, e := LlistxattrNS(link, nsid, dest[pos:])
   253  
   254  		if e != nil {
   255  			if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
   256  				continue
   257  			}
   258  			return s, e
   259  		}
   260  
   261  		s += stmp
   262  		pos = s
   263  		if pos > destsiz {
   264  			pos = destsiz
   265  		}
   266  	}
   267  
   268  	return s, nil
   269  }
   270  
   271  func LlistxattrNS(link string, nsid int, dest []byte) (sz int, err error) {
   272  	d := initxattrdest(dest, 0)
   273  	destsiz := len(dest)
   274  
   275  	s, e := ExtattrListLink(link, nsid, uintptr(d), destsiz)
   276  	if e != nil {
   277  		return 0, err
   278  	}
   279  
   280  	return s, nil
   281  }