winfsp / cgofuse

Cross-platform FUSE library for Go - Works on Windows, macOS, Linux, FreeBSD, NetBSD, OpenBSD
https://winfsp.dev
MIT License
511 stars 82 forks source link

Failing to mount: The service has failed to start (Status=c0000034) #84

Closed marius-enlock closed 2 months ago

marius-enlock commented 2 months ago

Hello,

On Windows 10 amd64, and winfsp installed from winfsp-2.0.23075.msi with development and fuse mode installed. go version go1.22.2 windows/amd64

I have managed to run the examples folder (memfs) using cgo - it works as expected.

But when I launch my program in go I get the following error on mount (the only error message besides false returned from the mount call):

The service has failed to start (Status=c0000034).

I have checked the code and it corresponds to: STATUS_OBJECT_NAME_NOT_FOUND

The return value from mount is false.

My program is usable, but the filesystem is not mounted, as opposed to when I launch it via the memfs example.

I have tried the following flags

d.pathToMount = "Z:"
r:= &DirNode{}
d.winFSfileSystemHost.Mount = winfuse.NewFileSystemHost(r)
d.winFSfileSystemHost.SetCapReaddirPlus(true)
ok:= d.winFSfileSystemHost.Mount(d.pathToMount, []string{"-D -", "-d -1", "-i"})

&DirNode{} on linux get's mounted with bazil.org/fuse and works as expected.

&DirNode{} on windows implements most of the interfaces in cgofuse and gets mounted via winfsp.

The CC/CXX for go are the following:

Test 1:
/c/Strawberry/c/bin/(gcc|g++)

Test 2:
C:\ProgramData\mingw64\mingw64\bin\(gcc|g++)

Other details that might be relevant: I launch the program that attempts the mount as a sub process that uses the stdin/stdout for grpc communication, which might interfere with winfsp (if winfsp requires stdin/stdout for communication). For example:

in,out:=io.Pipe()
in2,out2:=io.Pipe()
cmd:= exec.Command("myProgramThatAttemptsTheMount.exe")
cmd.Stdin:= in
cmd.Stdout :=out2
cmd.Start()
// Handle the grpc setup using the out/in2 ends of the pipe

I am a bit stuck and do not know where to look for further clues that explains why the mount fails, any input is appreciated.

My implementations of init, statfs, getattr:

func (d *DirNode) Init() {
}

func (d *DirNode) Statfs(path string, stat *winfuse.Statfs_t) int {
    var freeBytesAvailable uint64
    var totalNumberOfBytes uint64
    var totalNumberOfFreeBytes uint64
    err := win.GetDiskFreeSpaceEx(win.StringToUTF16Ptr(d.rootCipherTextPath),
        &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes)
    if err != nil {
        fmt.Println("Failed to get disk free space", "error", err)
        return -1
    }
    stat.Bavail = freeBytesAvailable / FileBlockSize
    stat.Bfree = totalNumberOfFreeBytes / FileBlockSize
    stat.Blocks = totalNumberOfBytes / FileBlockSize
    stat.Bsize = FileBlockSize
    stat.Frsize = FileBlockSize
        stat.Namemax = uint64(128) // We allow only 128 characters in the name.

    return 0
}
func (d *DirNode) Getattr(path string, stat *winfuse.Stat_t, fh uint64) int {
        /* .... */
        // mode = uint32(os.ModeDir | 0o750) if Dir
        // mode = uint32(0o700) if File
    fi, err := os.Stat(statPath)
    if err != nil {
        fmt.Println("Failed to stat file", "error", err)
        return int(convertOsErrToSyscallErrno(err))
    }

    finfo, ok := fi.Sys().(*syscall.Win32FileAttributeData)
    if !ok {
        fmt.Println("Failed to cast datastructure")
        return -1
    }
    stat.Atim = winfuse.Timespec{Nsec: finfo.LastAccessTime.Nanoseconds()}
    stat.Birthtim = winfuse.Timespec{Nsec: finfo.CreationTime.Nanoseconds()}
    stat.Ino = cipher.fileNode.nodeID
    stat.Mtim = winfuse.Timespec{Nsec: finfo.LastWriteTime.Nanoseconds()}
    stat.Size = cipherFileSizeToAttributeSize(fi.Size())
    stat.Mode = mode //fi.Mode
}
marius-enlock commented 2 months ago

I figured it out,

The response code corresponds to ENOENT, which is thrown by the commented-out code not shown in my example of Getattr.

I got confused between the UNIX error codes and the windows ones.