mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
[vfs2][gofer] Update file size to 0 on O_TRUNC
Some Open:TruncateXxx syscall tests were failing because the file size was not being updated when the file was opened with O_TRUNC. Fixes Truncate tests in test/syscalls:open_test_runsc_ptrace_vfs2. Updates #2923 PiperOrigin-RevId: 319340127
This commit is contained in:
@@ -869,11 +869,22 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
|
||||
if err := d.checkPermissions(rp.Credentials(), ats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trunc := opts.Flags&linux.O_TRUNC != 0 && d.fileType() == linux.S_IFREG
|
||||
if trunc {
|
||||
// Lock metadataMu *while* we open a regular file with O_TRUNC because
|
||||
// open(2) will change the file size on server.
|
||||
d.metadataMu.Lock()
|
||||
defer d.metadataMu.Unlock()
|
||||
}
|
||||
|
||||
var vfd *vfs.FileDescription
|
||||
var err error
|
||||
mnt := rp.Mount()
|
||||
switch d.fileType() {
|
||||
case linux.S_IFREG:
|
||||
if !d.fs.opts.regularFilesUseSpecialFileFD {
|
||||
if err := d.ensureSharedHandle(ctx, ats&vfs.MayRead != 0, ats&vfs.MayWrite != 0, opts.Flags&linux.O_TRUNC != 0); err != nil {
|
||||
if err := d.ensureSharedHandle(ctx, ats&vfs.MayRead != 0, ats&vfs.MayWrite != 0, trunc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fd := ®ularFileFD{}
|
||||
@@ -883,7 +894,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fd.vfsfd, nil
|
||||
vfd = &fd.vfsfd
|
||||
}
|
||||
case linux.S_IFDIR:
|
||||
// Can't open directories with O_CREAT.
|
||||
@@ -923,7 +934,25 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
|
||||
return d.pipe.Open(ctx, mnt, &d.vfsd, opts.Flags, &d.locks)
|
||||
}
|
||||
}
|
||||
return d.openSpecialFileLocked(ctx, mnt, opts)
|
||||
|
||||
if vfd == nil {
|
||||
if vfd, err = d.openSpecialFileLocked(ctx, mnt, opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if trunc {
|
||||
// If no errors occured so far then update file size in memory. This
|
||||
// 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)
|
||||
|
||||
if d.cachedMetadataAuthoritative() {
|
||||
d.touchCMtimeLocked()
|
||||
}
|
||||
}
|
||||
return vfd, err
|
||||
}
|
||||
|
||||
func (d *dentry) connectSocketLocked(ctx context.Context, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
|
||||
@@ -794,9 +794,7 @@ func (d *dentry) updateFromP9Attrs(mask p9.AttrMask, attr *p9.Attr) {
|
||||
atomic.StoreUint32(&d.nlink, uint32(attr.NLink))
|
||||
}
|
||||
if mask.Size {
|
||||
d.dataMu.Lock()
|
||||
atomic.StoreUint64(&d.size, attr.Size)
|
||||
d.dataMu.Unlock()
|
||||
d.updateFileSizeLocked(attr.Size)
|
||||
}
|
||||
d.metadataMu.Unlock()
|
||||
}
|
||||
@@ -964,41 +962,46 @@ func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, stat *lin
|
||||
}
|
||||
atomic.StoreInt64(&d.ctime, now)
|
||||
if stat.Mask&linux.STATX_SIZE != 0 {
|
||||
d.dataMu.Lock()
|
||||
oldSize := d.size
|
||||
d.size = stat.Size
|
||||
// d.dataMu must be unlocked to lock d.mapsMu and invalidate mappings
|
||||
// below. This allows concurrent calls to Read/Translate/etc. These
|
||||
// functions synchronize with truncation by refusing to use cache
|
||||
// contents beyond the new d.size. (We are still holding d.metadataMu,
|
||||
// so we can't race with Write or another truncate.)
|
||||
d.dataMu.Unlock()
|
||||
if d.size < oldSize {
|
||||
oldpgend, _ := usermem.PageRoundUp(oldSize)
|
||||
newpgend, _ := usermem.PageRoundUp(d.size)
|
||||
if oldpgend != newpgend {
|
||||
d.mapsMu.Lock()
|
||||
d.mappings.Invalidate(memmap.MappableRange{newpgend, oldpgend}, memmap.InvalidateOpts{
|
||||
// Compare Linux's mm/truncate.c:truncate_setsize() =>
|
||||
// truncate_pagecache() =>
|
||||
// mm/memory.c:unmap_mapping_range(evencows=1).
|
||||
InvalidatePrivate: true,
|
||||
})
|
||||
d.mapsMu.Unlock()
|
||||
}
|
||||
// We are now guaranteed that there are no translations of
|
||||
// truncated pages, and can remove them from the cache. Since
|
||||
// truncated pages have been removed from the remote file, they
|
||||
// should be dropped without being written back.
|
||||
d.dataMu.Lock()
|
||||
d.cache.Truncate(d.size, d.fs.mfp.MemoryFile())
|
||||
d.dirty.KeepClean(memmap.MappableRange{d.size, oldpgend})
|
||||
d.dataMu.Unlock()
|
||||
}
|
||||
d.updateFileSizeLocked(stat.Size)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Preconditions: d.metadataMu must be locked.
|
||||
func (d *dentry) updateFileSizeLocked(newSize uint64) {
|
||||
d.dataMu.Lock()
|
||||
oldSize := d.size
|
||||
d.size = newSize
|
||||
// d.dataMu must be unlocked to lock d.mapsMu and invalidate mappings
|
||||
// below. This allows concurrent calls to Read/Translate/etc. These
|
||||
// functions synchronize with truncation by refusing to use cache
|
||||
// contents beyond the new d.size. (We are still holding d.metadataMu,
|
||||
// so we can't race with Write or another truncate.)
|
||||
d.dataMu.Unlock()
|
||||
if d.size < oldSize {
|
||||
oldpgend, _ := usermem.PageRoundUp(oldSize)
|
||||
newpgend, _ := usermem.PageRoundUp(d.size)
|
||||
if oldpgend != newpgend {
|
||||
d.mapsMu.Lock()
|
||||
d.mappings.Invalidate(memmap.MappableRange{newpgend, oldpgend}, memmap.InvalidateOpts{
|
||||
// Compare Linux's mm/truncate.c:truncate_setsize() =>
|
||||
// truncate_pagecache() =>
|
||||
// mm/memory.c:unmap_mapping_range(evencows=1).
|
||||
InvalidatePrivate: true,
|
||||
})
|
||||
d.mapsMu.Unlock()
|
||||
}
|
||||
// We are now guaranteed that there are no translations of
|
||||
// truncated pages, and can remove them from the cache. Since
|
||||
// truncated pages have been removed from the remote file, they
|
||||
// should be dropped without being written back.
|
||||
d.dataMu.Lock()
|
||||
d.cache.Truncate(d.size, d.fs.mfp.MemoryFile())
|
||||
d.dirty.KeepClean(memmap.MappableRange{d.size, oldpgend})
|
||||
d.dataMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dentry) checkPermissions(creds *auth.Credentials, ats vfs.AccessTypes) error {
|
||||
return vfs.GenericCheckPermissions(creds, ats, linux.FileMode(atomic.LoadUint32(&d.mode)), auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid)))
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func statxTimestampFromDentry(ns int64) linux.StatxTimestamp {
|
||||
}
|
||||
}
|
||||
|
||||
// Preconditions: fs.interop != InteropModeShared.
|
||||
// Preconditions: d.cachedMetadataAuthoritative() == true.
|
||||
func (d *dentry) touchAtime(mnt *vfs.Mount) {
|
||||
if mnt.Flags.NoATime {
|
||||
return
|
||||
@@ -51,8 +51,8 @@ func (d *dentry) touchAtime(mnt *vfs.Mount) {
|
||||
mnt.EndWrite()
|
||||
}
|
||||
|
||||
// Preconditions: fs.interop != InteropModeShared. The caller has successfully
|
||||
// called vfs.Mount.CheckBeginWrite().
|
||||
// Preconditions: d.cachedMetadataAuthoritative() == true. The caller has
|
||||
// successfully called vfs.Mount.CheckBeginWrite().
|
||||
func (d *dentry) touchCtime() {
|
||||
now := d.fs.clock.Now().Nanoseconds()
|
||||
d.metadataMu.Lock()
|
||||
@@ -60,8 +60,8 @@ func (d *dentry) touchCtime() {
|
||||
d.metadataMu.Unlock()
|
||||
}
|
||||
|
||||
// Preconditions: fs.interop != InteropModeShared. The caller has successfully
|
||||
// called vfs.Mount.CheckBeginWrite().
|
||||
// Preconditions: d.cachedMetadataAuthoritative() == true. The caller has
|
||||
// successfully called vfs.Mount.CheckBeginWrite().
|
||||
func (d *dentry) touchCMtime() {
|
||||
now := d.fs.clock.Now().Nanoseconds()
|
||||
d.metadataMu.Lock()
|
||||
@@ -70,6 +70,8 @@ func (d *dentry) touchCMtime() {
|
||||
d.metadataMu.Unlock()
|
||||
}
|
||||
|
||||
// Preconditions: d.cachedMetadataAuthoritative() == true. The caller has
|
||||
// locked d.metadataMu.
|
||||
func (d *dentry) touchCMtimeLocked() {
|
||||
now := d.fs.clock.Now().Nanoseconds()
|
||||
atomic.StoreInt64(&d.mtime, now)
|
||||
|
||||
@@ -410,7 +410,7 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
|
||||
if err := fd.vfsfd.Init(&fd, opts.Flags, rp.Mount(), &d.vfsd, &vfs.FileDescriptionOptions{AllowDirectIO: true}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.Flags&linux.O_TRUNC != 0 {
|
||||
if !afterCreate && opts.Flags&linux.O_TRUNC != 0 {
|
||||
if _, err := impl.truncate(0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user