cmount: Vendor github.com/billziss-gh/cgofuse
This commit is contained in:
parent
b553c23d5b
commit
ea0bc278ba
17 changed files with 3333 additions and 1 deletions
8
Gopkg.lock
generated
8
Gopkg.lock
generated
|
@ -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"]
|
||||
|
|
29
vendor/github.com/billziss-gh/cgofuse/.appveyor.yml
generated
vendored
Normal file
29
vendor/github.com/billziss-gh/cgofuse/.appveyor.yml
generated
vendored
Normal file
|
@ -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'
|
47
vendor/github.com/billziss-gh/cgofuse/.travis.yml
generated
vendored
Normal file
47
vendor/github.com/billziss-gh/cgofuse/.travis.yml
generated
vendored
Normal file
|
@ -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
|
6
vendor/github.com/billziss-gh/cgofuse/Changelog.md
generated
vendored
Normal file
6
vendor/github.com/billziss-gh/cgofuse/Changelog.md
generated
vendored
Normal file
|
@ -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`).
|
21
vendor/github.com/billziss-gh/cgofuse/License.txt
generated
vendored
Normal file
21
vendor/github.com/billziss-gh/cgofuse/License.txt
generated
vendored
Normal file
|
@ -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.
|
63
vendor/github.com/billziss-gh/cgofuse/README.md
generated
vendored
Normal file
63
vendor/github.com/billziss-gh/cgofuse/README.md
generated
vendored
Normal file
|
@ -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 \<billziss at navimatics.com>
|
||||
- Nick Craig-Wood \<nick at craig-wood.com>
|
79
vendor/github.com/billziss-gh/cgofuse/examples/hellofs/hellofs.go
generated
vendored
Normal file
79
vendor/github.com/billziss-gh/cgofuse/examples/hellofs/hellofs.go
generated
vendored
Normal file
|
@ -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:])
|
||||
}
|
545
vendor/github.com/billziss-gh/cgofuse/examples/memfs/memfs.go
generated
vendored
Normal file
545
vendor/github.com/billziss-gh/cgofuse/examples/memfs/memfs.go
generated
vendored
Normal file
|
@ -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:])
|
||||
}
|
268
vendor/github.com/billziss-gh/cgofuse/examples/passthrough/passthrough.go
generated
vendored
Normal file
268
vendor/github.com/billziss-gh/cgofuse/examples/passthrough/passthrough.go
generated
vendored
Normal file
|
@ -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:])
|
||||
}
|
68
vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_darwin.go
generated
vendored
Normal file
68
vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_darwin.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
67
vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_linux.go
generated
vendored
Normal file
67
vendor/github.com/billziss-gh/cgofuse/examples/passthrough/port_linux.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
115
vendor/github.com/billziss-gh/cgofuse/examples/shared/trace.go
generated
vendored
Normal file
115
vendor/github.com/billziss-gh/cgofuse/examples/shared/trace.go
generated
vendored
Normal file
|
@ -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 := "<UNKNOWN>"
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
730
vendor/github.com/billziss-gh/cgofuse/fuse/fsop.go
generated
vendored
Normal file
730
vendor/github.com/billziss-gh/cgofuse/fuse/fsop.go
generated
vendored
Normal file
|
@ -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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#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 <fcntl.h>
|
||||
#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 <sys/xattr.h>
|
||||
#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)
|
1129
vendor/github.com/billziss-gh/cgofuse/fuse/host.go
generated
vendored
Normal file
1129
vendor/github.com/billziss-gh/cgofuse/fuse/host.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
109
vendor/github.com/billziss-gh/cgofuse/fuse/host_test.go
generated
vendored
Normal file
109
vendor/github.com/billziss-gh/cgofuse/fuse/host_test.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
23
vendor/github.com/billziss-gh/cgofuse/fuse/host_unix_test.go
generated
vendored
Normal file
23
vendor/github.com/billziss-gh/cgofuse/fuse/host_unix_test.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
27
vendor/github.com/billziss-gh/cgofuse/fuse/host_windows_test.go
generated
vendored
Normal file
27
vendor/github.com/billziss-gh/cgofuse/fuse/host_windows_test.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue