github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsbridge/fs.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 fsbridge
    16  
    17  import (
    18  	"io"
    19  	"strings"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/memmap"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    27  	"github.com/SagerNet/gvisor/pkg/syserror"
    28  	"github.com/SagerNet/gvisor/pkg/usermem"
    29  )
    30  
    31  // fsFile implements File interface over fs.File.
    32  //
    33  // +stateify savable
    34  type fsFile struct {
    35  	file *fs.File
    36  }
    37  
    38  var _ File = (*fsFile)(nil)
    39  
    40  // NewFSFile creates a new File over fs.File.
    41  func NewFSFile(file *fs.File) File {
    42  	return &fsFile{file: file}
    43  }
    44  
    45  // PathnameWithDeleted implements File.
    46  func (f *fsFile) PathnameWithDeleted(ctx context.Context) string {
    47  	root := fs.RootFromContext(ctx)
    48  	if root == nil {
    49  		// This doesn't correspond to anything in Linux because the vfs is
    50  		// global there.
    51  		return ""
    52  	}
    53  	defer root.DecRef(ctx)
    54  
    55  	name, _ := f.file.Dirent.FullName(root)
    56  	return name
    57  }
    58  
    59  // ReadFull implements File.
    60  func (f *fsFile) ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
    61  	var total int64
    62  	for dst.NumBytes() > 0 {
    63  		n, err := f.file.Preadv(ctx, dst, offset+total)
    64  		total += n
    65  		if err == io.EOF && total != 0 {
    66  			return total, io.ErrUnexpectedEOF
    67  		} else if err != nil {
    68  			return total, err
    69  		}
    70  		dst = dst.DropFirst64(n)
    71  	}
    72  	return total, nil
    73  }
    74  
    75  // ConfigureMMap implements File.
    76  func (f *fsFile) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
    77  	return f.file.ConfigureMMap(ctx, opts)
    78  }
    79  
    80  // Type implements File.
    81  func (f *fsFile) Type(context.Context) (linux.FileMode, error) {
    82  	return linux.FileMode(f.file.Dirent.Inode.StableAttr.Type.LinuxType()), nil
    83  }
    84  
    85  // IncRef implements File.
    86  func (f *fsFile) IncRef() {
    87  	f.file.IncRef()
    88  }
    89  
    90  // DecRef implements File.
    91  func (f *fsFile) DecRef(ctx context.Context) {
    92  	f.file.DecRef(ctx)
    93  }
    94  
    95  // fsLookup implements Lookup interface using fs.File.
    96  //
    97  // +stateify savable
    98  type fsLookup struct {
    99  	mntns *fs.MountNamespace
   100  
   101  	root       *fs.Dirent
   102  	workingDir *fs.Dirent
   103  }
   104  
   105  var _ Lookup = (*fsLookup)(nil)
   106  
   107  // NewFSLookup creates a new Lookup using VFS1.
   108  func NewFSLookup(mntns *fs.MountNamespace, root, workingDir *fs.Dirent) Lookup {
   109  	return &fsLookup{
   110  		mntns:      mntns,
   111  		root:       root,
   112  		workingDir: workingDir,
   113  	}
   114  }
   115  
   116  // OpenPath implements Lookup.
   117  func (l *fsLookup) OpenPath(ctx context.Context, path string, opts vfs.OpenOptions, remainingTraversals *uint, resolveFinal bool) (File, error) {
   118  	var d *fs.Dirent
   119  	var err error
   120  	if resolveFinal {
   121  		d, err = l.mntns.FindInode(ctx, l.root, l.workingDir, path, remainingTraversals)
   122  	} else {
   123  		d, err = l.mntns.FindLink(ctx, l.root, l.workingDir, path, remainingTraversals)
   124  	}
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	defer d.DecRef(ctx)
   129  
   130  	if !resolveFinal && fs.IsSymlink(d.Inode.StableAttr) {
   131  		return nil, linuxerr.ELOOP
   132  	}
   133  
   134  	fsPerm := openOptionsToPermMask(&opts)
   135  	if err := d.Inode.CheckPermission(ctx, fsPerm); err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	// If they claim it's a directory, then make sure.
   140  	if strings.HasSuffix(path, "/") {
   141  		if d.Inode.StableAttr.Type != fs.Directory {
   142  			return nil, syserror.ENOTDIR
   143  		}
   144  	}
   145  
   146  	if opts.FileExec && d.Inode.StableAttr.Type != fs.RegularFile {
   147  		ctx.Infof("%q is not a regular file: %v", path, d.Inode.StableAttr.Type)
   148  		return nil, linuxerr.EACCES
   149  	}
   150  
   151  	f, err := d.Inode.GetFile(ctx, d, flagsToFileFlags(opts.Flags))
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	return &fsFile{file: f}, nil
   157  }
   158  
   159  func openOptionsToPermMask(opts *vfs.OpenOptions) fs.PermMask {
   160  	mode := opts.Flags & linux.O_ACCMODE
   161  	return fs.PermMask{
   162  		Read:    mode == linux.O_RDONLY || mode == linux.O_RDWR,
   163  		Write:   mode == linux.O_WRONLY || mode == linux.O_RDWR,
   164  		Execute: opts.FileExec,
   165  	}
   166  }
   167  
   168  func flagsToFileFlags(flags uint32) fs.FileFlags {
   169  	return fs.FileFlags{
   170  		Direct:      flags&linux.O_DIRECT != 0,
   171  		DSync:       flags&(linux.O_DSYNC|linux.O_SYNC) != 0,
   172  		Sync:        flags&linux.O_SYNC != 0,
   173  		NonBlocking: flags&linux.O_NONBLOCK != 0,
   174  		Read:        (flags & linux.O_ACCMODE) != linux.O_WRONLY,
   175  		Write:       (flags & linux.O_ACCMODE) != linux.O_RDONLY,
   176  		Append:      flags&linux.O_APPEND != 0,
   177  		Directory:   flags&linux.O_DIRECTORY != 0,
   178  		Async:       flags&linux.O_ASYNC != 0,
   179  		LargeFile:   flags&linux.O_LARGEFILE != 0,
   180  		Truncate:    flags&linux.O_TRUNC != 0,
   181  	}
   182  }