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)