gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/erofs/filesystem.go (about)

     1  // Copyright 2023 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 erofs
    16  
    17  import (
    18  	"gvisor.dev/gvisor/pkg/abi/linux"
    19  	"gvisor.dev/gvisor/pkg/context"
    20  	"gvisor.dev/gvisor/pkg/erofs"
    21  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    22  	"gvisor.dev/gvisor/pkg/fspath"
    23  	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
    24  	"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
    25  	"gvisor.dev/gvisor/pkg/sentry/vfs"
    26  )
    27  
    28  // step resolves rp.Component() to an existing file, starting from the given directory.
    29  //
    30  // step is loosely analogous to fs/namei.c:walk_component().
    31  //
    32  // Preconditions:
    33  //   - !rp.Done().
    34  func step(ctx context.Context, rp *vfs.ResolvingPath, d *dentry) (*dentry, bool, error) {
    35  	if !d.inode.IsDir() {
    36  		return nil, false, linuxerr.ENOTDIR
    37  	}
    38  	if err := d.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil {
    39  		return nil, false, err
    40  	}
    41  	name := rp.Component()
    42  	if name == "." {
    43  		rp.Advance()
    44  		return d, false, nil
    45  	}
    46  	if name == ".." {
    47  		parent := d.parent.Load()
    48  		if isRoot, err := rp.CheckRoot(ctx, &d.vfsd); err != nil {
    49  			return nil, false, err
    50  		} else if isRoot || parent == nil {
    51  			rp.Advance()
    52  			return d, false, nil
    53  		}
    54  		if err := rp.CheckMount(ctx, &parent.vfsd); err != nil {
    55  			return nil, false, err
    56  		}
    57  		rp.Advance()
    58  		return parent, false, nil
    59  	}
    60  	if len(name) > erofs.MaxNameLen {
    61  		return nil, false, linuxerr.ENAMETOOLONG
    62  	}
    63  	child, err := d.lookup(ctx, name)
    64  	if err != nil {
    65  		return nil, false, err
    66  	}
    67  	if err := rp.CheckMount(ctx, &child.vfsd); err != nil {
    68  		return nil, false, err
    69  	}
    70  	if child.inode.IsSymlink() && rp.ShouldFollowSymlink() {
    71  		target, err := child.inode.Readlink()
    72  		if err != nil {
    73  			return nil, false, err
    74  		}
    75  		followedSymlink, err := rp.HandleSymlink(target)
    76  		return d, followedSymlink, err
    77  	}
    78  	rp.Advance()
    79  	return child, false, nil
    80  }
    81  
    82  // walkParentDir resolves all but the last path component of rp to an existing
    83  // directory, starting from the gvien directory. It does not check that the
    84  // returned directory is searchable by the provider of rp.
    85  //
    86  // walkParentDir is loosely analogous to Linux's fs/namei.c:path_parentat().
    87  //
    88  // Preconditions:
    89  //   - !rp.Done().
    90  func walkParentDir(ctx context.Context, rp *vfs.ResolvingPath, d *dentry) (*dentry, error) {
    91  	for !rp.Final() {
    92  		next, _, err := step(ctx, rp, d)
    93  		if err != nil {
    94  			return nil, err
    95  		}
    96  		d = next
    97  	}
    98  	if !d.inode.IsDir() {
    99  		return nil, linuxerr.ENOTDIR
   100  	}
   101  	return d, nil
   102  }
   103  
   104  // resolve resolves rp to an existing file.
   105  //
   106  // resolve is loosely analogous to Linux's fs/namei.c:path_lookupat().
   107  func resolve(ctx context.Context, rp *vfs.ResolvingPath) (*dentry, error) {
   108  	d := rp.Start().Impl().(*dentry)
   109  	for !rp.Done() {
   110  		next, _, err := step(ctx, rp, d)
   111  		if err != nil {
   112  			return nil, err
   113  		}
   114  		d = next
   115  	}
   116  	if rp.MustBeDir() && !d.inode.IsDir() {
   117  		return nil, linuxerr.ENOTDIR
   118  	}
   119  	return d, nil
   120  }
   121  
   122  // doCreateAt checks that creating a file at rp is permitted.
   123  //
   124  // doCreateAt is loosely analogous to a conjunction of Linux's
   125  // fs/namei.c:filename_create() and done_path_create().
   126  //
   127  // Preconditions:
   128  //   - !rp.Done().
   129  //   - For the final path component in rp, !rp.ShouldFollowSymlink().
   130  func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir bool) error {
   131  	parentDir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry))
   132  	if err != nil {
   133  		return err
   134  	}
   135  	// Order of checks is important. First check if parent directory can be
   136  	// executed, then check for existence, and lastly check if mount is writable.
   137  	if err := parentDir.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil {
   138  		return err
   139  	}
   140  	name := rp.Component()
   141  	if name == "." || name == ".." {
   142  		return linuxerr.EEXIST
   143  	}
   144  	if len(name) > erofs.MaxNameLen {
   145  		return linuxerr.ENAMETOOLONG
   146  	}
   147  	if _, err := parentDir.lookup(ctx, name); err == nil {
   148  		return linuxerr.EEXIST
   149  	} else if !linuxerr.Equals(linuxerr.ENOENT, err) {
   150  		return err
   151  	}
   152  	if !dir && rp.MustBeDir() {
   153  		return linuxerr.ENOENT
   154  	}
   155  	return linuxerr.EROFS
   156  }
   157  
   158  // Sync implements vfs.FilesystemImpl.Sync.
   159  func (fs *filesystem) Sync(ctx context.Context) error {
   160  	return nil
   161  }
   162  
   163  // AccessAt implements vfs.FilesystemImpl.AccessAt.
   164  func (fs *filesystem) AccessAt(ctx context.Context, rp *vfs.ResolvingPath, creds *auth.Credentials, ats vfs.AccessTypes) error {
   165  	d, err := resolve(ctx, rp)
   166  	if err != nil {
   167  		return err
   168  	}
   169  	if ats.MayWrite() {
   170  		return linuxerr.EROFS
   171  	}
   172  	return d.inode.checkPermissions(creds, ats)
   173  }
   174  
   175  // GetDentryAt implements vfs.FilesystemImpl.GetDentryAt.
   176  func (fs *filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) {
   177  	d, err := resolve(ctx, rp)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	if opts.CheckSearchable {
   182  		if !d.inode.IsDir() {
   183  			return nil, linuxerr.ENOTDIR
   184  		}
   185  		if err := d.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil {
   186  			return nil, err
   187  		}
   188  	}
   189  	d.IncRef()
   190  	return &d.vfsd, nil
   191  }
   192  
   193  // GetParentDentryAt implements vfs.FilesystemImpl.GetParentDentryAt.
   194  func (fs *filesystem) GetParentDentryAt(ctx context.Context, rp *vfs.ResolvingPath) (*vfs.Dentry, error) {
   195  	dir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry))
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  	dir.IncRef()
   200  	return &dir.vfsd, nil
   201  }
   202  
   203  // LinkAt implements vfs.FilesystemImpl.LinkAt.
   204  func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error {
   205  	return fs.doCreateAt(ctx, rp, false /* dir */)
   206  }
   207  
   208  // MkdirAt implements vfs.FilesystemImpl.MkdirAt.
   209  func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MkdirOptions) error {
   210  	return fs.doCreateAt(ctx, rp, true /* dir */)
   211  }
   212  
   213  // MknodAt implements vfs.FilesystemImpl.MknodAt.
   214  func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error {
   215  	return fs.doCreateAt(ctx, rp, false /* dir */)
   216  }
   217  
   218  // OpenAt implements vfs.FilesystemImpl.OpenAt.
   219  func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
   220  	if opts.Flags&linux.O_TMPFILE != 0 {
   221  		return nil, linuxerr.EOPNOTSUPP
   222  	}
   223  
   224  	if opts.Flags&linux.O_CREAT == 0 {
   225  		d, err := resolve(ctx, rp)
   226  		if err != nil {
   227  			return nil, err
   228  		}
   229  		return d.open(ctx, rp, &opts)
   230  	}
   231  
   232  	mustCreate := opts.Flags&linux.O_EXCL != 0
   233  	start := rp.Start().Impl().(*dentry)
   234  	if rp.Done() {
   235  		// Reject attempts to open mount root directory with O_CREAT.
   236  		if rp.MustBeDir() {
   237  			return nil, linuxerr.EISDIR
   238  		}
   239  		if mustCreate {
   240  			return nil, linuxerr.EEXIST
   241  		}
   242  		return start.open(ctx, rp, &opts)
   243  	}
   244  afterTrailingSymlink:
   245  	parentDir, err := walkParentDir(ctx, rp, start)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  	// Check for search permission in the parent directory.
   250  	if err := parentDir.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil {
   251  		return nil, err
   252  	}
   253  	// Reject attempts to open directories with O_CREAT.
   254  	if rp.MustBeDir() {
   255  		return nil, linuxerr.EISDIR
   256  	}
   257  	child, followedSymlink, err := step(ctx, rp, parentDir)
   258  	if followedSymlink {
   259  		if mustCreate {
   260  			// EEXIST must be returned if an existing symlink is opened with O_EXCL.
   261  			return nil, linuxerr.EEXIST
   262  		}
   263  		if err != nil {
   264  			// If followedSymlink && err != nil, then this symlink resolution error
   265  			// must be handled by the VFS layer.
   266  			return nil, err
   267  		}
   268  		start = parentDir
   269  		goto afterTrailingSymlink
   270  	}
   271  	if linuxerr.Equals(linuxerr.ENOENT, err) {
   272  		return nil, linuxerr.EROFS
   273  	}
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	if mustCreate {
   278  		return nil, linuxerr.EEXIST
   279  	}
   280  	if rp.MustBeDir() && !child.inode.IsDir() {
   281  		return nil, linuxerr.ENOTDIR
   282  	}
   283  	return child.open(ctx, rp, &opts)
   284  }
   285  
   286  // ReadlinkAt implements vfs.FilesystemImpl.ReadlinkAt.
   287  func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (string, error) {
   288  	d, err := resolve(ctx, rp)
   289  	if err != nil {
   290  		return "", err
   291  	}
   292  	return d.inode.Readlink()
   293  }
   294  
   295  // RenameAt implements vfs.FilesystemImpl.RenameAt.
   296  func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error {
   297  	// Resolve newParent first to verify that it's on this Mount.
   298  	newParentDir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry))
   299  	if err != nil {
   300  		return err
   301  	}
   302  	newName := rp.Component()
   303  	if len(newName) > erofs.MaxNameLen {
   304  		return linuxerr.ENAMETOOLONG
   305  	}
   306  	mnt := rp.Mount()
   307  	if mnt != oldParentVD.Mount() {
   308  		return linuxerr.EXDEV
   309  	}
   310  	if err := newParentDir.inode.checkPermissions(rp.Credentials(), vfs.MayWrite|vfs.MayExec); err != nil {
   311  		return err
   312  	}
   313  	oldParentDir := oldParentVD.Dentry().Impl().(*dentry)
   314  	if err := oldParentDir.inode.checkPermissions(rp.Credentials(), vfs.MayWrite|vfs.MayExec); err != nil {
   315  		return err
   316  	}
   317  	return linuxerr.EROFS
   318  }
   319  
   320  // RmdirAt implements vfs.FilesystemImpl.RmdirAt.
   321  func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error {
   322  	parentDir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry))
   323  	if err != nil {
   324  		return err
   325  	}
   326  	if err := parentDir.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil {
   327  		return err
   328  	}
   329  	name := rp.Component()
   330  	if name == "." {
   331  		return linuxerr.EINVAL
   332  	}
   333  	if name == ".." {
   334  		return linuxerr.ENOTEMPTY
   335  	}
   336  	return linuxerr.EROFS
   337  }
   338  
   339  // SetStatAt implements vfs.FilesystemImpl.SetStatAt.
   340  func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error {
   341  	if _, err := resolve(ctx, rp); err != nil {
   342  		return err
   343  	}
   344  	return linuxerr.EROFS
   345  }
   346  
   347  // StatAt implements vfs.FilesystemImpl.StatAt.
   348  func (fs *filesystem) StatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.StatOptions) (linux.Statx, error) {
   349  	d, err := resolve(ctx, rp)
   350  	if err != nil {
   351  		return linux.Statx{}, err
   352  	}
   353  	var stat linux.Statx
   354  	d.inode.statTo(&stat)
   355  	return stat, nil
   356  }
   357  
   358  // StatFSAt implements vfs.FilesystemImpl.StatFSAt.
   359  func (fs *filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linux.Statfs, error) {
   360  	if _, err := resolve(ctx, rp); err != nil {
   361  		return linux.Statfs{}, err
   362  	}
   363  	return fs.statFS(), nil
   364  }
   365  
   366  // SymlinkAt implements vfs.FilesystemImpl.SymlinkAt.
   367  func (fs *filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, target string) error {
   368  	return fs.doCreateAt(ctx, rp, false /* dir */)
   369  }
   370  
   371  // UnlinkAt implements vfs.FilesystemImpl.UnlinkAt.
   372  func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error {
   373  	parentDir, err := walkParentDir(ctx, rp, rp.Start().Impl().(*dentry))
   374  	if err != nil {
   375  		return err
   376  	}
   377  	if err := parentDir.inode.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil {
   378  		return err
   379  	}
   380  	name := rp.Component()
   381  	if name == "." || name == ".." {
   382  		return linuxerr.EISDIR
   383  	}
   384  	return linuxerr.EROFS
   385  }
   386  
   387  // BoundEndpointAt implements vfs.FilesystemImpl.BoundEndpointAt.
   388  func (fs *filesystem) BoundEndpointAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.BoundEndpointOptions) (transport.BoundEndpoint, error) {
   389  	d, err := resolve(ctx, rp)
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  	if err := d.inode.checkPermissions(rp.Credentials(), vfs.MayWrite); err != nil {
   394  		return nil, err
   395  	}
   396  	return nil, linuxerr.ECONNREFUSED
   397  }
   398  
   399  // ListXattrAt implements vfs.FilesystemImpl.ListXattrAt.
   400  func (fs *filesystem) ListXattrAt(ctx context.Context, rp *vfs.ResolvingPath, size uint64) ([]string, error) {
   401  	if _, err := resolve(ctx, rp); err != nil {
   402  		return nil, err
   403  	}
   404  	return nil, linuxerr.ENOTSUP
   405  }
   406  
   407  // GetXattrAt implements vfs.FilesystemImpl.GetXattrAt.
   408  func (fs *filesystem) GetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetXattrOptions) (string, error) {
   409  	if _, err := resolve(ctx, rp); err != nil {
   410  		return "", err
   411  	}
   412  	return "", linuxerr.ENOTSUP
   413  }
   414  
   415  // SetXattrAt implements vfs.FilesystemImpl.SetXattrAt.
   416  func (fs *filesystem) SetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetXattrOptions) error {
   417  	if _, err := resolve(ctx, rp); err != nil {
   418  		return err
   419  	}
   420  	return linuxerr.EROFS
   421  }
   422  
   423  // RemoveXattrAt implements vfs.FilesystemImpl.RemoveXattrAt.
   424  func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath, name string) error {
   425  	if _, err := resolve(ctx, rp); err != nil {
   426  		return err
   427  	}
   428  	return linuxerr.EROFS
   429  }
   430  
   431  // PrependPath implements vfs.FilesystemImpl.PrependPath.
   432  func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
   433  	return genericPrependPath(vfsroot, vd.Mount(), vd.Dentry().Impl().(*dentry), b)
   434  }
   435  
   436  // MountOptions implements vfs.FilesystemImpl.MountOptions.
   437  func (fs *filesystem) MountOptions() string {
   438  	return fs.mopts
   439  }
   440  
   441  // IsDescendant implements vfs.FilesystemImpl.IsDescendant.
   442  func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
   443  	return genericIsDescendant(vfsroot.Dentry(), vd.Dentry().Impl().(*dentry))
   444  }