github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/ext/ext.go (about)

     1  // Copyright 2019 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 ext implements readonly ext(2/3/4) filesystems.
    16  package ext
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  	"io"
    22  
    23  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    24  	"github.com/SagerNet/gvisor/pkg/context"
    25  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    26  	"github.com/SagerNet/gvisor/pkg/fd"
    27  	"github.com/SagerNet/gvisor/pkg/log"
    28  	"github.com/SagerNet/gvisor/pkg/sentry/fsimpl/ext/disklayout"
    29  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    30  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    31  )
    32  
    33  // Name is the name of this filesystem.
    34  const Name = "ext"
    35  
    36  // FilesystemType implements vfs.FilesystemType.
    37  //
    38  // +stateify savable
    39  type FilesystemType struct{}
    40  
    41  // getDeviceFd returns an io.ReaderAt to the underlying device.
    42  // Currently there are two ways of mounting an ext(2/3/4) fs:
    43  //   1. Specify a mount with our internal special MountType in the OCI spec.
    44  //   2. Expose the device to the container and mount it from application layer.
    45  func getDeviceFd(source string, opts vfs.GetFilesystemOptions) (io.ReaderAt, error) {
    46  	if opts.InternalData == nil {
    47  		// User mount call.
    48  		// TODO(b/134676337): Open the device specified by `source` and return that.
    49  		panic("unimplemented")
    50  	}
    51  
    52  	// GetFilesystem call originated from within the sentry.
    53  	devFd, ok := opts.InternalData.(int)
    54  	if !ok {
    55  		return nil, errors.New("internal data for ext fs must be an int containing the file descriptor to device")
    56  	}
    57  
    58  	if devFd < 0 {
    59  		return nil, fmt.Errorf("ext device file descriptor is not valid: %d", devFd)
    60  	}
    61  
    62  	// The fd.ReadWriter returned from fd.NewReadWriter() does not take ownership
    63  	// of the file descriptor and hence will not close it when it is garbage
    64  	// collected.
    65  	return fd.NewReadWriter(devFd), nil
    66  }
    67  
    68  // isCompatible checks if the superblock has feature sets which are compatible.
    69  // We only need to check the superblock incompatible feature set since we are
    70  // mounting readonly. We will also need to check readonly compatible feature
    71  // set when mounting for read/write.
    72  func isCompatible(sb disklayout.SuperBlock) bool {
    73  	// Please note that what is being checked is limited based on the fact that we
    74  	// are mounting readonly and that we are not journaling. When mounting
    75  	// read/write or with a journal, this must be reevaluated.
    76  	incompatFeatures := sb.IncompatibleFeatures()
    77  	if incompatFeatures.MetaBG {
    78  		log.Warningf("ext fs: meta block groups are not supported")
    79  		return false
    80  	}
    81  	if incompatFeatures.MMP {
    82  		log.Warningf("ext fs: multiple mount protection is not supported")
    83  		return false
    84  	}
    85  	if incompatFeatures.Encrypted {
    86  		log.Warningf("ext fs: encrypted inodes not supported")
    87  		return false
    88  	}
    89  	if incompatFeatures.InlineData {
    90  		log.Warningf("ext fs: inline files not supported")
    91  		return false
    92  	}
    93  	return true
    94  }
    95  
    96  // Name implements vfs.FilesystemType.Name.
    97  func (FilesystemType) Name() string {
    98  	return Name
    99  }
   100  
   101  // Release implements vfs.FilesystemType.Release.
   102  func (FilesystemType) Release(ctx context.Context) {}
   103  
   104  // GetFilesystem implements vfs.FilesystemType.GetFilesystem.
   105  func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
   106  	// TODO(b/134676337): Ensure that the user is mounting readonly. If not,
   107  	// EACCESS should be returned according to mount(2). Filesystem independent
   108  	// flags (like readonly) are currently not available in pkg/sentry/vfs.
   109  
   110  	devMinor, err := vfsObj.GetAnonBlockDevMinor()
   111  	if err != nil {
   112  		return nil, nil, err
   113  	}
   114  
   115  	dev, err := getDeviceFd(source, opts)
   116  	if err != nil {
   117  		return nil, nil, err
   118  	}
   119  
   120  	fs := filesystem{
   121  		dev:        dev,
   122  		inodeCache: make(map[uint32]*inode),
   123  		devMinor:   devMinor,
   124  	}
   125  	fs.vfsfs.Init(vfsObj, &fsType, &fs)
   126  	fs.sb, err = readSuperBlock(dev)
   127  	if err != nil {
   128  		fs.vfsfs.DecRef(ctx)
   129  		return nil, nil, err
   130  	}
   131  
   132  	if fs.sb.Magic() != linux.EXT_SUPER_MAGIC {
   133  		// mount(2) specifies that EINVAL should be returned if the superblock is
   134  		// invalid.
   135  		fs.vfsfs.DecRef(ctx)
   136  		return nil, nil, linuxerr.EINVAL
   137  	}
   138  
   139  	// Refuse to mount if the filesystem is incompatible.
   140  	if !isCompatible(fs.sb) {
   141  		fs.vfsfs.DecRef(ctx)
   142  		return nil, nil, linuxerr.EINVAL
   143  	}
   144  
   145  	fs.bgs, err = readBlockGroups(dev, fs.sb)
   146  	if err != nil {
   147  		fs.vfsfs.DecRef(ctx)
   148  		return nil, nil, err
   149  	}
   150  
   151  	rootInode, err := fs.getOrCreateInodeLocked(disklayout.RootDirInode)
   152  	if err != nil {
   153  		fs.vfsfs.DecRef(ctx)
   154  		return nil, nil, err
   155  	}
   156  	rootInode.incRef()
   157  
   158  	return &fs.vfsfs, &newDentry(rootInode).vfsd, nil
   159  }