summaryrefslogtreecommitdiffhomepage
path: root/pkg/p9
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/p9')
-rw-r--r--pkg/p9/BUILD1
-rw-r--r--pkg/p9/client_file.go12
-rw-r--r--pkg/p9/file.go4
-rw-r--r--pkg/p9/handlers.go34
-rw-r--r--pkg/p9/local_server/local_server.go5
-rw-r--r--pkg/p9/messages.go59
-rw-r--r--pkg/p9/p9.go81
-rw-r--r--pkg/p9/version.go7
8 files changed, 202 insertions, 1 deletions
diff --git a/pkg/p9/BUILD b/pkg/p9/BUILD
index 5d972309d..36b2ec5f6 100644
--- a/pkg/p9/BUILD
+++ b/pkg/p9/BUILD
@@ -26,6 +26,7 @@ go_library(
"//pkg/fd",
"//pkg/log",
"//pkg/unet",
+ "@org_golang_x_sys//unix:go_default_library",
],
)
diff --git a/pkg/p9/client_file.go b/pkg/p9/client_file.go
index 63c65129a..471c3a80b 100644
--- a/pkg/p9/client_file.go
+++ b/pkg/p9/client_file.go
@@ -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
diff --git a/pkg/p9/file.go b/pkg/p9/file.go
index a52a0f3e7..89e814d50 100644
--- a/pkg/p9/file.go
+++ b/pkg/p9/file.go
@@ -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.
//
diff --git a/pkg/p9/handlers.go b/pkg/p9/handlers.go
index 6da2ce4e3..533ead98a 100644
--- a/pkg/p9/handlers.go
+++ b/pkg/p9/handlers.go
@@ -878,6 +878,40 @@ func (t *Tsetattr) handle(cs *connState) message {
}
// 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.
ref, ok := cs.LookupFID(t.FID)
diff --git a/pkg/p9/local_server/local_server.go b/pkg/p9/local_server/local_server.go
index f4077a9d4..d49d94550 100644
--- a/pkg/p9/local_server/local_server.go
+++ b/pkg/p9/local_server/local_server.go
@@ -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)
diff --git a/pkg/p9/messages.go b/pkg/p9/messages.go
index 3c7898cc1..703753c31 100644
--- a/pkg/p9/messages.go
+++ b/pkg/p9/messages.go
@@ -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.
@@ -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{} })
}
diff --git a/pkg/p9/p9.go b/pkg/p9/p9.go
index 78c7d3f86..4039862e6 100644
--- a/pkg/p9/p9.go
+++ b/pkg/p9/p9.go
@@ -22,6 +22,8 @@ import (
"strings"
"sync/atomic"
"syscall"
+
+ "golang.org/x/sys/unix"
)
// OpenFlags is the mode passed to Open and Create operations.
@@ -374,6 +376,8 @@ const (
MsgRusymlink = 135
MsgTlconnect = 136
MsgRlconnect = 137
+ MsgTallocate = 138
+ MsgRallocate = 139
)
// QIDType represents the file type for QIDs.
@@ -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)
+}
diff --git a/pkg/p9/version.go b/pkg/p9/version.go
index a36a499a1..c2a2885ae 100644
--- a/pkg/p9/version.go
+++ b/pkg/p9/version.go
@@ -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.
@@ -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
+}