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  }