Files
Jing ChenandgVisor bot c5c379f9ba Implement O_DSYNC and fdatasync(2)
The goal is to differentiate O_DSYNC and O_SYNC, O_DSYNC behaves same
as O_SYNC today in gVisor.

assisted-by: agy
Fix #1897
PiperOrigin-RevId: 958610974
2026-08-03 15:38:06 -07:00

172 lines
4.4 KiB
Go

// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gofer
import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/lisafs"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/hostfd"
"gvisor.dev/gvisor/pkg/sync"
)
var noHandle = handle{
fdLisa: lisafs.ClientFD{}, // zero value is fine.
fd: -1,
}
// handle represents a remote "open file descriptor", consisting of an opened
// lisafs FD and optionally a host file descriptor.
//
// These are explicitly not savable.
type handle struct {
fdLisa lisafs.ClientFD
fd int32 // -1 if unavailable
}
func (h *handle) close(ctx context.Context) {
if h.fdLisa.Ok() {
h.fdLisa.Close(ctx, true /* flush */)
}
if h.fd >= 0 {
unix.Close(int(h.fd))
h.fd = -1
}
}
func (h *handle) readToBlocksAt(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error) {
if dsts.IsEmpty() {
return 0, nil
}
if h.fd >= 0 {
ctx.UninterruptibleSleepStart()
n, err := hostfd.Preadv2(h.fd, dsts, int64(offset), 0 /* flags */)
ctx.UninterruptibleSleepFinish()
return n, err
}
rw := getHandleReadWriter(ctx, h, int64(offset))
defer putHandleReadWriter(rw)
return safemem.FromIOReader{rw}.ReadToBlocks(dsts)
}
func (h *handle) writeFromBlocksAt(ctx context.Context, srcs safemem.BlockSeq, offset uint64) (uint64, error) {
if srcs.IsEmpty() {
return 0, nil
}
if h.fd >= 0 {
ctx.UninterruptibleSleepStart()
n, err := hostfd.Pwritev2(h.fd, srcs, int64(offset), 0 /* flags */)
ctx.UninterruptibleSleepFinish()
return n, err
}
rw := getHandleReadWriter(ctx, h, int64(offset))
defer putHandleReadWriter(rw)
return safemem.FromIOWriter{rw}.WriteFromBlocks(srcs)
}
func (h *handle) allocate(ctx context.Context, mode, offset, length uint64) error {
if h.fdLisa.Ok() {
return h.fdLisa.Allocate(ctx, mode, offset, length)
}
if h.fd >= 0 {
return unix.Fallocate(int(h.fd), uint32(mode), int64(offset), int64(length))
}
return nil
}
func (h *handle) sync(ctx context.Context) error {
// If we have a host FD, fsyncing it is likely to be faster than an fsync
// RPC.
if h.fd >= 0 {
ctx.UninterruptibleSleepStart()
err := unix.Fsync(int(h.fd))
ctx.UninterruptibleSleepFinish()
return err
}
if h.fdLisa.Ok() {
return h.fdLisa.Sync(ctx)
}
return nil
}
func (h *handle) syncData(ctx context.Context) error {
if h.fd >= 0 {
ctx.UninterruptibleSleepStart()
err := unix.Fdatasync(int(h.fd))
ctx.UninterruptibleSleepFinish()
return err
}
if h.fdLisa.Ok() {
// Fallback to Sync since lisafs doesn't support SyncData yet.
return h.fdLisa.Sync(ctx)
}
return nil
}
type handleReadWriter struct {
ctx context.Context
h handle
off uint64
}
var handleReadWriterPool = sync.Pool{
New: func() any {
return &handleReadWriter{}
},
}
func getHandleReadWriter(ctx context.Context, h *handle, offset int64) *handleReadWriter {
rw := handleReadWriterPool.Get().(*handleReadWriter)
rw.ctx = ctx
rw.h = *h
rw.off = uint64(offset)
return rw
}
func putHandleReadWriter(rw *handleReadWriter) {
rw.ctx = nil
rw.h = noHandle
handleReadWriterPool.Put(rw)
}
// Read implements io.Reader.Read.
func (rw *handleReadWriter) Read(dst []byte) (int, error) {
n, err := rw.h.fdLisa.Read(rw.ctx, dst, rw.off)
rw.off += n
return int(n), err
}
// Write implements io.Writer.Write.
func (rw *handleReadWriter) Write(src []byte) (int, error) {
n, err := rw.h.fdLisa.Write(rw.ctx, src, rw.off)
rw.off += n
return int(n), err
}
// ReadToBlocks implements safemem.Reader.ReadToBlocks.
func (rw *handleReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) {
n, err := rw.h.readToBlocksAt(rw.ctx, dsts, rw.off)
rw.off += n
return n, err
}
// WriteFromBlocks implements safemem.Writer.WriteFromBlocks.
func (rw *handleReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error) {
n, err := rw.h.writeFromBlocksAt(rw.ctx, srcs, rw.off)
rw.off += n
return n, err
}