github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/linux/sys_xattr.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package linux
    16  
    17  import (
    18  	"strings"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    21  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    22  	"github.com/SagerNet/gvisor/pkg/hostarch"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/arch"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    26  	"github.com/SagerNet/gvisor/pkg/syserror"
    27  )
    28  
    29  // LINT.IfChange
    30  
    31  // GetXattr implements linux syscall getxattr(2).
    32  func GetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    33  	return getXattrFromPath(t, args, true)
    34  }
    35  
    36  // LGetXattr implements linux syscall lgetxattr(2).
    37  func LGetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    38  	return getXattrFromPath(t, args, false)
    39  }
    40  
    41  // FGetXattr implements linux syscall fgetxattr(2).
    42  func FGetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    43  	fd := args[0].Int()
    44  	nameAddr := args[1].Pointer()
    45  	valueAddr := args[2].Pointer()
    46  	size := uint64(args[3].SizeT())
    47  
    48  	// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
    49  	f := t.GetFile(fd)
    50  	if f == nil {
    51  		return 0, nil, linuxerr.EBADF
    52  	}
    53  	defer f.DecRef(t)
    54  
    55  	n, err := getXattr(t, f.Dirent, nameAddr, valueAddr, size)
    56  	if err != nil {
    57  		return 0, nil, err
    58  	}
    59  
    60  	return uintptr(n), nil, nil
    61  }
    62  
    63  func getXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink bool) (uintptr, *kernel.SyscallControl, error) {
    64  	pathAddr := args[0].Pointer()
    65  	nameAddr := args[1].Pointer()
    66  	valueAddr := args[2].Pointer()
    67  	size := uint64(args[3].SizeT())
    68  
    69  	path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */)
    70  	if err != nil {
    71  		return 0, nil, err
    72  	}
    73  
    74  	n := 0
    75  	err = fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(_ *fs.Dirent, d *fs.Dirent, _ uint) error {
    76  		if dirPath && !fs.IsDir(d.Inode.StableAttr) {
    77  			return syserror.ENOTDIR
    78  		}
    79  
    80  		n, err = getXattr(t, d, nameAddr, valueAddr, size)
    81  		return err
    82  	})
    83  	if err != nil {
    84  		return 0, nil, err
    85  	}
    86  
    87  	return uintptr(n), nil, nil
    88  }
    89  
    90  // getXattr implements getxattr(2) from the given *fs.Dirent.
    91  func getXattr(t *kernel.Task, d *fs.Dirent, nameAddr, valueAddr hostarch.Addr, size uint64) (int, error) {
    92  	name, err := copyInXattrName(t, nameAddr)
    93  	if err != nil {
    94  		return 0, err
    95  	}
    96  
    97  	if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Read: true}); err != nil {
    98  		return 0, err
    99  	}
   100  
   101  	// TODO(b/148380782): Support xattrs in namespaces other than "user".
   102  	if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) {
   103  		return 0, syserror.EOPNOTSUPP
   104  	}
   105  
   106  	// If getxattr(2) is called with size 0, the size of the value will be
   107  	// returned successfully even if it is nonzero. In that case, we need to
   108  	// retrieve the entire attribute value so we can return the correct size.
   109  	requestedSize := size
   110  	if size == 0 || size > linux.XATTR_SIZE_MAX {
   111  		requestedSize = linux.XATTR_SIZE_MAX
   112  	}
   113  
   114  	value, err := d.Inode.GetXattr(t, name, requestedSize)
   115  	if err != nil {
   116  		return 0, err
   117  	}
   118  	n := len(value)
   119  	if uint64(n) > requestedSize {
   120  		return 0, syserror.ERANGE
   121  	}
   122  
   123  	// Don't copy out the attribute value if size is 0.
   124  	if size == 0 {
   125  		return n, nil
   126  	}
   127  
   128  	if _, err = t.CopyOutBytes(valueAddr, []byte(value)); err != nil {
   129  		return 0, err
   130  	}
   131  	return n, nil
   132  }
   133  
   134  // SetXattr implements linux syscall setxattr(2).
   135  func SetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   136  	return setXattrFromPath(t, args, true)
   137  }
   138  
   139  // LSetXattr implements linux syscall lsetxattr(2).
   140  func LSetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   141  	return setXattrFromPath(t, args, false)
   142  }
   143  
   144  // FSetXattr implements linux syscall fsetxattr(2).
   145  func FSetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   146  	fd := args[0].Int()
   147  	nameAddr := args[1].Pointer()
   148  	valueAddr := args[2].Pointer()
   149  	size := uint64(args[3].SizeT())
   150  	flags := args[4].Uint()
   151  
   152  	// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
   153  	f := t.GetFile(fd)
   154  	if f == nil {
   155  		return 0, nil, linuxerr.EBADF
   156  	}
   157  	defer f.DecRef(t)
   158  
   159  	return 0, nil, setXattr(t, f.Dirent, nameAddr, valueAddr, uint64(size), flags)
   160  }
   161  
   162  func setXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink bool) (uintptr, *kernel.SyscallControl, error) {
   163  	pathAddr := args[0].Pointer()
   164  	nameAddr := args[1].Pointer()
   165  	valueAddr := args[2].Pointer()
   166  	size := uint64(args[3].SizeT())
   167  	flags := args[4].Uint()
   168  
   169  	path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */)
   170  	if err != nil {
   171  		return 0, nil, err
   172  	}
   173  
   174  	return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(_ *fs.Dirent, d *fs.Dirent, _ uint) error {
   175  		if dirPath && !fs.IsDir(d.Inode.StableAttr) {
   176  			return syserror.ENOTDIR
   177  		}
   178  
   179  		return setXattr(t, d, nameAddr, valueAddr, uint64(size), flags)
   180  	})
   181  }
   182  
   183  // setXattr implements setxattr(2) from the given *fs.Dirent.
   184  func setXattr(t *kernel.Task, d *fs.Dirent, nameAddr, valueAddr hostarch.Addr, size uint64, flags uint32) error {
   185  	if flags&^(linux.XATTR_CREATE|linux.XATTR_REPLACE) != 0 {
   186  		return linuxerr.EINVAL
   187  	}
   188  
   189  	name, err := copyInXattrName(t, nameAddr)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Write: true}); err != nil {
   195  		return err
   196  	}
   197  
   198  	if size > linux.XATTR_SIZE_MAX {
   199  		return linuxerr.E2BIG
   200  	}
   201  	buf := make([]byte, size)
   202  	if _, err := t.CopyInBytes(valueAddr, buf); err != nil {
   203  		return err
   204  	}
   205  	value := string(buf)
   206  
   207  	if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) {
   208  		return syserror.EOPNOTSUPP
   209  	}
   210  
   211  	if err := d.Inode.SetXattr(t, d, name, value, flags); err != nil {
   212  		return err
   213  	}
   214  	d.InotifyEvent(linux.IN_ATTRIB, 0)
   215  	return nil
   216  }
   217  
   218  func copyInXattrName(t *kernel.Task, nameAddr hostarch.Addr) (string, error) {
   219  	name, err := t.CopyInString(nameAddr, linux.XATTR_NAME_MAX+1)
   220  	if err != nil {
   221  		if linuxerr.Equals(linuxerr.ENAMETOOLONG, err) {
   222  			return "", syserror.ERANGE
   223  		}
   224  		return "", err
   225  	}
   226  	if len(name) == 0 {
   227  		return "", syserror.ERANGE
   228  	}
   229  	return name, nil
   230  }
   231  
   232  // Restrict xattrs to regular files and directories.
   233  //
   234  // TODO(b/148380782): In Linux, this restriction technically only applies to
   235  // xattrs in the "user.*" namespace. Make file type checks specific to the
   236  // namespace once we allow other xattr prefixes.
   237  func xattrFileTypeOk(i *fs.Inode) bool {
   238  	return fs.IsRegular(i.StableAttr) || fs.IsDir(i.StableAttr)
   239  }
   240  
   241  func checkXattrPermissions(t *kernel.Task, i *fs.Inode, perms fs.PermMask) error {
   242  	// Restrict xattrs to regular files and directories.
   243  	if !xattrFileTypeOk(i) {
   244  		if perms.Write {
   245  			return linuxerr.EPERM
   246  		}
   247  		return linuxerr.ENODATA
   248  	}
   249  
   250  	return i.CheckPermission(t, perms)
   251  }
   252  
   253  // ListXattr implements linux syscall listxattr(2).
   254  func ListXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   255  	return listXattrFromPath(t, args, true)
   256  }
   257  
   258  // LListXattr implements linux syscall llistxattr(2).
   259  func LListXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   260  	return listXattrFromPath(t, args, false)
   261  }
   262  
   263  // FListXattr implements linux syscall flistxattr(2).
   264  func FListXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   265  	fd := args[0].Int()
   266  	listAddr := args[1].Pointer()
   267  	size := uint64(args[2].SizeT())
   268  
   269  	// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
   270  	f := t.GetFile(fd)
   271  	if f == nil {
   272  		return 0, nil, linuxerr.EBADF
   273  	}
   274  	defer f.DecRef(t)
   275  
   276  	n, err := listXattr(t, f.Dirent, listAddr, size)
   277  	if err != nil {
   278  		return 0, nil, err
   279  	}
   280  
   281  	return uintptr(n), nil, nil
   282  }
   283  
   284  func listXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink bool) (uintptr, *kernel.SyscallControl, error) {
   285  	pathAddr := args[0].Pointer()
   286  	listAddr := args[1].Pointer()
   287  	size := uint64(args[2].SizeT())
   288  
   289  	path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */)
   290  	if err != nil {
   291  		return 0, nil, err
   292  	}
   293  
   294  	n := 0
   295  	err = fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(_ *fs.Dirent, d *fs.Dirent, _ uint) error {
   296  		if dirPath && !fs.IsDir(d.Inode.StableAttr) {
   297  			return syserror.ENOTDIR
   298  		}
   299  
   300  		n, err = listXattr(t, d, listAddr, size)
   301  		return err
   302  	})
   303  	if err != nil {
   304  		return 0, nil, err
   305  	}
   306  
   307  	return uintptr(n), nil, nil
   308  }
   309  
   310  func listXattr(t *kernel.Task, d *fs.Dirent, addr hostarch.Addr, size uint64) (int, error) {
   311  	if !xattrFileTypeOk(d.Inode) {
   312  		return 0, nil
   313  	}
   314  
   315  	// If listxattr(2) is called with size 0, the buffer size needed to contain
   316  	// the xattr list will be returned successfully even if it is nonzero. In
   317  	// that case, we need to retrieve the entire list so we can compute and
   318  	// return the correct size.
   319  	requestedSize := size
   320  	if size == 0 || size > linux.XATTR_SIZE_MAX {
   321  		requestedSize = linux.XATTR_SIZE_MAX
   322  	}
   323  	xattrs, err := d.Inode.ListXattr(t, requestedSize)
   324  	if err != nil {
   325  		return 0, err
   326  	}
   327  
   328  	// TODO(b/148380782): support namespaces other than "user".
   329  	for x := range xattrs {
   330  		if !strings.HasPrefix(x, linux.XATTR_USER_PREFIX) {
   331  			delete(xattrs, x)
   332  		}
   333  	}
   334  
   335  	listSize := xattrListSize(xattrs)
   336  	if listSize > linux.XATTR_SIZE_MAX {
   337  		return 0, linuxerr.E2BIG
   338  	}
   339  	if uint64(listSize) > requestedSize {
   340  		return 0, syserror.ERANGE
   341  	}
   342  
   343  	// Don't copy out the attributes if size is 0.
   344  	if size == 0 {
   345  		return listSize, nil
   346  	}
   347  
   348  	buf := make([]byte, 0, listSize)
   349  	for x := range xattrs {
   350  		buf = append(buf, []byte(x)...)
   351  		buf = append(buf, 0)
   352  	}
   353  	if _, err := t.CopyOutBytes(addr, buf); err != nil {
   354  		return 0, err
   355  	}
   356  
   357  	return len(buf), nil
   358  }
   359  
   360  func xattrListSize(xattrs map[string]struct{}) int {
   361  	size := 0
   362  	for x := range xattrs {
   363  		size += len(x) + 1
   364  	}
   365  	return size
   366  }
   367  
   368  // RemoveXattr implements linux syscall removexattr(2).
   369  func RemoveXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   370  	return removeXattrFromPath(t, args, true)
   371  }
   372  
   373  // LRemoveXattr implements linux syscall lremovexattr(2).
   374  func LRemoveXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   375  	return removeXattrFromPath(t, args, false)
   376  }
   377  
   378  // FRemoveXattr implements linux syscall fremovexattr(2).
   379  func FRemoveXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   380  	fd := args[0].Int()
   381  	nameAddr := args[1].Pointer()
   382  
   383  	// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
   384  	f := t.GetFile(fd)
   385  	if f == nil {
   386  		return 0, nil, linuxerr.EBADF
   387  	}
   388  	defer f.DecRef(t)
   389  
   390  	return 0, nil, removeXattr(t, f.Dirent, nameAddr)
   391  }
   392  
   393  func removeXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink bool) (uintptr, *kernel.SyscallControl, error) {
   394  	pathAddr := args[0].Pointer()
   395  	nameAddr := args[1].Pointer()
   396  
   397  	path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */)
   398  	if err != nil {
   399  		return 0, nil, err
   400  	}
   401  
   402  	return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(_ *fs.Dirent, d *fs.Dirent, _ uint) error {
   403  		if dirPath && !fs.IsDir(d.Inode.StableAttr) {
   404  			return syserror.ENOTDIR
   405  		}
   406  
   407  		return removeXattr(t, d, nameAddr)
   408  	})
   409  }
   410  
   411  // removeXattr implements removexattr(2) from the given *fs.Dirent.
   412  func removeXattr(t *kernel.Task, d *fs.Dirent, nameAddr hostarch.Addr) error {
   413  	name, err := copyInXattrName(t, nameAddr)
   414  	if err != nil {
   415  		return err
   416  	}
   417  
   418  	if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Write: true}); err != nil {
   419  		return err
   420  	}
   421  
   422  	if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) {
   423  		return syserror.EOPNOTSUPP
   424  	}
   425  
   426  	if err := d.Inode.RemoveXattr(t, d, name); err != nil {
   427  		return err
   428  	}
   429  	d.InotifyEvent(linux.IN_ATTRIB, 0)
   430  	return nil
   431  }
   432  
   433  // LINT.ThenChange(vfs2/xattr.go)