Search code examples
gosystem-callsfuseosxfuse

osxfuse cgofuse mkdir input/output error


while using cgofuse i cant create dir inside mounted fs with os.Mkdir:

panic: mkdir mp/testDir: input/output error

Code:

import (
    "fmt"
    "os"
    "path/filepath"
    "syscall"

    "github.com/billziss-gh/cgofuse/fuse"
)
type Ptfs struct {
    fuse.FileSystemBase
    root string
}

var (
    dirName = "testDir"
)

func main() {
    mountPoint = os.Args[1]

    fs := Ptfs{}
    host := fuse.NewFileSystemHost(&fs)
    host.SetCapReaddirPlus(true)

    go host.Mount(mountPoint, []string{"-d"})
    defer host.Unmount()

    fmt.Println("init completed...")
    fmt.Scanln()
    fmt.Println("make dir...")

    err := os.Mkdir(filepath.Join(mountPoint, dirName), 0700)
    if err != nil {
        panic(err)
    }
}

Getattr definition for cgofuse is next (almost same as in example from github):

func (self *Ptfs) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
    stgo := syscall.Stat_t{}
    if ^uint64(0) == fh {
        path = filepath.Join(self.root, path)
        errc = errno(syscall.Lstat(path, &stgo))
    } else {
        errc = errno(syscall.Fstat(int(fh), &stgo))
    }
    return
}

func (self *Ptfs) Mkdir(path string, mode uint32) (errc int) {
    path = filepath.Join(self.root, path)
    return errno(syscall.Mkdir(path, mode))
}

giving the "-d" mount options i get this additional output:

make dir... unique: 9, opcode: LOOKUP (1), nodeid: 1, insize: 48, pid: 27053 LOOKUP /testDir getattr /testDir unique: 9, error: -2 (No such file or directory), outsize: 16 unique: 7, opcode: GETATTR (3), nodeid: 1, insize: 56, pid: 27053 getattr / unique: 7, success, outsize: 136 unique: 2, opcode: DESTROY (38), nodeid: 1, insize: 40, pid: 27053 unique: 2, success, outsize: 16

dir "testDir" inside root dir in mountpoint isnt exist yet, so i guess "No such file or directory" is okay for this. But even after "success" of GETATTR of "root" dir i still can't create dir. Opcode DESTROY i believe gets by unmounting of system caused by defer host.Unmount().

[EDIT] Diving deeper: func syscall.Lstat() is used from Getattr():

func Lstat(path string, stat *Stat_t) (err error) {
    var _p0 *byte
    _p0, err = BytePtrFromString(path)
    if err != nil {
        return
    }
    _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
    if e1 != 0 {
        err = errnoErr(e1)
    }
    return
}

Solution

  • My mistake was in Getattr() function implementation. It is necessary to correctly put stats inside given stat pointer struct, not rewriting another address with stat struct instead of given. Correctly fill given stat struct via pointer and it will work as intended.