Complete vfs2 implementation of fallocate.

This change includes overlay, special regular gofer files, and hostfs.

Fixes #3589.

PiperOrigin-RevId: 332330860
This commit is contained in:
Dean Deng
2020-09-17 15:38:44 -07:00
committed by gVisor bot
parent 8070cc3386
commit 319d1b8ba0
11 changed files with 73 additions and 38 deletions
+1 -1
View File
@@ -1026,7 +1026,7 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
// step is required even if !d.cachedMetadataAuthoritative() because
// d.mappings has to be updated.
// d.metadataMu has already been acquired if trunc == true.
d.updateFileSizeLocked(0)
d.updateSizeLocked(0)
if d.cachedMetadataAuthoritative() {
d.touchCMtimeLocked()
+26 -3
View File
@@ -833,7 +833,7 @@ func (d *dentry) updateFromP9AttrsLocked(mask p9.AttrMask, attr *p9.Attr) {
atomic.StoreUint32(&d.nlink, uint32(attr.NLink))
}
if mask.Size {
d.updateFileSizeLocked(attr.Size)
d.updateSizeLocked(attr.Size)
}
}
@@ -987,7 +987,7 @@ func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, opts *vfs
// d.size should be kept up to date, and privatized
// copy-on-write mappings of truncated pages need to be
// invalidated, even if InteropModeShared is in effect.
d.updateFileSizeLocked(stat.Size)
d.updateSizeLocked(stat.Size)
}
}
if d.fs.opts.interop == InteropModeShared {
@@ -1024,8 +1024,31 @@ func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, opts *vfs
return nil
}
// doAllocate performs an allocate operation on d. Note that d.metadataMu will
// be held when allocate is called.
func (d *dentry) doAllocate(ctx context.Context, offset, length uint64, allocate func() error) error {
d.metadataMu.Lock()
defer d.metadataMu.Unlock()
// Allocating a smaller size is a noop.
size := offset + length
if d.cachedMetadataAuthoritative() && size <= d.size {
return nil
}
err := allocate()
if err != nil {
return err
}
d.updateSizeLocked(size)
if d.cachedMetadataAuthoritative() {
d.touchCMtimeLocked()
}
return nil
}
// Preconditions: d.metadataMu must be locked.
func (d *dentry) updateFileSizeLocked(newSize uint64) {
func (d *dentry) updateSizeLocked(newSize uint64) {
d.dataMu.Lock()
oldSize := d.size
atomic.StoreUint64(&d.size, newSize)
+5 -22
View File
@@ -79,28 +79,11 @@ func (fd *regularFileFD) OnClose(ctx context.Context) error {
// Allocate implements vfs.FileDescriptionImpl.Allocate.
func (fd *regularFileFD) Allocate(ctx context.Context, mode, offset, length uint64) error {
d := fd.dentry()
d.metadataMu.Lock()
defer d.metadataMu.Unlock()
// Allocating a smaller size is a noop.
size := offset + length
if d.cachedMetadataAuthoritative() && size <= d.size {
return nil
}
d.handleMu.RLock()
err := d.writeFile.allocate(ctx, p9.ToAllocateMode(mode), offset, length)
d.handleMu.RUnlock()
if err != nil {
return err
}
d.dataMu.Lock()
atomic.StoreUint64(&d.size, size)
d.dataMu.Unlock()
if d.cachedMetadataAuthoritative() {
d.touchCMtimeLocked()
}
return nil
return d.doAllocate(ctx, offset, length, func() error {
d.handleMu.RLock()
defer d.handleMu.RUnlock()
return d.writeFile.allocate(ctx, p9.ToAllocateMode(mode), offset, length)
})
}
// PRead implements vfs.FileDescriptionImpl.PRead.
+11
View File
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/p9"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
@@ -135,6 +136,16 @@ func (fd *specialFileFD) EventUnregister(e *waiter.Entry) {
fd.fileDescription.EventUnregister(e)
}
func (fd *specialFileFD) Allocate(ctx context.Context, mode, offset, length uint64) error {
if fd.isRegularFile {
d := fd.dentry()
return d.doAllocate(ctx, offset, length, func() error {
return fd.handle.file.allocate(ctx, p9.ToAllocateMode(mode), offset, length)
})
}
return fd.FileDescriptionDefaultImpl.Allocate(ctx, mode, offset, length)
}
// PRead implements vfs.FileDescriptionImpl.PRead.
func (fd *specialFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
if fd.seekable && offset < 0 {
+1 -6
View File
@@ -560,12 +560,7 @@ func (f *fileDescription) Release(context.Context) {
// Allocate implements vfs.FileDescriptionImpl.
func (f *fileDescription) Allocate(ctx context.Context, mode, offset, length uint64) error {
if !f.inode.seekable {
return syserror.ESPIPE
}
// TODO(gvisor.dev/issue/3589): Implement Allocate for non-pipe hostfds.
return syserror.EOPNOTSUPP
return unix.Fallocate(f.inode.hostFD, uint32(mode), int64(offset), int64(length))
}
// PRead implements FileDescriptionImpl.
@@ -147,6 +147,16 @@ func (fd *nonDirectoryFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux
return stat, nil
}
// Allocate implements vfs.FileDescriptionImpl.Allocate.
func (fd *nonDirectoryFD) Allocate(ctx context.Context, mode, offset, length uint64) error {
wrappedFD, err := fd.getCurrentFD(ctx)
if err != nil {
return err
}
defer wrappedFD.DecRef(ctx)
return wrappedFD.Allocate(ctx, mode, offset, length)
}
// SetStat implements vfs.FileDescriptionImpl.SetStat.
func (fd *nonDirectoryFD) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
d := fd.dentry()
+5
View File
@@ -67,6 +67,11 @@ func (vp *VFSPipe) ReaderWriterPair(mnt *vfs.Mount, vfsd *vfs.Dentry, statusFlag
return vp.newFD(mnt, vfsd, linux.O_RDONLY|statusFlags, locks), vp.newFD(mnt, vfsd, linux.O_WRONLY|statusFlags, locks)
}
// Allocate implements vfs.FileDescriptionImpl.Allocate.
func (*VFSPipe) Allocate(context.Context, uint64, uint64, uint64) error {
return syserror.ESPIPE
}
// Open opens the pipe represented by vp.
func (vp *VFSPipe) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, statusFlags uint32, locks *vfs.FileLocks) (*vfs.FileDescription, error) {
vp.mu.Lock()
@@ -97,11 +97,6 @@ func (s *socketVFS2) Ioctl(ctx context.Context, uio usermem.IO, args arch.Syscal
return ioctl(ctx, s.fd, uio, args)
}
// Allocate implements vfs.FileDescriptionImpl.Allocate.
func (s *socketVFS2) Allocate(ctx context.Context, mode, offset, length uint64) error {
return syserror.ENODEV
}
// PRead implements vfs.FileDescriptionImpl.PRead.
func (s *socketVFS2) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
return 0, syserror.ESPIPE
+3
View File
@@ -326,6 +326,9 @@ type FileDescriptionImpl interface {
// Allocate grows the file to offset + length bytes.
// Only mode == 0 is supported currently.
//
// Allocate should return EISDIR on directories, ESPIPE on pipes, and ENODEV on
// other files where it is not supported.
//
// Preconditions: The FileDescription was opened for writing.
Allocate(ctx context.Context, mode, offset, length uint64) error
+5 -1
View File
@@ -57,7 +57,11 @@ func (FileDescriptionDefaultImpl) StatFS(ctx context.Context) (linux.Statfs, err
}
// Allocate implements FileDescriptionImpl.Allocate analogously to
// fallocate called on regular file, directory or FIFO in Linux.
// fallocate called on an invalid type of file in Linux.
//
// Note that directories can rely on this implementation even though they
// should technically return EISDIR. Allocate should never be called for a
// directory, because it requires a writable fd.
func (FileDescriptionDefaultImpl) Allocate(ctx context.Context, mode, offset, length uint64) error {
return syserror.ENODEV
}
+6
View File
@@ -179,6 +179,12 @@ TEST_F(AllocateTest, FallocateOtherFDs) {
auto sock0 = FileDescriptor(socks[0]);
auto sock1 = FileDescriptor(socks[1]);
EXPECT_THAT(fallocate(sock0.get(), 0, 0, 10), SyscallFailsWithErrno(ENODEV));
int pipefds[2];
ASSERT_THAT(pipe(pipefds), SyscallSucceeds());
EXPECT_THAT(fallocate(pipefds[1], 0, 0, 10), SyscallFailsWithErrno(ESPIPE));
close(pipefds[0]);
close(pipefds[1]);
}
} // namespace