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  }