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 }