fusego/fuseops/convert.go

407 lines
8.7 KiB
Go
Raw Normal View History

2015-03-24 06:24:00 +03:00
// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fuseops
2015-03-24 06:36:44 +03:00
import (
2015-07-24 01:02:49 +03:00
"bytes"
2015-05-25 07:16:19 +03:00
"log"
2015-03-24 07:00:05 +03:00
"time"
2015-07-24 00:53:16 +03:00
"unsafe"
2015-03-24 07:00:05 +03:00
2015-07-23 09:22:32 +03:00
"github.com/jacobsa/fuse/internal/fusekernel"
"github.com/jacobsa/fuse/internal/fuseshim"
"golang.org/x/net/context"
2015-03-24 06:36:44 +03:00
)
2015-03-24 06:24:00 +03:00
// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
2015-05-05 03:00:18 +03:00
//
// Convert the supplied fuse kernel message to an Op. finished will be called
// with the error supplied to o.Respond when the user invokes that method,
// before a response is sent to the kernel. o.Respond will destroy the message.
2015-05-05 03:29:48 +03:00
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
//
// The debug logging function and error logger may be nil.
func Convert(
2015-05-05 03:00:18 +03:00
opCtx context.Context,
m *fuseshim.Message,
2015-07-24 01:02:49 +03:00
protocol fusekernel.Protocol,
debugLogForOp func(int, string, ...interface{}),
2015-05-25 07:16:19 +03:00
errorLogger *log.Logger,
2015-05-05 03:00:18 +03:00
finished func(error)) (o Op) {
2015-03-24 06:36:44 +03:00
var co *commonOp
2015-05-05 03:21:21 +03:00
var io internalOp
2015-07-23 09:37:58 +03:00
switch m.Hdr.Opcode {
2015-07-23 09:40:27 +03:00
case fusekernel.OpLookup:
buf := m.Bytes()
n := len(buf)
if n == 0 || buf[n-1] != '\x00' {
goto corrupt
}
2015-03-24 06:46:31 +03:00
to := &LookUpInodeOp{
2015-07-23 09:40:27 +03:00
Parent: InodeID(m.Header().Node),
Name: string(buf[:n-1]),
2015-03-24 06:46:31 +03:00
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:40:58 +03:00
co = &to.commonOp
2015-07-23 09:41:34 +03:00
case fusekernel.OpGetattr:
2015-03-24 06:46:31 +03:00
to := &GetInodeAttributesOp{
2015-07-23 09:41:34 +03:00
Inode: InodeID(m.Header().Node),
2015-03-24 06:46:31 +03:00
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:40:58 +03:00
co = &to.commonOp
2015-07-24 00:53:16 +03:00
case fusekernel.OpSetattr:
in := (*fusekernel.SetattrIn)(m.Data())
if m.Len() < unsafe.Sizeof(*in) {
goto corrupt
}
2015-03-24 06:46:31 +03:00
to := &SetInodeAttributesOp{
2015-07-24 00:53:16 +03:00
Inode: InodeID(m.Header().Node),
2015-03-24 06:46:31 +03:00
}
2015-03-24 08:32:12 +03:00
2015-07-24 00:53:16 +03:00
valid := fusekernel.SetattrValid(in.Valid)
if valid&fusekernel.SetattrSize != 0 {
to.Size = &in.Size
2015-03-24 08:32:12 +03:00
}
2015-07-24 00:53:16 +03:00
if valid&fusekernel.SetattrMode != 0 {
mode := fuseshim.FileMode(in.Mode)
to.Mode = &mode
2015-03-24 08:32:12 +03:00
}
2015-07-24 00:53:16 +03:00
if valid&fusekernel.SetattrAtime != 0 {
t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
to.Atime = &t
2015-03-24 08:32:12 +03:00
}
2015-07-24 00:53:16 +03:00
if valid&fusekernel.SetattrMtime != 0 {
t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
to.Mtime = &t
2015-03-24 08:32:12 +03:00
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:40:58 +03:00
co = &to.commonOp
2015-07-24 00:57:31 +03:00
case fusekernel.OpForget:
in := (*fusekernel.ForgetIn)(m.Data())
if m.Len() < unsafe.Sizeof(*in) {
goto corrupt
}
to := &ForgetInodeOp{
2015-07-24 00:57:31 +03:00
Inode: InodeID(m.Header().Node),
N: in.Nlookup,
}
2015-05-05 03:21:21 +03:00
io = to
co = &to.commonOp
2015-07-24 01:02:49 +03:00
case fusekernel.OpMkdir:
size := fusekernel.MkdirInSize(protocol)
if m.Len() < size {
goto corrupt
}
in := (*fusekernel.MkdirIn)(m.Data())
name := m.Bytes()[size:]
i := bytes.IndexByte(name, '\x00')
if i < 0 {
goto corrupt
}
name = name[:i]
2015-03-24 06:46:31 +03:00
to := &MkDirOp{
2015-07-24 01:02:49 +03:00
Parent: InodeID(m.Header().Node),
Name: string(name),
Mode: fuseshim.FileMode(in.Mode),
2015-03-24 06:46:31 +03:00
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:40:58 +03:00
co = &to.commonOp
2015-07-24 01:04:03 +03:00
case fusekernel.OpCreate:
size := fusekernel.CreateInSize(protocol)
if m.Len() < size {
goto corrupt
}
in := (*fusekernel.CreateIn)(m.Data())
name := m.Bytes()[size:]
i := bytes.IndexByte(name, '\x00')
if i < 0 {
goto corrupt
}
name = name[:i]
2015-03-24 06:46:31 +03:00
to := &CreateFileOp{
2015-07-24 01:04:03 +03:00
Parent: InodeID(m.Header().Node),
Name: string(name),
Mode: fuseshim.FileMode(in.Mode),
2015-03-24 06:46:31 +03:00
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:40:58 +03:00
co = &to.commonOp
2015-07-24 01:04:59 +03:00
case fusekernel.OpSymlink:
// m.Bytes() is "newName\0target\0"
names := m.Bytes()
if len(names) == 0 || names[len(names)-1] != 0 {
goto corrupt
}
i := bytes.IndexByte(names, '\x00')
if i < 0 {
goto corrupt
}
newName, target := names[0:i], names[i+1:len(names)-1]
2015-05-19 08:34:14 +03:00
to := &CreateSymlinkOp{
2015-07-24 01:04:59 +03:00
Parent: InodeID(m.Header().Node),
Name: string(newName),
Target: string(target),
2015-05-19 08:34:14 +03:00
}
io = to
co = &to.commonOp
2015-07-24 01:07:22 +03:00
case fusekernel.OpRename:
in := (*fusekernel.RenameIn)(m.Data())
if m.Len() < unsafe.Sizeof(*in) {
goto corrupt
}
names := m.Bytes()[unsafe.Sizeof(*in):]
// names should be "old\x00new\x00"
if len(names) < 4 {
goto corrupt
}
if names[len(names)-1] != '\x00' {
goto corrupt
}
i := bytes.IndexByte(names, '\x00')
if i < 0 {
goto corrupt
}
oldName, newName := names[:i], names[i+1:len(names)-1]
2015-06-25 08:37:04 +03:00
to := &RenameOp{
2015-07-24 01:07:22 +03:00
OldParent: InodeID(m.Header().Node),
OldName: string(oldName),
NewParent: InodeID(in.Newdir),
NewName: string(newName),
2015-06-25 08:37:04 +03:00
}
io = to
co = &to.commonOp
2015-07-24 01:08:41 +03:00
case fusekernel.OpUnlink:
buf := m.Bytes()
n := len(buf)
if n == 0 || buf[n-1] != '\x00' {
goto corrupt
}
to := &UnlinkOp{
Parent: InodeID(m.Header().Node),
Name: string(buf[:n-1]),
}
io = to
co = &to.commonOp
2015-07-24 01:09:07 +03:00
case fusekernel.OpRmdir:
buf := m.Bytes()
n := len(buf)
if n == 0 || buf[n-1] != '\x00' {
goto corrupt
}
to := &RmDirOp{
Parent: InodeID(m.Header().Node),
Name: string(buf[:n-1]),
2015-03-24 06:51:07 +03:00
}
2015-07-24 01:09:07 +03:00
io = to
co = &to.commonOp
2015-03-24 06:40:58 +03:00
case *fuseshim.OpenRequest:
2015-03-24 06:56:41 +03:00
if typed.Dir {
to := &OpenDirOp{
2015-07-22 15:27:48 +03:00
bfReq: typed,
2015-03-24 06:56:41 +03:00
Inode: InodeID(typed.Header.Node),
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:56:41 +03:00
co = &to.commonOp
} else {
to := &OpenFileOp{
2015-07-22 15:27:48 +03:00
bfReq: typed,
2015-03-24 06:56:41 +03:00
Inode: InodeID(typed.Header.Node),
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:56:41 +03:00
co = &to.commonOp
}
2015-03-24 06:40:58 +03:00
2015-07-24 01:10:45 +03:00
case fusekernel.OpRead:
in := (*fusekernel.ReadIn)(m.Data())
if m.Len() < fusekernel.ReadInSize(protocol) {
goto corrupt
}
to := &ReadFileOp{
Inode: InodeID(m.Header().Node),
Handle: HandleID(in.Fh),
Offset: int64(in.Offset),
Size: int(in.Size),
}
io = to
co = &to.commonOp
2015-07-24 01:11:24 +03:00
case fusekernel.OpReaddir:
in := (*fusekernel.ReadIn)(m.Data())
if m.Len() < fusekernel.ReadInSize(protocol) {
goto corrupt
2015-03-24 06:56:41 +03:00
}
2015-03-24 06:40:58 +03:00
2015-07-24 01:11:24 +03:00
to := &ReadDirOp{
Inode: InodeID(m.Header().Node),
Handle: HandleID(in.Fh),
Offset: int64(in.Offset),
Size: int(in.Size),
}
io = to
co = &to.commonOp
case *fuseshim.ReleaseRequest:
2015-03-24 06:56:41 +03:00
if typed.Dir {
to := &ReleaseDirHandleOp{
2015-07-22 15:27:48 +03:00
bfReq: typed,
2015-03-24 06:56:41 +03:00
Handle: HandleID(typed.Handle),
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:56:41 +03:00
co = &to.commonOp
} else {
2015-03-24 08:02:40 +03:00
to := &ReleaseFileHandleOp{
2015-07-22 15:27:48 +03:00
bfReq: typed,
2015-03-24 06:56:41 +03:00
Handle: HandleID(typed.Handle),
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:56:41 +03:00
co = &to.commonOp
}
2015-03-24 06:40:58 +03:00
case *fuseshim.WriteRequest:
2015-03-24 06:46:31 +03:00
to := &WriteFileOp{
2015-07-22 15:27:48 +03:00
bfReq: typed,
2015-03-24 06:50:17 +03:00
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
Data: typed.Data,
Offset: typed.Offset,
2015-03-24 06:46:31 +03:00
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:40:58 +03:00
co = &to.commonOp
case *fuseshim.FsyncRequest:
2015-03-24 06:50:17 +03:00
// We don't currently support this for directories.
if typed.Dir {
to := &unknownOp{}
io = to
co = &to.commonOp
} else {
to := &SyncFileOp{
2015-07-22 15:27:48 +03:00
bfReq: typed,
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
}
io = to
co = &to.commonOp
2015-03-24 06:46:31 +03:00
}
2015-03-24 06:40:58 +03:00
case *fuseshim.FlushRequest:
2015-03-24 06:46:31 +03:00
to := &FlushFileOp{
2015-07-22 15:27:48 +03:00
bfReq: typed,
2015-03-24 06:50:17 +03:00
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
2015-03-24 06:46:31 +03:00
}
2015-05-05 03:21:21 +03:00
io = to
2015-03-24 06:40:58 +03:00
co = &to.commonOp
case *fuseshim.ReadlinkRequest:
2015-05-19 09:05:54 +03:00
to := &ReadSymlinkOp{
2015-07-22 15:27:48 +03:00
bfReq: typed,
2015-05-19 09:05:54 +03:00
Inode: InodeID(typed.Header.Node),
}
io = to
co = &to.commonOp
2015-03-24 06:36:44 +03:00
default:
2015-05-05 03:32:57 +03:00
to := &unknownOp{}
io = to
co = &to.commonOp
2015-03-24 06:36:44 +03:00
}
2015-05-25 07:16:19 +03:00
co.init(
opCtx,
io,
r,
debugLogForOp,
errorLogger,
finished)
2015-05-05 03:21:21 +03:00
o = io
2015-03-24 06:36:44 +03:00
return
}
func convertAttributes(
inode InodeID,
attr InodeAttributes,
expiration time.Time) fuseshim.Attr {
return fuseshim.Attr{
2015-03-24 07:00:05 +03:00
Inode: uint64(inode),
Size: attr.Size,
Mode: attr.Mode,
Nlink: uint32(attr.Nlink),
Atime: attr.Atime,
Mtime: attr.Mtime,
Ctime: attr.Ctime,
Crtime: attr.Crtime,
Uid: attr.Uid,
Gid: attr.Gid,
Valid: convertExpirationTime(expiration),
2015-03-24 07:00:05 +03:00
}
}
// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
// package converts time.Duration values to this form in a straightforward
// way (cf. http://goo.gl/FJhV8j).
//
// So negative durations are right out. There is no need to cap the positive
// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
// time.Duration.
d = t.Sub(time.Now())
if d < 0 {
d = 0
}
return
}
func convertChildInodeEntry(
in *ChildInodeEntry,
out *fuseshim.LookupResponse) {
out.Node = fuseshim.NodeID(in.Child)
2015-03-24 07:00:05 +03:00
out.Generation = uint64(in.Generation)
out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
2015-03-24 07:00:05 +03:00
out.EntryValid = convertExpirationTime(in.EntryExpiration)
}