Skip to content
This repository has been archived by the owner on Feb 1, 2020. It is now read-only.

Commit

Permalink
Implement fallocate(2)
Browse files Browse the repository at this point in the history
Closes google#225

PiperOrigin-RevId: 247508791
Change-Id: I04f47cf2770b30043e5a272aba4ba6e11d0476cc
  • Loading branch information
fvoznika authored and shentubot committed May 9, 2019
1 parent 0f4be95 commit 1bee43b
Show file tree
Hide file tree
Showing 44 changed files with 589 additions and 44 deletions.
11 changes: 11 additions & 0 deletions pkg/abi/linux/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,14 @@ const (
F_SEAL_GROW = 0x0004 // Prevent file from growing.
F_SEAL_WRITE = 0x0008 // Prevent writes.
)

// Constants related to fallocate(2). Source: include/uapi/linux/falloc.h
const (
FALLOC_FL_KEEP_SIZE = 0x01
FALLOC_FL_PUNCH_HOLE = 0x02
FALLOC_FL_NO_HIDE_STALE = 0x04
FALLOC_FL_COLLAPSE_RANGE = 0x08
FALLOC_FL_ZERO_RANGE = 0x10
FALLOC_FL_INSERT_RANGE = 0x20
FALLOC_FL_UNSHARE_RANGE = 0x40
)
1 change: 1 addition & 0 deletions pkg/p9/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ go_library(
"//pkg/fd",
"//pkg/log",
"//pkg/unet",
"@org_golang_x_sys//unix:go_default_library",
],
)

Expand Down
12 changes: 12 additions & 0 deletions pkg/p9/client_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,18 @@ func (c *clientFile) SetAttr(valid SetAttrMask, attr SetAttr) error {
return c.client.sendRecv(&Tsetattr{FID: c.fid, Valid: valid, SetAttr: attr}, &Rsetattr{})
}

// Allocate implements File.Allocate.
func (c *clientFile) Allocate(mode AllocateMode, offset, length uint64) error {
if atomic.LoadUint32(&c.closed) != 0 {
return syscall.EBADF
}
if !versionSupportsTallocate(c.client.version) {
return syscall.EOPNOTSUPP
}

return c.client.sendRecv(&Tallocate{FID: c.fid, Mode: mode, Offset: offset, Length: length}, &Rallocate{})
}

// Remove implements File.Remove.
//
// N.B. This method is no longer part of the file interface and should be
Expand Down
4 changes: 4 additions & 0 deletions pkg/p9/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ type File interface {
// On the server, SetAttr has a write concurrency guarantee.
SetAttr(valid SetAttrMask, attr SetAttr) error

// Allocate allows the caller to directly manipulate the allocated disk space
// for the file. See fallocate(2) for more details.
Allocate(mode AllocateMode, offset, length uint64) error

// Close is called when all references are dropped on the server side,
// and Close should be called by the client to drop all references.
//
Expand Down
34 changes: 34 additions & 0 deletions pkg/p9/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,40 @@ func (t *Tsetattr) handle(cs *connState) message {
return &Rsetattr{}
}

// handle implements handler.handle.
func (t *Tallocate) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()

if err := ref.safelyWrite(func() error {
// Has it been opened already?
openFlags, opened := ref.OpenFlags()
if !opened {
return syscall.EINVAL
}

// Can it be written? Check permissions.
if openFlags&OpenFlagsModeMask == ReadOnly {
return syscall.EBADF
}

// We don't allow allocate on files that have been deleted.
if ref.isDeleted() {
return syscall.EINVAL
}

return ref.file.Allocate(t.Mode, t.Offset, t.Length)
}); err != nil {
return newErr(err)
}

return &Rallocate{}
}

