github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/kernel/fs_context.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 kernel
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/context"
    21  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    23  	"github.com/SagerNet/gvisor/pkg/sync"
    24  )
    25  
    26  // FSContext contains filesystem context.
    27  //
    28  // This includes umask and working directory.
    29  //
    30  // +stateify savable
    31  type FSContext struct {
    32  	FSContextRefs
    33  
    34  	// mu protects below.
    35  	mu sync.Mutex `state:"nosave"`
    36  
    37  	// root is the filesystem root. Will be nil iff the FSContext has been
    38  	// destroyed.
    39  	root *fs.Dirent
    40  
    41  	// rootVFS2 is the filesystem root.
    42  	rootVFS2 vfs.VirtualDentry
    43  
    44  	// cwd is the current working directory. Will be nil iff the FSContext
    45  	// has been destroyed.
    46  	cwd *fs.Dirent
    47  
    48  	// cwdVFS2 is the current working directory.
    49  	cwdVFS2 vfs.VirtualDentry
    50  
    51  	// umask is the current file mode creation mask. When a thread using this
    52  	// context invokes a syscall that creates a file, bits set in umask are
    53  	// removed from the permissions that the file is created with.
    54  	umask uint
    55  }
    56  
    57  // newFSContext returns a new filesystem context.
    58  func newFSContext(root, cwd *fs.Dirent, umask uint) *FSContext {
    59  	root.IncRef()
    60  	cwd.IncRef()
    61  	f := FSContext{
    62  		root:  root,
    63  		cwd:   cwd,
    64  		umask: umask,
    65  	}
    66  	f.InitRefs()
    67  	return &f
    68  }
    69  
    70  // NewFSContextVFS2 returns a new filesystem context.
    71  func NewFSContextVFS2(root, cwd vfs.VirtualDentry, umask uint) *FSContext {
    72  	root.IncRef()
    73  	cwd.IncRef()
    74  	f := FSContext{
    75  		rootVFS2: root,
    76  		cwdVFS2:  cwd,
    77  		umask:    umask,
    78  	}
    79  	f.InitRefs()
    80  	return &f
    81  }
    82  
    83  // DecRef implements RefCounter.DecRef.
    84  //
    85  // When f reaches zero references, DecRef will be called on both root and cwd
    86  // Dirents.
    87  //
    88  // Note that there may still be calls to WorkingDirectory() or RootDirectory()
    89  // (that return nil).  This is because valid references may still be held via
    90  // proc files or other mechanisms.
    91  func (f *FSContext) DecRef(ctx context.Context) {
    92  	f.FSContextRefs.DecRef(func() {
    93  		// Hold f.mu so that we don't race with RootDirectory() and
    94  		// WorkingDirectory().
    95  		f.mu.Lock()
    96  		defer f.mu.Unlock()
    97  
    98  		if VFS2Enabled {
    99  			f.rootVFS2.DecRef(ctx)
   100  			f.rootVFS2 = vfs.VirtualDentry{}
   101  			f.cwdVFS2.DecRef(ctx)
   102  			f.cwdVFS2 = vfs.VirtualDentry{}
   103  		} else {
   104  			f.root.DecRef(ctx)
   105  			f.root = nil
   106  			f.cwd.DecRef(ctx)
   107  			f.cwd = nil
   108  		}
   109  	})
   110  }
   111  
   112  // Fork forks this FSContext.
   113  //
   114  // This is not a valid call after f is destroyed.
   115  func (f *FSContext) Fork() *FSContext {
   116  	f.mu.Lock()
   117  	defer f.mu.Unlock()
   118  
   119  	if VFS2Enabled {
   120  		if !f.cwdVFS2.Ok() {
   121  			panic("FSContext.Fork() called after destroy")
   122  		}
   123  		f.cwdVFS2.IncRef()
   124  		f.rootVFS2.IncRef()
   125  	} else {
   126  		if f.cwd == nil {
   127  			panic("FSContext.Fork() called after destroy")
   128  		}
   129  		f.cwd.IncRef()
   130  		f.root.IncRef()
   131  	}
   132  
   133  	ctx := &FSContext{
   134  		cwd:      f.cwd,
   135  		root:     f.root,
   136  		cwdVFS2:  f.cwdVFS2,
   137  		rootVFS2: f.rootVFS2,
   138  		umask:    f.umask,
   139  	}
   140  	ctx.InitRefs()
   141  	return ctx
   142  }
   143  
   144  // WorkingDirectory returns the current working directory.
   145  //
   146  // This will return nil if called after f is destroyed, otherwise it will return
   147  // a Dirent with a reference taken.
   148  func (f *FSContext) WorkingDirectory() *fs.Dirent {
   149  	f.mu.Lock()
   150  	defer f.mu.Unlock()
   151  
   152  	if f.cwd != nil {
   153  		f.cwd.IncRef()
   154  	}
   155  	return f.cwd
   156  }
   157  
   158  // WorkingDirectoryVFS2 returns the current working directory.
   159  //
   160  // This will return an empty vfs.VirtualDentry if called after f is
   161  // destroyed, otherwise it will return a Dirent with a reference taken.
   162  func (f *FSContext) WorkingDirectoryVFS2() vfs.VirtualDentry {
   163  	f.mu.Lock()
   164  	defer f.mu.Unlock()
   165  
   166  	if f.cwdVFS2.Ok() {
   167  		f.cwdVFS2.IncRef()
   168  	}
   169  	return f.cwdVFS2
   170  }
   171  
   172  // SetWorkingDirectory sets the current working directory.
   173  // This will take an extra reference on the Dirent.
   174  //
   175  // This is not a valid call after f is destroyed.
   176  func (f *FSContext) SetWorkingDirectory(ctx context.Context, d *fs.Dirent) {
   177  	if d == nil {
   178  		panic("FSContext.SetWorkingDirectory called with nil dirent")
   179  	}
   180  
   181  	f.mu.Lock()
   182  	defer f.mu.Unlock()
   183  
   184  	if f.cwd == nil {
   185  		panic(fmt.Sprintf("FSContext.SetWorkingDirectory(%v)) called after destroy", d))
   186  	}
   187  
   188  	old := f.cwd
   189  	f.cwd = d
   190  	d.IncRef()
   191  	old.DecRef(ctx)
   192  }
   193  
   194  // SetWorkingDirectoryVFS2 sets the current working directory.
   195  // This will take an extra reference on the VirtualDentry.
   196  //
   197  // This is not a valid call after f is destroyed.
   198  func (f *FSContext) SetWorkingDirectoryVFS2(ctx context.Context, d vfs.VirtualDentry) {
   199  	f.mu.Lock()
   200  	defer f.mu.Unlock()
   201  
   202  	if !f.cwdVFS2.Ok() {
   203  		panic(fmt.Sprintf("FSContext.SetWorkingDirectoryVFS2(%v)) called after destroy", d))
   204  	}
   205  
   206  	old := f.cwdVFS2
   207  	f.cwdVFS2 = d
   208  	d.IncRef()
   209  	old.DecRef(ctx)
   210  }
   211  
   212  // RootDirectory returns the current filesystem root.
   213  //
   214  // This will return nil if called after f is destroyed, otherwise it will return
   215  // a Dirent with a reference taken.
   216  func (f *FSContext) RootDirectory() *fs.Dirent {
   217  	f.mu.Lock()
   218  	defer f.mu.Unlock()
   219  	if f.root != nil {
   220  		f.root.IncRef()
   221  	}
   222  	return f.root
   223  }
   224  
   225  // RootDirectoryVFS2 returns the current filesystem root.
   226  //
   227  // This will return an empty vfs.VirtualDentry if called after f is
   228  // destroyed, otherwise it will return a Dirent with a reference taken.
   229  func (f *FSContext) RootDirectoryVFS2() vfs.VirtualDentry {
   230  	f.mu.Lock()
   231  	defer f.mu.Unlock()
   232  
   233  	if f.rootVFS2.Ok() {
   234  		f.rootVFS2.IncRef()
   235  	}
   236  	return f.rootVFS2
   237  }
   238  
   239  // SetRootDirectory sets the root directory.
   240  // This will take an extra reference on the Dirent.
   241  //
   242  // This is not a valid call after f is destroyed.
   243  func (f *FSContext) SetRootDirectory(ctx context.Context, d *fs.Dirent) {
   244  	if d == nil {
   245  		panic("FSContext.SetRootDirectory called with nil dirent")
   246  	}
   247  
   248  	f.mu.Lock()
   249  	defer f.mu.Unlock()
   250  
   251  	if f.root == nil {
   252  		panic(fmt.Sprintf("FSContext.SetRootDirectory(%v)) called after destroy", d))
   253  	}
   254  
   255  	old := f.root
   256  	f.root = d
   257  	d.IncRef()
   258  	old.DecRef(ctx)
   259  }
   260  
   261  // SetRootDirectoryVFS2 sets the root directory. It takes a reference on vd.
   262  //
   263  // This is not a valid call after f is destroyed.
   264  func (f *FSContext) SetRootDirectoryVFS2(ctx context.Context, vd vfs.VirtualDentry) {
   265  	if !vd.Ok() {
   266  		panic("FSContext.SetRootDirectoryVFS2 called with zero-value VirtualDentry")
   267  	}
   268  
   269  	f.mu.Lock()
   270  
   271  	if !f.rootVFS2.Ok() {
   272  		f.mu.Unlock()
   273  		panic(fmt.Sprintf("FSContext.SetRootDirectoryVFS2(%v)) called after destroy", vd))
   274  	}
   275  
   276  	old := f.rootVFS2
   277  	vd.IncRef()
   278  	f.rootVFS2 = vd
   279  	f.mu.Unlock()
   280  	old.DecRef(ctx)
   281  }
   282  
   283  // Umask returns the current umask.
   284  func (f *FSContext) Umask() uint {
   285  	f.mu.Lock()
   286  	defer f.mu.Unlock()
   287  	return f.umask
   288  }
   289  
   290  // SwapUmask atomically sets the current umask and returns the old umask.
   291  func (f *FSContext) SwapUmask(mask uint) uint {
   292  	f.mu.Lock()
   293  	defer f.mu.Unlock()
   294  	old := f.umask
   295  	f.umask = mask
   296  	return old
   297  }