2019-04-29 21:25:05 +00:00
// Copyright 2018 The gVisor Authors.
2018-04-27 17:37:02 +00:00
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gofer
import (
2018-05-08 18:36:11 +00:00
"errors"
2018-04-27 17:37:02 +00:00
"fmt"
2018-06-29 21:46:45 +00:00
"path/filepath"
2018-04-27 17:37:02 +00:00
"strings"
2019-06-13 23:49:09 +00:00
"gvisor.dev/gvisor/pkg/p9"
"gvisor.dev/gvisor/pkg/sentry/context"
"gvisor.dev/gvisor/pkg/sentry/device"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel/time"
2018-04-27 17:37:02 +00:00
)
// Some fs implementations may not support atime, ctime, or mtime in getattr.
// The unstable() logic would try to use clock time for them. However, we do not
// want to use such time during S/R as that would cause restore timestamp
// checking failure. Hence a dummy stable-time clock is needed.
//
// Note that application-visible UnstableAttrs either come from CachingInodeOps
// (in which case they are saved), or they are requested from the gofer on each
// stat (for non-caching), so the dummy time only affects the modification
// timestamp check.
type dummyClock struct {
time . Clock
}
// Now returns a stable dummy time.
func ( d * dummyClock ) Now ( ) time . Time {
return time . Time { }
}
type dummyClockContext struct {
context . Context
}
// Value implements context.Context
func ( d * dummyClockContext ) Value ( key interface { } ) interface { } {
switch key {
case time . CtxRealtimeClock :
return & dummyClock { }
default :
return d . Context . Value ( key )
}
}
// beforeSave is invoked by stateify.
func ( i * inodeFileState ) beforeSave ( ) {
if _ , ok := i . s . inodeMappings [ i . sattr . InodeID ] ; ! ok {
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 ) ) )
}
if i . sattr . Type == fs . RegularFile {
uattr , err := i . unstableAttr ( & dummyClockContext { context . Background ( ) } )
if err != nil {
2018-08-22 20:18:21 +00:00
panic ( fs . ErrSaveRejection { fmt . Errorf ( "failed to get unstable atttribute of %s: %v" , i . s . inodeMappings [ i . sattr . InodeID ] , err ) } )
2018-04-27 17:37:02 +00:00
}
i . savedUAttr = & uattr
}
}
// saveLoading is invoked by stateify.
func ( i * inodeFileState ) saveLoading ( ) struct { } {
return struct { } { }
}
2018-06-29 21:46:45 +00:00
// splitAbsolutePath splits the path on slashes ignoring the leading slash.
func splitAbsolutePath ( path string ) [ ] string {
if len ( path ) == 0 {
panic ( "There is no path!" )
}
if path != filepath . Clean ( path ) {
panic ( fmt . Sprintf ( "path %q is not clean" , path ) )
}
// This case is to return {} rather than {""}
if path == "/" {
return [ ] string { }
}
if path [ 0 ] != '/' {
panic ( fmt . Sprintf ( "path %q is not absolute" , path ) )
}
s := strings . Split ( path , "/" )
// Since p is absolute, the first component of s
// is an empty string. We must remove that.
return s [ 1 : ]
}
2018-04-27 17:37:02 +00:00
// loadLoading is invoked by stateify.
func ( i * inodeFileState ) loadLoading ( _ struct { } ) {
i . loading . Lock ( )
}
// afterLoad is invoked by stateify.
func ( i * inodeFileState ) afterLoad ( ) {
2018-08-21 23:51:08 +00:00
load := func ( ) ( err error ) {
2018-04-27 17:37:02 +00:00
// See comment on i.loading().
2018-08-21 23:51:08 +00:00
defer func ( ) {
if err == nil {
i . loading . Unlock ( )
}
} ( )
2018-04-27 17:37:02 +00:00
// Manually restore the p9.File.
name , ok := i . s . inodeMappings [ i . sattr . InodeID ]
if ! ok {
// This should be impossible, see assertion in
// beforeSave.
2018-05-08 18:36:11 +00:00
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 ) )
2018-04-27 17:37:02 +00:00
}
2019-04-29 21:03:04 +00:00
// TODO(b/38173783): Context is not plumbed to save/restore.
2018-04-27 17:37:02 +00:00
ctx := & dummyClockContext { context . Background ( ) }
2018-06-29 21:46:45 +00:00
_ , i . file , err = i . s . attach . walk ( ctx , splitAbsolutePath ( name ) )
2018-04-27 17:37:02 +00:00
if err != nil {
2018-08-22 20:18:21 +00:00
return fs . ErrCorruption { fmt . Errorf ( "failed to walk to %q: %v" , name , err ) }
2018-04-27 17:37:02 +00:00
}
// Remap the saved inode number into the gofer device using the
// actual device and actual inode that exists in our new
// environment.
qid , mask , attrs , err := i . file . getAttr ( ctx , p9 . AttrMaskAll ( ) )
if err != nil {
2018-08-22 20:18:21 +00:00
return fs . ErrCorruption { fmt . Errorf ( "failed to get file attributes of %s: %v" , name , err ) }
2018-04-27 17:37:02 +00:00
}
if ! mask . RDev {
2018-05-08 18:36:11 +00:00
return fs . ErrCorruption { fmt . Errorf ( "file %s lacks device" , name ) }
2018-04-27 17:37:02 +00:00
}
i . key = device . MultiDeviceKey {
Device : attrs . RDev ,
SecondaryDevice : i . s . connID ,
Inode : qid . Path ,
}
if ! goferDevice . Load ( i . key , i . sattr . InodeID ) {
2018-05-08 18:36:11 +00:00
return fs . ErrCorruption { fmt . Errorf ( "gofer device %s -> %d conflict in gofer device mappings: %s" , i . key , i . sattr . InodeID , goferDevice ) }
2018-04-27 17:37:02 +00:00
}
if i . sattr . Type == fs . RegularFile {
env , ok := fs . CurrentRestoreEnvironment ( )
if ! ok {
2018-05-08 18:36:11 +00:00
return errors . New ( "missing restore environment" )
2018-04-27 17:37:02 +00:00
}
uattr := unstable ( ctx , mask , attrs , i . s . mounter , i . s . client )
if env . ValidateFileSize && uattr . Size != i . savedUAttr . Size {
2018-05-08 18:36:11 +00:00
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 ) }
2018-04-27 17:37:02 +00:00
}
if env . ValidateFileTimestamp && uattr . ModificationTime != i . savedUAttr . ModificationTime {
2018-05-08 18:36:11 +00:00
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 ) }
2018-04-27 17:37:02 +00:00
}
i . savedUAttr = nil
}
2018-05-08 18:36:11 +00:00
return nil
2018-04-27 17:37:02 +00:00
}
2018-05-08 18:36:11 +00:00
fs . Async ( fs . CatchError ( load ) )
2018-04-27 17:37:02 +00:00
}