// handle implements handler.handle.
func (t *Txattrwalk) handle(cs *connState) message {
// Lookup the FID.
Expand Down
5 changes: 5 additions & 0 deletions pkg/p9/local_server/local_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,11 @@ func (l *local) Renamed(parent p9.File, newName string) {
l.path = path.Join(parent.(*local).path, newName)
}

// Allocate implements p9.File.Allocate.
func (l *local) Allocate(mode p9.AllocateMode, offset, length uint64) error {
return syscall.Fallocate(int(l.file.Fd()), mode.ToLinux(), int64(offset), int64(length))
}

func main() {
log.SetLevel(log.Debug)

Expand Down
59 changes: 59 additions & 0 deletions pkg/p9/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,63 @@ func (r *Rsetattr) String() string {
return fmt.Sprintf("Rsetattr{}")
}

// Tallocate is an allocate request. This is an extension to 9P protocol, not
// present in the 9P2000.L standard.
type Tallocate struct {
FID FID
Mode AllocateMode
Offset uint64
Length uint64
}

// Decode implements encoder.Decode.
func (t *Tallocate) Decode(b *buffer) {
t.FID = b.ReadFID()
t.Mode.Decode(b)
t.Offset = b.Read64()
t.Length = b.Read64()
}

// Encode implements encoder.Encode.
func (t *Tallocate) Encode(b *buffer) {
b.WriteFID(t.FID)
t.Mode.Encode(b)
b.Write64(t.Offset)
b.Write64(t.Length)
}

// Type implements message.Type.
func (*Tallocate) Type() MsgType {
return MsgTallocate
}

// String implements fmt.Stringer.
func (t *Tallocate) String() string {
return fmt.Sprintf("Tallocate{FID: %d, Offset: %d, Length: %d}", t.FID, t.Offset, t.Length)
}

// Rallocate is an allocate response.
type Rallocate struct {
}

// Decode implements encoder.Decode.
func (*Rallocate) Decode(b *buffer) {
}

// Encode implements encoder.Encode.
func (*Rallocate) Encode(b *buffer) {
}

// Type implements message.Type.
func (*Rallocate) Type() MsgType {
return MsgRallocate
}

// String implements fmt.Stringer.
func (r *Rallocate) String() string {
return fmt.Sprintf("Rallocate{}")
}

// Txattrwalk walks extended attributes.
type Txattrwalk struct {
// FID is the FID to check for attributes.
Expand Down Expand Up @@ -2297,4 +2354,6 @@ func init() {
msgRegistry.register(MsgRusymlink, func() message { return &Rusymlink{} })
msgRegistry.register(MsgTlconnect, func() message { return &Tlconnect{} })
msgRegistry.register(MsgRlconnect, func() message { return &Rlconnect{} })
msgRegistry.register(MsgTallocate, func() message { return &Tallocate{} })
msgRegistry.register(MsgRallocate, func() message { return &Rallocate{} })
}
81 changes: 81 additions & 0 deletions pkg/p9/p9.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"strings"
"sync/atomic"
"syscall"

"golang.org/x/sys/unix"
)

// OpenFlags is the mode passed to Open and Create operations.
Expand Down Expand Up @@ -374,6 +376,8 @@ const (
MsgRusymlink = 135
MsgTlconnect = 136
MsgRlconnect = 137
MsgTallocate = 138
MsgRallocate = 139
)

// QIDType represents the file type for QIDs.
Expand Down Expand Up @@ -1058,3 +1062,80 @@ func (d *Dirent) Encode(b *buffer) {
b.WriteQIDType(d.Type)
b.WriteString(d.Name)
}

// AllocateMode are possible modes to p9.File.Allocate().
type AllocateMode struct {
KeepSize bool
PunchHole bool
NoHideStale bool
CollapseRange bool
ZeroRange bool
InsertRange bool
Unshare bool
}

// ToLinux converts to a value compatible with fallocate(2)'s mode.
func (a *AllocateMode) ToLinux() uint32 {
rv := uint32(0)
if a.KeepSize {
rv |= unix.FALLOC_FL_KEEP_SIZE
}
if a.PunchHole {
rv |= unix.FALLOC_FL_PUNCH_HOLE
}
if a.NoHideStale {
rv |= unix.FALLOC_FL_NO_HIDE_STALE
}
if a.CollapseRange {
rv |= unix.FALLOC_FL_COLLAPSE_RANGE
}
if a.ZeroRange {
rv |= unix.FALLOC_FL_ZERO_RANGE
}
if a.InsertRange {
rv |= unix.FALLOC_FL_INSERT_RANGE
}
if a.Unshare {
rv |= unix.FALLOC_FL_UNSHARE_RANGE
}
return rv
}

// Decode implements encoder.Decode.
func (a *AllocateMode) Decode(b *buffer) {
mask := b.Read32()
a.KeepSize = mask&0x01 != 0
a.PunchHole = mask&0x02 != 0
a.NoHideStale = mask&0x04 != 0
a.CollapseRange = mask&0x08 != 0
a.ZeroRange = mask&0x10 != 0
a.InsertRange = mask&0x20 != 0
a.Unshare = mask&0x40 != 0
}

// Encode implements encoder.Encode.
func (a *AllocateMode) Encode(b *buffer) {
mask := uint32(0)
if a.KeepSize {
mask |= 0x01
}
if a.PunchHole {
mask |= 0x02
}
if a.NoHideStale {
mask |= 0x04
}
if a.CollapseRange {
mask |= 0x08
}
if a.ZeroRange {
mask |= 0x10
}
if a.InsertRange {
mask |= 0x20
}
if a.Unshare {
mask |= 0x40
}
b.Write32(mask)
}
7 changes: 6 additions & 1 deletion pkg/p9/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const (
//
// Clients are expected to start requesting this version number and
// to continuously decrement it until a Tversion request succeeds.
highestSupportedVersion uint32 = 6
highestSupportedVersion uint32 = 7

// lowestSupportedVersion is the lowest supported version X in a
// version string of the format 9P2000.L.Google.X.
Expand Down Expand Up @@ -143,3 +143,8 @@ func VersionSupportsAnonymous(v uint32) bool {
func VersionSupportsMultiUser(v uint32) bool {
return v >= 6
}

// versionSupportsTallocate returns true if version v supports Allocate().
func versionSupportsTallocate(v uint32) bool {
return v >= 7
}
1 change: 1 addition & 0 deletions pkg/sentry/fs/ashmem/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
type Device struct {
fsutil.InodeGenericChecker `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNoopAllocate `state:"nosave"`
fsutil.InodeNoopRelease `state:"nosave"`
fsutil.InodeNoopTruncate `state:"nosave"`
fsutil.InodeNoopWriteOut `state:"nosave"`
Expand Down
1 change: 1 addition & 0 deletions pkg/sentry/fs/binder/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const (
type Device struct {
fsutil.InodeGenericChecker `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNoopAllocate `state:"nosave"`
fsutil.InodeNoopRelease `state:"nosave"`
fsutil.InodeNoopTruncate `state:"nosave"`
fsutil.InodeNoopWriteOut `state:"nosave"`
Expand Down
3 changes: 2 additions & 1 deletion pkg/sentry/fs/dev/full.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
type fullDevice struct {
fsutil.InodeGenericChecker `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNoopAllocate `state:"nosave"`
fsutil.InodeNoopRelease `state:"nosave"`
fsutil.InodeNoopTruncate `state:"nosave"`
fsutil.InodeNoopWriteOut `state:"nosave"`
Expand Down Expand Up @@ -59,7 +60,6 @@ func (f *fullDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.Fi

// +stateify savable
type fullFileOperations struct {
waiter.AlwaysReady `state:"nosave"`
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
Expand All @@ -69,6 +69,7 @@ type fullFileOperations struct {
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
readZeros `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
}

var _ fs.FileOperations = (*fullFileOperations)(nil)
Expand Down
9 changes: 5 additions & 4 deletions pkg/sentry/fs/dev/null.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
type nullDevice struct {
fsutil.InodeGenericChecker `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNoopAllocate `state:"nosave"`
fsutil.InodeNoopRelease `state:"nosave"`
fsutil.InodeNoopTruncate `state:"nosave"`
fsutil.InodeNoopWriteOut `state:"nosave"`
Expand Down Expand Up @@ -60,17 +61,17 @@ func (n *nullDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.Fi

// +stateify savable
type nullFileOperations struct {
waiter.AlwaysReady `state:"nosave"`
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRead `state:"nosave"`
fsutil.FileNoopWrite `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNoopWrite `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
}

var _ fs.FileOperations = (*nullFileOperations)(nil)
Expand Down Expand Up @@ -101,16 +102,16 @@ func (zd *zeroDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.F

// +stateify savable
type zeroFileOperations struct {
waiter.AlwaysReady `state:"nosave"`
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNoopWrite `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
readZeros `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
}

var _ fs.FileOperations = (*zeroFileOperations)(nil)
Expand Down
Loading

0 comments on commit 1bee43b

Please sign in to comment.