github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/syscalls/linux/sys_stat.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 linux 16 17 import ( 18 "github.com/metacubex/gvisor/pkg/abi/linux" 19 "github.com/metacubex/gvisor/pkg/bits" 20 "github.com/metacubex/gvisor/pkg/errors/linuxerr" 21 "github.com/metacubex/gvisor/pkg/fspath" 22 "github.com/metacubex/gvisor/pkg/hostarch" 23 "github.com/metacubex/gvisor/pkg/sentry/arch" 24 "github.com/metacubex/gvisor/pkg/sentry/kernel" 25 "github.com/metacubex/gvisor/pkg/sentry/kernel/auth" 26 "github.com/metacubex/gvisor/pkg/sentry/vfs" 27 ) 28 29 // Stat implements Linux syscall stat(2). 30 func Stat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 31 pathAddr := args[0].Pointer() 32 statAddr := args[1].Pointer() 33 return 0, nil, fstatat(t, linux.AT_FDCWD, pathAddr, statAddr, 0 /* flags */) 34 } 35 36 // Lstat implements Linux syscall lstat(2). 37 func Lstat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 38 pathAddr := args[0].Pointer() 39 statAddr := args[1].Pointer() 40 return 0, nil, fstatat(t, linux.AT_FDCWD, pathAddr, statAddr, linux.AT_SYMLINK_NOFOLLOW) 41 } 42 43 // Newfstatat implements Linux syscall newfstatat, which backs fstatat(2). 44 func Newfstatat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 45 dirfd := args[0].Int() 46 pathAddr := args[1].Pointer() 47 statAddr := args[2].Pointer() 48 flags := args[3].Int() 49 return 0, nil, fstatat(t, dirfd, pathAddr, statAddr, flags) 50 } 51 52 func fstatat(t *kernel.Task, dirfd int32, pathAddr, statAddr hostarch.Addr, flags int32) error { 53 // TODO(b/270247637): gVisor does not yet support automount, so 54 // AT_NO_AUTOMOUNT flag is a no-op. 55 flags &= ^linux.AT_NO_AUTOMOUNT 56 if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW) != 0 { 57 return linuxerr.EINVAL 58 } 59 60 opts := vfs.StatOptions{ 61 Mask: linux.STATX_BASIC_STATS, 62 } 63 64 path, err := copyInPath(t, pathAddr) 65 if err != nil { 66 return err 67 } 68 69 root := t.FSContext().RootDirectory() 70 defer root.DecRef(t) 71 start := root 72 if !path.Absolute { 73 if !path.HasComponents() && flags&linux.AT_EMPTY_PATH == 0 { 74 return linuxerr.ENOENT 75 } 76 if dirfd == linux.AT_FDCWD { 77 start = t.FSContext().WorkingDirectory() 78 defer start.DecRef(t) 79 } else { 80 dirfile := t.GetFile(dirfd) 81 if dirfile == nil { 82 return linuxerr.EBADF 83 } 84 if !path.HasComponents() { 85 // Use FileDescription.Stat() instead of 86 // VirtualFilesystem.StatAt() for fstatat(fd, ""), since the 87 // former may be able to use opened file state to expedite the 88 // Stat. 89 statx, err := dirfile.Stat(t, opts) 90 dirfile.DecRef(t) 91 if err != nil { 92 return err 93 } 94 var stat linux.Stat 95 convertStatxToUserStat(t, &statx, &stat) 96 _, err = stat.CopyOut(t, statAddr) 97 return err 98 } 99 start = dirfile.VirtualDentry() 100 start.IncRef() 101 defer start.DecRef(t) 102 dirfile.DecRef(t) 103 } 104 } 105 106 statx, err := t.Kernel().VFS().StatAt(t, t.Credentials(), &vfs.PathOperation{ 107 Root: root, 108 Start: start, 109 Path: path, 110 FollowFinalSymlink: flags&linux.AT_SYMLINK_NOFOLLOW == 0, 111 }, &opts) 112 if err != nil { 113 return err 114 } 115 var stat linux.Stat 116 convertStatxToUserStat(t, &statx, &stat) 117 _, err = stat.CopyOut(t, statAddr) 118 return err 119 } 120 121 func timespecFromStatxTimestamp(sxts linux.StatxTimestamp) linux.Timespec { 122 return linux.Timespec{ 123 Sec: sxts.Sec, 124 Nsec: int64(sxts.Nsec), 125 } 126 } 127 128 // Fstat implements Linux syscall fstat(2). 129 func Fstat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 130 fd := args[0].Int() 131 statAddr := args[1].Pointer() 132 133 file := t.GetFile(fd) 134 if file == nil { 135 return 0, nil, linuxerr.EBADF 136 } 137 defer file.DecRef(t) 138 139 statx, err := file.Stat(t, vfs.StatOptions{ 140 Mask: linux.STATX_BASIC_STATS, 141 }) 142 if err != nil { 143 return 0, nil, err 144 } 145 var stat linux.Stat 146 convertStatxToUserStat(t, &statx, &stat) 147 _, err = stat.CopyOut(t, statAddr) 148 return 0, nil, err 149 } 150 151 // Statx implements Linux syscall statx(2). 152 func Statx(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 153 dirfd := args[0].Int() 154 pathAddr := args[1].Pointer() 155 flags := args[2].Int() 156 mask := args[3].Uint() 157 statxAddr := args[4].Pointer() 158 159 // TODO(b/270247637): gVisor does not yet support automount, so 160 // AT_NO_AUTOMOUNT flag is a no-op. 161 flags &= ^linux.AT_NO_AUTOMOUNT 162 163 if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW|linux.AT_STATX_SYNC_TYPE) != 0 { 164 return 0, nil, linuxerr.EINVAL 165 } 166 // Make sure that only one sync type option is set. 167 syncType := uint32(flags & linux.AT_STATX_SYNC_TYPE) 168 if syncType != 0 && !bits.IsPowerOfTwo32(syncType) { 169 return 0, nil, linuxerr.EINVAL 170 } 171 if mask&linux.STATX__RESERVED != 0 { 172 return 0, nil, linuxerr.EINVAL 173 } 174 175 opts := vfs.StatOptions{ 176 Mask: mask, 177 Sync: uint32(flags & linux.AT_STATX_SYNC_TYPE), 178 } 179 180 path, err := copyInPath(t, pathAddr) 181 if err != nil { 182 return 0, nil, err 183 } 184 185 root := t.FSContext().RootDirectory() 186 defer root.DecRef(t) 187 start := root 188 if !path.Absolute { 189 if !path.HasComponents() && flags&linux.AT_EMPTY_PATH == 0 { 190 return 0, nil, linuxerr.ENOENT 191 } 192 if dirfd == linux.AT_FDCWD { 193 start = t.FSContext().WorkingDirectory() 194 defer start.DecRef(t) 195 } else { 196 dirfile := t.GetFile(dirfd) 197 if dirfile == nil { 198 return 0, nil, linuxerr.EBADF 199 } 200 if !path.HasComponents() { 201 // Use FileDescription.Stat() instead of 202 // VirtualFilesystem.StatAt() for statx(fd, ""), since the 203 // former may be able to use opened file state to expedite the 204 // Stat. 205 statx, err := dirfile.Stat(t, opts) 206 dirfile.DecRef(t) 207 if err != nil { 208 return 0, nil, err 209 } 210 userifyStatx(t, &statx) 211 _, err = statx.CopyOut(t, statxAddr) 212 return 0, nil, err 213 } 214 start = dirfile.VirtualDentry() 215 start.IncRef() 216 defer start.DecRef(t) 217 dirfile.DecRef(t) 218 } 219 } 220 221 statx, err := t.Kernel().VFS().StatAt(t, t.Credentials(), &vfs.PathOperation{ 222 Root: root, 223 Start: start, 224 Path: path, 225 FollowFinalSymlink: flags&linux.AT_SYMLINK_NOFOLLOW == 0, 226 }, &opts) 227 if err != nil { 228 return 0, nil, err 229 } 230 userifyStatx(t, &statx) 231 _, err = statx.CopyOut(t, statxAddr) 232 return 0, nil, err 233 } 234 235 func userifyStatx(t *kernel.Task, statx *linux.Statx) { 236 userns := t.UserNamespace() 237 statx.UID = uint32(auth.KUID(statx.UID).In(userns).OrOverflow()) 238 statx.GID = uint32(auth.KGID(statx.GID).In(userns).OrOverflow()) 239 } 240 241 // Statfs implements Linux syscall statfs(2). 242 func Statfs(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 243 pathAddr := args[0].Pointer() 244 bufAddr := args[1].Pointer() 245 246 path, err := copyInPath(t, pathAddr) 247 if err != nil { 248 return 0, nil, err 249 } 250 tpop, err := getTaskPathOperation(t, linux.AT_FDCWD, path, disallowEmptyPath, followFinalSymlink) 251 if err != nil { 252 return 0, nil, err 253 } 254 defer tpop.Release(t) 255 256 statfs, err := t.Kernel().VFS().StatFSAt(t, t.Credentials(), &tpop.pop) 257 if err != nil { 258 return 0, nil, err 259 } 260 _, err = statfs.CopyOut(t, bufAddr) 261 return 0, nil, err 262 } 263 264 // Fstatfs implements Linux syscall fstatfs(2). 265 func Fstatfs(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 266 fd := args[0].Int() 267 bufAddr := args[1].Pointer() 268 269 tpop, err := getTaskPathOperation(t, fd, fspath.Path{}, allowEmptyPath, nofollowFinalSymlink) 270 if err != nil { 271 return 0, nil, err 272 } 273 defer tpop.Release(t) 274 275 statfs, err := t.Kernel().VFS().StatFSAt(t, t.Credentials(), &tpop.pop) 276 if err != nil { 277 return 0, nil, err 278 } 279 _, err = statfs.CopyOut(t, bufAddr) 280 return 0, nil, err 281 }