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