summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/file_overlay.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fs/file_overlay.go')
-rw-r--r--pkg/sentry/fs/file_overlay.go136
1 files changed, 93 insertions, 43 deletions
diff --git a/pkg/sentry/fs/file_overlay.go b/pkg/sentry/fs/file_overlay.go
index 5fb5fbe97..9820f0b13 100644
--- a/pkg/sentry/fs/file_overlay.go
+++ b/pkg/sentry/fs/file_overlay.go
@@ -85,12 +85,6 @@ type overlayFileOperations struct {
// protected by File.mu of the owning file, which is held during
// Readdir and Seek calls.
dirCursor string
-
- // dirCacheMu protects dirCache.
- dirCacheMu sync.RWMutex `state:"nosave"`
-
- // dirCache is cache of DentAttrs from upper and lower Inodes.
- dirCache *SortedDentryMap
}
// Release implements FileOperations.Release.
@@ -171,53 +165,68 @@ func (f *overlayFileOperations) Readdir(ctx context.Context, file *File, seriali
if root != nil {
defer root.DecRef()
}
+
dirCtx := &DirCtx{
Serializer: serializer,
DirCursor: &f.dirCursor,
}
+ return DirentReaddir(ctx, file.Dirent, f, root, dirCtx, file.Offset())
+}
- // If the directory dirent is frozen, then DirentReaddir will calculate
- // the children based off the frozen dirent tree. There is no need to
- // call readdir on the upper/lower layers.
- if file.Dirent.frozen {
- return DirentReaddir(ctx, file.Dirent, f, root, dirCtx, file.Offset())
+// IterateDir implements DirIterator.IterateDir.
+func (f *overlayFileOperations) IterateDir(ctx context.Context, d *Dirent, dirCtx *DirCtx, offset int) (int, error) {
+ o := d.Inode.overlay
+
+ if !d.Inode.MountSource.CacheReaddir() {
+ // Can't use the dirCache. Simply read the entries.
+ entries, err := readdirEntries(ctx, o)
+ if err != nil {
+ return offset, err
+ }
+ n, err := GenericReaddir(dirCtx, entries)
+ return offset + n, err
}
- // Otherwise proceed with usual overlay readdir.
- o := file.Dirent.Inode.overlay
+ // Otherwise, use or create cached entries.
+
+ o.dirCacheMu.RLock()
+ if o.dirCache != nil {
+ n, err := GenericReaddir(dirCtx, o.dirCache)
+ o.dirCacheMu.RUnlock()
+ return offset + n, err
+ }
+ o.dirCacheMu.RUnlock()
// readdirEntries holds o.copyUpMu to ensure that copy-up does not
- // occur while calculating the readir results.
+ // occur while calculating the readdir results.
//
// However, it is possible for a copy-up to occur after the call to
- // readdirEntries, but before setting f.dirCache. This is OK, since
- // copy-up only does not change the children in a way that would affect
- // the children returned in dirCache. Copy-up only moves
- // files/directories between layers in the overlay.
+ // readdirEntries, but before setting o.dirCache. This is OK, since
+ // copy-up does not change the children in a way that would affect the
+ // children returned in dirCache. Copy-up only moves files/directories
+ // between layers in the overlay.
//
- // It is also possible for Readdir to race with a Create operation
- // (which may trigger a copy-up during it's execution). Depending on
- // whether the Create happens before or after the readdirEntries call,
- // the newly created file may or may not appear in the readdir results.
- // But this can only be caused by a real race between readdir and
- // create syscalls, so it's also OK.
- dirCache, err := readdirEntries(ctx, o)
- if err != nil {
- return file.Offset(), err
+ // We must hold dirCacheMu around both readdirEntries and setting
+ // o.dirCache to synchronize with dirCache invalidations done by
+ // Create, Remove, Rename.
+ o.dirCacheMu.Lock()
+
+ // We expect dirCache to be nil (we just checked above), but there is a
+ // chance that a racing call managed to just set it, in which case we
+ // can use that new value.
+ if o.dirCache == nil {
+ dirCache, err := readdirEntries(ctx, o)
+ if err != nil {
+ o.dirCacheMu.Unlock()
+ return offset, err
+ }
+ o.dirCache = dirCache
}
- f.dirCacheMu.Lock()
- f.dirCache = dirCache
- f.dirCacheMu.Unlock()
+ o.dirCacheMu.DowngradeLock()
+ n, err := GenericReaddir(dirCtx, o.dirCache)
+ o.dirCacheMu.RUnlock()
- return DirentReaddir(ctx, file.Dirent, f, root, dirCtx, file.Offset())
-}
-
-// IterateDir implements DirIterator.IterateDir.
-func (f *overlayFileOperations) IterateDir(ctx context.Context, dirCtx *DirCtx, offset int) (int, error) {
- f.dirCacheMu.RLock()
- n, err := GenericReaddir(dirCtx, f.dirCache)
- f.dirCacheMu.RUnlock()
return offset + n, err
}
@@ -338,13 +347,14 @@ func (*overlayFileOperations) ConfigureMMap(ctx context.Context, file *File, opt
// preventing us from saving a proper inode mapping for the
// file.
file.IncRef()
- id := &overlayMappingIdentity{
+ id := overlayMappingIdentity{
id: opts.MappingIdentity,
overlayFile: file,
}
+ id.EnableLeakCheck("fs.overlayMappingIdentity")
// Swap out the old MappingIdentity for the wrapped one.
- opts.MappingIdentity = id
+ opts.MappingIdentity = &id
return nil
}
@@ -388,9 +398,49 @@ func (f *overlayFileOperations) UnstableAttr(ctx context.Context, file *File) (U
return f.lower.UnstableAttr(ctx)
}
-// Ioctl implements fs.FileOperations.Ioctl and always returns ENOTTY.
-func (*overlayFileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
- return 0, syserror.ENOTTY
+// Ioctl implements fs.FileOperations.Ioctl.
+func (f *overlayFileOperations) Ioctl(ctx context.Context, overlayFile *File, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
+ f.upperMu.Lock()
+ defer f.upperMu.Unlock()
+
+ if f.upper == nil {
+ // It's possible that ioctl changes the file. Since we don't know all
+ // possible ioctls, only allow them to propagate to the upper. Triggering a
+ // copy up on any ioctl would be too drastic. In the future, it can have a
+ // list of ioctls that are safe to send to lower and a list that triggers a
+ // copy up.
+ return 0, syserror.ENOTTY
+ }
+ return f.upper.FileOperations.Ioctl(ctx, f.upper, io, args)
+}
+
+// FifoSize implements FifoSizer.FifoSize.
+func (f *overlayFileOperations) FifoSize(ctx context.Context, overlayFile *File) (rv int64, err error) {
+ err = f.onTop(ctx, overlayFile, func(file *File, ops FileOperations) error {
+ sz, ok := ops.(FifoSizer)
+ if !ok {
+ return syserror.EINVAL
+ }
+ rv, err = sz.FifoSize(ctx, file)
+ return err
+ })
+ return
+}
+
+// SetFifoSize implements FifoSizer.SetFifoSize.
+func (f *overlayFileOperations) SetFifoSize(size int64) (rv int64, err error) {
+ f.upperMu.Lock()
+ defer f.upperMu.Unlock()
+
+ if f.upper == nil {
+ // Named pipes cannot be copied up and changes to the lower are prohibited.
+ return 0, syserror.EINVAL
+ }
+ sz, ok := f.upper.FileOperations.(FifoSizer)
+ if !ok {
+ return 0, syserror.EINVAL
+ }
+ return sz.SetFifoSize(size)
}
// readdirEntries returns a sorted map of directory entries from the