github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/linux/vfs2/setstat.go (about) 1 // Copyright 2020 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 vfs2 16 17 import ( 18 "github.com/SagerNet/gvisor/pkg/abi/linux" 19 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 20 "github.com/SagerNet/gvisor/pkg/fspath" 21 "github.com/SagerNet/gvisor/pkg/hostarch" 22 "github.com/SagerNet/gvisor/pkg/sentry/arch" 23 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 24 "github.com/SagerNet/gvisor/pkg/sentry/kernel/auth" 25 "github.com/SagerNet/gvisor/pkg/sentry/limits" 26 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 27 "github.com/SagerNet/gvisor/pkg/syserror" 28 ) 29 30 const chmodMask = 0777 | linux.S_ISUID | linux.S_ISGID | linux.S_ISVTX 31 32 // Chmod implements Linux syscall chmod(2). 33 func Chmod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 34 pathAddr := args[0].Pointer() 35 mode := args[1].ModeT() 36 return 0, nil, fchmodat(t, linux.AT_FDCWD, pathAddr, mode) 37 } 38 39 // Fchmodat implements Linux syscall fchmodat(2). 40 func Fchmodat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 41 dirfd := args[0].Int() 42 pathAddr := args[1].Pointer() 43 mode := args[2].ModeT() 44 return 0, nil, fchmodat(t, dirfd, pathAddr, mode) 45 } 46 47 func fchmodat(t *kernel.Task, dirfd int32, pathAddr hostarch.Addr, mode uint) error { 48 path, err := copyInPath(t, pathAddr) 49 if err != nil { 50 return err 51 } 52 53 return setstatat(t, dirfd, path, disallowEmptyPath, followFinalSymlink, &vfs.SetStatOptions{ 54 Stat: linux.Statx{ 55 Mask: linux.STATX_MODE, 56 Mode: uint16(mode & chmodMask), 57 }, 58 }) 59 } 60 61 // Fchmod implements Linux syscall fchmod(2). 62 func Fchmod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 63 fd := args[0].Int() 64 mode := args[1].ModeT() 65 66 file := t.GetFileVFS2(fd) 67 if file == nil { 68 return 0, nil, linuxerr.EBADF 69 } 70 defer file.DecRef(t) 71 72 return 0, nil, file.SetStat(t, vfs.SetStatOptions{ 73 Stat: linux.Statx{ 74 Mask: linux.STATX_MODE, 75 Mode: uint16(mode & chmodMask), 76 }, 77 }) 78 } 79 80 // Chown implements Linux syscall chown(2). 81 func Chown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 82 pathAddr := args[0].Pointer() 83 owner := args[1].Int() 84 group := args[2].Int() 85 return 0, nil, fchownat(t, linux.AT_FDCWD, pathAddr, owner, group, 0 /* flags */) 86 } 87 88 // Lchown implements Linux syscall lchown(2). 89 func Lchown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 90 pathAddr := args[0].Pointer() 91 owner := args[1].Int() 92 group := args[2].Int() 93 return 0, nil, fchownat(t, linux.AT_FDCWD, pathAddr, owner, group, linux.AT_SYMLINK_NOFOLLOW) 94 } 95 96 // Fchownat implements Linux syscall fchownat(2). 97 func Fchownat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 98 dirfd := args[0].Int() 99 pathAddr := args[1].Pointer() 100 owner := args[2].Int() 101 group := args[3].Int() 102 flags := args[4].Int() 103 return 0, nil, fchownat(t, dirfd, pathAddr, owner, group, flags) 104 } 105 106 func fchownat(t *kernel.Task, dirfd int32, pathAddr hostarch.Addr, owner, group, flags int32) error { 107 if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW) != 0 { 108 return linuxerr.EINVAL 109 } 110 111 path, err := copyInPath(t, pathAddr) 112 if err != nil { 113 return err 114 } 115 116 var opts vfs.SetStatOptions 117 if err := populateSetStatOptionsForChown(t, owner, group, &opts); err != nil { 118 return err 119 } 120 121 return setstatat(t, dirfd, path, shouldAllowEmptyPath(flags&linux.AT_EMPTY_PATH != 0), shouldFollowFinalSymlink(flags&linux.AT_SYMLINK_NOFOLLOW == 0), &opts) 122 } 123 124 func populateSetStatOptionsForChown(t *kernel.Task, owner, group int32, opts *vfs.SetStatOptions) error { 125 userns := t.UserNamespace() 126 if owner != -1 { 127 kuid := userns.MapToKUID(auth.UID(owner)) 128 if !kuid.Ok() { 129 return linuxerr.EINVAL 130 } 131 opts.Stat.Mask |= linux.STATX_UID 132 opts.Stat.UID = uint32(kuid) 133 } 134 if group != -1 { 135 kgid := userns.MapToKGID(auth.GID(group)) 136 if !kgid.Ok() { 137 return linuxerr.EINVAL 138 } 139 opts.Stat.Mask |= linux.STATX_GID 140 opts.Stat.GID = uint32(kgid) 141 } 142 return nil 143 } 144 145 // Fchown implements Linux syscall fchown(2). 146 func Fchown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 147 fd := args[0].Int() 148 owner := args[1].Int() 149 group := args[2].Int() 150 151 file := t.GetFileVFS2(fd) 152 if file == nil { 153 return 0, nil, linuxerr.EBADF 154 } 155 defer file.DecRef(t) 156 157 var opts vfs.SetStatOptions 158 if err := populateSetStatOptionsForChown(t, owner, group, &opts); err != nil { 159 return 0, nil, err 160 } 161 return 0, nil, file.SetStat(t, opts) 162 } 163 164 // Truncate implements Linux syscall truncate(2). 165 func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 166 addr := args[0].Pointer() 167 length := args[1].Int64() 168 169 if length < 0 { 170 return 0, nil, linuxerr.EINVAL 171 } 172 173 path, err := copyInPath(t, addr) 174 if err != nil { 175 return 0, nil, err 176 } 177 178 err = setstatat(t, linux.AT_FDCWD, path, disallowEmptyPath, followFinalSymlink, &vfs.SetStatOptions{ 179 Stat: linux.Statx{ 180 Mask: linux.STATX_SIZE, 181 Size: uint64(length), 182 }, 183 NeedWritePerm: true, 184 }) 185 return 0, nil, handleSetSizeError(t, err) 186 } 187 188 // Ftruncate implements Linux syscall ftruncate(2). 189 func Ftruncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 190 fd := args[0].Int() 191 length := args[1].Int64() 192 193 if length < 0 { 194 return 0, nil, linuxerr.EINVAL 195 } 196 197 file := t.GetFileVFS2(fd) 198 if file == nil { 199 return 0, nil, linuxerr.EBADF 200 } 201 defer file.DecRef(t) 202 203 if !file.IsWritable() { 204 return 0, nil, linuxerr.EINVAL 205 } 206 207 err := file.SetStat(t, vfs.SetStatOptions{ 208 Stat: linux.Statx{ 209 Mask: linux.STATX_SIZE, 210 Size: uint64(length), 211 }, 212 }) 213 return 0, nil, handleSetSizeError(t, err) 214 } 215 216 // Fallocate implements linux system call fallocate(2). 217 func Fallocate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 218 fd := args[0].Int() 219 mode := args[1].Uint64() 220 offset := args[2].Int64() 221 length := args[3].Int64() 222 223 file := t.GetFileVFS2(fd) 224 if file == nil { 225 return 0, nil, linuxerr.EBADF 226 } 227 defer file.DecRef(t) 228 229 if !file.IsWritable() { 230 return 0, nil, linuxerr.EBADF 231 } 232 if mode != 0 { 233 return 0, nil, linuxerr.ENOTSUP 234 } 235 if offset < 0 || length <= 0 { 236 return 0, nil, linuxerr.EINVAL 237 } 238 239 size := offset + length 240 if size < 0 { 241 return 0, nil, linuxerr.EFBIG 242 } 243 limit := limits.FromContext(t).Get(limits.FileSize).Cur 244 if uint64(size) >= limit { 245 t.SendSignal(&linux.SignalInfo{ 246 Signo: int32(linux.SIGXFSZ), 247 Code: linux.SI_USER, 248 }) 249 return 0, nil, linuxerr.EFBIG 250 } 251 252 return 0, nil, file.Allocate(t, mode, uint64(offset), uint64(length)) 253 } 254 255 // Utime implements Linux syscall utime(2). 256 func Utime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 257 pathAddr := args[0].Pointer() 258 timesAddr := args[1].Pointer() 259 260 path, err := copyInPath(t, pathAddr) 261 if err != nil { 262 return 0, nil, err 263 } 264 265 opts := vfs.SetStatOptions{ 266 Stat: linux.Statx{ 267 Mask: linux.STATX_ATIME | linux.STATX_MTIME, 268 }, 269 } 270 if timesAddr == 0 { 271 opts.Stat.Atime.Nsec = linux.UTIME_NOW 272 opts.Stat.Mtime.Nsec = linux.UTIME_NOW 273 } else { 274 var times linux.Utime 275 if _, err := times.CopyIn(t, timesAddr); err != nil { 276 return 0, nil, err 277 } 278 opts.Stat.Atime.Sec = times.Actime 279 opts.Stat.Mtime.Sec = times.Modtime 280 } 281 282 return 0, nil, setstatat(t, linux.AT_FDCWD, path, disallowEmptyPath, followFinalSymlink, &opts) 283 } 284 285 // Utimes implements Linux syscall utimes(2). 286 func Utimes(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 287 pathAddr := args[0].Pointer() 288 timesAddr := args[1].Pointer() 289 290 path, err := copyInPath(t, pathAddr) 291 if err != nil { 292 return 0, nil, err 293 } 294 295 var opts vfs.SetStatOptions 296 if err := populateSetStatOptionsForUtimes(t, timesAddr, &opts); err != nil { 297 return 0, nil, err 298 } 299 300 return 0, nil, setstatat(t, linux.AT_FDCWD, path, disallowEmptyPath, followFinalSymlink, &opts) 301 } 302 303 // Futimesat implements Linux syscall futimesat(2). 304 func Futimesat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 305 dirfd := args[0].Int() 306 pathAddr := args[1].Pointer() 307 timesAddr := args[2].Pointer() 308 309 // "If filename is NULL and dfd refers to an open file, then operate on the 310 // file. Otherwise look up filename, possibly using dfd as a starting 311 // point." - fs/utimes.c 312 var path fspath.Path 313 shouldAllowEmptyPath := allowEmptyPath 314 if dirfd == linux.AT_FDCWD || pathAddr != 0 { 315 var err error 316 path, err = copyInPath(t, pathAddr) 317 if err != nil { 318 return 0, nil, err 319 } 320 shouldAllowEmptyPath = disallowEmptyPath 321 } 322 323 var opts vfs.SetStatOptions 324 if err := populateSetStatOptionsForUtimes(t, timesAddr, &opts); err != nil { 325 return 0, nil, err 326 } 327 328 return 0, nil, setstatat(t, dirfd, path, shouldAllowEmptyPath, followFinalSymlink, &opts) 329 } 330 331 func populateSetStatOptionsForUtimes(t *kernel.Task, timesAddr hostarch.Addr, opts *vfs.SetStatOptions) error { 332 if timesAddr == 0 { 333 opts.Stat.Mask = linux.STATX_ATIME | linux.STATX_MTIME 334 opts.Stat.Atime.Nsec = linux.UTIME_NOW 335 opts.Stat.Mtime.Nsec = linux.UTIME_NOW 336 return nil 337 } 338 var times [2]linux.Timeval 339 if _, err := linux.CopyTimevalSliceIn(t, timesAddr, times[:]); err != nil { 340 return err 341 } 342 if times[0].Usec < 0 || times[0].Usec > 999999 || times[1].Usec < 0 || times[1].Usec > 999999 { 343 return linuxerr.EINVAL 344 } 345 opts.Stat.Mask = linux.STATX_ATIME | linux.STATX_MTIME 346 opts.Stat.Atime = linux.StatxTimestamp{ 347 Sec: times[0].Sec, 348 Nsec: uint32(times[0].Usec * 1000), 349 } 350 opts.Stat.Mtime = linux.StatxTimestamp{ 351 Sec: times[1].Sec, 352 Nsec: uint32(times[1].Usec * 1000), 353 } 354 return nil 355 } 356 357 // Utimensat implements Linux syscall utimensat(2). 358 func Utimensat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 359 dirfd := args[0].Int() 360 pathAddr := args[1].Pointer() 361 timesAddr := args[2].Pointer() 362 flags := args[3].Int() 363 364 // Linux requires that the UTIME_OMIT check occur before checking path or 365 // flags. 366 var opts vfs.SetStatOptions 367 if err := populateSetStatOptionsForUtimens(t, timesAddr, &opts); err != nil { 368 return 0, nil, err 369 } 370 if opts.Stat.Mask == 0 { 371 return 0, nil, nil 372 } 373 374 if flags&^linux.AT_SYMLINK_NOFOLLOW != 0 { 375 return 0, nil, linuxerr.EINVAL 376 } 377 378 // "If filename is NULL and dfd refers to an open file, then operate on the 379 // file. Otherwise look up filename, possibly using dfd as a starting 380 // point." - fs/utimes.c 381 var path fspath.Path 382 shouldAllowEmptyPath := allowEmptyPath 383 if dirfd == linux.AT_FDCWD || pathAddr != 0 { 384 var err error 385 path, err = copyInPath(t, pathAddr) 386 if err != nil { 387 return 0, nil, err 388 } 389 shouldAllowEmptyPath = disallowEmptyPath 390 } 391 392 return 0, nil, setstatat(t, dirfd, path, shouldAllowEmptyPath, shouldFollowFinalSymlink(flags&linux.AT_SYMLINK_NOFOLLOW == 0), &opts) 393 } 394 395 func populateSetStatOptionsForUtimens(t *kernel.Task, timesAddr hostarch.Addr, opts *vfs.SetStatOptions) error { 396 if timesAddr == 0 { 397 opts.Stat.Mask = linux.STATX_ATIME | linux.STATX_MTIME 398 opts.Stat.Atime.Nsec = linux.UTIME_NOW 399 opts.Stat.Mtime.Nsec = linux.UTIME_NOW 400 return nil 401 } 402 var times [2]linux.Timespec 403 if _, err := linux.CopyTimespecSliceIn(t, timesAddr, times[:]); err != nil { 404 return err 405 } 406 if times[0].Nsec != linux.UTIME_OMIT { 407 if times[0].Nsec != linux.UTIME_NOW && (times[0].Nsec < 0 || times[0].Nsec > 999999999) { 408 return linuxerr.EINVAL 409 } 410 opts.Stat.Mask |= linux.STATX_ATIME 411 opts.Stat.Atime = linux.StatxTimestamp{ 412 Sec: times[0].Sec, 413 Nsec: uint32(times[0].Nsec), 414 } 415 } 416 if times[1].Nsec != linux.UTIME_OMIT { 417 if times[1].Nsec != linux.UTIME_NOW && (times[1].Nsec < 0 || times[1].Nsec > 999999999) { 418 return linuxerr.EINVAL 419 } 420 opts.Stat.Mask |= linux.STATX_MTIME 421 opts.Stat.Mtime = linux.StatxTimestamp{ 422 Sec: times[1].Sec, 423 Nsec: uint32(times[1].Nsec), 424 } 425 } 426 return nil 427 } 428 429 func setstatat(t *kernel.Task, dirfd int32, path fspath.Path, shouldAllowEmptyPath shouldAllowEmptyPath, shouldFollowFinalSymlink shouldFollowFinalSymlink, opts *vfs.SetStatOptions) error { 430 root := t.FSContext().RootDirectoryVFS2() 431 defer root.DecRef(t) 432 start := root 433 if !path.Absolute { 434 if !path.HasComponents() && !bool(shouldAllowEmptyPath) { 435 return syserror.ENOENT 436 } 437 if dirfd == linux.AT_FDCWD { 438 start = t.FSContext().WorkingDirectoryVFS2() 439 defer start.DecRef(t) 440 } else { 441 dirfile := t.GetFileVFS2(dirfd) 442 if dirfile == nil { 443 return linuxerr.EBADF 444 } 445 if !path.HasComponents() { 446 // Use FileDescription.SetStat() instead of 447 // VirtualFilesystem.SetStatAt(), since the former may be able 448 // to use opened file state to expedite the SetStat. 449 err := dirfile.SetStat(t, *opts) 450 dirfile.DecRef(t) 451 return err 452 } 453 start = dirfile.VirtualDentry() 454 start.IncRef() 455 defer start.DecRef(t) 456 dirfile.DecRef(t) 457 } 458 } 459 return t.Kernel().VFS().SetStatAt(t, t.Credentials(), &vfs.PathOperation{ 460 Root: root, 461 Start: start, 462 Path: path, 463 FollowFinalSymlink: bool(shouldFollowFinalSymlink), 464 }, opts) 465 } 466 467 func handleSetSizeError(t *kernel.Task, err error) error { 468 if err == syserror.ErrExceedsFileSizeLimit { 469 // Convert error to EFBIG and send a SIGXFSZ per setrlimit(2). 470 t.SendSignal(kernel.SignalInfoNoInfo(linux.SIGXFSZ, t, t)) 471 return linuxerr.EFBIG 472 } 473 return err 474 }