github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/gofer/inode_state.go (about) 1 // Copyright 2018 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 gofer 16 17 import ( 18 "errors" 19 "fmt" 20 "path/filepath" 21 "strings" 22 23 "github.com/SagerNet/gvisor/pkg/context" 24 "github.com/SagerNet/gvisor/pkg/p9" 25 "github.com/SagerNet/gvisor/pkg/sentry/device" 26 "github.com/SagerNet/gvisor/pkg/sentry/fs" 27 "github.com/SagerNet/gvisor/pkg/sentry/kernel/time" 28 ) 29 30 // Some fs implementations may not support atime, ctime, or mtime in getattr. 31 // The unstable() logic would try to use clock time for them. However, we do not 32 // want to use such time during S/R as that would cause restore timestamp 33 // checking failure. Hence a dummy stable-time clock is needed. 34 // 35 // Note that application-visible UnstableAttrs either come from CachingInodeOps 36 // (in which case they are saved), or they are requested from the gofer on each 37 // stat (for non-caching), so the dummy time only affects the modification 38 // timestamp check. 39 type dummyClock struct { 40 time.Clock 41 } 42 43 // Now returns a stable dummy time. 44 func (d *dummyClock) Now() time.Time { 45 return time.Time{} 46 } 47 48 type dummyClockContext struct { 49 context.Context 50 } 51 52 // Value implements context.Context 53 func (d *dummyClockContext) Value(key interface{}) interface{} { 54 switch key { 55 case time.CtxRealtimeClock: 56 return &dummyClock{} 57 default: 58 return d.Context.Value(key) 59 } 60 } 61 62 // beforeSave is invoked by stateify. 63 func (i *inodeFileState) beforeSave() { 64 if _, ok := i.s.inodeMappings[i.sattr.InodeID]; !ok { 65 panic(fmt.Sprintf("failed to find path for inode number %d. Device %s contains %s", i.sattr.InodeID, i.s.connID, fs.InodeMappings(i.s.inodeMappings))) 66 } 67 if i.sattr.Type == fs.RegularFile { 68 uattr, err := i.unstableAttr(&dummyClockContext{context.Background()}) 69 if err != nil { 70 panic(&fs.ErrSaveRejection{ 71 Err: fmt.Errorf("failed to get unstable atttribute of %s: %w", i.s.inodeMappings[i.sattr.InodeID], err), 72 }) 73 } 74 i.savedUAttr = &uattr 75 } 76 } 77 78 // saveLoading is invoked by stateify. 79 func (i *inodeFileState) saveLoading() struct{} { 80 return struct{}{} 81 } 82 83 // splitAbsolutePath splits the path on slashes ignoring the leading slash. 84 func splitAbsolutePath(path string) []string { 85 if len(path) == 0 { 86 panic("There is no path!") 87 } 88 if path != filepath.Clean(path) { 89 panic(fmt.Sprintf("path %q is not clean", path)) 90 } 91 // This case is to return {} rather than {""} 92 if path == "/" { 93 return []string{} 94 } 95 if path[0] != '/' { 96 panic(fmt.Sprintf("path %q is not absolute", path)) 97 } 98 99 s := strings.Split(path, "/") 100 101 // Since p is absolute, the first component of s 102 // is an empty string. We must remove that. 103 return s[1:] 104 } 105 106 // loadLoading is invoked by stateify. 107 func (i *inodeFileState) loadLoading(_ struct{}) { 108 i.loading.Lock() 109 } 110 111 // afterLoad is invoked by stateify. 112 // +checklocks:i.loading 113 func (i *inodeFileState) afterLoad() { 114 load := func() (err error) { 115 // Manually restore the p9.File. 116 name, ok := i.s.inodeMappings[i.sattr.InodeID] 117 if !ok { 118 // This should be impossible, see assertion in 119 // beforeSave. 120 return fmt.Errorf("failed to find path for inode number %d. Device %s contains %s", i.sattr.InodeID, i.s.connID, fs.InodeMappings(i.s.inodeMappings)) 121 } 122 ctx := &dummyClockContext{context.Background()} 123 124 _, i.file, err = i.s.attach.walk(ctx, splitAbsolutePath(name)) 125 if err != nil { 126 return fs.ErrCorruption{fmt.Errorf("failed to walk to %q: %v", name, err)} 127 } 128 129 // Remap the saved inode number into the gofer device using the 130 // actual device and actual inode that exists in our new 131 // environment. 132 qid, mask, attrs, err := i.file.getAttr(ctx, p9.AttrMaskAll()) 133 if err != nil { 134 return fs.ErrCorruption{fmt.Errorf("failed to get file attributes of %s: %v", name, err)} 135 } 136 if !mask.RDev { 137 return fs.ErrCorruption{fmt.Errorf("file %s lacks device", name)} 138 } 139 i.key = device.MultiDeviceKey{ 140 Device: attrs.RDev, 141 SecondaryDevice: i.s.connID, 142 Inode: qid.Path, 143 } 144 if !goferDevice.Load(i.key, i.sattr.InodeID) { 145 return fs.ErrCorruption{fmt.Errorf("gofer device %s -> %d conflict in gofer device mappings: %s", i.key, i.sattr.InodeID, goferDevice)} 146 } 147 148 if i.sattr.Type == fs.RegularFile { 149 env, ok := fs.CurrentRestoreEnvironment() 150 if !ok { 151 return errors.New("missing restore environment") 152 } 153 uattr := unstable(ctx, mask, attrs, i.s.mounter, i.s.client) 154 if env.ValidateFileSize && uattr.Size != i.savedUAttr.Size { 155 return fs.ErrCorruption{fmt.Errorf("file size has changed for %s: previously %d, now %d", i.s.inodeMappings[i.sattr.InodeID], i.savedUAttr.Size, uattr.Size)} 156 } 157 if env.ValidateFileTimestamp && uattr.ModificationTime != i.savedUAttr.ModificationTime { 158 return fs.ErrCorruption{fmt.Errorf("file modification time has changed for %s: previously %v, now %v", i.s.inodeMappings[i.sattr.InodeID], i.savedUAttr.ModificationTime, uattr.ModificationTime)} 159 } 160 i.savedUAttr = nil 161 } 162 163 // See comment on i.loading(). This only unlocks on the 164 // non-error path. 165 i.loading.Unlock() // +checklocksforce: per comment. 166 return nil 167 } 168 169 fs.Async(fs.CatchError(load)) 170 }