xattr_bsd.go raw

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