From ea0bc278ba6a257645be7facab3460d96d17a803 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 12 May 2017 11:01:05 +0100 Subject: [PATCH] cmount: Vendor github.com/billziss-gh/cgofuse --- Gopkg.lock | 8 +- .../billziss-gh/cgofuse/.appveyor.yml | 29 + .../billziss-gh/cgofuse/.travis.yml | 47 + .../billziss-gh/cgofuse/Changelog.md | 6 + .../billziss-gh/cgofuse/License.txt | 21 + .../github.com/billziss-gh/cgofuse/README.md | 63 + .../cgofuse/examples/hellofs/hellofs.go | 79 ++ .../cgofuse/examples/memfs/memfs.go | 545 ++++++++ .../examples/passthrough/passthrough.go | 268 ++++ .../examples/passthrough/port_darwin.go | 68 + .../examples/passthrough/port_linux.go | 67 + .../cgofuse/examples/shared/trace.go | 115 ++ .../billziss-gh/cgofuse/fuse/fsop.go | 730 +++++++++++ .../billziss-gh/cgofuse/fuse/host.go | 1129 +++++++++++++++++ .../billziss-gh/cgofuse/fuse/host_test.go | 109 ++ .../cgofuse/fuse/host_unix_test.go | 23 + .../cgofuse/fuse/host_windows_test.go | 27 + 17 files changed, 3333 insertions(+), 1 deletion(-) create mode 100644 vendor/github.com/billziss-gh/cgofuse/.appveyor.yml create mode 100644 vendor/github.com/billziss-gh/cgofuse/.travis.yml create mode 100644 vendor/github.com/billziss-gh/cgofuse/Changelog.md create mode 100644 vendor/github.com/billziss-gh/cgofuse/License.txt create mode 100644 vendor/github.com/billziss-gh/cgofuse/README.md create mode 100644 vendor/github.com/billziss-gh/cgofuse/examples/hellofs/hellofs.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/examples/memfs/memfs.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/examples/passthrough/passthrough.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_darwin.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_linux.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/examples/shared/trace.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/fuse/fsop.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/fuse/host.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/fuse/host_test.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/fuse/host_unix_test.go create mode 100644 vendor/github.com/billziss-gh/cgofuse/fuse/host_windows_test.go diff --git a/Gopkg.lock b/Gopkg.lock index ff1fd8f56..841a3d600 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,4 +1,4 @@ -memo = "0c86c911e62d7207fb8549d69519a5a795ce4b36aadce87b84504f109eefe60b" +memo = "28c7ca08636da6264c587a36b72ce4f3a45fdf5c32969ffd2092b98456c73685" [[projects]] branch = "master" @@ -30,6 +30,12 @@ memo = "0c86c911e62d7207fb8549d69519a5a795ce4b36aadce87b84504f109eefe60b" packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/s3","service/s3/s3iface","service/s3/s3manager","service/sts"] revision = "57572ec625c9aa8bf5c45453efa923bacabc0afe" +[[projects]] + name = "github.com/billziss-gh/cgofuse" + packages = ["fuse"] + revision = "b402ef9fb28afcc443348ba2d46b5bfd88867fea" + version = "v1.0" + [[projects]] name = "github.com/cpuguy83/go-md2man" packages = ["md2man"] diff --git a/vendor/github.com/billziss-gh/cgofuse/.appveyor.yml b/vendor/github.com/billziss-gh/cgofuse/.appveyor.yml new file mode 100644 index 000000000..5c12994f4 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/.appveyor.yml @@ -0,0 +1,29 @@ +version: '{build}' + +clone_folder: C:\projects\go\src\github.com\billziss-gh\cgofuse + +environment: + CPATH: C:\Program Files (x86)\WinFsp\inc\fuse + GODEBUG: cgocheck=2 + GOPATH: C:\projects\go + PATH: C:\mingw-w64\x86_64-6.3.0-posix-seh-rt_v5-rev1\mingw64\bin;%PATH% + +install: +- choco install winfsp -y + +build_script: +- go install -v ./... + +test_script: +- go test ./fuse +- git clone -q https://github.com/billziss-gh/winfsp.git C:\projects\winfsp +- git -C C:\projects\winfsp checkout -q v1.0 +- call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" x64 +- devenv C:\projects\winfsp\build\VStudio\winfsp.sln /build "Release|x64" +# hard code fsreg parameters because appveyor does not like percents ("--VolumePrefix=%1 %2") +- C:\projects\winfsp\tools\fsreg.bat gomemfs "C:\projects\go\bin\memfs.exe" "--VolumePrefix=\gomemfs\share M:" "D:P(A;;RPWPLC;;;WD)" +- 'net use M: \\gomemfs\share' +- 'M: & cd' +- C:\projects\winfsp\build\VStudio\build\Release\winfsp-tests-x64.exe --external --resilient --share-prefix=\gomemfs\share -create_allocation_test -getfileinfo_name_test -setfileinfo_test -delete_access_test -setsecurity_test -reparse* -stream* +- 'C: & cd' +- 'net use M: /delete' diff --git a/vendor/github.com/billziss-gh/cgofuse/.travis.yml b/vendor/github.com/billziss-gh/cgofuse/.travis.yml new file mode 100644 index 000000000..54a014d44 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/.travis.yml @@ -0,0 +1,47 @@ +os: +- osx +- linux + +language: go + +env: +- GODEBUG=cgocheck=2 + +before_install: +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update; fi +# FUSE +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install caskroom/cask/osxfuse; fi +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq install libfuse-dev; fi +# secfs.test +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq install libacl1-dev; fi +- mkdir -p /tmp/t/{m,p} +- git clone -q https://github.com/billziss-gh/secfs.test.git /tmp/t/secfs.test +- git -C /tmp/t/secfs.test checkout -q a00c9165646f78ad53d3ec052860384a029683e5 +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sed -e 's/^fs=.*$/fs="cgofuse"/' -i "" /tmp/t/secfs.test/fstest/fstest/tests/conf; fi +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sed -e 's/^fs=.*$/fs="cgofuse"/' -i"" /tmp/t/secfs.test/fstest/fstest/tests/conf; fi +# the following test succeeds on my Mac but fails on Travis OSX; I have no clue why; disabling! +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mv /tmp/t/secfs.test/fstest/fstest/tests/rmdir/{,.}12.t; fi +- mv /tmp/t/secfs.test/fstest/fstest/tests/{,.}zzz_ResourceFork +- make -C /tmp/t/secfs.test + +install: +- go install -v ./... + +script: +- go test ./fuse +# cgofuse/memfs +- sudo $GOPATH/bin/memfs -o allow_other,default_permissions,use_ino,attr_timeout=0 /tmp/t/m & +- (cd /tmp/t/m && sudo prove -fr /tmp/t/secfs.test/fstest/fstest/tests) +- (cd /tmp/t/m && /tmp/t/secfs.test/tools/bin/fsx -N 10000 test xxxxxx) +- (cd /tmp/t/m && /tmp/t/secfs.test/tools/bin/fsx -e -N 1000 test xxxx) +- sudo umount /tmp/t/m +# cgofuse/passthrough +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sed -e 's/lchmod)/lchmod) return 1/' -i "" /tmp/t/secfs.test/fstest/fstest/tests/misc.sh; fi +- sudo $GOPATH/bin/passthrough -o allow_other,default_permissions,use_ino,attr_timeout=0 /tmp/t/p /tmp/t/m & +- (cd /tmp/t/m && sudo prove -fr /tmp/t/secfs.test/fstest/fstest/tests) +- (cd /tmp/t/m && /tmp/t/secfs.test/tools/bin/fsx -N 10000 test xxxxxx) +- sudo umount /tmp/t/m + +notifications: + email: false diff --git a/vendor/github.com/billziss-gh/cgofuse/Changelog.md b/vendor/github.com/billziss-gh/cgofuse/Changelog.md new file mode 100644 index 000000000..224fdba6e --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/Changelog.md @@ -0,0 +1,6 @@ +# Changelog + +**v1.0** + +- Initial cgofuse release. +- The API is now **FROZEN**. Breaking API changes will receive a major version update (`2.0`). Incremental API changes will receive a minor version update (`1.x`). diff --git a/vendor/github.com/billziss-gh/cgofuse/License.txt b/vendor/github.com/billziss-gh/cgofuse/License.txt new file mode 100644 index 000000000..45d70c72e --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/License.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Bill Zissimopoulos + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/billziss-gh/cgofuse/README.md b/vendor/github.com/billziss-gh/cgofuse/README.md new file mode 100644 index 000000000..e7c246202 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/README.md @@ -0,0 +1,63 @@ +# Cross-platform FUSE library for Go + +[![Travis CI](https://img.shields.io/travis/billziss-gh/cgofuse.svg?label=osx/linux)](https://travis-ci.org/billziss-gh/cgofuse) +[![AppVeyor](https://img.shields.io/appveyor/ci/billziss-gh/cgofuse.svg?label=windows)](https://ci.appveyor.com/project/billziss-gh/cgofuse) +[![GoDoc](https://godoc.org/github.com/billziss-gh/cgofuse/fuse?status.svg)](https://godoc.org/github.com/billziss-gh/cgofuse/fuse) + +Cgofuse is a cross-platform FUSE library for Go. It is implemented using [cgo](https://golang.org/cmd/cgo/) and can be ported to any platform that has a FUSE implementation. + +Cgofuse currently runs on **OSX**, **Linux** and **Windows** (using [WinFsp](https://github.com/billziss-gh/winfsp)). + +## How to build + +**OSX** +- Prerequisites: [OSXFUSE](https://osxfuse.github.io), [command line tools](https://developer.apple.com/library/content/technotes/tn2339/_index.html) +- Build: + ``` + $ cd cgofuse + $ go install -v ./fuse ./examples/memfs ./examples/passthrough + ``` + +**Linux** +- Prerequisites: libfuse-dev, gcc +- Build: + ``` + $ cd cgofuse + $ go install -v ./fuse ./examples/memfs ./examples/passthrough + ``` +**Windows** +- Prerequisites: [WinFsp](https://github.com/billziss-gh/winfsp), gcc (e.g. from [Mingw-builds](http://mingw-w64.org/doku.php/download)) +- Build: + ``` + > cd cgofuse + > set CPATH=C:\Program Files (x86)\WinFsp\inc\fuse + > go install -v ./fuse ./examples/memfs + ``` + +## How to use + +User mode file systems are expected to implement `fuse.FileSystemInterface`. To make implementation simpler a file system can embed ("inherit") a `fuse.FileSystemBase` which provides default implementations for all operations. To mount a file system one must instantiate a `fuse.FileSystemHost` using `fuse.NewFileSystemHost`. + +The full documentation is available at GoDoc.org: [package fuse](https://godoc.org/github.com/billziss-gh/cgofuse/fuse) + +There are currently three example file systems: + +- [Hellofs](examples/hellofs/hellofs.go) is an extremely simple file system. Runs on OSX, Linux and Windows. +- [Memfs](examples/memfs/memfs.go) is an in memory file system. Runs on OSX, Linux and Windows. +- [Passthrough](examples/passthrough/passthrough.go) is a file system that passes all operations to the underlying file system. Runs on OSX, Linux. + +## How it is tested + +Cgofuse is regularly built and tested on [Travis CI](https://travis-ci.org/billziss-gh/cgofuse) and [AppVeyor](https://ci.appveyor.com/project/billziss-gh/cgofuse). The following software is being used to test cgofuse. + +**OSX/Linux** +- [fstest](https://github.com/billziss-gh/secfs.test/tree/master/fstest/ntfs-3g-pjd-fstest-8af5670) +- [fsx](https://github.com/billziss-gh/secfs.test/tree/master/fstools/src/fsx) + +**Windows** +- [winfsp-tests](https://github.com/billziss-gh/winfsp/tree/master/tst/winfsp-tests) + +## Contributors + +- Bill Zissimopoulos \ +- Nick Craig-Wood \ diff --git a/vendor/github.com/billziss-gh/cgofuse/examples/hellofs/hellofs.go b/vendor/github.com/billziss-gh/cgofuse/examples/hellofs/hellofs.go new file mode 100644 index 000000000..92e8982e7 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/examples/hellofs/hellofs.go @@ -0,0 +1,79 @@ +/* + * hellofs.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package main + +import ( + "os" + + "github.com/billziss-gh/cgofuse/fuse" +) + +const ( + filename = "hello" + contents = "hello, world\n" +) + +type Hellofs struct { + fuse.FileSystemBase +} + +func (self *Hellofs) Open(path string, flags int) (errc int, fh uint64) { + switch path { + case "/" + filename: + return 0, 0 + default: + return -fuse.ENOENT, ^uint64(0) + } +} + +func (self *Hellofs) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) { + switch path { + case "/": + stat.Mode = fuse.S_IFDIR | 0555 + return 0 + case "/" + filename: + stat.Mode = fuse.S_IFREG | 0444 + stat.Size = int64(len(contents)) + return 0 + default: + return -fuse.ENOENT + } +} + +func (self *Hellofs) Read(path string, buff []byte, ofst int64, fh uint64) (n int) { + endofst := ofst + int64(len(buff)) + if endofst > int64(len(contents)) { + endofst = int64(len(contents)) + } + if endofst < ofst { + return 0 + } + n = copy(buff, contents[ofst:endofst]) + return +} + +func (self *Hellofs) Readdir(path string, + fill func(name string, stat *fuse.Stat_t, ofst int64) bool, + ofst int64, + fh uint64) (errc int) { + fill(".", nil, 0) + fill("..", nil, 0) + fill(filename, nil, 0) + return 0 +} + +func main() { + hellofs := &Hellofs{} + host := fuse.NewFileSystemHost(hellofs) + host.Mount("", os.Args[1:]) +} diff --git a/vendor/github.com/billziss-gh/cgofuse/examples/memfs/memfs.go b/vendor/github.com/billziss-gh/cgofuse/examples/memfs/memfs.go new file mode 100644 index 000000000..afa37f9ba --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/examples/memfs/memfs.go @@ -0,0 +1,545 @@ +/* + * memfs.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package main + +import ( + "fmt" + "os" + "strings" + "sync" + + "github.com/billziss-gh/cgofuse/examples/shared" + "github.com/billziss-gh/cgofuse/fuse" +) + +func trace(vals ...interface{}) func(vals ...interface{}) { + uid, gid, _ := fuse.Getcontext() + return shared.Trace(1, fmt.Sprintf("[uid=%v,gid=%v]", uid, gid), vals...) +} + +func split(path string) []string { + return strings.Split(path, "/") +} + +func resize(slice []byte, size int64, zeroinit bool) []byte { + const allocunit = 64 * 1024 + allocsize := (size + allocunit - 1) / allocunit * allocunit + if cap(slice) != int(allocsize) { + var newslice []byte + { + defer func() { + if r := recover(); nil != r { + panic(fuse.Error(-fuse.ENOSPC)) + } + }() + newslice = make([]byte, size, allocsize) + } + copy(newslice, slice) + slice = newslice + } else if zeroinit { + i := len(slice) + slice = slice[:size] + for ; len(slice) > i; i++ { + slice[i] = 0 + } + } + return slice +} + +type node_t struct { + stat fuse.Stat_t + xatr map[string][]byte + chld map[string]*node_t + data []byte + opencnt int +} + +func newNode(dev uint64, ino uint64, mode uint32, uid uint32, gid uint32) *node_t { + tmsp := fuse.Now() + self := node_t{ + fuse.Stat_t{ + Dev: dev, + Ino: ino, + Mode: mode, + Nlink: 1, + Uid: uid, + Gid: gid, + Atim: tmsp, + Mtim: tmsp, + Ctim: tmsp, + Birthtim: tmsp, + }, + nil, + nil, + nil, + 0} + if fuse.S_IFDIR == self.stat.Mode&fuse.S_IFMT { + self.chld = map[string]*node_t{} + } + return &self +} + +type Memfs struct { + fuse.FileSystemBase + lock sync.Mutex + ino uint64 + root *node_t + openmap map[uint64]*node_t +} + +func (self *Memfs) Mknod(path string, mode uint32, dev uint64) (errc int) { + defer trace(path, mode, dev)(&errc) + defer self.synchronize()() + return self.makeNode(path, mode, dev, nil) +} + +func (self *Memfs) Mkdir(path string, mode uint32) (errc int) { + defer trace(path, mode)(&errc) + defer self.synchronize()() + return self.makeNode(path, fuse.S_IFDIR|(mode&07777), 0, nil) +} + +func (self *Memfs) Unlink(path string) (errc int) { + defer trace(path)(&errc) + defer self.synchronize()() + return self.removeNode(path, false) +} + +func (self *Memfs) Rmdir(path string) (errc int) { + defer trace(path)(&errc) + defer self.synchronize()() + return self.removeNode(path, true) +} + +func (self *Memfs) Link(oldpath string, newpath string) (errc int) { + defer trace(oldpath, newpath)(&errc) + defer self.synchronize()() + _, _, oldnode := self.lookupNode(oldpath, nil) + if nil == oldnode { + return -fuse.ENOENT + } + newprnt, newname, newnode := self.lookupNode(newpath, nil) + if nil == newprnt { + return -fuse.ENOENT + } + if nil != newnode { + return -fuse.EEXIST + } + oldnode.stat.Nlink++ + newprnt.chld[newname] = oldnode + tmsp := fuse.Now() + oldnode.stat.Ctim = tmsp + newprnt.stat.Ctim = tmsp + newprnt.stat.Mtim = tmsp + return 0 +} + +func (self *Memfs) Symlink(target string, newpath string) (errc int) { + defer trace(target, newpath)(&errc) + defer self.synchronize()() + return self.makeNode(newpath, fuse.S_IFLNK|00777, 0, []byte(target)) +} + +func (self *Memfs) Readlink(path string) (errc int, target string) { + defer trace(path)(&errc, &target) + defer self.synchronize()() + _, _, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT, "" + } + if fuse.S_IFLNK != node.stat.Mode&fuse.S_IFMT { + return -fuse.EINVAL, "" + } + return 0, string(node.data) +} + +func (self *Memfs) Rename(oldpath string, newpath string) (errc int) { + defer trace(oldpath, newpath)(&errc) + defer self.synchronize()() + oldprnt, oldname, oldnode := self.lookupNode(oldpath, nil) + if nil == oldnode { + return -fuse.ENOENT + } + newprnt, newname, newnode := self.lookupNode(newpath, oldnode) + if nil == newprnt { + return -fuse.ENOENT + } + if "" == newname { + // guard against directory loop creation + return -fuse.EINVAL + } + if oldprnt == newprnt && oldname == newname { + return 0 + } + if nil != newnode { + errc = self.removeNode(newpath, fuse.S_IFDIR == oldnode.stat.Mode&fuse.S_IFMT) + if 0 != errc { + return errc + } + } + delete(oldprnt.chld, oldname) + newprnt.chld[newname] = oldnode + return 0 +} + +func (self *Memfs) Chmod(path string, mode uint32) (errc int) { + defer trace(path, mode)(&errc) + defer self.synchronize()() + _, _, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT + } + node.stat.Mode = (node.stat.Mode & fuse.S_IFMT) | mode&07777 + node.stat.Ctim = fuse.Now() + return 0 +} + +func (self *Memfs) Chown(path string, uid uint32, gid uint32) (errc int) { + defer trace(path, uid, gid)(&errc) + defer self.synchronize()() + _, _, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT + } + if ^uint32(0) != uid { + node.stat.Uid = uid + } + if ^uint32(0) != gid { + node.stat.Gid = gid + } + node.stat.Ctim = fuse.Now() + return 0 +} + +func (self *Memfs) Utimens(path string, tmsp []fuse.Timespec) (errc int) { + defer trace(path, tmsp)(&errc) + defer self.synchronize()() + _, _, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT + } + if nil == tmsp { + tmsp0 := fuse.Now() + tmsa := [2]fuse.Timespec{tmsp0, tmsp0} + tmsp = tmsa[:] + } + node.stat.Atim = tmsp[0] + node.stat.Mtim = tmsp[1] + return 0 +} + +func (self *Memfs) Open(path string, flags int) (errc int, fh uint64) { + defer trace(path, flags)(&errc, &fh) + defer self.synchronize()() + return self.openNode(path, false) +} + +func (self *Memfs) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) { + defer trace(path, fh)(&errc, stat) + defer self.synchronize()() + node := self.getNode(path, fh) + if nil == node { + return -fuse.ENOENT + } + *stat = node.stat + return 0 +} + +func (self *Memfs) Truncate(path string, size int64, fh uint64) (errc int) { + defer trace(path, size, fh)(&errc) + defer self.synchronize()() + node := self.getNode(path, fh) + if nil == node { + return -fuse.ENOENT + } + node.data = resize(node.data, size, true) + node.stat.Size = size + tmsp := fuse.Now() + node.stat.Ctim = tmsp + node.stat.Mtim = tmsp + return 0 +} + +func (self *Memfs) Read(path string, buff []byte, ofst int64, fh uint64) (n int) { + defer trace(path, buff, ofst, fh)(&n) + defer self.synchronize()() + node := self.getNode(path, fh) + if nil == node { + return -fuse.ENOENT + } + endofst := ofst + int64(len(buff)) + if endofst > node.stat.Size { + endofst = node.stat.Size + } + if endofst < ofst { + return 0 + } + n = copy(buff, node.data[ofst:endofst]) + node.stat.Atim = fuse.Now() + return +} + +func (self *Memfs) Write(path string, buff []byte, ofst int64, fh uint64) (n int) { + defer trace(path, buff, ofst, fh)(&n) + defer self.synchronize()() + node := self.getNode(path, fh) + if nil == node { + return -fuse.ENOENT + } + endofst := ofst + int64(len(buff)) + if endofst > node.stat.Size { + node.data = resize(node.data, endofst, true) + node.stat.Size = endofst + } + n = copy(node.data[ofst:endofst], buff) + tmsp := fuse.Now() + node.stat.Ctim = tmsp + node.stat.Mtim = tmsp + return +} + +func (self *Memfs) Release(path string, fh uint64) (errc int) { + defer trace(path, fh)(&errc) + defer self.synchronize()() + return self.closeNode(fh) +} + +func (self *Memfs) Opendir(path string) (errc int, fh uint64) { + defer trace(path)(&errc, &fh) + defer self.synchronize()() + return self.openNode(path, true) +} + +func (self *Memfs) Readdir(path string, + fill func(name string, stat *fuse.Stat_t, ofst int64) bool, + ofst int64, + fh uint64) (errc int) { + defer trace(path, fill, ofst, fh)(&errc) + defer self.synchronize()() + node := self.openmap[fh] + fill(".", &node.stat, 0) + fill("..", nil, 0) + for name, chld := range node.chld { + if !fill(name, &chld.stat, 0) { + break + } + } + return 0 +} + +func (self *Memfs) Releasedir(path string, fh uint64) (errc int) { + defer trace(path, fh)(&errc) + defer self.synchronize()() + return self.closeNode(fh) +} + +func (self *Memfs) Setxattr(path string, name string, value []byte, flags int) (errc int) { + defer trace(path, name, value, flags)(&errc) + defer self.synchronize()() + _, _, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT + } + if "com.apple.ResourceFork" == name { + return -fuse.ENOTSUP + } + if fuse.XATTR_CREATE == flags { + if _, ok := node.xatr[name]; ok { + return -fuse.EEXIST + } + } else if fuse.XATTR_REPLACE == flags { + if _, ok := node.xatr[name]; !ok { + return -fuse.ENOATTR + } + } + xatr := make([]byte, len(value)) + copy(xatr, value) + if nil == node.xatr { + node.xatr = map[string][]byte{} + } + node.xatr[name] = xatr + return 0 +} + +func (self *Memfs) Getxattr(path string, name string) (errc int, xatr []byte) { + defer trace(path, name)(&errc, &xatr) + defer self.synchronize()() + _, _, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT, nil + } + if "com.apple.ResourceFork" == name { + return -fuse.ENOTSUP, nil + } + xatr, ok := node.xatr[name] + if !ok { + return -fuse.ENOATTR, nil + } + return 0, xatr +} + +func (self *Memfs) Removexattr(path string, name string) (errc int) { + defer trace(path, name)(&errc) + defer self.synchronize()() + _, _, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT + } + if "com.apple.ResourceFork" == name { + return -fuse.ENOTSUP + } + if _, ok := node.xatr[name]; !ok { + return -fuse.ENOATTR + } + delete(node.xatr, name) + return 0 +} + +func (self *Memfs) Listxattr(path string, fill func(name string) bool) (errc int) { + defer trace(path, fill)(&errc) + defer self.synchronize()() + _, _, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT + } + for name := range node.xatr { + if !fill(name) { + return -fuse.ERANGE + } + } + return 0 +} + +func (self *Memfs) lookupNode(path string, ancestor *node_t) (prnt *node_t, name string, node *node_t) { + prnt = self.root + name = "" + node = self.root + for _, c := range split(path) { + if "" != c { + if 255 < len(c) { + panic(fuse.Error(-fuse.ENAMETOOLONG)) + } + prnt, name = node, c + node = node.chld[c] + if nil != ancestor && node == ancestor { + name = "" // special case loop condition + return + } + } + } + return +} + +func (self *Memfs) makeNode(path string, mode uint32, dev uint64, data []byte) int { + prnt, name, node := self.lookupNode(path, nil) + if nil == prnt { + return -fuse.ENOENT + } + if nil != node { + return -fuse.EEXIST + } + self.ino++ + uid, gid, _ := fuse.Getcontext() + node = newNode(dev, self.ino, mode, uid, gid) + if nil != data { + node.data = make([]byte, len(data)) + node.stat.Size = int64(len(data)) + copy(node.data, data) + } + prnt.chld[name] = node + prnt.stat.Ctim = node.stat.Ctim + prnt.stat.Mtim = node.stat.Ctim + return 0 +} + +func (self *Memfs) removeNode(path string, dir bool) int { + prnt, name, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT + } + if !dir && fuse.S_IFDIR == node.stat.Mode&fuse.S_IFMT { + return -fuse.EISDIR + } + if dir && fuse.S_IFDIR != node.stat.Mode&fuse.S_IFMT { + return -fuse.ENOTDIR + } + if 0 < len(node.chld) { + return -fuse.ENOTEMPTY + } + node.stat.Nlink-- + delete(prnt.chld, name) + tmsp := fuse.Now() + node.stat.Ctim = tmsp + prnt.stat.Ctim = tmsp + prnt.stat.Mtim = tmsp + return 0 +} + +func (self *Memfs) openNode(path string, dir bool) (int, uint64) { + _, _, node := self.lookupNode(path, nil) + if nil == node { + return -fuse.ENOENT, ^uint64(0) + } + if !dir && fuse.S_IFDIR == node.stat.Mode&fuse.S_IFMT { + return -fuse.EISDIR, ^uint64(0) + } + if dir && fuse.S_IFDIR != node.stat.Mode&fuse.S_IFMT { + return -fuse.ENOTDIR, ^uint64(0) + } + node.opencnt++ + if 1 == node.opencnt { + self.openmap[node.stat.Ino] = node + } + return 0, node.stat.Ino +} + +func (self *Memfs) closeNode(fh uint64) int { + node := self.openmap[fh] + node.opencnt-- + if 0 == node.opencnt { + delete(self.openmap, node.stat.Ino) + } + return 0 +} + +func (self *Memfs) getNode(path string, fh uint64) *node_t { + if ^uint64(0) == fh { + _, _, node := self.lookupNode(path, nil) + return node + } else { + return self.openmap[fh] + } +} + +func (self *Memfs) synchronize() func() { + self.lock.Lock() + return func() { + self.lock.Unlock() + } +} + +func NewMemfs() *Memfs { + self := Memfs{} + defer self.synchronize()() + self.ino++ + self.root = newNode(0, self.ino, fuse.S_IFDIR|00777, 0, 0) + self.openmap = map[uint64]*node_t{} + return &self +} + +func main() { + memfs := NewMemfs() + host := fuse.NewFileSystemHost(memfs) + host.SetCapReaddirPlus(true) + host.Mount("", os.Args[1:]) +} diff --git a/vendor/github.com/billziss-gh/cgofuse/examples/passthrough/passthrough.go b/vendor/github.com/billziss-gh/cgofuse/examples/passthrough/passthrough.go new file mode 100644 index 000000000..0247321d7 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/examples/passthrough/passthrough.go @@ -0,0 +1,268 @@ +// +build darwin linux + +/* + * passthrough.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package main + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + + "github.com/billziss-gh/cgofuse/examples/shared" + "github.com/billziss-gh/cgofuse/fuse" +) + +func trace(vals ...interface{}) func(vals ...interface{}) { + uid, gid, _ := fuse.Getcontext() + return shared.Trace(1, fmt.Sprintf("[uid=%v,gid=%v]", uid, gid), vals...) +} + +func errno(err error) int { + if nil != err { + return -int(err.(syscall.Errno)) + } else { + return 0 + } +} + +var ( + _host *fuse.FileSystemHost +) + +type Ptfs struct { + fuse.FileSystemBase + root string +} + +func (self *Ptfs) Init() { + defer trace()() + e := syscall.Chdir(self.root) + if nil == e { + self.root = "./" + } +} + +func (self *Ptfs) Statfs(path string, stat *fuse.Statfs_t) (errc int) { + defer trace(path)(&errc, stat) + path = filepath.Join(self.root, path) + stgo := syscall.Statfs_t{} + errc = errno(syscall.Statfs(path, &stgo)) + copyFusestatfsFromGostatfs(stat, &stgo) + return +} + +func (self *Ptfs) Mknod(path string, mode uint32, dev uint64) (errc int) { + defer trace(path, mode, dev)(&errc) + defer setuidgid()() + path = filepath.Join(self.root, path) + return errno(syscall.Mknod(path, mode, int(dev))) +} + +func (self *Ptfs) Mkdir(path string, mode uint32) (errc int) { + defer trace(path, mode)(&errc) + defer setuidgid()() + path = filepath.Join(self.root, path) + return errno(syscall.Mkdir(path, mode)) +} + +func (self *Ptfs) Unlink(path string) (errc int) { + defer trace(path)(&errc) + path = filepath.Join(self.root, path) + return errno(syscall.Unlink(path)) +} + +func (self *Ptfs) Rmdir(path string) (errc int) { + defer trace(path)(&errc) + path = filepath.Join(self.root, path) + return errno(syscall.Rmdir(path)) +} + +func (self *Ptfs) Link(oldpath string, newpath string) (errc int) { + defer trace(oldpath, newpath)(&errc) + defer setuidgid()() + oldpath = filepath.Join(self.root, oldpath) + newpath = filepath.Join(self.root, newpath) + return errno(syscall.Link(oldpath, newpath)) +} + +func (self *Ptfs) Symlink(target string, newpath string) (errc int) { + defer trace(target, newpath)(&errc) + defer setuidgid()() + newpath = filepath.Join(self.root, newpath) + return errno(syscall.Symlink(target, newpath)) +} + +func (self *Ptfs) Readlink(path string) (errc int, target string) { + defer trace(path)(&errc, &target) + path = filepath.Join(self.root, path) + buff := [1024]byte{} + n, e := syscall.Readlink(path, buff[:]) + if nil != e { + return errno(e), "" + } + return 0, string(buff[:n]) +} + +func (self *Ptfs) Rename(oldpath string, newpath string) (errc int) { + defer trace(oldpath, newpath)(&errc) + defer setuidgid()() + oldpath = filepath.Join(self.root, oldpath) + newpath = filepath.Join(self.root, newpath) + return errno(syscall.Rename(oldpath, newpath)) +} + +func (self *Ptfs) Chmod(path string, mode uint32) (errc int) { + defer trace(path, mode)(&errc) + path = filepath.Join(self.root, path) + return errno(syscall.Chmod(path, mode)) +} + +func (self *Ptfs) Chown(path string, uid uint32, gid uint32) (errc int) { + defer trace(path, uid, gid)(&errc) + path = filepath.Join(self.root, path) + return errno(syscall.Lchown(path, int(uid), int(gid))) +} + +func (self *Ptfs) Utimens(path string, tmsp1 []fuse.Timespec) (errc int) { + defer trace(path, tmsp1)(&errc) + path = filepath.Join(self.root, path) + tmsp := [2]syscall.Timespec{} + tmsp[0].Sec, tmsp[0].Nsec = tmsp1[0].Sec, tmsp1[0].Nsec + tmsp[1].Sec, tmsp[1].Nsec = tmsp1[1].Sec, tmsp1[1].Nsec + return errno(syscall.UtimesNano(path, tmsp[:])) +} + +func (self *Ptfs) Create(path string, flags int, mode uint32) (errc int, fh uint64) { + defer trace(path, flags, mode)(&errc, &fh) + defer setuidgid()() + return self.open(path, flags, mode) +} + +func (self *Ptfs) Open(path string, flags int) (errc int, fh uint64) { + defer trace(path, flags)(&errc, &fh) + return self.open(path, flags, 0) +} + +func (self *Ptfs) open(path string, flags int, mode uint32) (errc int, fh uint64) { + path = filepath.Join(self.root, path) + f, e := syscall.Open(path, flags, mode) + if nil != e { + return errno(e), ^uint64(0) + } + return 0, uint64(f) +} + +func (self *Ptfs) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) { + defer trace(path, fh)(&errc, stat) + 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)) + } + copyFusestatFromGostat(stat, &stgo) + return +} + +func (self *Ptfs) Truncate(path string, size int64, fh uint64) (errc int) { + defer trace(path, size, fh)(&errc) + if ^uint64(0) == fh { + path = filepath.Join(self.root, path) + errc = errno(syscall.Truncate(path, size)) + } else { + errc = errno(syscall.Ftruncate(int(fh), size)) + } + return +} + +func (self *Ptfs) Read(path string, buff []byte, ofst int64, fh uint64) (n int) { + defer trace(path, buff, ofst, fh)(&n) + n, e := syscall.Pread(int(fh), buff, ofst) + if nil != e { + return errno(e) + } + return n +} + +func (self *Ptfs) Write(path string, buff []byte, ofst int64, fh uint64) (n int) { + defer trace(path, buff, ofst, fh)(&n) + n, e := syscall.Pwrite(int(fh), buff, ofst) + if nil != e { + return errno(e) + } + return n +} + +func (self *Ptfs) Release(path string, fh uint64) (errc int) { + defer trace(path, fh)(&errc) + return errno(syscall.Close(int(fh))) +} + +func (self *Ptfs) Fsync(path string, datasync bool, fh uint64) (errc int) { + defer trace(path, datasync, fh)(&errc) + return errno(syscall.Fsync(int(fh))) +} + +func (self *Ptfs) Opendir(path string) (errc int, fh uint64) { + defer trace(path)(&errc, &fh) + path = filepath.Join(self.root, path) + f, e := syscall.Open(path, syscall.O_RDONLY|syscall.O_DIRECTORY, 0) + if nil != e { + return errno(e), ^uint64(0) + } + return 0, uint64(f) +} + +func (self *Ptfs) Readdir(path string, + fill func(name string, stat *fuse.Stat_t, ofst int64) bool, + ofst int64, + fh uint64) (errc int) { + defer trace(path, fill, ofst, fh)(&errc) + path = filepath.Join(self.root, path) + file, e := os.Open(path) + if nil != e { + return errno(e) + } + defer file.Close() + nams, e := file.Readdirnames(0) + if nil != e { + return errno(e) + } + nams = append([]string{".", ".."}, nams...) + for _, name := range nams { + if !fill(name, nil, 0) { + break + } + } + return 0 +} + +func (self *Ptfs) Releasedir(path string, fh uint64) (errc int) { + defer trace(path, fh)(&errc) + return errno(syscall.Close(int(fh))) +} + +func main() { + syscall.Umask(0) + ptfs := Ptfs{} + args := os.Args + if 3 <= len(args) && '-' != args[len(args)-2][0] && '-' != args[len(args)-1][0] { + ptfs.root, _ = filepath.Abs(args[len(args)-2]) + args = append(args[:len(args)-2], args[len(args)-1]) + } + _host = fuse.NewFileSystemHost(&ptfs) + _host.Mount("", args[1:]) +} diff --git a/vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_darwin.go b/vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_darwin.go new file mode 100644 index 000000000..44f561394 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_darwin.go @@ -0,0 +1,68 @@ +// +build darwin + +/* + * struct_darwin.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package main + +import ( + "syscall" + + "github.com/billziss-gh/cgofuse/fuse" +) + +func setuidgid() func() { + euid := syscall.Geteuid() + if 0 == euid { + uid, gid, _ := fuse.Getcontext() + egid := syscall.Getegid() + syscall.Setegid(int(gid)) + syscall.Seteuid(int(uid)) + return func() { + syscall.Seteuid(euid) + syscall.Setegid(egid) + } + } + return func() { + } +} + +func copyFusestatfsFromGostatfs(dst *fuse.Statfs_t, src *syscall.Statfs_t) { + *dst = fuse.Statfs_t{} + dst.Bsize = uint64(src.Bsize) + dst.Frsize = 1 + dst.Blocks = uint64(src.Blocks) + dst.Bfree = uint64(src.Bfree) + dst.Bavail = uint64(src.Bavail) + dst.Files = uint64(src.Files) + dst.Ffree = uint64(src.Ffree) + dst.Favail = uint64(src.Ffree) + dst.Namemax = 255 //uint64(src.Namelen) +} + +func copyFusestatFromGostat(dst *fuse.Stat_t, src *syscall.Stat_t) { + *dst = fuse.Stat_t{} + dst.Dev = uint64(src.Dev) + dst.Ino = uint64(src.Ino) + dst.Mode = uint32(src.Mode) + dst.Nlink = uint32(src.Nlink) + dst.Uid = uint32(src.Uid) + dst.Gid = uint32(src.Gid) + dst.Rdev = uint64(src.Rdev) + dst.Size = int64(src.Size) + dst.Atim.Sec, dst.Atim.Nsec = src.Atimespec.Sec, src.Atimespec.Nsec + dst.Mtim.Sec, dst.Mtim.Nsec = src.Mtimespec.Sec, src.Mtimespec.Nsec + dst.Ctim.Sec, dst.Ctim.Nsec = src.Ctimespec.Sec, src.Ctimespec.Nsec + dst.Blksize = int64(src.Blksize) + dst.Blocks = int64(src.Blocks) + dst.Birthtim.Sec, dst.Birthtim.Nsec = src.Birthtimespec.Sec, src.Birthtimespec.Nsec +} diff --git a/vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_linux.go b/vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_linux.go new file mode 100644 index 000000000..c95fd4a06 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_linux.go @@ -0,0 +1,67 @@ +// +build linux + +/* + * struct_linux.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package main + +import ( + "syscall" + + "github.com/billziss-gh/cgofuse/fuse" +) + +func setuidgid() func() { + euid := syscall.Geteuid() + if 0 == euid { + uid, gid, _ := fuse.Getcontext() + egid := syscall.Getegid() + syscall.Setregid(-1, int(gid)) + syscall.Setreuid(-1, int(uid)) + return func() { + syscall.Setreuid(-1, int(euid)) + syscall.Setregid(-1, int(egid)) + } + } + return func() { + } +} + +func copyFusestatfsFromGostatfs(dst *fuse.Statfs_t, src *syscall.Statfs_t) { + *dst = fuse.Statfs_t{} + dst.Bsize = uint64(src.Bsize) + dst.Frsize = 1 + dst.Blocks = uint64(src.Blocks) + dst.Bfree = uint64(src.Bfree) + dst.Bavail = uint64(src.Bavail) + dst.Files = uint64(src.Files) + dst.Ffree = uint64(src.Ffree) + dst.Favail = uint64(src.Ffree) + dst.Namemax = 255 //uint64(src.Namelen) +} + +func copyFusestatFromGostat(dst *fuse.Stat_t, src *syscall.Stat_t) { + *dst = fuse.Stat_t{} + dst.Dev = uint64(src.Dev) + dst.Ino = uint64(src.Ino) + dst.Mode = uint32(src.Mode) + dst.Nlink = uint32(src.Nlink) + dst.Uid = uint32(src.Uid) + dst.Gid = uint32(src.Gid) + dst.Rdev = uint64(src.Rdev) + dst.Size = int64(src.Size) + dst.Atim.Sec, dst.Atim.Nsec = src.Atim.Sec, src.Atim.Nsec + dst.Mtim.Sec, dst.Mtim.Nsec = src.Mtim.Sec, src.Mtim.Nsec + dst.Ctim.Sec, dst.Ctim.Nsec = src.Ctim.Sec, src.Ctim.Nsec + dst.Blksize = int64(src.Blksize) + dst.Blocks = int64(src.Blocks) +} diff --git a/vendor/github.com/billziss-gh/cgofuse/examples/shared/trace.go b/vendor/github.com/billziss-gh/cgofuse/examples/shared/trace.go new file mode 100644 index 000000000..b41732709 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/examples/shared/trace.go @@ -0,0 +1,115 @@ +/* + * trace.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package shared + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" +) + +var ( + TracePattern = os.Getenv("CGOFUSE_TRACE") +) + +func traceJoin(deref bool, vals []interface{}) string { + rslt := "" + for _, v := range vals { + if deref { + switch i := v.(type) { + case *bool: + rslt += fmt.Sprintf(", %#v", *i) + case *int: + rslt += fmt.Sprintf(", %#v", *i) + case *int8: + rslt += fmt.Sprintf(", %#v", *i) + case *int16: + rslt += fmt.Sprintf(", %#v", *i) + case *int32: + rslt += fmt.Sprintf(", %#v", *i) + case *int64: + rslt += fmt.Sprintf(", %#v", *i) + case *uint: + rslt += fmt.Sprintf(", %#v", *i) + case *uint8: + rslt += fmt.Sprintf(", %#v", *i) + case *uint16: + rslt += fmt.Sprintf(", %#v", *i) + case *uint32: + rslt += fmt.Sprintf(", %#v", *i) + case *uint64: + rslt += fmt.Sprintf(", %#v", *i) + case *uintptr: + rslt += fmt.Sprintf(", %#v", *i) + case *float32: + rslt += fmt.Sprintf(", %#v", *i) + case *float64: + rslt += fmt.Sprintf(", %#v", *i) + case *complex64: + rslt += fmt.Sprintf(", %#v", *i) + case *complex128: + rslt += fmt.Sprintf(", %#v", *i) + case *string: + rslt += fmt.Sprintf(", %#v", *i) + default: + rslt += fmt.Sprintf(", %#v", v) + } + } else { + rslt += fmt.Sprintf(", %#v", v) + } + } + if len(rslt) > 0 { + rslt = rslt[2:] + } + return rslt +} + +func Trace(skip int, prfx string, vals ...interface{}) func(vals ...interface{}) { + if "" == TracePattern { + return func(vals ...interface{}) { + } + } + pc, _, _, ok := runtime.Caller(skip + 1) + name := "" + if ok { + fn := runtime.FuncForPC(pc) + name = fn.Name() + if m, _ := filepath.Match(TracePattern, name); !m { + return func(vals ...interface{}) { + } + } + } + if "" != prfx { + prfx = prfx + ": " + } + args := traceJoin(false, vals) + return func(vals ...interface{}) { + form := "%v%v(%v) = %v" + rslt := "" + rcvr := recover() + if nil != rcvr { + rslt = fmt.Sprintf("!PANIC:%v", rcvr) + } else { + if len(vals) != 1 { + form = "%v%v(%v) = (%v)" + } + rslt = traceJoin(true, vals) + } + log.Printf(form, prfx, name, args, rslt) + if nil != rcvr { + panic(rcvr) + } + } +} diff --git a/vendor/github.com/billziss-gh/cgofuse/fuse/fsop.go b/vendor/github.com/billziss-gh/cgofuse/fuse/fsop.go new file mode 100644 index 000000000..161105688 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/fuse/fsop.go @@ -0,0 +1,730 @@ +/* + * fsop.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +// Package fuse allows the creation of user mode file systems in Go. +// +// A user mode file system must implement the methods in FileSystemInterface +// and be hosted (mounted) by a FileSystemHost. +// Alternatively a user mode file system can use the FileSystemBase struct which +// provides default implementations of the methods in FileSystemInterface. +package fuse + +/* +#if !(defined(__APPLE__) || defined(__linux__) || defined(_WIN32)) +#error platform not supported +#endif + +#if defined(__APPLE__) || defined(__linux__) + +#include +#include + +#elif defined(_WIN32) + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define EDEADLK 36 +#define ENAMETOOLONG 38 +#define ENOLCK 39 +#define ENOSYS 40 +#define ENOTEMPTY 41 +#define EINVAL 22 +#define ERANGE 34 +#define EILSEQ 42 +#define EADDRINUSE 100 +#define EADDRNOTAVAIL 101 +#define EAFNOSUPPORT 102 +#define EALREADY 103 +#define EBADMSG 104 +#define ECANCELED 105 +#define ECONNABORTED 106 +#define ECONNREFUSED 107 +#define ECONNRESET 108 +#define EDESTADDRREQ 109 +#define EHOSTUNREACH 110 +#define EIDRM 111 +#define EINPROGRESS 112 +#define EISCONN 113 +#define ELOOP 114 +#define EMSGSIZE 115 +#define ENETDOWN 116 +#define ENETRESET 117 +#define ENETUNREACH 118 +#define ENOBUFS 119 +#define ENODATA 120 +#define ENOLINK 121 +#define ENOMSG 122 +#define ENOPROTOOPT 123 +#define ENOSR 124 +#define ENOSTR 125 +#define ENOTCONN 126 +#define ENOTRECOVERABLE 127 +#define ENOTSOCK 128 +#define ENOTSUP 129 +#define EOPNOTSUPP 130 +#define EOTHER 131 +#define EOVERFLOW 132 +#define EOWNERDEAD 133 +#define EPROTO 134 +#define EPROTONOSUPPORT 135 +#define EPROTOTYPE 136 +#define ETIME 137 +#define ETIMEDOUT 138 +#define ETXTBSY 139 +#define EWOULDBLOCK 140 + +#include +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_RDWR _O_RDWR +#define O_APPEND _O_APPEND +#define O_CREAT _O_CREAT +#define O_EXCL _O_EXCL +#define O_TRUNC _O_TRUNC +#if !defined(O_ACCMODE) +#define O_ACCMODE (_O_RDONLY|_O_WRONLY|_O_RDWR) +#endif + +#endif + +#if defined(__linux__) || defined(_WIN32) +// incantation needed for cgo to figure out "kind of name" for ENOATTR +#define ENOATTR ((int)ENODATA) +#endif + +#if defined(__APPLE__) || defined(__linux__) +#include +#elif defined(_WIN32) +#define XATTR_CREATE 1 +#define XATTR_REPLACE 2 +#endif +*/ +import "C" +import ( + "strconv" + "time" +) + +const ( + E2BIG = int(C.E2BIG) + EACCES = int(C.EACCES) + EADDRINUSE = int(C.EADDRINUSE) + EADDRNOTAVAIL = int(C.EADDRNOTAVAIL) + EAFNOSUPPORT = int(C.EAFNOSUPPORT) + EAGAIN = int(C.EAGAIN) + EALREADY = int(C.EALREADY) + EBADF = int(C.EBADF) + EBADMSG = int(C.EBADMSG) + EBUSY = int(C.EBUSY) + ECANCELED = int(C.ECANCELED) + ECHILD = int(C.ECHILD) + ECONNABORTED = int(C.ECONNABORTED) + ECONNREFUSED = int(C.ECONNREFUSED) + ECONNRESET = int(C.ECONNRESET) + EDEADLK = int(C.EDEADLK) + EDESTADDRREQ = int(C.EDESTADDRREQ) + EDOM = int(C.EDOM) + EEXIST = int(C.EEXIST) + EFAULT = int(C.EFAULT) + EFBIG = int(C.EFBIG) + EHOSTUNREACH = int(C.EHOSTUNREACH) + EIDRM = int(C.EIDRM) + EILSEQ = int(C.EILSEQ) + EINPROGRESS = int(C.EINPROGRESS) + EINTR = int(C.EINTR) + EINVAL = int(C.EINVAL) + EIO = int(C.EIO) + EISCONN = int(C.EISCONN) + EISDIR = int(C.EISDIR) + ELOOP = int(C.ELOOP) + EMFILE = int(C.EMFILE) + EMLINK = int(C.EMLINK) + EMSGSIZE = int(C.EMSGSIZE) + ENAMETOOLONG = int(C.ENAMETOOLONG) + ENETDOWN = int(C.ENETDOWN) + ENETRESET = int(C.ENETRESET) + ENETUNREACH = int(C.ENETUNREACH) + ENFILE = int(C.ENFILE) + ENOATTR = int(C.ENOATTR) + ENOBUFS = int(C.ENOBUFS) + ENODATA = int(C.ENODATA) + ENODEV = int(C.ENODEV) + ENOENT = int(C.ENOENT) + ENOEXEC = int(C.ENOEXEC) + ENOLCK = int(C.ENOLCK) + ENOLINK = int(C.ENOLINK) + ENOMEM = int(C.ENOMEM) + ENOMSG = int(C.ENOMSG) + ENOPROTOOPT = int(C.ENOPROTOOPT) + ENOSPC = int(C.ENOSPC) + ENOSR = int(C.ENOSR) + ENOSTR = int(C.ENOSTR) + ENOSYS = int(C.ENOSYS) + ENOTCONN = int(C.ENOTCONN) + ENOTDIR = int(C.ENOTDIR) + ENOTEMPTY = int(C.ENOTEMPTY) + ENOTRECOVERABLE = int(C.ENOTRECOVERABLE) + ENOTSOCK = int(C.ENOTSOCK) + ENOTSUP = int(C.ENOTSUP) + ENOTTY = int(C.ENOTTY) + ENXIO = int(C.ENXIO) + EOPNOTSUPP = int(C.EOPNOTSUPP) + EOVERFLOW = int(C.EOVERFLOW) + EOWNERDEAD = int(C.EOWNERDEAD) + EPERM = int(C.EPERM) + EPIPE = int(C.EPIPE) + EPROTO = int(C.EPROTO) + EPROTONOSUPPORT = int(C.EPROTONOSUPPORT) + EPROTOTYPE = int(C.EPROTOTYPE) + ERANGE = int(C.ERANGE) + EROFS = int(C.EROFS) + ESPIPE = int(C.ESPIPE) + ESRCH = int(C.ESRCH) + ETIME = int(C.ETIME) + ETIMEDOUT = int(C.ETIMEDOUT) + ETXTBSY = int(C.ETXTBSY) + EWOULDBLOCK = int(C.EWOULDBLOCK) + EXDEV = int(C.EXDEV) +) + +const ( + O_RDONLY = int(C.O_RDONLY) + O_WRONLY = int(C.O_WRONLY) + O_RDWR = int(C.O_RDWR) + O_APPEND = int(C.O_APPEND) + O_CREAT = int(C.O_CREAT) + O_EXCL = int(C.O_EXCL) + O_TRUNC = int(C.O_TRUNC) + O_ACCMODE = int(C.O_ACCMODE) +) + +const ( + S_IFMT = 0170000 + S_IFBLK = 0060000 + S_IFCHR = 0020000 + S_IFIFO = 0010000 + S_IFREG = 0100000 + S_IFDIR = 0040000 + S_IFLNK = 0120000 + S_IFSOCK = 0140000 + + S_IRWXU = 00700 + S_IRUSR = 00400 + S_IWUSR = 00200 + S_IXUSR = 00100 + S_IRWXG = 00070 + S_IRGRP = 00040 + S_IWGRP = 00020 + S_IXGRP = 00010 + S_IRWXO = 00007 + S_IROTH = 00004 + S_IWOTH = 00002 + S_IXOTH = 00001 + S_ISUID = 04000 + S_ISGID = 02000 + S_ISVTX = 01000 +) + +const ( + XATTR_CREATE = int(C.XATTR_CREATE) + XATTR_REPLACE = int(C.XATTR_REPLACE) +) + +// Timespec contains a time as the UNIX time in seconds and nanoseconds. +// This structure is analogous to the POSIX struct timespec. +type Timespec struct { + Sec int64 + Nsec int64 +} + +// NewTimespec creates a Timespec from a time.Time. +func NewTimespec(t time.Time) Timespec { + return Timespec{t.Unix(), int64(t.Nanosecond())} +} + +// Now creates a Timespec that contains the current time. +func Now() Timespec { + return NewTimespec(time.Now()) +} + +// Time returns the Timespec as a time.Time. +func (ts *Timespec) Time() time.Time { + return time.Unix(ts.Sec, ts.Nsec) +} + +// Statfs_t contains file system information. +// This structure is analogous to the POSIX struct statvfs (NOT struct statfs). +// Not all fields are honored by all FUSE implementations. +type Statfs_t struct { + // File system block size. + Bsize uint64 + + // Fundamental file system block size. + Frsize uint64 + + // Total number of blocks on file system in units of Frsize. + Blocks uint64 + + // Total number of free blocks. + Bfree uint64 + + // Number of free blocks available to non-privileged process. + Bavail uint64 + + // Total number of file serial numbers. + Files uint64 + + // Total number of free file serial numbers. + Ffree uint64 + + // Number of file serial numbers available to non-privileged process. + Favail uint64 + + // File system ID. [IGNORED] + Fsid uint64 + + // Bit mask of Flag values. [IGNORED] + Flag uint64 + + // Maximum filename length. + Namemax uint64 +} + +// Stat_t contains file metadata information. +// This structure is analogous to the POSIX struct stat. +// Not all fields are honored by all FUSE implementations. +type Stat_t struct { + // Device ID of device containing file. [IGNORED] + Dev uint64 + + // File serial number. [IGNORED unless the use_ino mount option is given.] + Ino uint64 + + // Mode of file. + Mode uint32 + + // Number of hard links to the file. + Nlink uint32 + + // User ID of file. + Uid uint32 + + // Group ID of file. + Gid uint32 + + // Device ID (if file is character or block special). + Rdev uint64 + + // For regular files, the file size in bytes. + // For symbolic links, the length in bytes of the + // pathname contained in the symbolic link. + Size int64 + + // Last data access timestamp. + Atim Timespec + + // Last data modification timestamp. + Mtim Timespec + + // Last file status change timestamp. + Ctim Timespec + + // A file system-specific preferred I/O block size for this object. + Blksize int64 + + // Number of blocks allocated for this object. + Blocks int64 + + // File creation (birth) timestamp. [OSX only] + Birthtim Timespec +} + +/* +// Lock_t contains file locking information. +// This structure is analogous to the POSIX struct flock. +type Lock_t struct { + // Type of lock; F_RDLCK, F_WRLCK, F_UNLCK. + Type int16 + + // Flag for starting offset. + Whence int16 + + // Relative offset in bytes. + Start int64 + + // Size; if 0 then until EOF. + Len int64 + + // Process ID of the process holding the lock + Pid int +} +*/ + +// FileSystemInterface is the interface that all file systems must implement. +// The file system will receive an Init() call when it is mounted and a Destroy() +// call when it is unmounted (note that depending on how the file system is +// terminated the file system may not receive the Destroy() call). All other +// operations must return 0 on success or a FUSE error on failure. To return an +// error return the NEGATIVE value of a particular error. For example, to report +// "file not found" return -fuse.ENOENT. +type FileSystemInterface interface { + // Init is called when the file system is mounted. + Init() + + // Destroy is called when the file system is unmounted. + Destroy() + + // Statfs gets file system statistics. + Statfs(path string, stat *Statfs_t) int + + // Mknod creates a file node. + Mknod(path string, mode uint32, dev uint64) int + + // Mkdir creates a directory. + Mkdir(path string, mode uint32) int + + // Unlink removes a file. + Unlink(path string) int + + // Rmdir removes a directory. + Rmdir(path string) int + + // Link creates a hard link to a file. + Link(oldpath string, newpath string) int + + // Symlink creates a symbolic link. + Symlink(target string, newpath string) int + + // Readlink reads the target of a symbolic link. + Readlink(path string) (int, string) + + // Rename renames a file. + Rename(oldpath string, newpath string) int + + // Chmod changes the permission bits of a file. + Chmod(path string, mode uint32) int + + // Chown changes the owner and group of a file. + Chown(path string, uid uint32, gid uint32) int + + // Utimens changes the access and modification times of a file. + Utimens(path string, tmsp []Timespec) int + + // Access checks file access permissions. + Access(path string, mask uint32) int + + // Create creates and opens a file. + Create(path string, flags int, mode uint32) (int, uint64) + + // Open opens a file. + Open(path string, flags int) (int, uint64) + + // Getattr gets file attributes. + Getattr(path string, stat *Stat_t, fh uint64) int + + // Truncate changes the size of a file. + Truncate(path string, size int64, fh uint64) int + + // Read reads data from a file. + Read(path string, buff []byte, ofst int64, fh uint64) int + + // Write writes data to a file. + Write(path string, buff []byte, ofst int64, fh uint64) int + + // Flush flushes cached file data. + Flush(path string, fh uint64) int + + // Release closes an open file. + Release(path string, fh uint64) int + + // Fsync synchronizes file contents. + Fsync(path string, datasync bool, fh uint64) int + + // Lock performs a file locking operation. + //Lock(path string, cmd int, lock *Lock_t, fh uint64) int + + // Opendir opens a directory. + Opendir(path string) (int, uint64) + + // Readdir reads a directory. + Readdir(path string, + fill func(name string, stat *Stat_t, ofst int64) bool, + ofst int64, + fh uint64) int + + // Releasedir closes an open directory. + Releasedir(path string, fh uint64) int + + // Fsyncdir synchronizes directory contents. + Fsyncdir(path string, datasync bool, fh uint64) int + + // Setxattr sets extended attributes. + Setxattr(path string, name string, value []byte, flags int) int + + // Getxattr gets extended attributes. + Getxattr(path string, name string) (int, []byte) + + // Removexattr removes extended attributes. + Removexattr(path string, name string) int + + // Listxattr lists extended attributes. + Listxattr(path string, fill func(name string) bool) int +} + +// Error encapsulates a FUSE error code. In some rare circumstances it is useful +// to signal an error to the FUSE layer by boxing the error code using Error and +// calling panic(). The FUSE layer will recover and report the boxed error code +// to the OS. +type Error int + +func (self Error) Error() string { + return "fuse.Error(" + strconv.Itoa(int(self)) + ")" +} + +var _ error = (*Error)(nil) + +// FileSystemBase provides default implementations of the methods in FileSystemInterface. +// The default implementations are either empty or return -ENOSYS to signal that the +// file system does not implement a particular operation to the FUSE layer. +type FileSystemBase struct { +} + +// Init is called when the file system is mounted. +// The FileSystemBase implementation does nothing. +func (*FileSystemBase) Init() { +} + +// Destroy is called when the file system is unmounted. +// The FileSystemBase implementation does nothing. +func (*FileSystemBase) Destroy() { +} + +// Statfs gets file system statistics. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Statfs(path string, stat *Statfs_t) int { + return -ENOSYS +} + +// Mknod creates a file node. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Mknod(path string, mode uint32, dev uint64) int { + return -ENOSYS +} + +// Mkdir creates a directory. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Mkdir(path string, mode uint32) int { + return -ENOSYS +} + +// Unlink removes a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Unlink(path string) int { + return -ENOSYS +} + +// Rmdir removes a directory. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Rmdir(path string) int { + return -ENOSYS +} + +// Link creates a hard link to a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Link(oldpath string, newpath string) int { + return -ENOSYS +} + +// Symlink creates a symbolic link. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Symlink(target string, newpath string) int { + return -ENOSYS +} + +// Readlink reads the target of a symbolic link. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Readlink(path string) (int, string) { + return -ENOSYS, "" +} + +// Rename renames a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Rename(oldpath string, newpath string) int { + return -ENOSYS +} + +// Chmod changes the permission bits of a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Chmod(path string, mode uint32) int { + return -ENOSYS +} + +// Chown changes the owner and group of a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Chown(path string, uid uint32, gid uint32) int { + return -ENOSYS +} + +// Utimens changes the access and modification times of a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Utimens(path string, tmsp []Timespec) int { + return -ENOSYS +} + +// Access checks file access permissions. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Access(path string, mask uint32) int { + return -ENOSYS +} + +// Create creates and opens a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Create(path string, flags int, mode uint32) (int, uint64) { + return -ENOSYS, ^uint64(0) +} + +// Open opens a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Open(path string, flags int) (int, uint64) { + return -ENOSYS, ^uint64(0) +} + +// Getattr gets file attributes. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Getattr(path string, stat *Stat_t, fh uint64) int { + return -ENOSYS +} + +// Truncate changes the size of a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Truncate(path string, size int64, fh uint64) int { + return -ENOSYS +} + +// Read reads data from a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Read(path string, buff []byte, ofst int64, fh uint64) int { + return -ENOSYS +} + +// Write writes data to a file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Write(path string, buff []byte, ofst int64, fh uint64) int { + return -ENOSYS +} + +// Flush flushes cached file data. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Flush(path string, fh uint64) int { + return -ENOSYS +} + +// Release closes an open file. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Release(path string, fh uint64) int { + return -ENOSYS +} + +// Fsync synchronizes file contents. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Fsync(path string, datasync bool, fh uint64) int { + return -ENOSYS +} + +/* +// Lock performs a file locking operation. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Lock(path string, cmd int, lock *Lock_t, fh uint64) int { + return -ENOSYS +} +*/ + +// Opendir opens a directory. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Opendir(path string) (int, uint64) { + return -ENOSYS, ^uint64(0) +} + +// Readdir reads a directory. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Readdir(path string, + fill func(name string, stat *Stat_t, ofst int64) bool, + ofst int64, + fh uint64) int { + return -ENOSYS +} + +// Releasedir closes an open directory. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Releasedir(path string, fh uint64) int { + return -ENOSYS +} + +// Fsyncdir synchronizes directory contents. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Fsyncdir(path string, datasync bool, fh uint64) int { + return -ENOSYS +} + +// Setxattr sets extended attributes. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Setxattr(path string, name string, value []byte, flags int) int { + return -ENOSYS +} + +// Getxattr gets extended attributes. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Getxattr(path string, name string) (int, []byte) { + return -ENOSYS, nil +} + +// Removexattr removes extended attributes. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Removexattr(path string, name string) int { + return -ENOSYS +} + +// Listxattr lists extended attributes. +// The FileSystemBase implementation returns -ENOSYS. +func (*FileSystemBase) Listxattr(path string, fill func(name string) bool) int { + return -ENOSYS +} + +var _ FileSystemInterface = (*FileSystemBase)(nil) diff --git a/vendor/github.com/billziss-gh/cgofuse/fuse/host.go b/vendor/github.com/billziss-gh/cgofuse/fuse/host.go new file mode 100644 index 000000000..f7bbb84ed --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/fuse/host.go @@ -0,0 +1,1129 @@ +/* + * host.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package fuse + +/* +#cgo darwin CFLAGS: -DFUSE_USE_VERSION=28 -D_FILE_OFFSET_BITS=64 -I/usr/local/include/osxfuse/fuse +#cgo darwin LDFLAGS: -L/usr/local/lib -losxfuse +#cgo linux CFLAGS: -DFUSE_USE_VERSION=28 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse +#cgo linux LDFLAGS: -lfuse +#cgo windows CFLAGS: -D_WIN32_WINNT=0x0600 -DFUSE_USE_VERSION=28 + +#if !(defined(__APPLE__) || defined(__linux__) || defined(_WIN32)) +#error platform not supported +#endif + +#include +#include +#include + +#if defined(__APPLE__) || defined(__linux__) + +#include +#include +#include +#include + +#elif defined(_WIN32) + +#include + +static PVOID cgofuse_init_slow(int hardfail); +static VOID cgofuse_init_fail(VOID); +static PVOID cgofuse_init_winfsp(VOID); + +static SRWLOCK cgofuse_lock = SRWLOCK_INIT; +static PVOID cgofuse_module = 0; + +static inline PVOID cgofuse_init_fast(int hardfail) +{ + PVOID Module = cgofuse_module; + MemoryBarrier(); + if (0 == Module) + Module = cgofuse_init_slow(hardfail); + return Module; +} + +static PVOID cgofuse_init_slow(int hardfail) +{ + PVOID Module; + AcquireSRWLockExclusive(&cgofuse_lock); + Module = cgofuse_module; + if (0 == Module) + { + Module = cgofuse_init_winfsp(); + MemoryBarrier(); + cgofuse_module = Module; + } + ReleaseSRWLockExclusive(&cgofuse_lock); + if (0 == Module && hardfail) + cgofuse_init_fail(); + return Module; +} + +static VOID cgofuse_init_fail(VOID) +{ + static const char *message = "cgofuse: cannot find winfsp\n"; + DWORD BytesTransferred; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), message, lstrlenA(message), &BytesTransferred, 0); + ExitProcess(ERROR_DLL_NOT_FOUND); +} + +#define FSP_FUSE_API static +#define FSP_FUSE_API_NAME(api) (* pfn_ ## api) +#define FSP_FUSE_API_CALL(api) (cgofuse_init_fast(1), pfn_ ## api) +#define FSP_FUSE_SYM(proto, ...) static inline proto { __VA_ARGS__ } +#include +#include +#include + +static NTSTATUS FspLoad(PVOID *PModule) +{ +#if defined(_WIN64) +#define FSP_DLLNAME "winfsp-x64.dll" +#else +#define FSP_DLLNAME "winfsp-x86.dll" +#endif +#define FSP_DLLPATH "bin\\" FSP_DLLNAME + + WINADVAPI + LSTATUS + APIENTRY + RegGetValueW( + HKEY hkey, + LPCWSTR lpSubKey, + LPCWSTR lpValue, + DWORD dwFlags, + LPDWORD pdwType, + PVOID pvData, + LPDWORD pcbData); + + WCHAR PathBuf[MAX_PATH]; + DWORD Size; + HKEY RegKey; + LONG Result; + HMODULE Module; + + if (0 != PModule) + *PModule = 0; + + Module = LoadLibraryW(L"" FSP_DLLNAME); + if (0 == Module) + { + Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\WinFsp", + 0, KEY_READ | KEY_WOW64_32KEY, &RegKey); + if (ERROR_SUCCESS == Result) + { + Size = sizeof PathBuf - sizeof L"" FSP_DLLPATH + sizeof(WCHAR); + Result = RegGetValueW(RegKey, 0, L"InstallDir", + RRF_RT_REG_SZ, 0, PathBuf, &Size); + RegCloseKey(RegKey); + } + if (ERROR_SUCCESS != Result) + return 0xC0000034;//STATUS_OBJECT_NAME_NOT_FOUND + + RtlCopyMemory(PathBuf + (Size / sizeof(WCHAR) - 1), L"" FSP_DLLPATH, sizeof L"" FSP_DLLPATH); + Module = LoadLibraryW(PathBuf); + if (0 == Module) + return 0xC0000135;//STATUS_DLL_NOT_FOUND + } + + if (0 != PModule) + *PModule = Module; + + return 0;//STATUS_SUCCESS + +#undef FSP_DLLNAME +#undef FSP_DLLPATH +} + +#define CGOFUSE_GET_API(h, n) \ + if (0 == (*(void **)&(pfn_ ## n) = GetProcAddress(Module, #n)))\ + return 0; + +static PVOID cgofuse_init_winfsp(VOID) +{ + PVOID Module; + NTSTATUS Result; + + Result = FspLoad(&Module); + if (0 > Result) + return 0; + + // fuse_common.h + CGOFUSE_GET_API(h, fsp_fuse_version); + CGOFUSE_GET_API(h, fsp_fuse_mount); + CGOFUSE_GET_API(h, fsp_fuse_unmount); + CGOFUSE_GET_API(h, fsp_fuse_parse_cmdline); + CGOFUSE_GET_API(h, fsp_fuse_ntstatus_from_errno); + + // fuse.h + CGOFUSE_GET_API(h, fsp_fuse_main_real); + CGOFUSE_GET_API(h, fsp_fuse_is_lib_option); + CGOFUSE_GET_API(h, fsp_fuse_new); + CGOFUSE_GET_API(h, fsp_fuse_destroy); + CGOFUSE_GET_API(h, fsp_fuse_loop); + CGOFUSE_GET_API(h, fsp_fuse_loop_mt); + CGOFUSE_GET_API(h, fsp_fuse_exit); + CGOFUSE_GET_API(h, fsp_fuse_get_context); + + // fuse_opt.h + CGOFUSE_GET_API(h, fsp_fuse_opt_parse); + CGOFUSE_GET_API(h, fsp_fuse_opt_add_arg); + CGOFUSE_GET_API(h, fsp_fuse_opt_insert_arg); + CGOFUSE_GET_API(h, fsp_fuse_opt_free_args); + CGOFUSE_GET_API(h, fsp_fuse_opt_add_opt); + CGOFUSE_GET_API(h, fsp_fuse_opt_add_opt_escaped); + CGOFUSE_GET_API(h, fsp_fuse_opt_match); + + return Module; +} + +#endif + +#if defined(__APPLE__) || defined(__linux__) +typedef struct stat fuse_stat_t; +typedef struct statvfs fuse_statvfs_t; +typedef struct timespec fuse_timespec_t; +typedef mode_t fuse_mode_t; +typedef dev_t fuse_dev_t; +typedef uid_t fuse_uid_t; +typedef gid_t fuse_gid_t; +typedef off_t fuse_off_t; +#elif defined(_WIN32) +typedef struct fuse_stat fuse_stat_t; +typedef struct fuse_statvfs fuse_statvfs_t; +typedef struct fuse_timespec fuse_timespec_t; +#endif + +extern int hostGetattr(char *path, fuse_stat_t *stbuf); +extern int hostReadlink(char *path, char *buf, size_t size); +extern int hostMknod(char *path, fuse_mode_t mode, fuse_dev_t dev); +extern int hostMkdir(char *path, fuse_mode_t mode); +extern int hostUnlink(char *path); +extern int hostRmdir(char *path); +extern int hostSymlink(char *target, char *newpath); +extern int hostRename(char *oldpath, char *newpath); +extern int hostLink(char *oldpath, char *newpath); +extern int hostChmod(char *path, fuse_mode_t mode); +extern int hostChown(char *path, fuse_uid_t uid, fuse_gid_t gid); +extern int hostTruncate(char *path, fuse_off_t size); +extern int hostOpen(char *path, struct fuse_file_info *fi); +extern int hostRead(char *path, char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi); +extern int hostWrite(char *path, char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi); +extern int hostStatfs(char *path, fuse_statvfs_t *stbuf); +extern int hostFlush(char *path, struct fuse_file_info *fi); +extern int hostRelease(char *path, struct fuse_file_info *fi); +extern int hostFsync(char *path, int datasync, struct fuse_file_info *fi); +extern int hostSetxattr(char *path, char *name, char *value, size_t size, int flags); +extern int hostGetxattr(char *path, char *name, char *value, size_t size); +extern int hostListxattr(char *path, char *namebuf, size_t size); +extern int hostRemovexattr(char *path, char *name); +extern int hostOpendir(char *path, struct fuse_file_info *fi); +extern int hostReaddir(char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off, + struct fuse_file_info *fi); +extern int hostReleasedir(char *path, struct fuse_file_info *fi); +extern int hostFsyncdir(char *path, int datasync, struct fuse_file_info *fi); +extern void *hostInit(struct fuse_conn_info *conn); +extern void hostDestroy(void *data); +extern int hostAccess(char *path, int mask); +extern int hostCreate(char *path, fuse_mode_t mode, struct fuse_file_info *fi); +extern int hostFtruncate(char *path, fuse_off_t off, struct fuse_file_info *fi); +extern int hostFgetattr(char *path, fuse_stat_t *stbuf, struct fuse_file_info *fi); +//extern int hostLock(char *path, struct fuse_file_info *fi, int cmd, struct fuse_flock *lock); +extern int hostUtimens(char *path, fuse_timespec_t tv[2]); + +static inline void hostAsgnCconninfo(struct fuse_conn_info *conn, + bool capCaseInsensitive, + bool capReaddirPlus) +{ +#if defined(__APPLE__) + if (capCaseInsensitive) + FUSE_ENABLE_CASE_INSENSITIVE(conn); +#elif defined(__linux__) +#elif defined(_WIN32) + if (capCaseInsensitive) + conn->want |= conn->capable & FSP_FUSE_CAP_CASE_INSENSITIVE; + if (capReaddirPlus) + conn->want |= conn->capable & FSP_FUSE_CAP_READDIR_PLUS; +#endif +} + +static inline void hostCstatvfsFromFusestatfs(fuse_statvfs_t *stbuf, + uint64_t bsize, + uint64_t frsize, + uint64_t blocks, + uint64_t bfree, + uint64_t bavail, + uint64_t files, + uint64_t ffree, + uint64_t favail, + uint64_t fsid, + uint64_t flag, + uint64_t namemax) +{ + memset(stbuf, 0, sizeof *stbuf); + stbuf->f_bsize = bsize; + stbuf->f_frsize = frsize; + stbuf->f_blocks = blocks; + stbuf->f_bfree = bfree; + stbuf->f_bavail = bavail; + stbuf->f_files = files; + stbuf->f_ffree = ffree; + stbuf->f_favail = favail; + stbuf->f_fsid = fsid; + stbuf->f_flag = flag; + stbuf->f_namemax = namemax; +} + +static inline void hostCstatFromFusestat(fuse_stat_t *stbuf, + uint64_t dev, + uint64_t ino, + uint32_t mode, + uint32_t nlink, + uint32_t uid, + uint32_t gid, + uint64_t rdev, + int64_t size, + int64_t atimSec, int64_t atimNsec, + int64_t mtimSec, int64_t mtimNsec, + int64_t ctimSec, int64_t ctimNsec, + int64_t blksize, + int64_t blocks, + int64_t birthtimSec, int64_t birthtimNsec) +{ + memset(stbuf, 0, sizeof *stbuf); + stbuf->st_dev = dev; + stbuf->st_ino = ino; + stbuf->st_mode = mode; + stbuf->st_nlink = nlink; + stbuf->st_uid = uid; + stbuf->st_gid = gid; + stbuf->st_rdev = rdev; + stbuf->st_size = size; +#if defined(__APPLE__) + stbuf->st_atimespec.tv_sec = atimSec; stbuf->st_atimespec.tv_nsec = atimNsec; + stbuf->st_mtimespec.tv_sec = mtimSec; stbuf->st_mtimespec.tv_nsec = mtimNsec; + stbuf->st_ctimespec.tv_sec = ctimSec; stbuf->st_ctimespec.tv_nsec = ctimNsec; + stbuf->st_birthtimespec.tv_sec = birthtimSec; stbuf->st_birthtimespec.tv_nsec = birthtimNsec; +#else + stbuf->st_atim.tv_sec = atimSec; stbuf->st_atim.tv_nsec = atimNsec; + stbuf->st_mtim.tv_sec = mtimSec; stbuf->st_mtim.tv_nsec = mtimNsec; + stbuf->st_ctim.tv_sec = ctimSec; stbuf->st_ctim.tv_nsec = ctimNsec; +#endif + stbuf->st_blksize = blksize; + stbuf->st_blocks = blocks; +} + +static inline int hostFilldir(fuse_fill_dir_t filler, void *buf, + char *name, fuse_stat_t *stbuf, fuse_off_t off) +{ + return filler(buf, name, stbuf, off); +} + +#if defined(__APPLE__) +static int _hostSetxattr(char *path, char *name, char *value, size_t size, int flags, + uint32_t position) +{ + // OSX uses position only for the resource fork; we do not support it! + return hostSetxattr(path, name, value, size, flags); +} +static int _hostGetxattr(char *path, char *name, char *value, size_t size, + uint32_t position) +{ + // OSX uses position only for the resource fork; we do not support it! + return hostGetxattr(path, name, value, size); +} +#else +#define _hostSetxattr hostSetxattr +#define _hostGetxattr hostGetxattr +#endif + +static int hostInitializeFuse(void) +{ +#if defined(__APPLE__) || defined(__linux__) + return 1; +#elif defined(_WIN32) + return 0 != cgofuse_init_fast(0); +#endif +} + +static int hostMountpointOptProc(void *opt_data, const char *arg, int key, + struct fuse_args *outargs) +{ + char **pmountpoint = opt_data; + switch (key) + { + default: + return 1; + case FUSE_OPT_KEY_NONOPT: + if (0 == *pmountpoint) + { + size_t size = strlen(arg) + 1; + *pmountpoint = malloc(size); + if (0 == *pmountpoint) + return -1; + memcpy(*pmountpoint, arg, size); + } + return 1; + } +} + +static const char *hostMountpoint(int argc, char *argv[]) +{ + static struct fuse_opt opts[] = { FUSE_OPT_END }; + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + char *mountpoint = 0; + if (-1 == fuse_opt_parse(&args, &mountpoint, opts, hostMountpointOptProc)) + return 0; + fuse_opt_free_args(&args); + return mountpoint; +} + +static int hostMount(int argc, char *argv[], void *data) +{ +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wincompatible-pointer-types" +#endif + static struct fuse_operations fsop = + { + .getattr = (int (*)())hostGetattr, + .readlink = (int (*)())hostReadlink, + .mknod = (int (*)())hostMknod, + .mkdir = (int (*)())hostMkdir, + .unlink = (int (*)())hostUnlink, + .rmdir = (int (*)())hostRmdir, + .symlink = (int (*)())hostSymlink, + .rename = (int (*)())hostRename, + .link = (int (*)())hostLink, + .chmod = (int (*)())hostChmod, + .chown = (int (*)())hostChown, + .truncate = (int (*)())hostTruncate, + .open = (int (*)())hostOpen, + .read = (int (*)())hostRead, + .write = (int (*)())hostWrite, + .statfs = (int (*)())hostStatfs, + .flush = (int (*)())hostFlush, + .release = (int (*)())hostRelease, + .fsync = (int (*)())hostFsync, + .setxattr = (int (*)())_hostSetxattr, + .getxattr = (int (*)())_hostGetxattr, + .listxattr = (int (*)())hostListxattr, + .removexattr = (int (*)())hostRemovexattr, + .opendir = (int (*)())hostOpendir, + .readdir = (int (*)())hostReaddir, + .releasedir = (int (*)())hostReleasedir, + .fsyncdir = (int (*)())hostFsyncdir, + .init = (void *(*)())hostInit, + .destroy = (void (*)())hostDestroy, + .access = (int (*)())hostAccess, + .create = (int (*)())hostCreate, + .ftruncate = (int (*)())hostFtruncate, + .fgetattr = (int (*)())hostFgetattr, + //.lock = (int (*)())hostFlock, + .utimens = (int (*)())hostUtimens, + }; +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + return 0 == fuse_main_real(argc, argv, &fsop, sizeof fsop, data); +} + +static int hostUnmount(struct fuse *fuse, char *mountpoint) +{ +#if defined(__APPLE__) + if (0 == mountpoint) + return 0; + // darwin: unmount is available to non-root + return 0 == unmount(mountpoint, MNT_FORCE); +#elif defined(__linux__) + if (0 == mountpoint) + return 0; + // linux: try umount2 first in case we are root + if (0 == umount2(mountpoint, MNT_DETACH)) + return 1; + // linux: umount2 failed; try fusermount + char *argv[] = + { + "/bin/fusermount", + "-z", + "-u", + mountpoint, + 0, + }; + pid_t pid = 0; + int status = 0; + return + 0 == posix_spawn(&pid, argv[0], 0, 0, argv, 0) && + pid == waitpid(pid, &status, 0) && + WIFEXITED(status) && 0 == WEXITSTATUS(status); +#elif defined(_WIN32) + // windows/winfsp: fuse_exit just works from anywhere + fuse_exit(fuse); + return 1; +#endif +} +*/ +import "C" +import ( + "os" + "os/signal" + "runtime" + "sync" + "syscall" + "unsafe" +) + +// FileSystemHost is used to host a file system. +type FileSystemHost struct { + fsop FileSystemInterface + fuse *C.struct_fuse + mntp *C.char + sigc chan os.Signal + + capCaseInsensitive, capReaddirPlus bool +} + +var ( + hostGuard = sync.Mutex{} + hostTable = map[unsafe.Pointer]*FileSystemHost{} +) + +func hostHandleNew(host *FileSystemHost) unsafe.Pointer { + p := C.malloc(1) + hostGuard.Lock() + defer hostGuard.Unlock() + hostTable[p] = host + return p +} + +func hostHandleDel(p unsafe.Pointer) *FileSystemHost { + hostGuard.Lock() + defer hostGuard.Unlock() + if host, ok := hostTable[p]; ok { + delete(hostTable, p) + C.free(p) + return host + } + return nil +} + +func hostHandleGet(p unsafe.Pointer) *FileSystemHost { + hostGuard.Lock() + defer hostGuard.Unlock() + if host, ok := hostTable[p]; ok { + return host + } + return nil +} + +func copyCstatvfsFromFusestatfs(dst *C.fuse_statvfs_t, src *Statfs_t) { + C.hostCstatvfsFromFusestatfs(dst, + C.uint64_t(src.Bsize), + C.uint64_t(src.Frsize), + C.uint64_t(src.Blocks), + C.uint64_t(src.Bfree), + C.uint64_t(src.Bavail), + C.uint64_t(src.Files), + C.uint64_t(src.Ffree), + C.uint64_t(src.Favail), + C.uint64_t(src.Fsid), + C.uint64_t(src.Flag), + C.uint64_t(src.Namemax)) +} + +func copyCstatFromFusestat(dst *C.fuse_stat_t, src *Stat_t) { + C.hostCstatFromFusestat(dst, + C.uint64_t(src.Dev), + C.uint64_t(src.Ino), + C.uint32_t(src.Mode), + C.uint32_t(src.Nlink), + C.uint32_t(src.Uid), + C.uint32_t(src.Gid), + C.uint64_t(src.Rdev), + C.int64_t(src.Size), + C.int64_t(src.Atim.Sec), C.int64_t(src.Atim.Nsec), + C.int64_t(src.Mtim.Sec), C.int64_t(src.Mtim.Nsec), + C.int64_t(src.Ctim.Sec), C.int64_t(src.Ctim.Nsec), + C.int64_t(src.Blksize), + C.int64_t(src.Blocks), + C.int64_t(src.Birthtim.Sec), C.int64_t(src.Birthtim.Nsec)) +} + +func copyFusetimespecFromCtimespec(dst *Timespec, src *C.fuse_timespec_t) { + dst.Sec = int64(src.tv_sec) + dst.Nsec = int64(src.tv_nsec) +} + +func recoverAsErrno(errc0 *C.int) { + if r := recover(); nil != r { + switch e := r.(type) { + case Error: + *errc0 = C.int(e) + default: + *errc0 = -C.int(EIO) + } + } +} + +//export hostGetattr +func hostGetattr(path0 *C.char, stat0 *C.fuse_stat_t) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + stat := &Stat_t{} + errc := fsop.Getattr(path, stat, ^uint64(0)) + copyCstatFromFusestat(stat0, stat) + return C.int(errc) +} + +//export hostReadlink +func hostReadlink(path0 *C.char, buff0 *C.char, size0 C.size_t) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc, rslt := fsop.Readlink(path) + buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) + copy(buff[:size0-1], rslt) + rlen := len(rslt) + if C.size_t(rlen) < size0 { + buff[rlen] = 0 + } + return C.int(errc) +} + +//export hostMknod +func hostMknod(path0 *C.char, mode0 C.fuse_mode_t, dev0 C.fuse_dev_t) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Mknod(path, uint32(mode0), uint64(dev0)) + return C.int(errc) +} + +//export hostMkdir +func hostMkdir(path0 *C.char, mode0 C.fuse_mode_t) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Mkdir(path, uint32(mode0)) + return C.int(errc) +} + +//export hostUnlink +func hostUnlink(path0 *C.char) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Unlink(path) + return C.int(errc) +} + +//export hostRmdir +func hostRmdir(path0 *C.char) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Rmdir(path) + return C.int(errc) +} + +//export hostSymlink +func hostSymlink(target0 *C.char, newpath0 *C.char) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + target, newpath := C.GoString(target0), C.GoString(newpath0) + errc := fsop.Symlink(target, newpath) + return C.int(errc) +} + +//export hostRename +func hostRename(oldpath0 *C.char, newpath0 *C.char) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + oldpath, newpath := C.GoString(oldpath0), C.GoString(newpath0) + errc := fsop.Rename(oldpath, newpath) + return C.int(errc) +} + +//export hostLink +func hostLink(oldpath0 *C.char, newpath0 *C.char) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + oldpath, newpath := C.GoString(oldpath0), C.GoString(newpath0) + errc := fsop.Link(oldpath, newpath) + return C.int(errc) +} + +//export hostChmod +func hostChmod(path0 *C.char, mode0 C.fuse_mode_t) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Chmod(path, uint32(mode0)) + return C.int(errc) +} + +//export hostChown +func hostChown(path0 *C.char, uid0 C.fuse_uid_t, gid0 C.fuse_gid_t) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Chown(path, uint32(uid0), uint32(gid0)) + return C.int(errc) +} + +//export hostTruncate +func hostTruncate(path0 *C.char, size0 C.fuse_off_t) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Truncate(path, int64(size0), ^uint64(0)) + return C.int(errc) +} + +//export hostOpen +func hostOpen(path0 *C.char, fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc, rslt := fsop.Open(path, int(fi0.flags)) + fi0.fh = C.uint64_t(rslt) + return C.int(errc) +} + +//export hostRead +func hostRead(path0 *C.char, buff0 *C.char, size0 C.size_t, ofst0 C.fuse_off_t, + fi0 *C.struct_fuse_file_info) (nbyt0 C.int) { + defer recoverAsErrno(&nbyt0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) + nbyt := fsop.Read(path, buff[:size0], int64(ofst0), uint64(fi0.fh)) + return C.int(nbyt) +} + +//export hostWrite +func hostWrite(path0 *C.char, buff0 *C.char, size0 C.size_t, ofst0 C.fuse_off_t, + fi0 *C.struct_fuse_file_info) (nbyt0 C.int) { + defer recoverAsErrno(&nbyt0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) + nbyt := fsop.Write(path, buff[:size0], int64(ofst0), uint64(fi0.fh)) + return C.int(nbyt) +} + +//export hostStatfs +func hostStatfs(path0 *C.char, stat0 *C.fuse_statvfs_t) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + stat := &Statfs_t{} + errc := fsop.Statfs(path, stat) + if -ENOSYS == errc { + stat = &Statfs_t{} + errc = 0 + } + copyCstatvfsFromFusestatfs(stat0, stat) + return C.int(errc) +} + +//export hostFlush +func hostFlush(path0 *C.char, fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Flush(path, uint64(fi0.fh)) + return C.int(errc) +} + +//export hostRelease +func hostRelease(path0 *C.char, fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Release(path, uint64(fi0.fh)) + return C.int(errc) +} + +//export hostFsync +func hostFsync(path0 *C.char, datasync C.int, fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Fsync(path, 0 != datasync, uint64(fi0.fh)) + if -ENOSYS == errc { + errc = 0 + } + return C.int(errc) +} + +//export hostSetxattr +func hostSetxattr(path0 *C.char, name0 *C.char, buff0 *C.char, size0 C.size_t, + flags C.int) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + name := C.GoString(name0) + buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) + errc := fsop.Setxattr(path, name, buff[:size0], int(flags)) + return C.int(errc) +} + +//export hostGetxattr +func hostGetxattr(path0 *C.char, name0 *C.char, buff0 *C.char, size0 C.size_t) (nbyt0 C.int) { + defer recoverAsErrno(&nbyt0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + name := C.GoString(name0) + errc, rslt := fsop.Getxattr(path, name) + if 0 != errc { + return C.int(errc) + } + if 0 != size0 { + if len(rslt) > int(size0) { + return -C.int(ERANGE) + } + buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) + copy(buff[:size0], rslt) + } + return C.int(len(rslt)) +} + +//export hostListxattr +func hostListxattr(path0 *C.char, buff0 *C.char, size0 C.size_t) (nbyt0 C.int) { + defer recoverAsErrno(&nbyt0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) + size := int(size0) + nbyt := 0 + fill := func(name1 string) bool { + nlen := len(name1) + if 0 != size { + if nbyt+nlen+1 > size { + return false + } + copy(buff[nbyt:nbyt+nlen], name1) + buff[nbyt+nlen] = 0 + } + nbyt += nlen + 1 + return true + } + errc := fsop.Listxattr(path, fill) + if 0 != errc { + return C.int(errc) + } + return C.int(nbyt) +} + +//export hostRemovexattr +func hostRemovexattr(path0 *C.char, name0 *C.char) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + name := C.GoString(name0) + errc := fsop.Removexattr(path, name) + return C.int(errc) +} + +//export hostOpendir +func hostOpendir(path0 *C.char, fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc, rslt := fsop.Opendir(path) + if -ENOSYS == errc { + errc = 0 + } + fi0.fh = C.uint64_t(rslt) + return C.int(errc) +} + +//export hostReaddir +func hostReaddir(path0 *C.char, buff0 unsafe.Pointer, fill0 C.fuse_fill_dir_t, ofst0 C.fuse_off_t, + fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + fill := func(name1 string, stat1 *Stat_t, off1 int64) bool { + name := C.CString(name1) + defer C.free(unsafe.Pointer(name)) + if nil == stat1 { + return 0 == C.hostFilldir(fill0, buff0, name, nil, C.fuse_off_t(off1)) + } else { + stat := C.fuse_stat_t{} + copyCstatFromFusestat(&stat, stat1) + return 0 == C.hostFilldir(fill0, buff0, name, &stat, C.fuse_off_t(off1)) + } + } + errc := fsop.Readdir(path, fill, int64(ofst0), uint64(fi0.fh)) + return C.int(errc) +} + +//export hostReleasedir +func hostReleasedir(path0 *C.char, fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Releasedir(path, uint64(fi0.fh)) + return C.int(errc) +} + +//export hostFsyncdir +func hostFsyncdir(path0 *C.char, datasync C.int, fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Fsyncdir(path, 0 != datasync, uint64(fi0.fh)) + if -ENOSYS == errc { + errc = 0 + } + return C.int(errc) +} + +//export hostInit +func hostInit(conn0 *C.struct_fuse_conn_info) (user_data unsafe.Pointer) { + defer recover() + fctx := C.fuse_get_context() + user_data = fctx.private_data + host := hostHandleGet(user_data) + host.fuse = fctx.fuse + C.hostAsgnCconninfo(conn0, + C.bool(host.capCaseInsensitive), + C.bool(host.capReaddirPlus)) + if nil != host.sigc { + signal.Notify(host.sigc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) + } + host.fsop.Init() + return +} + +//export hostDestroy +func hostDestroy(user_data unsafe.Pointer) { + defer recover() + host := hostHandleGet(user_data) + host.fsop.Destroy() + if nil != host.sigc { + signal.Stop(host.sigc) + } + host.fuse = nil +} + +//export hostAccess +func hostAccess(path0 *C.char, mask0 C.int) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Access(path, uint32(mask0)) + return C.int(errc) +} + +//export hostCreate +func hostCreate(path0 *C.char, mode0 C.fuse_mode_t, fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc, rslt := fsop.Create(path, int(fi0.flags), uint32(mode0)) + if -ENOSYS == errc { + errc = fsop.Mknod(path, S_IFREG|uint32(mode0), 0) + if 0 == errc { + errc, rslt = fsop.Open(path, int(fi0.flags)) + } + } + fi0.fh = C.uint64_t(rslt) + return C.int(errc) +} + +//export hostFtruncate +func hostFtruncate(path0 *C.char, size0 C.fuse_off_t, fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + errc := fsop.Truncate(path, int64(size0), uint64(fi0.fh)) + return C.int(errc) +} + +//export hostFgetattr +func hostFgetattr(path0 *C.char, stat0 *C.fuse_stat_t, + fi0 *C.struct_fuse_file_info) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + stat := &Stat_t{} + errc := fsop.Getattr(path, stat, uint64(fi0.fh)) + copyCstatFromFusestat(stat0, stat) + return C.int(errc) +} + +//export hostUtimens +func hostUtimens(path0 *C.char, tmsp0 *C.fuse_timespec_t) (errc0 C.int) { + defer recoverAsErrno(&errc0) + fsop := hostHandleGet(C.fuse_get_context().private_data).fsop + path := C.GoString(path0) + if nil == tmsp0 { + errc := fsop.Utimens(path, nil) + return C.int(errc) + } else { + tmsp := [2]Timespec{} + tmsa := (*[2]C.fuse_timespec_t)(unsafe.Pointer(tmsp0)) + copyFusetimespecFromCtimespec(&tmsp[0], &tmsa[0]) + copyFusetimespecFromCtimespec(&tmsp[1], &tmsa[1]) + errc := fsop.Utimens(path, tmsp[:]) + return C.int(errc) + } +} + +// NewFileSystemHost creates a file system host. +func NewFileSystemHost(fsop FileSystemInterface) *FileSystemHost { + host := &FileSystemHost{} + host.fsop = fsop + return host +} + +// SetCapCaseInsensitive informs the host that the hosted file system is case insensitive +// [OSX and Windows only]. +func (host *FileSystemHost) SetCapCaseInsensitive(value bool) { + host.capCaseInsensitive = value +} + +// SetCapReaddirPlus informs the host that the hosted file system has the readdir-plus +// capability [Windows only]. A file system that has the readdir-plus capability can send +// full stat information during Readdir, thus avoiding extraneous Getattr calls. +func (host *FileSystemHost) SetCapReaddirPlus(value bool) { + host.capReaddirPlus = value +} + +// Mount mounts a file system on the given mountpoint with the mount options in opts. +// +// Many of the mount options in opts are specific to the underlying FUSE implementation. +// Some of the common options include: +// +// -h --help print help +// -V --version print FUSE version +// -d -o debug enable FUSE debug output +// -s disable multi-threaded operation +// +// Please refer to the individual FUSE implementation documentation for additional options. +// +// It is allowed for the mountpoint to be the empty string ("") in which case opts is assumed +// to contain the mountpoint. It is also allowed for opts to be nil, although in this case the +// mountpoint must be non-empty. +// +// The file system is considered mounted only after its Init() method has been called +// and before its Destroy() method has been called. +func (host *FileSystemHost) Mount(mountpoint string, opts []string) bool { + if 0 == C.hostInitializeFuse() { + panic("cgofuse: cannot find winfsp") + } + + /* + * Command line handling + * + * We must prepare a command line to send to FUSE. This command line will look like this: + * + * execname [mountpoint] "-f" [opts...] NULL + * + * We add the "-f" option because Go cannot handle daemonization (at least on OSX). + */ + exec := "" + if 0 < len(os.Args) { + exec = os.Args[0] + } + argc := len(opts) + 2 + if "" != mountpoint { + argc++ + } + argv := make([]*C.char, argc+1) + argv[0] = C.CString(exec) + defer C.free(unsafe.Pointer(argv[0])) + opti := 1 + if "" != mountpoint { + argv[1] = C.CString(mountpoint) + defer C.free(unsafe.Pointer(argv[1])) + opti++ + } + argv[opti] = C.CString("-f") + defer C.free(unsafe.Pointer(argv[opti])) + opti++ + for i := 0; len(opts) > i; i++ { + argv[i+opti] = C.CString(opts[i]) + defer C.free(unsafe.Pointer(argv[i+opti])) + } + + /* + * Mountpoint extraction + * + * We need to determine the mountpoint that FUSE is going (to try) to use, so that we + * can unmount later. + */ + host.mntp = C.hostMountpoint(C.int(argc), &argv[0]) + defer func() { + C.free(unsafe.Pointer(host.mntp)) + host.mntp = nil + }() + + /* + * Handle zombie mounts + * + * FUSE on UNIX does not automatically unmount the file system, leaving behind "zombie" + * mounts. So set things up to always unmount the file system (unless forcibly terminated). + * This has the added benefit that the file system Destroy() always gets called. + * + * On Windows (WinFsp) this is handled by the FUSE layer and we do not have to do anything. + */ + if "windows" != runtime.GOOS { + done := make(chan bool) + defer func() { + <-done + }() + host.sigc = make(chan os.Signal, 1) + defer close(host.sigc) + go func() { + _, ok := <-host.sigc + if ok { + host.Unmount() + } + close(done) + }() + } + + /* + * Tell FUSE to do its job! + */ + hndl := hostHandleNew(host) + defer hostHandleDel(hndl) + return 0 != C.hostMount(C.int(argc), &argv[0], hndl) +} + +// Unmount unmounts a mounted file system. +// Unmount may be called at any time after the Init() method has been called +// and before the Destroy() method has been called. +func (host *FileSystemHost) Unmount() bool { + if nil == host.fuse { + return false + } + return 0 != C.hostUnmount(host.fuse, host.mntp) +} + +// Getcontext gets information related to a file system operation. +func Getcontext() (uid uint32, gid uint32, pid int) { + uid = uint32(C.fuse_get_context().uid) + gid = uint32(C.fuse_get_context().gid) + pid = int(C.fuse_get_context().pid) + return +} diff --git a/vendor/github.com/billziss-gh/cgofuse/fuse/host_test.go b/vendor/github.com/billziss-gh/cgofuse/fuse/host_test.go new file mode 100644 index 000000000..552efbc51 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/fuse/host_test.go @@ -0,0 +1,109 @@ +/* + * host_test.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package fuse + +import ( + "io/ioutil" + "os" + "path/filepath" + "runtime" + "testing" + "time" +) + +type testfs struct { + FileSystemBase + init, dstr int +} + +func (self *testfs) Init() { + self.init++ +} + +func (self *testfs) Destroy() { + self.dstr++ +} + +func (self *testfs) Getattr(path string, stat *Stat_t, fh uint64) (errc int) { + switch path { + case "/": + stat.Mode = S_IFDIR | 0555 + return 0 + default: + return -ENOENT + } +} + +func (self *testfs) Readdir(path string, + fill func(name string, stat *Stat_t, ofst int64) bool, + ofst int64, + fh uint64) (errc int) { + fill(".", nil, 0) + fill("..", nil, 0) + return 0 +} + +func testHost(t *testing.T, unmount bool) { + path, err := ioutil.TempDir("", "test") + if nil != err { + panic(err) + } + defer os.Remove(path) + mntp := filepath.Join(path, "m") + if "windows" != runtime.GOOS { + err = os.Mkdir(mntp, os.FileMode(0755)) + if nil != err { + panic(err) + } + defer os.Remove(mntp) + } + done := make(chan bool) + tmch := time.After(3 * time.Second) + tstf := &testfs{} + host := NewFileSystemHost(tstf) + mres := false + ures := false + go func() { + mres = host.Mount(mntp, nil) + done <- true + }() + <-tmch + if unmount { + ures = host.Unmount() + } else { + ures = sendInterrupt() + } + <-done + if !mres { + t.Error("Mount failed") + } + if !ures { + t.Error("Unmount failed") + } + if 1 != tstf.init { + t.Errorf("Init() called %v times; expected 1", tstf.init) + } + if 1 != tstf.dstr { + t.Errorf("Destroy() called %v times; expected 1", tstf.dstr) + } +} + +func TestUnmount(t *testing.T) { + testHost(t, true) +} + +func TestSignal(t *testing.T) { + if "windows" != runtime.GOOS { + testHost(t, false) + } +} diff --git a/vendor/github.com/billziss-gh/cgofuse/fuse/host_unix_test.go b/vendor/github.com/billziss-gh/cgofuse/fuse/host_unix_test.go new file mode 100644 index 000000000..f8c5f3632 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/fuse/host_unix_test.go @@ -0,0 +1,23 @@ +// +build darwin linux + +/* + * host_unix_test.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package fuse + +import ( + "syscall" +) + +func sendInterrupt() bool { + return nil == syscall.Kill(syscall.Getpid(), syscall.SIGINT) +} diff --git a/vendor/github.com/billziss-gh/cgofuse/fuse/host_windows_test.go b/vendor/github.com/billziss-gh/cgofuse/fuse/host_windows_test.go new file mode 100644 index 000000000..a65215bb9 --- /dev/null +++ b/vendor/github.com/billziss-gh/cgofuse/fuse/host_windows_test.go @@ -0,0 +1,27 @@ +// +build windows + +/* + * host_windows_test.go + * + * Copyright 2017 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package fuse + +import ( + "os" + "syscall" +) + +func sendInterrupt() bool { + dll := syscall.MustLoadDLL("kernel32") + prc := dll.MustFindProc("GenerateConsoleCtrlEvent") + r, _, _ := prc.Call(syscall.CTRL_BREAK_EVENT, uintptr(os.Getpid())) + return 0 != r +}