123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890 |
- // 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 fuse
- import (
- "bytes"
- "errors"
- "fmt"
- "os"
- "reflect"
- "syscall"
- "time"
- "unsafe"
- "github.com/jacobsa/fuse/fuseops"
- "github.com/jacobsa/fuse/internal/buffer"
- "github.com/jacobsa/fuse/internal/fusekernel"
- )
- ////////////////////////////////////////////////////////////////////////
- // Incoming messages
- ////////////////////////////////////////////////////////////////////////
- // Convert a kernel message to an appropriate op. If the op is unknown, a
- // special unexported type will be used.
- //
- // The caller is responsible for arranging for the message to be destroyed.
- func convertInMessage(
- inMsg *buffer.InMessage,
- outMsg *buffer.OutMessage,
- protocol fusekernel.Protocol) (o interface{}, err error) {
- switch inMsg.Header().Opcode {
- case fusekernel.OpLookup:
- buf := inMsg.ConsumeBytes(inMsg.Len())
- n := len(buf)
- if n == 0 || buf[n-1] != '\x00' {
- err = errors.New("Corrupt OpLookup")
- return
- }
- o = &fuseops.LookUpInodeOp{
- Parent: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(buf[:n-1]),
- }
- case fusekernel.OpGetattr:
- o = &fuseops.GetInodeAttributesOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- }
- case fusekernel.OpSetattr:
- type input fusekernel.SetattrIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpSetattr")
- return
- }
- to := &fuseops.SetInodeAttributesOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- }
- o = to
- valid := fusekernel.SetattrValid(in.Valid)
- if valid&fusekernel.SetattrSize != 0 {
- to.Size = &in.Size
- }
- if valid&fusekernel.SetattrMode != 0 {
- mode := convertFileMode(in.Mode)
- to.Mode = &mode
- }
- if valid&fusekernel.SetattrAtime != 0 {
- t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
- to.Atime = &t
- }
- if valid&fusekernel.SetattrMtime != 0 {
- t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
- to.Mtime = &t
- }
- case fusekernel.OpForget:
- type input fusekernel.ForgetIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpForget")
- return
- }
- o = &fuseops.ForgetInodeOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- N: in.Nlookup,
- }
- case fusekernel.OpMkdir:
- in := (*fusekernel.MkdirIn)(inMsg.Consume(fusekernel.MkdirInSize(protocol)))
- if in == nil {
- err = errors.New("Corrupt OpMkdir")
- return
- }
- name := inMsg.ConsumeBytes(inMsg.Len())
- i := bytes.IndexByte(name, '\x00')
- if i < 0 {
- err = errors.New("Corrupt OpMkdir")
- return
- }
- name = name[:i]
- o = &fuseops.MkDirOp{
- Parent: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(name),
- // On Linux, vfs_mkdir calls through to the inode with at most
- // permissions and sticky bits set (cf. https://goo.gl/WxgQXk), and fuse
- // passes that on directly (cf. https://goo.gl/f31aMo). In other words,
- // the fact that this is a directory is implicit in the fact that the
- // opcode is mkdir. But we want the correct mode to go through, so ensure
- // that os.ModeDir is set.
- Mode: convertFileMode(in.Mode) | os.ModeDir,
- }
- case fusekernel.OpMknod:
- in := (*fusekernel.MknodIn)(inMsg.Consume(fusekernel.MknodInSize(protocol)))
- if in == nil {
- err = errors.New("Corrupt OpMknod")
- return
- }
- name := inMsg.ConsumeBytes(inMsg.Len())
- i := bytes.IndexByte(name, '\x00')
- if i < 0 {
- err = errors.New("Corrupt OpMknod")
- return
- }
- name = name[:i]
- o = &fuseops.MkNodeOp{
- Parent: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(name),
- Mode: convertFileMode(in.Mode),
- }
- case fusekernel.OpCreate:
- in := (*fusekernel.CreateIn)(inMsg.Consume(fusekernel.CreateInSize(protocol)))
- if in == nil {
- err = errors.New("Corrupt OpCreate")
- return
- }
- name := inMsg.ConsumeBytes(inMsg.Len())
- i := bytes.IndexByte(name, '\x00')
- if i < 0 {
- err = errors.New("Corrupt OpCreate")
- return
- }
- name = name[:i]
- o = &fuseops.CreateFileOp{
- Parent: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(name),
- Mode: convertFileMode(in.Mode),
- }
- case fusekernel.OpSymlink:
- // The message is "newName\0target\0".
- names := inMsg.ConsumeBytes(inMsg.Len())
- if len(names) == 0 || names[len(names)-1] != 0 {
- err = errors.New("Corrupt OpSymlink")
- return
- }
- i := bytes.IndexByte(names, '\x00')
- if i < 0 {
- err = errors.New("Corrupt OpSymlink")
- return
- }
- newName, target := names[0:i], names[i+1:len(names)-1]
- o = &fuseops.CreateSymlinkOp{
- Parent: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(newName),
- Target: string(target),
- }
- case fusekernel.OpRename:
- type input fusekernel.RenameIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpRename")
- return
- }
- names := inMsg.ConsumeBytes(inMsg.Len())
- // names should be "old\x00new\x00"
- if len(names) < 4 {
- err = errors.New("Corrupt OpRename")
- return
- }
- if names[len(names)-1] != '\x00' {
- err = errors.New("Corrupt OpRename")
- return
- }
- i := bytes.IndexByte(names, '\x00')
- if i < 0 {
- err = errors.New("Corrupt OpRename")
- return
- }
- oldName, newName := names[:i], names[i+1:len(names)-1]
- o = &fuseops.RenameOp{
- OldParent: fuseops.InodeID(inMsg.Header().Nodeid),
- OldName: string(oldName),
- NewParent: fuseops.InodeID(in.Newdir),
- NewName: string(newName),
- }
- case fusekernel.OpUnlink:
- buf := inMsg.ConsumeBytes(inMsg.Len())
- n := len(buf)
- if n == 0 || buf[n-1] != '\x00' {
- err = errors.New("Corrupt OpUnlink")
- return
- }
- o = &fuseops.UnlinkOp{
- Parent: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(buf[:n-1]),
- }
- case fusekernel.OpRmdir:
- buf := inMsg.ConsumeBytes(inMsg.Len())
- n := len(buf)
- if n == 0 || buf[n-1] != '\x00' {
- err = errors.New("Corrupt OpRmdir")
- return
- }
- o = &fuseops.RmDirOp{
- Parent: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(buf[:n-1]),
- }
- case fusekernel.OpOpen:
- o = &fuseops.OpenFileOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- }
- case fusekernel.OpOpendir:
- o = &fuseops.OpenDirOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- }
- case fusekernel.OpRead:
- in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol)))
- if in == nil {
- err = errors.New("Corrupt OpRead")
- return
- }
- to := &fuseops.ReadFileOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- Handle: fuseops.HandleID(in.Fh),
- Offset: int64(in.Offset),
- }
- o = to
- readSize := int(in.Size)
- p := outMsg.GrowNoZero(readSize)
- if p == nil {
- err = fmt.Errorf("Can't grow for %d-byte read", readSize)
- return
- }
- sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
- sh.Data = uintptr(p)
- sh.Len = readSize
- sh.Cap = readSize
- case fusekernel.OpReaddir:
- in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol)))
- if in == nil {
- err = errors.New("Corrupt OpReaddir")
- return
- }
- to := &fuseops.ReadDirOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- Handle: fuseops.HandleID(in.Fh),
- Offset: fuseops.DirOffset(in.Offset),
- }
- o = to
- readSize := int(in.Size)
- p := outMsg.GrowNoZero(readSize)
- if p == nil {
- err = fmt.Errorf("Can't grow for %d-byte read", readSize)
- return
- }
- sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
- sh.Data = uintptr(p)
- sh.Len = readSize
- sh.Cap = readSize
- case fusekernel.OpRelease:
- type input fusekernel.ReleaseIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpRelease")
- return
- }
- o = &fuseops.ReleaseFileHandleOp{
- Handle: fuseops.HandleID(in.Fh),
- }
- case fusekernel.OpReleasedir:
- type input fusekernel.ReleaseIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpReleasedir")
- return
- }
- o = &fuseops.ReleaseDirHandleOp{
- Handle: fuseops.HandleID(in.Fh),
- }
- case fusekernel.OpWrite:
- in := (*fusekernel.WriteIn)(inMsg.Consume(fusekernel.WriteInSize(protocol)))
- if in == nil {
- err = errors.New("Corrupt OpWrite")
- return
- }
- buf := inMsg.ConsumeBytes(inMsg.Len())
- if len(buf) < int(in.Size) {
- err = errors.New("Corrupt OpWrite")
- return
- }
- o = &fuseops.WriteFileOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- Handle: fuseops.HandleID(in.Fh),
- Data: buf,
- Offset: int64(in.Offset),
- }
- case fusekernel.OpFsync:
- type input fusekernel.FsyncIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpFsync")
- return
- }
- o = &fuseops.SyncFileOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- Handle: fuseops.HandleID(in.Fh),
- }
- case fusekernel.OpFlush:
- type input fusekernel.FlushIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpFlush")
- return
- }
- o = &fuseops.FlushFileOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- Handle: fuseops.HandleID(in.Fh),
- }
- case fusekernel.OpReadlink:
- o = &fuseops.ReadSymlinkOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- }
- case fusekernel.OpStatfs:
- o = &fuseops.StatFSOp{}
- case fusekernel.OpInterrupt:
- type input fusekernel.InterruptIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpInterrupt")
- return
- }
- o = &interruptOp{
- FuseID: in.Unique,
- }
- case fusekernel.OpInit:
- type input fusekernel.InitIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpInit")
- return
- }
- o = &initOp{
- Kernel: fusekernel.Protocol{in.Major, in.Minor},
- MaxReadahead: in.MaxReadahead,
- Flags: fusekernel.InitFlags(in.Flags),
- }
- case fusekernel.OpRemovexattr:
- buf := inMsg.ConsumeBytes(inMsg.Len())
- n := len(buf)
- if n == 0 || buf[n-1] != '\x00' {
- err = errors.New("Corrupt OpRemovexattr")
- return
- }
- o = &fuseops.RemoveXattrOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(buf[:n-1]),
- }
- case fusekernel.OpGetxattr:
- type input fusekernel.GetxattrIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpGetxattr")
- return
- }
- name := inMsg.ConsumeBytes(inMsg.Len())
- i := bytes.IndexByte(name, '\x00')
- if i < 0 {
- err = errors.New("Corrupt OpGetxattr")
- return
- }
- name = name[:i]
- to := &fuseops.GetXattrOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(name),
- }
- o = to
- readSize := int(in.Size)
- p := outMsg.GrowNoZero(readSize)
- if p == nil {
- err = fmt.Errorf("Can't grow for %d-byte read", readSize)
- return
- }
- sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
- sh.Data = uintptr(p)
- sh.Len = readSize
- sh.Cap = readSize
- case fusekernel.OpListxattr:
- type input fusekernel.ListxattrIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpListxattr")
- return
- }
- to := &fuseops.ListXattrOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- }
- o = to
- readSize := int(in.Size)
- if readSize != 0 {
- p := outMsg.GrowNoZero(readSize)
- if p == nil {
- err = fmt.Errorf("Can't grow for %d-byte read", readSize)
- return
- }
- sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
- sh.Data = uintptr(p)
- sh.Len = readSize
- sh.Cap = readSize
- }
- case fusekernel.OpSetxattr:
- type input fusekernel.SetxattrIn
- in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
- if in == nil {
- err = errors.New("Corrupt OpSetxattr")
- return
- }
- payload := inMsg.ConsumeBytes(inMsg.Len())
- // payload should be "name\x00value"
- if len(payload) < 3 {
- err = errors.New("Corrupt OpSetxattr")
- return
- }
- i := bytes.IndexByte(payload, '\x00')
- if i < 0 {
- err = errors.New("Corrupt OpSetxattr")
- return
- }
- name, value := payload[:i], payload[i+1:len(payload)]
- o = &fuseops.SetXattrOp{
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- Name: string(name),
- Value: value,
- Flags: in.Flags,
- }
- default:
- o = &unknownOp{
- OpCode: inMsg.Header().Opcode,
- Inode: fuseops.InodeID(inMsg.Header().Nodeid),
- }
- }
- return
- }
- ////////////////////////////////////////////////////////////////////////
- // Outgoing messages
- ////////////////////////////////////////////////////////////////////////
- // Fill in the response that should be sent to the kernel, or set noResponse if
- // the op requires no response.
- func (c *Connection) kernelResponse(
- m *buffer.OutMessage,
- fuseID uint64,
- op interface{},
- opErr error) (noResponse bool) {
- h := m.OutHeader()
- h.Unique = fuseID
- // Special case: handle the ops for which the kernel expects no response.
- // interruptOp .
- switch op.(type) {
- case *fuseops.ForgetInodeOp:
- noResponse = true
- return
- case *interruptOp:
- noResponse = true
- return
- }
- // If the user returned the error, fill in the error field of the outgoing
- // message header.
- if opErr != nil {
- handled := false
- if opErr == syscall.ERANGE {
- switch o := op.(type) {
- case *fuseops.GetXattrOp:
- writeXattrSize(m, uint32(o.BytesRead))
- handled = true
- case *fuseops.ListXattrOp:
- writeXattrSize(m, uint32(o.BytesRead))
- handled = true
- }
- }
- if !handled {
- m.OutHeader().Error = -int32(syscall.EIO)
- if errno, ok := opErr.(syscall.Errno); ok {
- m.OutHeader().Error = -int32(errno)
- }
- // Special case: for some types, convertInMessage grew the message in order
- // to obtain a destination buffer. Make sure that we shrink back to just
- // the header, because on OS X the kernel otherwise returns EINVAL when we
- // attempt to write an error response with a length that extends beyond the
- // header.
- m.ShrinkTo(buffer.OutMessageHeaderSize)
- }
- }
- // Otherwise, fill in the rest of the response.
- if opErr == nil {
- c.kernelResponseForOp(m, op)
- }
- h.Len = uint32(m.Len())
- return
- }
- // Like kernelResponse, but assumes the user replied with a nil error to the
- // op.
- func (c *Connection) kernelResponseForOp(
- m *buffer.OutMessage,
- op interface{}) {
- // Create the appropriate output message
- switch o := op.(type) {
- case *fuseops.LookUpInodeOp:
- size := int(fusekernel.EntryOutSize(c.protocol))
- out := (*fusekernel.EntryOut)(m.Grow(size))
- convertChildInodeEntry(&o.Entry, out)
- case *fuseops.GetInodeAttributesOp:
- size := int(fusekernel.AttrOutSize(c.protocol))
- out := (*fusekernel.AttrOut)(m.Grow(size))
- out.AttrValid, out.AttrValidNsec = convertExpirationTime(
- o.AttributesExpiration)
- convertAttributes(o.Inode, &o.Attributes, &out.Attr)
- case *fuseops.SetInodeAttributesOp:
- size := int(fusekernel.AttrOutSize(c.protocol))
- out := (*fusekernel.AttrOut)(m.Grow(size))
- out.AttrValid, out.AttrValidNsec = convertExpirationTime(
- o.AttributesExpiration)
- convertAttributes(o.Inode, &o.Attributes, &out.Attr)
- case *fuseops.MkDirOp:
- size := int(fusekernel.EntryOutSize(c.protocol))
- out := (*fusekernel.EntryOut)(m.Grow(size))
- convertChildInodeEntry(&o.Entry, out)
- case *fuseops.MkNodeOp:
- size := int(fusekernel.EntryOutSize(c.protocol))
- out := (*fusekernel.EntryOut)(m.Grow(size))
- convertChildInodeEntry(&o.Entry, out)
- case *fuseops.CreateFileOp:
- eSize := int(fusekernel.EntryOutSize(c.protocol))
- e := (*fusekernel.EntryOut)(m.Grow(eSize))
- convertChildInodeEntry(&o.Entry, e)
- oo := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
- oo.Fh = uint64(o.Handle)
- case *fuseops.CreateSymlinkOp:
- size := int(fusekernel.EntryOutSize(c.protocol))
- out := (*fusekernel.EntryOut)(m.Grow(size))
- convertChildInodeEntry(&o.Entry, out)
- case *fuseops.RenameOp:
- // Empty response
- case *fuseops.RmDirOp:
- // Empty response
- case *fuseops.UnlinkOp:
- // Empty response
- case *fuseops.OpenDirOp:
- out := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
- out.Fh = uint64(o.Handle)
- case *fuseops.ReadDirOp:
- // convertInMessage already set up the destination buffer to be at the end
- // of the out message. We need only shrink to the right size based on how
- // much the user read.
- m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
- case *fuseops.ReleaseDirHandleOp:
- // Empty response
- case *fuseops.OpenFileOp:
- out := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
- out.Fh = uint64(o.Handle)
- if o.KeepPageCache {
- out.OpenFlags |= uint32(fusekernel.OpenKeepCache)
- }
- if o.UseDirectIO {
- out.OpenFlags |= uint32(fusekernel.OpenDirectIO)
- }
- case *fuseops.ReadFileOp:
- // convertInMessage already set up the destination buffer to be at the end
- // of the out message. We need only shrink to the right size based on how
- // much the user read.
- m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
- case *fuseops.WriteFileOp:
- out := (*fusekernel.WriteOut)(m.Grow(int(unsafe.Sizeof(fusekernel.WriteOut{}))))
- out.Size = uint32(len(o.Data))
- case *fuseops.SyncFileOp:
- // Empty response
- case *fuseops.FlushFileOp:
- // Empty response
- case *fuseops.ReleaseFileHandleOp:
- // Empty response
- case *fuseops.ReadSymlinkOp:
- m.AppendString(o.Target)
- case *fuseops.StatFSOp:
- out := (*fusekernel.StatfsOut)(m.Grow(int(unsafe.Sizeof(fusekernel.StatfsOut{}))))
- out.St.Blocks = o.Blocks
- out.St.Bfree = o.BlocksFree
- out.St.Bavail = o.BlocksAvailable
- out.St.Files = o.Inodes
- out.St.Ffree = o.InodesFree
- // The posix spec for sys/statvfs.h (http://goo.gl/LktgrF) defines the
- // following fields of statvfs, among others:
- //
- // f_bsize File system block size.
- // f_frsize Fundamental file system block size.
- // f_blocks Total number of blocks on file system in units of f_frsize.
- //
- // It appears as though f_bsize was the only thing supported by most unixes
- // originally, but then f_frsize was added when new sorts of file systems
- // came about. Quoth The Linux Programming Interface by Michael Kerrisk
- // (https://goo.gl/5LZMxQ):
- //
- // For most Linux file systems, the values of f_bsize and f_frsize are
- // the same. However, some file systems support the notion of block
- // fragments, which can be used to allocate a smaller unit of storage
- // at the end of the file if if a full block is not required. This
- // avoids the waste of space that would otherwise occur if a full block
- // was allocated. On such file systems, f_frsize is the size of a
- // fragment, and f_bsize is the size of a whole block. (The notion of
- // fragments in UNIX file systems first appeared in the early 1980s
- // with the 4.2BSD Fast File System.)
- //
- // Confusingly, it appears as though osxfuse surfaces fuse_kstatfs::bsize
- // as statfs::f_iosize (of advisory use only), and fuse_kstatfs::frsize as
- // statfs::f_bsize (which affects free space display in the Finder).
- out.St.Bsize = o.IoSize
- out.St.Frsize = o.BlockSize
- case *fuseops.RemoveXattrOp:
- // Empty response
- case *fuseops.GetXattrOp:
- // convertInMessage already set up the destination buffer to be at the end
- // of the out message. We need only shrink to the right size based on how
- // much the user read.
- if o.BytesRead == 0 {
- writeXattrSize(m, uint32(o.BytesRead))
- } else {
- m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
- }
- case *fuseops.ListXattrOp:
- if o.BytesRead == 0 {
- writeXattrSize(m, uint32(o.BytesRead))
- } else {
- m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
- }
- case *fuseops.SetXattrOp:
- // Empty response
- case *initOp:
- out := (*fusekernel.InitOut)(m.Grow(int(unsafe.Sizeof(fusekernel.InitOut{}))))
- out.Major = o.Library.Major
- out.Minor = o.Library.Minor
- out.MaxReadahead = o.MaxReadahead
- out.Flags = uint32(o.Flags)
- out.MaxWrite = o.MaxWrite
- default:
- panic(fmt.Sprintf("Unexpected op: %#v", op))
- }
- return
- }
- ////////////////////////////////////////////////////////////////////////
- // General conversions
- ////////////////////////////////////////////////////////////////////////
- func convertTime(t time.Time) (secs uint64, nsec uint32) {
- totalNano := t.UnixNano()
- secs = uint64(totalNano / 1e9)
- nsec = uint32(totalNano % 1e9)
- return
- }
- func convertAttributes(
- inodeID fuseops.InodeID,
- in *fuseops.InodeAttributes,
- out *fusekernel.Attr) {
- out.Ino = uint64(inodeID)
- out.Size = in.Size
- out.Atime, out.AtimeNsec = convertTime(in.Atime)
- out.Mtime, out.MtimeNsec = convertTime(in.Mtime)
- out.Ctime, out.CtimeNsec = convertTime(in.Ctime)
- out.SetCrtime(convertTime(in.Crtime))
- out.Nlink = in.Nlink
- out.Uid = in.Uid
- out.Gid = in.Gid
- // round up to the nearest 512 boundary
- out.Blocks = (in.Size + 512 - 1) / 512
- // Set the mode.
- out.Mode = uint32(in.Mode) & 0777
- switch {
- default:
- out.Mode |= syscall.S_IFREG
- case in.Mode&os.ModeDir != 0:
- out.Mode |= syscall.S_IFDIR
- case in.Mode&os.ModeDevice != 0:
- if in.Mode&os.ModeCharDevice != 0 {
- out.Mode |= syscall.S_IFCHR
- } else {
- out.Mode |= syscall.S_IFBLK
- }
- case in.Mode&os.ModeNamedPipe != 0:
- out.Mode |= syscall.S_IFIFO
- case in.Mode&os.ModeSymlink != 0:
- out.Mode |= syscall.S_IFLNK
- case in.Mode&os.ModeSocket != 0:
- out.Mode |= syscall.S_IFSOCK
- }
- }
- // Convert an absolute cache expiration time to a relative time from now for
- // consumption by the fuse kernel module.
- func convertExpirationTime(t time.Time) (secs uint64, nsecs uint32) {
- // Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
- // counts of nanoseconds (cf. http://goo.gl/EJupJV). 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 {
- secs = uint64(d / time.Second)
- nsecs = uint32((d % time.Second) / time.Nanosecond)
- }
- return
- }
- func convertChildInodeEntry(
- in *fuseops.ChildInodeEntry,
- out *fusekernel.EntryOut) {
- out.Nodeid = uint64(in.Child)
- out.Generation = uint64(in.Generation)
- out.EntryValid, out.EntryValidNsec = convertExpirationTime(in.EntryExpiration)
- out.AttrValid, out.AttrValidNsec = convertExpirationTime(in.AttributesExpiration)
- convertAttributes(in.Child, &in.Attributes, &out.Attr)
- }
- func convertFileMode(unixMode uint32) os.FileMode {
- mode := os.FileMode(unixMode & 0777)
- switch unixMode & syscall.S_IFMT {
- case syscall.S_IFREG:
- // nothing
- case syscall.S_IFDIR:
- mode |= os.ModeDir
- case syscall.S_IFCHR:
- mode |= os.ModeCharDevice | os.ModeDevice
- case syscall.S_IFBLK:
- mode |= os.ModeDevice
- case syscall.S_IFIFO:
- mode |= os.ModeNamedPipe
- case syscall.S_IFLNK:
- mode |= os.ModeSymlink
- case syscall.S_IFSOCK:
- mode |= os.ModeSocket
- default:
- // no idea
- mode |= os.ModeDevice
- }
- if unixMode&syscall.S_ISUID != 0 {
- mode |= os.ModeSetuid
- }
- if unixMode&syscall.S_ISGID != 0 {
- mode |= os.ModeSetgid
- }
- return mode
- }
- func writeXattrSize(m *buffer.OutMessage, size uint32) {
- out := (*fusekernel.GetxattrOut)(m.Grow(int(unsafe.Sizeof(fusekernel.GetxattrOut{}))))
- out.Size = size
- }
|