mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user