vendor: add github.com/hanwen/go-fuse/v2@master for mount2

This commit is contained in:
Nick Craig-Wood 2019-11-04 17:12:50 +00:00
parent dc31212c3d
commit c38d7be373
71 changed files with 9337 additions and 0 deletions

1
go.mod
View File

@ -20,6 +20,7 @@ require (
github.com/google/go-cmp v0.3.1 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect
github.com/hanwen/go-fuse/v2 v2.0.3-0.20191108143333-152e6ac32d54
github.com/jlaffaye/ftp v0.0.0-20191025175106-a59fe673c9b2
github.com/jzelinskie/whirlpool v0.0.0-20170603002051-c19460b8caa6
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect

6
go.sum
View File

@ -129,6 +129,10 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk=
github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc=
github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok=
github.com/hanwen/go-fuse/v2 v2.0.3-0.20191108143333-152e6ac32d54 h1:0JL4/kY3QKTRevfl0IbEncTzA+jczGba+swfDBBluuU=
github.com/hanwen/go-fuse/v2 v2.0.3-0.20191108143333-152e6ac32d54/go.mod h1:HH3ygZOoyRbP9y2q7y3+JM6hPL+Epe29IbWaS0UA81o=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -167,6 +171,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=

20
vendor/github.com/hanwen/go-fuse/v2/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,20 @@
Adam H. Leventhal <adam.leventhal@gmail.com>
Daniel Martí <mvdan@mvdan.cc>
Fazlul Shahriar <fshahriar@gmail.com>
Frederick Akalin <akalin@gmail.com>
Google Inc.
Haitao Li <lihaitao@gmail.com>
Jakob Unterwurzacher <jakobunt@gmail.com>
James D. Nurmi <james@abneptis.com>
Jeff <leterip@me.com>
Kaoet Ibe <kaoet.ibe@outlook.com>
Kirill Smelkov <kirr@nexedi.com>
Logan Hanks <logan@bitcasa.com>
Maria Shaldibina <mshaldibina@pivotal.io>
Nick Cooper <gh@smoogle.org>
Patrick Crosby <pcrosby@gmail.com>
Paul Jolly <paul@myitcv.org.uk>
Paul Warren <paul.warren@emc.com>
Shayan Pooya <shayan@arista.com>
Valient Gough <vgough@pobox.com>
Yongwoo Park <nnnlife@gmail.com>

30
vendor/github.com/hanwen/go-fuse/v2/LICENSE generated vendored Normal file
View File

@ -0,0 +1,30 @@
// New BSD License
//
// Copyright (c) 2010 the Go-FUSE Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Ivan Krasin nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

60
vendor/github.com/hanwen/go-fuse/v2/fs/README.md generated vendored Normal file
View File

@ -0,0 +1,60 @@
Objective
=========
A high-performance FUSE API that minimizes pitfalls with writing
correct filesystems.
Decisions
=========
* Nodes contain references to their children. This is useful
because most filesystems will need to construct tree-like
structures.
* Nodes contain references to their parents. As a result, we can
derive the path for each Inode, and there is no need for a
separate PathFS.
* Nodes can be "persistent", meaning their lifetime is not under
control of the kernel. This is useful for constructing FS trees
in advance, rather than driven by LOOKUP.
* The NodeID for FS tree node must be defined on creation and are
immutable. By contrast, reusing NodeIds (eg. rsc/bazil FUSE, as
well as old go-fuse/fuse/nodefs) needs extra synchronization to
avoid races with notify and FORGET, and makes handling the inode
Generation more complicated.
* The mode of an Inode is defined on creation. Files cannot change
type during their lifetime. This also prevents the common error
of forgetting to return the filetype in Lookup/GetAttr.
* The NodeID (used for communicating with kernel) is equal to
Attr.Ino (value shown in Stat and Lstat return values.).
* No global treelock, to ensure scalability.
* Support for hard links. libfuse doesn't support this in the
high-level API. Extra care for race conditions is needed when
looking up the same file through different paths.
* do not issue Notify{Entry,Delete} as part of
AddChild/RmChild/MvChild: because NodeIDs are unique and
immutable, there is no confusion about which nodes are
invalidated, and the notification doesn't have to happen under
lock.
* Directory reading uses the DirStream. Semantics for rewinding
directory reads, and adding files after opening (but before
reading) are handled automatically. No support for directory
seeks.
* Method names are based on syscall names. Where there is no
syscall (eg. "open directory"), we bias towards writing
everything together (Opendir)
To do/To decide
=========
* Symlink []byte vs string.

602
vendor/github.com/hanwen/go-fuse/v2/fs/api.go generated vendored Normal file
View File

@ -0,0 +1,602 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package fs provides infrastructure to build tree-organized filesystems.
//
// Structure of a file system implementation
//
// To create a file system, you should first define types for the
// nodes of the file system tree.
//
// struct myNode {
// fs.Inode
// }
//
// // Node types must be InodeEmbedders
// var _ = (fs.InodeEmbedder)((*myNode)(nil))
//
// // Node types should implement some file system operations, eg. Lookup
// var _ = (fs.NodeLookuper)((*myNode)(nil))
//
// func (n *myNode) Lookup(ctx context.Context, name string, ... ) (*Inode, syscall.Errno) {
// ops := myNode{}
// return n.NewInode(ctx, &ops, fs.StableAttr{Mode: syscall.S_IFDIR}), 0
// }
//
// The method names are inspired on the system call names, so we have
// Listxattr rather than ListXAttr.
//
// the file system is mounted by calling mount on the root of the tree,
//
// server, err := fs.Mount("/tmp/mnt", &myNode{}, &fs.Options{})
// ..
// // start serving the file system
// server.Wait()
//
// Error handling
//
// All error reporting must use the syscall.Errno type. This is an
// integer with predefined error codes, where the value 0 (`OK`)
// should be used to indicate success.
//
// File system concepts
//
// The FUSE API is very similar to Linux' internal VFS API for
// defining file systems in the kernel. It is therefore useful to
// understand some terminology.
//
// File content: the raw bytes that we store inside regular files.
//
// Path: a /-separated string path that describes location of a node
// in the file system tree. For example
//
// dir1/file
//
// describes path root → dir1 → file.
//
// There can be several paths leading from tree root to a particular node,
// known as hard-linking, for example
//
// root
// / \
// dir1 dir2
// \ /
// file
//
// Inode: ("index node") points to the file content, and stores
// metadata (size, timestamps) about a file or directory. Each
// inode has a type (directory, symlink, regular file, etc.) and
// an identity (a 64-bit number, unique to the file
// system). Directories can have children.
//
// The inode in the kernel is represented in Go-FUSE as the Inode
// type.
//
// While common OS APIs are phrased in terms of paths (strings), the
// precise semantics of a file system are better described in terms of
// Inodes. This allows us to specify what happens in corner cases,
// such as writing data to deleted files.
//
// File descriptor: a handle returned to opening a file. File
// descriptors always refer to a single inode.
//
// Dirent: a dirent maps (parent inode number, name string) tuple to
// child inode, thus representing a parent/child relation (or the
// absense thereof). Dirents do not have an equivalent type inside
// Go-FUSE, but the result of Lookup operation essentially is a
// dirent, which the kernel puts in a cache.
//
//
// Kernel caching
//
// The kernel caches several pieces of information from the FUSE process:
//
// 1. File contents: enabled with the fuse.FOPEN_KEEP_CACHE return flag
// in Open, manipulated with ReadCache and WriteCache, and invalidated
// with Inode.NotifyContent
//
// 2. File Attributes (size, mtime, etc.): controlled with the
// attribute timeout fields in fuse.AttrOut and fuse.EntryOut, which
// get be populated from Getattr and Lookup
//
// 3. Directory entries (parent/child relations in the FS tree):
// controlled with the timeout fields in fuse.EntryOut, and
// invalidated with Inode.NotifyEntry and Inode.NotifyDelete.
//
// Without Directory Entry timeouts, every operation on file "a/b/c"
// must first do lookups for "a", "a/b" and "a/b/c", which is
// expensive because of context switches between the kernel and the
// FUSE process.
//
// Unsuccessful entry lookups can also be cached by setting an entry
// timeout when Lookup returns ENOENT.
//
// The libfuse C library specifies 1 second timeouts for both
// attribute and directory entries, but no timeout for negative
// entries. by default. This can be achieve in go-fuse by setting
// options on mount, eg.
//
// sec := time.Second
// opts := fs.Options{
// EntryTimeout: &sec,
// AttrTimeout: &sec,
// }
//
// Locking
//
// Locks for networked filesystems are supported through the suite of
// Getlk, Setlk and Setlkw methods. They alllow locks on regions of
// regular files.
//
// Parallelism
//
// The VFS layer in the kernel is optimized to be highly parallel, and
// this parallelism also affects FUSE file systems: many FUSE
// operations can run in parallel, and this invites race
// conditions. It is strongly recommended to test your FUSE file
// system issuing file operations in parallel, and using the race
// detector to weed out data races.
//
// Dynamically discovered file systems
//
// File system data usually cannot fit all in RAM, so the kernel must
// discover the file system dynamically: as you are entering and list
// directory contents, the kernel asks the FUSE server about the files
// and directories you are busy reading/writing, and forgets parts of
// your file system when it is low on memory.
//
// The two important operations for dynamic file systems are:
// 1. Lookup, part of the NodeLookuper interface for discovering
// individual children of directories, and 2. Readdir, part of the
// NodeReaddirer interface for listing the contents of a directory.
//
// Static in-memory file systems
//
// For small, read-only file systems, getting the locking mechanics of
// Lookup correct is tedious, so Go-FUSE provides a feature to
// simplify building such file systems.
//
// Instead of discovering the FS tree on the fly, you can construct
// the entire tree from an OnAdd method. Then, that in-memory tree
// structure becomes the source of truth. This means you Go-FUSE must
// remember Inodes even if the kernel is no longer interested in
// them. This is done by instantiating "persistent" inodes from the
// OnAdd method of the root node. See the ZipFS example for a
// runnable example of how to do this.
package fs
import (
"context"
"syscall"
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
// InodeEmbedder is an interface for structs that embed Inode.
//
// InodeEmbedder objects usually should implement some of the NodeXxxx
// interfaces, to provide user-defined file system behaviors.
//
// In general, if an InodeEmbedder does not implement specific
// filesystem methods, the filesystem will react as if it is a
// read-only filesystem with a predefined tree structure.
type InodeEmbedder interface {
// populateInode and inode are used internally to link Inode
// to a Node.
//
// See Inode() for the public API to retrieve an inode from Node.
embed() *Inode
// EmbeddedInode returns a pointer to the embedded inode.
EmbeddedInode() *Inode
}
// Statfs implements statistics for the filesystem that holds this
// Inode. If not defined, the `out` argument will zeroed with an OK
// result. This is because OSX filesystems must Statfs, or the mount
// will not work.
type NodeStatfser interface {
Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno
}
// Access should return if the caller can access the file with the
// given mode. This is used for two purposes: to determine if a user
// may enter a directory, and to answer to implement the access system
// call. In the latter case, the context has data about the real
// UID. For example, a root-SUID binary called by user susan gets the
// UID and GID for susan here.
//
// If not defined, a default implementation will check traditional
// unix permissions of the Getattr result agains the caller. If so, it
// is necessary to either return permissions from GetAttr/Lookup or
// set Options.DefaultPermissions in order to allow chdir into the
// FUSE mount.
type NodeAccesser interface {
Access(ctx context.Context, mask uint32) syscall.Errno
}
// GetAttr reads attributes for an Inode. The library will ensure that
// Mode and Ino are set correctly. For files that are not opened with
// FOPEN_DIRECTIO, Size should be set so it can be read correctly. If
// returning zeroed permissions, the default behavior is to change the
// mode of 0755 (directory) or 0644 (files). This can be switched off
// with the Options.NullPermissions setting. If blksize is unset, 4096
// is assumed, and the 'blocks' field is set accordingly.
type NodeGetattrer interface {
Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno
}
// SetAttr sets attributes for an Inode.
type NodeSetattrer interface {
Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
}
// OnAdd is called when this InodeEmbedder is initialized.
type NodeOnAdder interface {
OnAdd(ctx context.Context)
}
// Getxattr should read data for the given attribute into
// `dest` and return the number of bytes. If `dest` is too
// small, it should return ERANGE and the size of the attribute.
// If not defined, Getxattr will return ENOATTR.
type NodeGetxattrer interface {
Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno)
}
// Setxattr should store data for the given attribute. See
// setxattr(2) for information about flags.
// If not defined, Setxattr will return ENOATTR.
type NodeSetxattrer interface {
Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno
}
// Removexattr should delete the given attribute.
// If not defined, Removexattr will return ENOATTR.
type NodeRemovexattrer interface {
Removexattr(ctx context.Context, attr string) syscall.Errno
}
// Listxattr should read all attributes (null terminated) into
// `dest`. If the `dest` buffer is too small, it should return ERANGE
// and the correct size. If not defined, return an empty list and
// success.
type NodeListxattrer interface {
Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno)
}
// Readlink reads the content of a symlink.
type NodeReadlinker interface {
Readlink(ctx context.Context) ([]byte, syscall.Errno)
}
// Open opens an Inode (of regular file type) for reading. It
// is optional but recommended to return a FileHandle.
type NodeOpener interface {
Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno)
}
// Reads data from a file. The data should be returned as
// ReadResult, which may be constructed from the incoming
// `dest` buffer. If the file was opened without FileHandle,
// the FileHandle argument here is nil. The default
// implementation forwards to the FileHandle.
type NodeReader interface {
Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
}
// Writes the data into the file handle at given offset. After
// returning, the data will be reused and may not referenced.
// The default implementation forwards to the FileHandle.
type NodeWriter interface {
Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, errno syscall.Errno)
}
// Fsync is a signal to ensure writes to the Inode are flushed
// to stable storage.
type NodeFsyncer interface {
Fsync(ctx context.Context, f FileHandle, flags uint32) syscall.Errno
}
// Flush is called for the close(2) call on a file descriptor. In case
// of a descriptor that was duplicated using dup(2), it may be called
// more than once for the same FileHandle. The default implementation
// forwards to the FileHandle, or if the handle does not support
// FileFlusher, returns OK.
type NodeFlusher interface {
Flush(ctx context.Context, f FileHandle) syscall.Errno
}
// This is called to before a FileHandle is forgotten. The
// kernel ignores the return value of this method,
// so any cleanup that requires specific synchronization or
// could fail with I/O errors should happen in Flush instead.
// The default implementation forwards to the FileHandle.
type NodeReleaser interface {
Release(ctx context.Context, f FileHandle) syscall.Errno
}
// Allocate preallocates space for future writes, so they will
// never encounter ESPACE.
type NodeAllocater interface {
Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) syscall.Errno
}
// CopyFileRange copies data between sections of two files,
// without the data having to pass through the calling process.
type NodeCopyFileRanger interface {
CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, syscall.Errno)
}
// Lseek is used to implement holes: it should return the
// first offset beyond `off` where there is data (SEEK_DATA)
// or where there is a hole (SEEK_HOLE).
type NodeLseeker interface {
Lseek(ctx context.Context, f FileHandle, Off uint64, whence uint32) (uint64, syscall.Errno)
}
// Getlk returns locks that would conflict with the given input
// lock. If no locks conflict, the output has type L_UNLCK. See
// fcntl(2) for more information.
// If not defined, returns ENOTSUP
type NodeGetlker interface {
Getlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno
}
// Setlk obtains a lock on a file, or fail if the lock could not
// obtained. See fcntl(2) for more information. If not defined,
// returns ENOTSUP
type NodeSetlker interface {
Setlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}
// Setlkw obtains a lock on a file, waiting if necessary. See fcntl(2)
// for more information. If not defined, returns ENOTSUP
type NodeSetlkwer interface {
Setlkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}
// DirStream lists directory entries.
type DirStream interface {
// HasNext indicates if there are further entries. HasNext
// might be called on already closed streams.
HasNext() bool
// Next retrieves the next entry. It is only called if HasNext
// has previously returned true. The Errno return may be used to
// indicate I/O errors
Next() (fuse.DirEntry, syscall.Errno)
// Close releases resources related to this directory
// stream.
Close()
}
// Lookup should find a direct child of a directory by the child's name. If
// the entry does not exist, it should return ENOENT and optionally
// set a NegativeTimeout in `out`. If it does exist, it should return
// attribute data in `out` and return the Inode for the child. A new
// inode can be created using `Inode.NewInode`. The new Inode will be
// added to the FS tree automatically if the return status is OK.
//
// If a directory does not implement NodeLookuper, the library looks
// for an existing child with the given name.
//
// The input to a Lookup is {parent directory, name string}.
//
// Lookup, if successful, must return an *Inode. Once the Inode is
// returned to the kernel, the kernel can issue further operations,
// such as Open or Getxattr on that node.
//
// A successful Lookup also returns an EntryOut. Among others, this
// contains file attributes (mode, size, mtime, etc.).
//
// FUSE supports other operations that modify the namespace. For
// example, the Symlink, Create, Mknod, Link methods all create new
// children in directories. Hence, they also return *Inode and must
// populate their fuse.EntryOut arguments.
//
type NodeLookuper interface {
Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno)
}
// OpenDir opens a directory Inode for reading its
// contents. The actual reading is driven from ReadDir, so
// this method is just for performing sanity/permission
// checks. The default is to return success.
type NodeOpendirer interface {
Opendir(ctx context.Context) syscall.Errno
}
// ReadDir opens a stream of directory entries.
//
// Readdir essentiallly returns a list of strings, and it is allowed
// for Readdir to return different results from Lookup. For example,
// you can return nothing for Readdir ("ls my-fuse-mount" is empty),
// while still implementing Lookup ("ls my-fuse-mount/a-specific-file"
// shows a single file).
//
// If a directory does not implement NodeReaddirer, a list of
// currently known children from the tree is returned. This means that
// static in-memory file systems need not implement NodeReaddirer.
type NodeReaddirer interface {
Readdir(ctx context.Context) (DirStream, syscall.Errno)
}
// Mkdir is similar to Lookup, but must create a directory entry and Inode.
// Default is to return EROFS.
type NodeMkdirer interface {
Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno)
}
// Mknod is similar to Lookup, but must create a device entry and Inode.
// Default is to return EROFS.
type NodeMknoder interface {
Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno)
}
// Link is similar to Lookup, but must create a new link to an existing Inode.
// Default is to return EROFS.
type NodeLinker interface {
Link(ctx context.Context, target InodeEmbedder, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno)
}
// Symlink is similar to Lookup, but must create a new symbolic link.
// Default is to return EROFS.
type NodeSymlinker interface {
Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno)
}
// Create is similar to Lookup, but should create a new
// child. It typically also returns a FileHandle as a
// reference for future reads/writes.
// Default is to return EROFS.
type NodeCreater interface {
Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (node *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno)
}
// Unlink should remove a child from this directory. If the
// return status is OK, the Inode is removed as child in the
// FS tree automatically. Default is to return EROFS.
type NodeUnlinker interface {
Unlink(ctx context.Context, name string) syscall.Errno
}
// Rmdir is like Unlink but for directories.
// Default is to return EROFS.
type NodeRmdirer interface {
Rmdir(ctx context.Context, name string) syscall.Errno
}
// Rename should move a child from one directory to a different
// one. The change is effected in the FS tree if the return status is
// OK. Default is to return EROFS.
type NodeRenamer interface {
Rename(ctx context.Context, name string, newParent InodeEmbedder, newName string, flags uint32) syscall.Errno
}
// FileHandle is a resource identifier for opened files. Usually, a
// FileHandle should implement some of the FileXxxx interfaces.
//
// All of the FileXxxx operations can also be implemented at the
// InodeEmbedder level, for example, one can implement NodeReader
// instead of FileReader.
//
// FileHandles are useful in two cases: First, if the underlying
// storage systems needs a handle for reading/writing. This is the
// case with Unix system calls, which need a file descriptor (See also
// the function `NewLoopbackFile`). Second, it is useful for
// implementing files whose contents are not tied to an inode. For
// example, a file like `/proc/interrupts` has no fixed content, but
// changes on each open call. This means that each file handle must
// have its own view of the content; this view can be tied to a
// FileHandle. Files that have such dynamic content should return the
// FOPEN_DIRECT_IO flag from their `Open` method. See directio_test.go
// for an example.
type FileHandle interface {
}
// See NodeReleaser.
type FileReleaser interface {
Release(ctx context.Context) syscall.Errno
}
// See NodeGetattrer.
type FileGetattrer interface {
Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno
}
// See NodeReader.
type FileReader interface {
Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
}
// See NodeWriter.
type FileWriter interface {
Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno)
}
// See NodeGetlker.
type FileGetlker interface {
Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno
}
// See NodeSetlker.
type FileSetlker interface {
Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}
// See NodeSetlkwer.
type FileSetlkwer interface {
Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}
// See NodeLseeker.
type FileLseeker interface {
Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno)
}
// See NodeFlusher.
type FileFlusher interface {
Flush(ctx context.Context) syscall.Errno
}
// See NodeFsync.
type FileFsyncer interface {
Fsync(ctx context.Context, flags uint32) syscall.Errno
}
// See NodeFsync.
type FileSetattrer interface {
Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
}
// See NodeAllocater.
type FileAllocater interface {
Allocate(ctx context.Context, off uint64, size uint64, mode uint32) syscall.Errno
}
// Options sets options for the entire filesystem
type Options struct {
// MountOptions contain the options for mounting the fuse server
fuse.MountOptions
// If set to nonnil, this defines the overall entry timeout
// for the file system. See fuse.EntryOut for more information.
EntryTimeout *time.Duration
// If set to nonnil, this defines the overall attribute
// timeout for the file system. See fuse.EntryOut for more
// information.
AttrTimeout *time.Duration
// If set to nonnil, this defines the overall entry timeout
// for failed lookups (fuse.ENOENT). See fuse.EntryOut for
// more information.
NegativeTimeout *time.Duration
// Automatic inode numbers are handed out sequentially
// starting from this number. If unset, use 2^63.
FirstAutomaticIno uint64
// OnAdd is an alternative way to specify the OnAdd
// functionality of the root node.
OnAdd func(ctx context.Context)
// NullPermissions if set, leaves null file permissions
// alone. Otherwise, they are set to 755 (dirs) or 644 (other
// files.), which is necessary for doing a chdir into the FUSE
// directories.
NullPermissions bool
// If nonzero, replace default (zero) UID with the given UID
UID uint32
// If nonzero, replace default (zero) GID with the given GID
GID uint32
}

972
vendor/github.com/hanwen/go-fuse/v2/fs/bridge.go generated vendored Normal file
View File

@ -0,0 +1,972 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"context"
"log"
"sync"
"syscall"
"time"
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/internal"
)
func errnoToStatus(errno syscall.Errno) fuse.Status {
return fuse.Status(errno)
}
type fileEntry struct {
file FileHandle
// index into Inode.openFiles
nodeIndex int
// Protects directory fields. Must be acquired before bridge.mu
mu sync.Mutex
// Directory
dirStream DirStream
hasOverflow bool
overflow fuse.DirEntry
wg sync.WaitGroup
}
type rawBridge struct {
options Options
root *Inode
server *fuse.Server
// mu protects the following data. Locks for inodes must be
// taken before rawBridge.mu
mu sync.Mutex
nodes map[uint64]*Inode
automaticIno uint64
files []*fileEntry
freeFiles []uint32
}
// newInode creates creates new inode pointing to ops.
func (b *rawBridge) newInodeUnlocked(ops InodeEmbedder, id StableAttr, persistent bool) *Inode {
b.mu.Lock()
defer b.mu.Unlock()
if id.Reserved() {
log.Panicf("using reserved ID %d for inode number", id.Ino)
}
// This ops already was populated. Just return it.
if ops.embed().bridge != nil {
return ops.embed()
}
if id.Ino == 0 {
for {
id.Ino = b.automaticIno
b.automaticIno++
_, ok := b.nodes[id.Ino]
if !ok {
break
}
}
}
// the same node can be looked up through 2 paths in parallel, eg.
//
// root
// / \
// dir1 dir2
// \ /
// file
//
// dir1.Lookup("file") and dir2.Lookup("file") are executed
// simultaneously. The matching StableAttrs ensure that we return the
// same node.
old := b.nodes[id.Ino]
if old != nil {
return old
}
id.Mode = id.Mode &^ 07777
if id.Mode == 0 {
id.Mode = fuse.S_IFREG
}
b.nodes[id.Ino] = ops.embed()
initInode(ops.embed(), ops, id, b, persistent)
return ops.embed()
}
func (b *rawBridge) newInode(ctx context.Context, ops InodeEmbedder, id StableAttr, persistent bool) *Inode {
ch := b.newInodeUnlocked(ops, id, persistent)
if ch != ops.embed() {
return ch
}
if oa, ok := ops.(NodeOnAdder); ok {
oa.OnAdd(ctx)
}
return ch
}
// addNewChild inserts the child into the tree. Returns file handle if file != nil.
func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, file FileHandle, fileFlags uint32, out *fuse.EntryOut) uint32 {
if name == "." || name == ".." {
log.Panicf("BUG: tried to add virtual entry %q to the actual tree", name)
}
lockNodes(parent, child)
parent.setEntry(name, child)
b.mu.Lock()
child.lookupCount++
var fh uint32
if file != nil {
fh = b.registerFile(child, file, fileFlags)
}
out.NodeId = child.stableAttr.Ino
out.Generation = child.stableAttr.Gen
out.Attr.Ino = child.stableAttr.Ino
b.mu.Unlock()
unlockNodes(parent, child)
return fh
}
func (b *rawBridge) setEntryOutTimeout(out *fuse.EntryOut) {
b.setAttr(&out.Attr)
if b.options.AttrTimeout != nil && out.AttrTimeout() == 0 {
out.SetAttrTimeout(*b.options.AttrTimeout)
}
if b.options.EntryTimeout != nil && out.EntryTimeout() == 0 {
out.SetEntryTimeout(*b.options.EntryTimeout)
}
}
func (b *rawBridge) setAttr(out *fuse.Attr) {
if !b.options.NullPermissions && out.Mode&07777 == 0 {
out.Mode |= 0644
if out.Mode&syscall.S_IFDIR != 0 {
out.Mode |= 0111
}
}
if b.options.UID != 0 && out.Uid == 0 {
out.Uid = b.options.UID
}
if b.options.GID != 0 && out.Gid == 0 {
out.Gid = b.options.GID
}
setBlocks(out)
}
func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
if b.options.AttrTimeout != nil && out.Timeout() == 0 {
out.SetTimeout(*b.options.AttrTimeout)
}
}
// NewNodeFS creates a node based filesystem based on the
// InodeEmbedder instance for the root of the tree.
func NewNodeFS(root InodeEmbedder, opts *Options) fuse.RawFileSystem {
bridge := &rawBridge{
automaticIno: opts.FirstAutomaticIno,
}
if bridge.automaticIno == 1 {
bridge.automaticIno++
}
if bridge.automaticIno == 0 {
bridge.automaticIno = 1 << 63
}
if opts != nil {
bridge.options = *opts
} else {
oneSec := time.Second
bridge.options.EntryTimeout = &oneSec
bridge.options.AttrTimeout = &oneSec
}
initInode(root.embed(), root,
StableAttr{
Ino: 1,
Mode: fuse.S_IFDIR,
},
bridge,
false,
)
bridge.root = root.embed()
bridge.root.lookupCount = 1
bridge.nodes = map[uint64]*Inode{
1: bridge.root,
}
// Fh 0 means no file handle.
bridge.files = []*fileEntry{{}}
if opts.OnAdd != nil {
opts.OnAdd(context.Background())
} else if oa, ok := root.(NodeOnAdder); ok {
oa.OnAdd(context.Background())
}
return bridge
}
func (b *rawBridge) String() string {
return "rawBridge"
}
func (b *rawBridge) inode(id uint64, fh uint64) (*Inode, *fileEntry) {
b.mu.Lock()
defer b.mu.Unlock()
n, f := b.nodes[id], b.files[fh]
if n == nil {
log.Panicf("unknown node %d", id)
}
return n, f
}
func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
ctx := &fuse.Context{Caller: header.Caller, Cancel: cancel}
child, errno := b.lookup(ctx, parent, name, out)
if errno != 0 {
if b.options.NegativeTimeout != nil && out.EntryTimeout() == 0 {
out.SetEntryTimeout(*b.options.NegativeTimeout)
}
return errnoToStatus(errno)
}
child.setEntryOut(out)
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
func (b *rawBridge) lookup(ctx *fuse.Context, parent *Inode, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
if lu, ok := parent.ops.(NodeLookuper); ok {
return lu.Lookup(ctx, name, out)
}
child := parent.GetChild(name)
if child == nil {
return nil, syscall.ENOENT
}
if ga, ok := child.ops.(NodeGetattrer); ok {
var a fuse.AttrOut
errno := ga.Getattr(ctx, nil, &a)
if errno == 0 {
out.Attr = a.Attr
}
}
return child, OK
}
func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
var errno syscall.Errno
if mops, ok := parent.ops.(NodeRmdirer); ok {
errno = mops.Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
}
if errno == 0 {
parent.RmChild(name)
}
return errnoToStatus(errno)
}
func (b *rawBridge) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
var errno syscall.Errno
if mops, ok := parent.ops.(NodeUnlinker); ok {
errno = mops.Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
}
if errno == 0 {
parent.RmChild(name)
}
return errnoToStatus(errno)
}
func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(input.NodeId, 0)
var child *Inode
var errno syscall.Errno
if mops, ok := parent.ops.(NodeMkdirer); ok {
child, errno = mops.Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out)
} else {
return fuse.ENOTSUP
}
if errno != 0 {
return errnoToStatus(errno)
}
if out.Attr.Mode&^07777 == 0 {
out.Attr.Mode |= fuse.S_IFDIR
}
if out.Attr.Mode&^07777 != fuse.S_IFDIR {
log.Panicf("Mkdir: mode must be S_IFDIR (%o), got %o", fuse.S_IFDIR, out.Attr.Mode)
}
child.setEntryOut(out)
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(input.NodeId, 0)
var child *Inode
var errno syscall.Errno
if mops, ok := parent.ops.(NodeMknoder); ok {
child, errno = mops.Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out)
}
if errno != 0 {
return errnoToStatus(errno)
}
child.setEntryOut(out)
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
func (b *rawBridge) Create(cancel <-chan struct{}, input *fuse.CreateIn, name string, out *fuse.CreateOut) fuse.Status {
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
parent, _ := b.inode(input.NodeId, 0)
var child *Inode
var errno syscall.Errno
var f FileHandle
var flags uint32
if mops, ok := parent.ops.(NodeCreater); ok {
child, f, flags, errno = mops.Create(ctx, name, input.Flags, input.Mode, &out.EntryOut)
} else {
return fuse.EROFS
}
if errno != 0 {
if b.options.NegativeTimeout != nil {
out.SetEntryTimeout(*b.options.NegativeTimeout)
}
return errnoToStatus(errno)
}
out.Fh = uint64(b.addNewChild(parent, name, child, f, input.Flags|syscall.O_CREAT, &out.EntryOut))
out.OpenFlags = flags
child.setEntryOut(&out.EntryOut)
b.setEntryOutTimeout(&out.EntryOut)
return fuse.OK
}
func (b *rawBridge) Forget(nodeid, nlookup uint64) {
n, _ := b.inode(nodeid, 0)
n.removeRef(nlookup, false)
}
func (b *rawBridge) SetDebug(debug bool) {}
func (b *rawBridge) GetAttr(cancel <-chan struct{}, input *fuse.GetAttrIn, out *fuse.AttrOut) fuse.Status {
n, fEntry := b.inode(input.NodeId, input.Fh())
f := fEntry.file
if f == nil {
// The linux kernel doesnt pass along the file
// descriptor, so we have to fake it here.
// See https://github.com/libfuse/libfuse/issues/62
b.mu.Lock()
for _, fh := range n.openFiles {
f = b.files[fh].file
b.files[fh].wg.Add(1)
defer b.files[fh].wg.Done()
break
}
b.mu.Unlock()
}
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
return errnoToStatus(b.getattr(ctx, n, f, out))
}
func (b *rawBridge) getattr(ctx context.Context, n *Inode, f FileHandle, out *fuse.AttrOut) syscall.Errno {
var errno syscall.Errno
var fg FileGetattrer
if f != nil {
fg, _ = f.(FileGetattrer)
}
if fops, ok := n.ops.(NodeGetattrer); ok {
errno = fops.Getattr(ctx, f, out)
} else if fg != nil {
errno = fg.Getattr(ctx, out)
} else {
// We set Mode below, which is the minimum for success
}
if errno == 0 {
out.Ino = n.stableAttr.Ino
out.Mode = (out.Attr.Mode & 07777) | n.stableAttr.Mode
b.setAttr(&out.Attr)
b.setAttrTimeout(out)
}
return errno
}
func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
ctx := &fuse.Context{Caller: in.Caller, Cancel: cancel}
fh, _ := in.GetFh()
n, fEntry := b.inode(in.NodeId, fh)
f := fEntry.file
var errno = syscall.ENOTSUP
if fops, ok := n.ops.(NodeSetattrer); ok {
errno = fops.Setattr(ctx, f, in, out)
} else if fops, ok := f.(FileSetattrer); ok {
errno = fops.Setattr(ctx, in, out)
}
out.Mode = n.stableAttr.Mode | (out.Mode & 07777)
return errnoToStatus(errno)
}
func (b *rawBridge) Rename(cancel <-chan struct{}, input *fuse.RenameIn, oldName string, newName string) fuse.Status {
p1, _ := b.inode(input.NodeId, 0)
p2, _ := b.inode(input.Newdir, 0)
if mops, ok := p1.ops.(NodeRenamer); ok {
errno := mops.Rename(&fuse.Context{Caller: input.Caller, Cancel: cancel}, oldName, p2.ops, newName, input.Flags)
if errno == 0 {
if input.Flags&RENAME_EXCHANGE != 0 {
p1.ExchangeChild(oldName, p2, newName)
} else {
if ok := p1.MvChild(oldName, p2, newName, true); !ok {
log.Println("MvChild failed")
}
}
}
return errnoToStatus(errno)
}
return fuse.ENOTSUP
}
func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(input.NodeId, 0)
target, _ := b.inode(input.Oldnodeid, 0)
if mops, ok := parent.ops.(NodeLinker); ok {
child, errno := mops.Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out)
if errno != 0 {
return errnoToStatus(errno)
}
child.setEntryOut(out)
b.addNewChild(parent, name, child, nil, 0, out)
b.setEntryOutTimeout(out)
return fuse.OK
}
return fuse.ENOTSUP
}
func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, target string, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(header.NodeId, 0)
if mops, ok := parent.ops.(NodeSymlinker); ok {
child, status := mops.Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out)
if status != 0 {
return errnoToStatus(status)
}
b.addNewChild(parent, name, child, nil, 0, out)
child.setEntryOut(out)
b.setEntryOutTimeout(out)
return fuse.OK
}
return fuse.ENOTSUP
}
func (b *rawBridge) Readlink(cancel <-chan struct{}, header *fuse.InHeader) (out []byte, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
if linker, ok := n.ops.(NodeReadlinker); ok {
result, errno := linker.Readlink(&fuse.Context{Caller: header.Caller, Cancel: cancel})
if errno != 0 {
return nil, errnoToStatus(errno)
}
return result, fuse.OK
}
return nil, fuse.ENOTSUP
}
func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
if a, ok := n.ops.(NodeAccesser); ok {
return errnoToStatus(a.Access(ctx, input.Mask))
}
// default: check attributes.
caller := input.Caller
var out fuse.AttrOut
if s := b.getattr(ctx, n, nil, &out); s != 0 {
return errnoToStatus(s)
}
if !internal.HasAccess(caller.Uid, caller.Gid, out.Uid, out.Gid, out.Mode, input.Mask) {
return fuse.EACCES
}
return fuse.OK
}
// Extended attributes.
func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, data []byte) (uint32, fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(NodeGetxattrer); ok {
nb, errno := xops.Getxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr, data)
return nb, errnoToStatus(errno)
}
return 0, fuse.ENOATTR
}
func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (sz uint32, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(NodeListxattrer); ok {
sz, errno := xops.Listxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, dest)
return sz, errnoToStatus(errno)
}
return 0, fuse.OK
}
func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
if xops, ok := n.ops.(NodeSetxattrer); ok {
return errnoToStatus(xops.Setxattr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, attr, data, input.Flags))
}
return fuse.ENOATTR
}
func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) fuse.Status {
n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(NodeRemovexattrer); ok {
return errnoToStatus(xops.Removexattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr))
}
return fuse.ENOATTR
}
func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
if op, ok := n.ops.(NodeOpener); ok {
f, flags, errno := op.Open(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Flags)
if errno != 0 {
return errnoToStatus(errno)
}
if f != nil {
b.mu.Lock()
defer b.mu.Unlock()
out.Fh = uint64(b.registerFile(n, f, input.Flags))
}
out.OpenFlags = flags
return fuse.OK
}
return fuse.ENOTSUP
}
// registerFile hands out a file handle. Must have bridge.mu
func (b *rawBridge) registerFile(n *Inode, f FileHandle, flags uint32) uint32 {
var fh uint32
if len(b.freeFiles) > 0 {
last := len(b.freeFiles) - 1
fh = b.freeFiles[last]
b.freeFiles = b.freeFiles[:last]
} else {
fh = uint32(len(b.files))
b.files = append(b.files, &fileEntry{})
}
fileEntry := b.files[fh]
fileEntry.nodeIndex = len(n.openFiles)
fileEntry.file = f
n.openFiles = append(n.openFiles, fh)
return fh
}
func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
if fops, ok := n.ops.(NodeReader); ok {
res, errno := fops.Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, buf, int64(input.Offset))
return res, errnoToStatus(errno)
}
if fr, ok := f.file.(FileReader); ok {
res, errno := fr.Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, buf, int64(input.Offset))
return res, errnoToStatus(errno)
}
return nil, fuse.ENOTSUP
}
func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.LkOut) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(NodeGetlker); ok {
return errnoToStatus(lops.Getlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk))
}
if gl, ok := f.file.(FileGetlker); ok {
return errnoToStatus(gl.Getlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Owner, &input.Lk, input.LkFlags, &out.Lk))
}
return fuse.ENOTSUP
}
func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(NodeSetlker); ok {
return errnoToStatus(lops.Setlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags))
}
if sl, ok := n.ops.(FileSetlker); ok {
return errnoToStatus(sl.Setlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Owner, &input.Lk, input.LkFlags))
}
return fuse.ENOTSUP
}
func (b *rawBridge) SetLkw(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(NodeSetlkwer); ok {
return errnoToStatus(lops.Setlkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags))
}
if sl, ok := n.ops.(FileSetlkwer); ok {
return errnoToStatus(sl.Setlkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Owner, &input.Lk, input.LkFlags))
}
return fuse.ENOTSUP
}
func (b *rawBridge) Release(cancel <-chan struct{}, input *fuse.ReleaseIn) {
n, f := b.releaseFileEntry(input.NodeId, input.Fh)
if f == nil {
return
}
f.wg.Wait()
if r, ok := n.ops.(NodeReleaser); ok {
r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file)
} else if r, ok := f.file.(FileReleaser); ok {
r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel})
}
b.mu.Lock()
defer b.mu.Unlock()
b.freeFiles = append(b.freeFiles, uint32(input.Fh))
}
func (b *rawBridge) ReleaseDir(input *fuse.ReleaseIn) {
_, f := b.releaseFileEntry(input.NodeId, input.Fh)
f.wg.Wait()
f.mu.Lock()
if f.dirStream != nil {
f.dirStream.Close()
f.dirStream = nil
}
f.mu.Unlock()
b.mu.Lock()
defer b.mu.Unlock()
b.freeFiles = append(b.freeFiles, uint32(input.Fh))
}
func (b *rawBridge) releaseFileEntry(nid uint64, fh uint64) (*Inode, *fileEntry) {
b.mu.Lock()
defer b.mu.Unlock()
n := b.nodes[nid]
var entry *fileEntry
if fh > 0 {
last := len(n.openFiles) - 1
entry = b.files[fh]
if last != entry.nodeIndex {
n.openFiles[entry.nodeIndex] = n.openFiles[last]
b.files[n.openFiles[entry.nodeIndex]].nodeIndex = entry.nodeIndex
}
n.openFiles = n.openFiles[:last]
}
return n, entry
}
func (b *rawBridge) Write(cancel <-chan struct{}, input *fuse.WriteIn, data []byte) (written uint32, status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh)
if wr, ok := n.ops.(NodeWriter); ok {
w, errno := wr.Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, data, int64(input.Offset))
return w, errnoToStatus(errno)
}
if fr, ok := f.file.(FileWriter); ok {
w, errno := fr.Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, data, int64(input.Offset))
return w, errnoToStatus(errno)
}
return 0, fuse.ENOTSUP
}
func (b *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if fl, ok := n.ops.(NodeFlusher); ok {
return errnoToStatus(fl.Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file))
}
if fl, ok := f.file.(FileFlusher); ok {
return errnoToStatus(fl.Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}))
}
return 0
}
func (b *rawBridge) Fsync(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if fs, ok := n.ops.(NodeFsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.FsyncFlags))
}
if fs, ok := f.file.(FileFsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.FsyncFlags))
}
return fuse.ENOTSUP
}
func (b *rawBridge) Fallocate(cancel <-chan struct{}, input *fuse.FallocateIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
if a, ok := n.ops.(NodeAllocater); ok {
return errnoToStatus(a.Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Offset, input.Length, input.Mode))
}
if a, ok := n.ops.(FileAllocater); ok {
return errnoToStatus(a.Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Offset, input.Length, input.Mode))
}
return fuse.ENOTSUP
}
func (b *rawBridge) OpenDir(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
if od, ok := n.ops.(NodeOpendirer); ok {
errno := od.Opendir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if errno != 0 {
return errnoToStatus(errno)
}
}
b.mu.Lock()
defer b.mu.Unlock()
out.Fh = uint64(b.registerFile(n, nil, 0))
return fuse.OK
}
// setStream sets the directory part of f. Must hold f.mu
func (b *rawBridge) setStream(cancel <-chan struct{}, input *fuse.ReadIn, inode *Inode, f *fileEntry) syscall.Errno {
if f.dirStream == nil || input.Offset == 0 {
if f.dirStream != nil {
f.dirStream.Close()
f.dirStream = nil
}
str, errno := b.getStream(&fuse.Context{Caller: input.Caller, Cancel: cancel}, inode)
if errno != 0 {
return errno
}
f.hasOverflow = false
f.dirStream = str
}
return 0
}
func (b *rawBridge) getStream(ctx context.Context, inode *Inode) (DirStream, syscall.Errno) {
if rd, ok := inode.ops.(NodeReaddirer); ok {
return rd.Readdir(ctx)
}
r := []fuse.DirEntry{}
for k, ch := range inode.Children() {
r = append(r, fuse.DirEntry{Mode: ch.Mode(),
Name: k,
Ino: ch.StableAttr().Ino})
}
return NewListDirStream(r), 0
}
func (b *rawBridge) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
f.mu.Lock()
defer f.mu.Unlock()
if errno := b.setStream(cancel, input, n, f); errno != 0 {
return errnoToStatus(errno)
}
if f.hasOverflow {
// always succeeds.
out.AddDirEntry(f.overflow)
f.hasOverflow = false
}
for f.dirStream.HasNext() {
e, errno := f.dirStream.Next()
if errno != 0 {
return errnoToStatus(errno)
}
if !out.AddDirEntry(e) {
f.overflow = e
f.hasOverflow = true
return errnoToStatus(errno)
}
}
return fuse.OK
}
func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh)
f.mu.Lock()
defer f.mu.Unlock()
if errno := b.setStream(cancel, input, n, f); errno != 0 {
return errnoToStatus(errno)
}
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
for f.dirStream.HasNext() || f.hasOverflow {
var e fuse.DirEntry
var errno syscall.Errno
if f.hasOverflow {
e = f.overflow
f.hasOverflow = false
} else {
e, errno = f.dirStream.Next()
}
if errno != 0 {
return errnoToStatus(errno)
}
entryOut := out.AddDirLookupEntry(e)
if entryOut == nil {
f.overflow = e
f.hasOverflow = true
return fuse.OK
}
// Virtual entries "." and ".." should be part of the
// directory listing, but not part of the filesystem tree.
// The values in EntryOut are ignored by Linux
// (see fuse_direntplus_link() in linux/fs/fuse/readdir.c), so leave
// them at zero-value.
if e.Name == "." || e.Name == ".." {
continue
}
child, errno := b.lookup(ctx, n, e.Name, entryOut)
if errno != 0 {
if b.options.NegativeTimeout != nil {
entryOut.SetEntryTimeout(*b.options.NegativeTimeout)
}
} else {
b.addNewChild(n, e.Name, child, nil, 0, entryOut)
child.setEntryOut(entryOut)
b.setEntryOutTimeout(entryOut)
if (e.Mode &^ 07777) != (child.stableAttr.Mode &^ 07777) {
// The file type has changed behind our back. Use the new value.
out.FixMode(child.stableAttr.Mode)
}
entryOut.Mode = child.stableAttr.Mode | (entryOut.Mode & 07777)
}
}
return fuse.OK
}
func (b *rawBridge) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status {
n, _ := b.inode(input.NodeId, input.Fh)
if fs, ok := n.ops.(NodeFsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, nil, input.FsyncFlags))
}
return fuse.ENOTSUP
}
func (b *rawBridge) StatFs(cancel <-chan struct{}, input *fuse.InHeader, out *fuse.StatfsOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0)
if sf, ok := n.ops.(NodeStatfser); ok {
return errnoToStatus(sf.Statfs(&fuse.Context{Caller: input.Caller, Cancel: cancel}, out))
}
// leave zeroed out
return fuse.OK
}
func (b *rawBridge) Init(s *fuse.Server) {
b.server = s
}
func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRangeIn) (size uint32, status fuse.Status) {
n1, f1 := b.inode(in.NodeId, in.FhIn)
cfr, ok := n1.ops.(NodeCopyFileRanger)
if !ok {
return 0, fuse.ENOTSUP
}
n2, f2 := b.inode(in.NodeIdOut, in.FhOut)
sz, errno := cfr.CopyFileRange(&fuse.Context{Caller: in.Caller, Cancel: cancel},
f1.file, in.OffIn, n2, f2.file, in.OffOut, in.Len, in.Flags)
return sz, errnoToStatus(errno)
}
func (b *rawBridge) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekOut) fuse.Status {
n, f := b.inode(in.NodeId, in.Fh)
ls, ok := n.ops.(NodeLseeker)
if ok {
off, errno := ls.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel},
f.file, in.Offset, in.Whence)
out.Offset = off
return errnoToStatus(errno)
}
if fs, ok := f.file.(FileLseeker); ok {
off, errno := fs.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel}, in.Offset, in.Whence)
out.Offset = off
return errnoToStatus(errno)
}
if in.Whence == _SEEK_DATA || in.Whence == _SEEK_HOLE {
out.Offset = in.Offset
return fuse.OK
}
return fuse.ENOTSUP
}

29
vendor/github.com/hanwen/go-fuse/v2/fs/constants.go generated vendored Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"syscall"
"github.com/hanwen/go-fuse/v2/fuse"
)
// OK is the Errno return value to indicate absense of errors.
var OK = syscall.Errno(0)
// ToErrno exhumes the syscall.Errno error from wrapped error values.
func ToErrno(err error) syscall.Errno {
s := fuse.ToStatus(err)
return syscall.Errno(s)
}
// RENAME_EXCHANGE is a flag argument for renameat2()
const RENAME_EXCHANGE = 0x2
// seek to the next data
const _SEEK_DATA = 3
// seek to the next hole
const _SEEK_HOLE = 4

View File

@ -0,0 +1,10 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import "syscall"
// ENOATTR indicates that an extended attribute was not present.
var ENOATTR = syscall.ENOATTR

View File

@ -0,0 +1,10 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import "syscall"
// ENOATTR indicates that an extended attribute was not present.
var ENOATTR = syscall.ENODATA

5
vendor/github.com/hanwen/go-fuse/v2/fs/default.go generated vendored Normal file
View File

@ -0,0 +1,5 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs

34
vendor/github.com/hanwen/go-fuse/v2/fs/dirstream.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"syscall"
"github.com/hanwen/go-fuse/v2/fuse"
)
type dirArray struct {
entries []fuse.DirEntry
}
func (a *dirArray) HasNext() bool {
return len(a.entries) > 0
}
func (a *dirArray) Next() (fuse.DirEntry, syscall.Errno) {
e := a.entries[0]
a.entries = a.entries[1:]
return e, 0
}
func (a *dirArray) Close() {
}
// NewListDirStream wraps a slice of DirEntry as a DirStream.
func NewListDirStream(list []fuse.DirEntry) DirStream {
return &dirArray{list}
}

View File

@ -0,0 +1,48 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"io"
"os"
"syscall"
"github.com/hanwen/go-fuse/v2/fuse"
)
func NewLoopbackDirStream(nm string) (DirStream, syscall.Errno) {
f, err := os.Open(nm)
if err != nil {
return nil, ToErrno(err)
}
defer f.Close()
var entries []fuse.DirEntry
for {
want := 100
infos, err := f.Readdir(want)
for _, info := range infos {
s := fuse.ToStatT(info)
if s == nil {
continue
}
entries = append(entries, fuse.DirEntry{
Name: info.Name(),
Mode: uint32(s.Mode),
Ino: s.Ino,
})
}
if len(infos) < want || err == io.EOF {
break
}
if err != nil {
return nil, ToErrno(err)
}
}
return &dirArray{entries}, OK
}

View File

@ -0,0 +1,93 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"sync"
"syscall"
"unsafe"
"github.com/hanwen/go-fuse/v2/fuse"
)
type loopbackDirStream struct {
buf []byte
todo []byte
// Protects fd so we can guard against double close
mu sync.Mutex
fd int
}
// NewLoopbackDirStream open a directory for reading as a DirStream
func NewLoopbackDirStream(name string) (DirStream, syscall.Errno) {
fd, err := syscall.Open(name, syscall.O_DIRECTORY, 0755)
if err != nil {
return nil, ToErrno(err)
}
ds := &loopbackDirStream{
buf: make([]byte, 4096),
fd: fd,
}
if err := ds.load(); err != 0 {
ds.Close()
return nil, err
}
return ds, OK
}
func (ds *loopbackDirStream) Close() {
ds.mu.Lock()
defer ds.mu.Unlock()
if ds.fd != -1 {
syscall.Close(ds.fd)
ds.fd = -1
}
}
func (ds *loopbackDirStream) HasNext() bool {
ds.mu.Lock()
defer ds.mu.Unlock()
return len(ds.todo) > 0
}
func (ds *loopbackDirStream) Next() (fuse.DirEntry, syscall.Errno) {
ds.mu.Lock()
defer ds.mu.Unlock()
de := (*syscall.Dirent)(unsafe.Pointer(&ds.todo[0]))
nameBytes := ds.todo[unsafe.Offsetof(syscall.Dirent{}.Name):de.Reclen]
ds.todo = ds.todo[de.Reclen:]
// After the loop, l contains the index of the first '\0'.
l := 0
for l = range nameBytes {
if nameBytes[l] == 0 {
break
}
}
nameBytes = nameBytes[:l]
result := fuse.DirEntry{
Ino: de.Ino,
Mode: (uint32(de.Type) << 12),
Name: string(nameBytes),
}
return result, ds.load()
}
func (ds *loopbackDirStream) load() syscall.Errno {
if len(ds.todo) > 0 {
return OK
}
n, err := syscall.Getdents(ds.fd, ds.buf)
if err != nil {
return ToErrno(err)
}
ds.todo = ds.buf[:n]
return OK
}

230
vendor/github.com/hanwen/go-fuse/v2/fs/files.go generated vendored Normal file
View File

@ -0,0 +1,230 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"context"
"sync"
// "time"
"syscall"
"github.com/hanwen/go-fuse/v2/fuse"
"golang.org/x/sys/unix"
)
// NewLoopbackFile creates a FileHandle out of a file descriptor. All
// operations are implemented.
func NewLoopbackFile(fd int) FileHandle {
return &loopbackFile{fd: fd}
}
type loopbackFile struct {
mu sync.Mutex
fd int
}
var _ = (FileHandle)((*loopbackFile)(nil))
var _ = (FileReleaser)((*loopbackFile)(nil))
var _ = (FileGetattrer)((*loopbackFile)(nil))
var _ = (FileReader)((*loopbackFile)(nil))
var _ = (FileWriter)((*loopbackFile)(nil))
var _ = (FileGetlker)((*loopbackFile)(nil))
var _ = (FileSetlker)((*loopbackFile)(nil))
var _ = (FileSetlkwer)((*loopbackFile)(nil))
var _ = (FileLseeker)((*loopbackFile)(nil))
var _ = (FileFlusher)((*loopbackFile)(nil))
var _ = (FileFsyncer)((*loopbackFile)(nil))
var _ = (FileSetattrer)((*loopbackFile)(nil))
var _ = (FileAllocater)((*loopbackFile)(nil))
func (f *loopbackFile) Read(ctx context.Context, buf []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
r := fuse.ReadResultFd(uintptr(f.fd), off, len(buf))
return r, OK
}
func (f *loopbackFile) Write(ctx context.Context, data []byte, off int64) (uint32, syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
n, err := syscall.Pwrite(f.fd, data, off)
return uint32(n), ToErrno(err)
}
func (f *loopbackFile) Release(ctx context.Context) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
if f.fd != -1 {
err := syscall.Close(f.fd)
f.fd = -1
return ToErrno(err)
}
return syscall.EBADF
}
func (f *loopbackFile) Flush(ctx context.Context) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
// Since Flush() may be called for each dup'd fd, we don't
// want to really close the file, we just want to flush. This
// is achieved by closing a dup'd fd.
newFd, err := syscall.Dup(f.fd)
if err != nil {
return ToErrno(err)
}
err = syscall.Close(newFd)
return ToErrno(err)
}
func (f *loopbackFile) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
r := ToErrno(syscall.Fsync(f.fd))
return r
}
const (
_OFD_GETLK = 36
_OFD_SETLK = 37
_OFD_SETLKW = 38
)
func (f *loopbackFile) Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (errno syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
flk := syscall.Flock_t{}
lk.ToFlockT(&flk)
errno = ToErrno(syscall.FcntlFlock(uintptr(f.fd), _OFD_GETLK, &flk))
out.FromFlockT(&flk)
return
}
func (f *loopbackFile) Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) {
return f.setLock(ctx, owner, lk, flags, false)
}
func (f *loopbackFile) Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) {
return f.setLock(ctx, owner, lk, flags, true)
}
func (f *loopbackFile) setLock(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, blocking bool) (errno syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
if (flags & fuse.FUSE_LK_FLOCK) != 0 {
var op int
switch lk.Typ {
case syscall.F_RDLCK:
op = syscall.LOCK_SH
case syscall.F_WRLCK:
op = syscall.LOCK_EX
case syscall.F_UNLCK:
op = syscall.LOCK_UN
default:
return syscall.EINVAL
}
if !blocking {
op |= syscall.LOCK_NB
}
return ToErrno(syscall.Flock(f.fd, op))
} else {
flk := syscall.Flock_t{}
lk.ToFlockT(&flk)
var op int
if blocking {
op = _OFD_SETLKW
} else {
op = _OFD_SETLK
}
return ToErrno(syscall.FcntlFlock(uintptr(f.fd), op, &flk))
}
}
func (f *loopbackFile) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
if errno := f.setAttr(ctx, in); errno != 0 {
return errno
}
return f.Getattr(ctx, out)
}
func (f *loopbackFile) setAttr(ctx context.Context, in *fuse.SetAttrIn) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
var errno syscall.Errno
if mode, ok := in.GetMode(); ok {
errno = ToErrno(syscall.Fchmod(f.fd, mode))
if errno != 0 {
return errno
}
}
uid32, uOk := in.GetUID()
gid32, gOk := in.GetGID()
if uOk || gOk {
uid := -1
gid := -1
if uOk {
uid = int(uid32)
}
if gOk {
gid = int(gid32)
}
errno = ToErrno(syscall.Fchown(f.fd, uid, gid))
if errno != 0 {
return errno
}
}
mtime, mok := in.GetMTime()
atime, aok := in.GetATime()
if mok || aok {
ap := &atime
mp := &mtime
if !aok {
ap = nil
}
if !mok {
mp = nil
}
errno = f.utimens(ap, mp)
if errno != 0 {
return errno
}
}
if sz, ok := in.GetSize(); ok {
errno = ToErrno(syscall.Ftruncate(f.fd, int64(sz)))
if errno != 0 {
return errno
}
}
return OK
}
func (f *loopbackFile) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
st := syscall.Stat_t{}
err := syscall.Fstat(f.fd, &st)
if err != nil {
return ToErrno(err)
}
a.FromStat(&st)
return OK
}
func (f *loopbackFile) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
n, err := unix.Seek(f.fd, int64(off), int(whence))
return uint64(n), ToErrno(err)
}

10
vendor/github.com/hanwen/go-fuse/v2/fs/files_darwin.go generated vendored Normal file
View File

@ -0,0 +1,10 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import "github.com/hanwen/go-fuse/v2/fuse"
func setBlocks(out *fuse.Attr) {
}

42
vendor/github.com/hanwen/go-fuse/v2/fs/files_linux.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"context"
"syscall"
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
func (f *loopbackFile) Allocate(ctx context.Context, off uint64, sz uint64, mode uint32) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
err := syscall.Fallocate(f.fd, mode, int64(off), int64(sz))
if err != nil {
return ToErrno(err)
}
return OK
}
// Utimens - file handle based version of loopbackFileSystem.Utimens()
func (f *loopbackFile) utimens(a *time.Time, m *time.Time) syscall.Errno {
var ts [2]syscall.Timespec
ts[0] = fuse.UtimeToTimespec(a)
ts[1] = fuse.UtimeToTimespec(m)
err := futimens(int(f.fd), &ts)
return ToErrno(err)
}
func setBlocks(out *fuse.Attr) {
if out.Blksize > 0 {
return
}
out.Blksize = 4096
pages := (out.Size + 4095) / 4096
out.Blocks = pages * 8
}

723
vendor/github.com/hanwen/go-fuse/v2/fs/inode.go generated vendored Normal file
View File

@ -0,0 +1,723 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"context"
"fmt"
"log"
"sort"
"strings"
"sync"
"syscall"
"unsafe"
"github.com/hanwen/go-fuse/v2/fuse"
)
type parentData struct {
name string
parent *Inode
}
// StableAttr holds immutable attributes of a object in the filesystem.
type StableAttr struct {
// Each Inode has a type, which does not change over the
// lifetime of the inode, for example fuse.S_IFDIR. The default (0)
// is interpreted as S_IFREG (regular file).
Mode uint32
// The inode number must be unique among the currently live
// objects in the file system. It is used to communicate to
// the kernel about this file object. The values uint64(-1),
// and 1 are reserved. When using Ino==0, a unique, sequential
// number is assigned (starting at 2^63 by default) on Inode creation.
Ino uint64
// When reusing a previously used inode number for a new
// object, the new object must have a different Gen
// number. This is irrelevant if the FS is not exported over
// NFS
Gen uint64
}
// Reserved returns if the StableAttr is using reserved Inode numbers.
func (i *StableAttr) Reserved() bool {
return i.Ino == 1 || i.Ino == ^uint64(0)
}
// Inode is a node in VFS tree. Inodes are one-to-one mapped to
// Operations instances, which is the extension interface for file
// systems. One can create fully-formed trees of Inodes ahead of time
// by creating "persistent" Inodes.
//
// The Inode struct contains a lock, so it should not be
// copied. Inodes should be obtained by calling Inode.NewInode() or
// Inode.NewPersistentInode().
type Inode struct {
stableAttr StableAttr
ops InodeEmbedder
bridge *rawBridge
// Following data is mutable.
// protected by bridge.mu
// file handles.
openFiles []uint32
// mu protects the following mutable fields. When locking
// multiple Inodes, locks must be acquired using
// lockNodes/unlockNodes
mu sync.Mutex
// persistent indicates that this node should not be removed
// from the tree, even if there are no live references. This
// must be set on creation, and can only be changed to false
// by calling removeRef.
persistent bool
// changeCounter increments every time the mutable state
// (lookupCount, persistent, children, parents) protected by
// mu is modified.
//
// This is used in places where we have to relock inode into inode
// group lock, and after locking the group we have to check if inode
// did not changed, and if it changed - retry the operation.
changeCounter uint32
// Number of kernel refs to this node.
lookupCount uint64
children map[string]*Inode
parents map[parentData]struct{}
}
func (n *Inode) IsDir() bool {
return n.stableAttr.Mode&syscall.S_IFDIR != 0
}
func (n *Inode) embed() *Inode {
return n
}
func (n *Inode) EmbeddedInode() *Inode {
return n
}
func initInode(n *Inode, ops InodeEmbedder, attr StableAttr, bridge *rawBridge, persistent bool) {
n.ops = ops
n.stableAttr = attr
n.bridge = bridge
n.persistent = persistent
n.parents = make(map[parentData]struct{})
if attr.Mode == fuse.S_IFDIR {
n.children = make(map[string]*Inode)
}
}
// Set node ID and mode in EntryOut
func (n *Inode) setEntryOut(out *fuse.EntryOut) {
out.NodeId = n.stableAttr.Ino
out.Ino = n.stableAttr.Ino
out.Mode = (out.Attr.Mode & 07777) | n.stableAttr.Mode
}
// StableAttr returns the (Ino, Gen) tuple for this node.
func (n *Inode) StableAttr() StableAttr {
return n.stableAttr
}
// Mode returns the filetype
func (n *Inode) Mode() uint32 {
return n.stableAttr.Mode
}
// Returns the root of the tree
func (n *Inode) Root() *Inode {
return n.bridge.root
}
// Returns whether this is the root of the tree
func (n *Inode) IsRoot() bool {
return n.bridge.root == n
}
func modeStr(m uint32) string {
return map[uint32]string{
syscall.S_IFREG: "reg",
syscall.S_IFLNK: "lnk",
syscall.S_IFDIR: "dir",
syscall.S_IFSOCK: "soc",
syscall.S_IFIFO: "pip",
syscall.S_IFCHR: "chr",
syscall.S_IFBLK: "blk",
}[m]
}
// debugString is used for debugging. Racy.
func (n *Inode) String() string {
n.mu.Lock()
defer n.mu.Unlock()
var ss []string
for nm, ch := range n.children {
ss = append(ss, fmt.Sprintf("%q=i%d[%s]", nm, ch.stableAttr.Ino, modeStr(ch.stableAttr.Mode)))
}
return fmt.Sprintf("i%d (%s): %s", n.stableAttr.Ino, modeStr(n.stableAttr.Mode), strings.Join(ss, ","))
}
// sortNodes rearranges inode group in consistent order.
//
// The nodes are ordered by their in-RAM address, which gives consistency
// property: for any A and B inodes, sortNodes will either always order A < B,
// or always order A > B.
//
// See lockNodes where this property is used to avoid deadlock when taking
// locks on inode group.
func sortNodes(ns []*Inode) {
sort.Slice(ns, func(i, j int) bool {
return nodeLess(ns[i], ns[j])
})
}
func nodeLess(a, b *Inode) bool {
return uintptr(unsafe.Pointer(a)) < uintptr(unsafe.Pointer(b))
}
// lockNodes locks group of inodes.
//
// It always lock the inodes in the same order - to avoid deadlocks.
// It also avoids locking an inode more than once, if it was specified multiple times.
// An example when an inode might be given multiple times is if dir/a and dir/b
// are hardlinked to the same inode and the caller needs to take locks on dir children.
func lockNodes(ns ...*Inode) {
sortNodes(ns)
// The default value nil prevents trying to lock nil nodes.
var nprev *Inode
for _, n := range ns {
if n != nprev {
n.mu.Lock()
nprev = n
}
}
}
// lockNode2 locks a and b in order consistent with lockNodes.
func lockNode2(a, b *Inode) {
if a == b {
a.mu.Lock()
} else if nodeLess(a, b) {
a.mu.Lock()
b.mu.Lock()
} else {
b.mu.Lock()
a.mu.Lock()
}
}
// unlockNode2 unlocks a and b
func unlockNode2(a, b *Inode) {
if a == b {
a.mu.Unlock()
} else {
a.mu.Unlock()
b.mu.Unlock()
}
}
// unlockNodes releases locks taken by lockNodes.
func unlockNodes(ns ...*Inode) {
// we don't need to unlock in the same order that was used in lockNodes.
// however it still helps to have nodes sorted to avoid duplicates.
sortNodes(ns)
var nprev *Inode
for _, n := range ns {
if n != nprev {
n.mu.Unlock()
nprev = n
}
}
}
// Forgotten returns true if the kernel holds no references to this
// inode. This can be used for background cleanup tasks, since the
// kernel has no way of reviving forgotten nodes by its own
// initiative.
func (n *Inode) Forgotten() bool {
n.mu.Lock()
defer n.mu.Unlock()
return n.lookupCount == 0 && len(n.parents) == 0 && !n.persistent
}
// Operations returns the object implementing the file system
// operations.
func (n *Inode) Operations() InodeEmbedder {
return n.ops
}
// Path returns a path string to the inode relative to the root.
func (n *Inode) Path(root *Inode) string {
var segments []string
p := n
for p != nil && p != root {
var pd parentData
// We don't try to take all locks at the same time, because
// the caller won't use the "path" string under lock anyway.
p.mu.Lock()
// Select an arbitrary parent
for pd = range p.parents {
break
}
p.mu.Unlock()
if pd.parent == nil {
break
}
segments = append(segments, pd.name)
p = pd.parent
}
if p == nil {
// NOSUBMIT - should replace rather than append?
segments = append(segments, ".deleted")
}
i := 0
j := len(segments) - 1
for i < j {
segments[i], segments[j] = segments[j], segments[i]
i++
j--
}
path := strings.Join(segments, "/")
return path
}
// setEntry does `iparent[name] = ichild` linking.
//
// setEntry must not be called simultaneously for any of iparent or ichild.
// This, for example could be satisfied if both iparent and ichild are locked,
// but it could be also valid if only iparent is locked and ichild was just
// created and only one goroutine keeps referencing it.
func (iparent *Inode) setEntry(name string, ichild *Inode) {
ichild.parents[parentData{name, iparent}] = struct{}{}
iparent.children[name] = ichild
ichild.changeCounter++
iparent.changeCounter++
}
// NewPersistentInode returns an Inode whose lifetime is not in
// control of the kernel.
//
// When the kernel is short on memory, it will forget cached file
// system information (directory entries and inode metadata). This is
// announced with FORGET messages. There are no guarantees if or when
// this happens. When it happens, these are handled transparently by
// go-fuse: all Inodes created with NewInode are released
// automatically. NewPersistentInode creates inodes that go-fuse keeps
// in memory, even if the kernel is not interested in them. This is
// convenient for building static trees up-front.
func (n *Inode) NewPersistentInode(ctx context.Context, node InodeEmbedder, id StableAttr) *Inode {
return n.newInode(ctx, node, id, true)
}
// ForgetPersistent manually marks the node as no longer important. If
// it has no children, and if the kernel as no references, the nodes
// gets removed from the tree.
func (n *Inode) ForgetPersistent() {
n.removeRef(0, true)
}
// NewInode returns an inode for the given InodeEmbedder. The mode
// should be standard mode argument (eg. S_IFDIR). The inode number in
// id.Ino argument is used to implement hard-links. If it is given,
// and another node with the same ID is known, that will node will be
// returned, and the passed-in `node` is ignored.
func (n *Inode) NewInode(ctx context.Context, node InodeEmbedder, id StableAttr) *Inode {
return n.newInode(ctx, node, id, false)
}
func (n *Inode) newInode(ctx context.Context, ops InodeEmbedder, id StableAttr, persistent bool) *Inode {
return n.bridge.newInode(ctx, ops, id, persistent)
}
// removeRef decreases references. Returns if this operation caused
// the node to be forgotten (for kernel references), and whether it is
// live (ie. was not dropped from the tree)
func (n *Inode) removeRef(nlookup uint64, dropPersistence bool) (forgotten bool, live bool) {
var lockme []*Inode
var parents []parentData
n.mu.Lock()
if nlookup > 0 && dropPersistence {
log.Panic("only one allowed")
} else if nlookup > 0 {
n.lookupCount -= nlookup
n.changeCounter++
} else if dropPersistence && n.persistent {
n.persistent = false
n.changeCounter++
}
retry:
for {
lockme = append(lockme[:0], n)
parents = parents[:0]
nChange := n.changeCounter
live = n.lookupCount > 0 || len(n.children) > 0 || n.persistent
forgotten = n.lookupCount == 0
for p := range n.parents {
parents = append(parents, p)
lockme = append(lockme, p.parent)
}
n.mu.Unlock()
if live {
return forgotten, live
}
lockNodes(lockme...)
if n.changeCounter != nChange {
unlockNodes(lockme...)
// could avoid unlocking and relocking n here.
n.mu.Lock()
continue retry
}
for _, p := range parents {
delete(p.parent.children, p.name)
p.parent.changeCounter++
}
n.parents = map[parentData]struct{}{}
n.changeCounter++
if n.lookupCount != 0 {
panic("lookupCount changed")
}
n.bridge.mu.Lock()
delete(n.bridge.nodes, n.stableAttr.Ino)
n.bridge.mu.Unlock()
unlockNodes(lockme...)
break
}
for _, p := range lockme {
if p != n {
p.removeRef(0, false)
}
}
return forgotten, false
}
// GetChild returns a child node with the given name, or nil if the
// directory has no child by that name.
func (n *Inode) GetChild(name string) *Inode {
n.mu.Lock()
defer n.mu.Unlock()
return n.children[name]
}
// AddChild adds a child to this node. If overwrite is false, fail if
// the destination already exists.
func (n *Inode) AddChild(name string, ch *Inode, overwrite bool) (success bool) {
if len(name) == 0 {
log.Panic("empty name for inode")
}
retry:
for {
lockNode2(n, ch)
prev, ok := n.children[name]
parentCounter := n.changeCounter
if !ok {
n.children[name] = ch
ch.parents[parentData{name, n}] = struct{}{}
n.changeCounter++
ch.changeCounter++
unlockNode2(n, ch)
return true
}
unlockNode2(n, ch)
if !overwrite {
return false
}
lockme := [3]*Inode{n, ch, prev}
lockNodes(lockme[:]...)
if parentCounter != n.changeCounter {
unlockNodes(lockme[:]...)
continue retry
}
delete(prev.parents, parentData{name, n})
n.children[name] = ch
ch.parents[parentData{name, n}] = struct{}{}
n.changeCounter++
ch.changeCounter++
prev.changeCounter++
unlockNodes(lockme[:]...)
return true
}
}
// Children returns the list of children of this directory Inode.
func (n *Inode) Children() map[string]*Inode {
n.mu.Lock()
defer n.mu.Unlock()
r := make(map[string]*Inode, len(n.children))
for k, v := range n.children {
r[k] = v
}
return r
}
// Parents returns a parent of this Inode, or nil if this Inode is
// deleted or is the root
func (n *Inode) Parent() (string, *Inode) {
n.mu.Lock()
defer n.mu.Unlock()
for k := range n.parents {
return k.name, k.parent
}
return "", nil
}
// RmAllChildren recursively drops a tree, forgetting all persistent
// nodes.
func (n *Inode) RmAllChildren() {
for {
chs := n.Children()
if len(chs) == 0 {
break
}
for nm, ch := range chs {
ch.RmAllChildren()
n.RmChild(nm)
}
}
n.removeRef(0, true)
}
// RmChild removes multiple children. Returns whether the removal
// succeeded and whether the node is still live afterward. The removal
// is transactional: it only succeeds if all names are children, and
// if they all were removed successfully. If the removal was
// successful, and there are no children left, the node may be removed
// from the FS tree. In that case, RmChild returns live==false.
func (n *Inode) RmChild(names ...string) (success, live bool) {
var lockme []*Inode
retry:
for {
n.mu.Lock()
lockme = append(lockme[:0], n)
nChange := n.changeCounter
for _, nm := range names {
ch := n.children[nm]
if ch == nil {
n.mu.Unlock()
return false, true
}
lockme = append(lockme, ch)
}
n.mu.Unlock()
lockNodes(lockme...)
if n.changeCounter != nChange {
unlockNodes(lockme...)
// could avoid unlocking and relocking n here.
n.mu.Lock()
continue retry
}
for _, nm := range names {
ch := n.children[nm]
delete(n.children, nm)
delete(ch.parents, parentData{nm, n})
ch.changeCounter++
}
n.changeCounter++
live = n.lookupCount > 0 || len(n.children) > 0 || n.persistent
unlockNodes(lockme...)
// removal successful
break
}
if !live {
_, live := n.removeRef(0, false)
return true, live
}
return true, true
}
// MvChild executes a rename. If overwrite is set, a child at the
// destination will be overwritten, should it exist.
func (n *Inode) MvChild(old string, newParent *Inode, newName string, overwrite bool) bool {
if len(newName) == 0 {
log.Panicf("empty newName for MvChild")
}
retry:
for {
lockNode2(n, newParent)
counter1 := n.changeCounter
counter2 := newParent.changeCounter
oldChild := n.children[old]
destChild := newParent.children[newName]
unlockNode2(n, newParent)
if destChild != nil && !overwrite {
return false
}
lockNodes(n, newParent, oldChild, destChild)
if counter2 != newParent.changeCounter || counter1 != n.changeCounter {
unlockNodes(n, newParent, oldChild, destChild)
continue retry
}
if oldChild != nil {
delete(n.children, old)
delete(oldChild.parents, parentData{old, n})
n.changeCounter++
oldChild.changeCounter++
}
if destChild != nil {
// This can cause the child to be slated for
// removal; see below
delete(newParent.children, newName)
delete(destChild.parents, parentData{newName, newParent})
destChild.changeCounter++
newParent.changeCounter++
}
if oldChild != nil {
newParent.children[newName] = oldChild
newParent.changeCounter++
oldChild.parents[parentData{newName, newParent}] = struct{}{}
oldChild.changeCounter++
}
unlockNodes(n, newParent, oldChild, destChild)
if destChild != nil {
destChild.removeRef(0, false)
}
return true
}
}
// ExchangeChild swaps the entries at (n, oldName) and (newParent,
// newName).
func (n *Inode) ExchangeChild(oldName string, newParent *Inode, newName string) {
oldParent := n
retry:
for {
lockNode2(oldParent, newParent)
counter1 := oldParent.changeCounter
counter2 := newParent.changeCounter
oldChild := oldParent.children[oldName]
destChild := newParent.children[newName]
unlockNode2(oldParent, newParent)
if destChild == oldChild {
return
}
lockNodes(oldParent, newParent, oldChild, destChild)
if counter2 != newParent.changeCounter || counter1 != oldParent.changeCounter {
unlockNodes(oldParent, newParent, oldChild, destChild)
continue retry
}
// Detach
if oldChild != nil {
delete(oldParent.children, oldName)
delete(oldChild.parents, parentData{oldName, oldParent})
oldParent.changeCounter++
oldChild.changeCounter++
}
if destChild != nil {
delete(newParent.children, newName)
delete(destChild.parents, parentData{newName, newParent})
destChild.changeCounter++
newParent.changeCounter++
}
// Attach
if oldChild != nil {
newParent.children[newName] = oldChild
newParent.changeCounter++
oldChild.parents[parentData{newName, newParent}] = struct{}{}
oldChild.changeCounter++
}
if destChild != nil {
oldParent.children[oldName] = oldChild
oldParent.changeCounter++
destChild.parents[parentData{oldName, oldParent}] = struct{}{}
destChild.changeCounter++
}
unlockNodes(oldParent, newParent, oldChild, destChild)
return
}
}
// NotifyEntry notifies the kernel that data for a (directory, name)
// tuple should be invalidated. On next access, a LOOKUP operation
// will be started.
func (n *Inode) NotifyEntry(name string) syscall.Errno {
status := n.bridge.server.EntryNotify(n.stableAttr.Ino, name)
return syscall.Errno(status)
}
// NotifyDelete notifies the kernel that the given inode was removed
// from this directory as entry under the given name. It is equivalent
// to NotifyEntry, but also sends an event to inotify watchers.
func (n *Inode) NotifyDelete(name string, child *Inode) syscall.Errno {
// XXX arg ordering?
return syscall.Errno(n.bridge.server.DeleteNotify(n.stableAttr.Ino, child.stableAttr.Ino, name))
}
// NotifyContent notifies the kernel that content under the given
// inode should be flushed from buffers.
func (n *Inode) NotifyContent(off, sz int64) syscall.Errno {
// XXX how does this work for directories?
return syscall.Errno(n.bridge.server.InodeNotify(n.stableAttr.Ino, off, sz))
}
// WriteCache stores data in the kernel cache.
func (n *Inode) WriteCache(offset int64, data []byte) syscall.Errno {
return syscall.Errno(n.bridge.server.InodeNotifyStoreCache(n.stableAttr.Ino, offset, data))
}
// ReadCache reads data from the kernel cache.
func (n *Inode) ReadCache(offset int64, dest []byte) (count int, errno syscall.Errno) {
c, s := n.bridge.server.InodeRetrieveCache(n.stableAttr.Ino, offset, dest)
return c, syscall.Errno(s)
}

389
vendor/github.com/hanwen/go-fuse/v2/fs/loopback.go generated vendored Normal file
View File

@ -0,0 +1,389 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"context"
"os"
"path/filepath"
"syscall"
"github.com/hanwen/go-fuse/v2/fuse"
)
type loopbackRoot struct {
loopbackNode
rootPath string
rootDev uint64
}
type loopbackNode struct {
Inode
}
var _ = (NodeStatfser)((*loopbackNode)(nil))
var _ = (NodeStatfser)((*loopbackNode)(nil))
var _ = (NodeGetattrer)((*loopbackNode)(nil))
var _ = (NodeGetxattrer)((*loopbackNode)(nil))
var _ = (NodeSetxattrer)((*loopbackNode)(nil))
var _ = (NodeRemovexattrer)((*loopbackNode)(nil))
var _ = (NodeListxattrer)((*loopbackNode)(nil))
var _ = (NodeReadlinker)((*loopbackNode)(nil))
var _ = (NodeOpener)((*loopbackNode)(nil))
var _ = (NodeCopyFileRanger)((*loopbackNode)(nil))
var _ = (NodeLookuper)((*loopbackNode)(nil))
var _ = (NodeOpendirer)((*loopbackNode)(nil))
var _ = (NodeReaddirer)((*loopbackNode)(nil))
var _ = (NodeMkdirer)((*loopbackNode)(nil))
var _ = (NodeMknoder)((*loopbackNode)(nil))
var _ = (NodeLinker)((*loopbackNode)(nil))
var _ = (NodeSymlinker)((*loopbackNode)(nil))
var _ = (NodeUnlinker)((*loopbackNode)(nil))
var _ = (NodeRmdirer)((*loopbackNode)(nil))
var _ = (NodeRenamer)((*loopbackNode)(nil))
func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
s := syscall.Statfs_t{}
err := syscall.Statfs(n.path(), &s)
if err != nil {
return ToErrno(err)
}
out.FromStatfsT(&s)
return OK
}
func (n *loopbackRoot) Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno {
st := syscall.Stat_t{}
err := syscall.Stat(n.rootPath, &st)
if err != nil {
return ToErrno(err)
}
out.FromStat(&st)
return OK
}
func (n *loopbackNode) root() *loopbackRoot {
return n.Root().Operations().(*loopbackRoot)
}
func (n *loopbackNode) path() string {
path := n.Path(nil)
return filepath.Join(n.root().rootPath, path)
}
func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name)
st := syscall.Stat_t{}
err := syscall.Lstat(p, &st)
if err != nil {
return nil, ToErrno(err)
}
out.Attr.FromStat(&st)
node := &loopbackNode{}
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
return ch, 0
}
func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name)
err := syscall.Mknod(p, mode, int(rdev))
if err != nil {
return nil, ToErrno(err)
}
st := syscall.Stat_t{}
if err := syscall.Lstat(p, &st); err != nil {
syscall.Rmdir(p)
return nil, ToErrno(err)
}
out.Attr.FromStat(&st)
node := &loopbackNode{}
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
return ch, 0
}
func (n *loopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name)
err := os.Mkdir(p, os.FileMode(mode))
if err != nil {
return nil, ToErrno(err)
}
st := syscall.Stat_t{}
if err := syscall.Lstat(p, &st); err != nil {
syscall.Rmdir(p)
return nil, ToErrno(err)
}
out.Attr.FromStat(&st)
node := &loopbackNode{}
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
return ch, 0
}
func (n *loopbackNode) Rmdir(ctx context.Context, name string) syscall.Errno {
p := filepath.Join(n.path(), name)
err := syscall.Rmdir(p)
return ToErrno(err)
}
func (n *loopbackNode) Unlink(ctx context.Context, name string) syscall.Errno {
p := filepath.Join(n.path(), name)
err := syscall.Unlink(p)
return ToErrno(err)
}
func toLoopbackNode(op InodeEmbedder) *loopbackNode {
if r, ok := op.(*loopbackRoot); ok {
return &r.loopbackNode
}
return op.(*loopbackNode)
}
func (n *loopbackNode) Rename(ctx context.Context, name string, newParent InodeEmbedder, newName string, flags uint32) syscall.Errno {
newParentLoopback := toLoopbackNode(newParent)
if flags&RENAME_EXCHANGE != 0 {
return n.renameExchange(name, newParentLoopback, newName)
}
p1 := filepath.Join(n.path(), name)
p2 := filepath.Join(newParentLoopback.path(), newName)
err := os.Rename(p1, p2)
return ToErrno(err)
}
func (r *loopbackRoot) idFromStat(st *syscall.Stat_t) StableAttr {
// We compose an inode number by the underlying inode, and
// mixing in the device number. In traditional filesystems,
// the inode numbers are small. The device numbers are also
// small (typically 16 bit). Finally, we mask out the root
// device number of the root, so a loopback FS that does not
// encompass multiple mounts will reflect the inode numbers of
// the underlying filesystem
swapped := (uint64(st.Dev) << 32) | (uint64(st.Dev) >> 32)
swappedRootDev := (r.rootDev << 32) | (r.rootDev >> 32)
return StableAttr{
Mode: uint32(st.Mode),
Gen: 1,
// This should work well for traditional backing FSes,
// not so much for other go-fuse FS-es
Ino: (swapped ^ swappedRootDev) ^ st.Ino,
}
}
var _ = (NodeCreater)((*loopbackNode)(nil))
func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (inode *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
p := filepath.Join(n.path(), name)
flags = flags &^ syscall.O_APPEND
fd, err := syscall.Open(p, int(flags)|os.O_CREATE, mode)
if err != nil {
return nil, nil, 0, ToErrno(err)
}
st := syscall.Stat_t{}
if err := syscall.Fstat(fd, &st); err != nil {
syscall.Close(fd)
return nil, nil, 0, ToErrno(err)
}
node := &loopbackNode{}
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
lf := NewLoopbackFile(fd)
out.FromStat(&st)
return ch, lf, 0, 0
}
func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name)
err := syscall.Symlink(target, p)
if err != nil {
return nil, ToErrno(err)
}
st := syscall.Stat_t{}
if syscall.Lstat(p, &st); err != nil {
syscall.Unlink(p)
return nil, ToErrno(err)
}
node := &loopbackNode{}
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
out.Attr.FromStat(&st)
return ch, 0
}
func (n *loopbackNode) Link(ctx context.Context, target InodeEmbedder, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name)
targetNode := toLoopbackNode(target)
err := syscall.Link(targetNode.path(), p)
if err != nil {
return nil, ToErrno(err)
}
st := syscall.Stat_t{}
if syscall.Lstat(p, &st); err != nil {
syscall.Unlink(p)
return nil, ToErrno(err)
}
node := &loopbackNode{}
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
out.Attr.FromStat(&st)
return ch, 0
}
func (n *loopbackNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
p := n.path()
for l := 256; ; l *= 2 {
buf := make([]byte, l)
sz, err := syscall.Readlink(p, buf)
if err != nil {
return nil, ToErrno(err)
}
if sz < len(buf) {
return buf[:sz], 0
}
}
}
func (n *loopbackNode) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
flags = flags &^ syscall.O_APPEND
p := n.path()
f, err := syscall.Open(p, int(flags), 0)
if err != nil {
return nil, 0, ToErrno(err)
}
lf := NewLoopbackFile(f)
return lf, 0, 0
}
func (n *loopbackNode) Opendir(ctx context.Context) syscall.Errno {
fd, err := syscall.Open(n.path(), syscall.O_DIRECTORY, 0755)
if err != nil {
return ToErrno(err)
}
syscall.Close(fd)
return OK
}
func (n *loopbackNode) Readdir(ctx context.Context) (DirStream, syscall.Errno) {
return NewLoopbackDirStream(n.path())
}
func (n *loopbackNode) Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno {
if f != nil {
return f.(FileGetattrer).Getattr(ctx, out)
}
p := n.path()
var err error = nil
st := syscall.Stat_t{}
err = syscall.Lstat(p, &st)
if err != nil {
return ToErrno(err)
}
out.FromStat(&st)
return OK
}
var _ = (NodeSetattrer)((*loopbackNode)(nil))
func (n *loopbackNode) Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
p := n.path()
fsa, ok := f.(FileSetattrer)
if ok && fsa != nil {
fsa.Setattr(ctx, in, out)
} else {
if m, ok := in.GetMode(); ok {
if err := syscall.Chmod(p, m); err != nil {
return ToErrno(err)
}
}
uid, uok := in.GetUID()
gid, gok := in.GetGID()
if uok || gok {
suid := -1
sgid := -1
if uok {
suid = int(uid)
}
if gok {
sgid = int(gid)
}
if err := syscall.Chown(p, suid, sgid); err != nil {
return ToErrno(err)
}
}
mtime, mok := in.GetMTime()
atime, aok := in.GetATime()
if mok || aok {
ap := &atime
mp := &mtime
if !aok {
ap = nil
}
if !mok {
mp = nil
}
var ts [2]syscall.Timespec
ts[0] = fuse.UtimeToTimespec(ap)
ts[1] = fuse.UtimeToTimespec(mp)
if err := syscall.UtimesNano(p, ts[:]); err != nil {
return ToErrno(err)
}
}
if sz, ok := in.GetSize(); ok {
if err := syscall.Truncate(p, int64(sz)); err != nil {
return ToErrno(err)
}
}
}
fga, ok := f.(FileGetattrer)
if ok && fga != nil {
fga.Getattr(ctx, out)
} else {
st := syscall.Stat_t{}
err := syscall.Lstat(p, &st)
if err != nil {
return ToErrno(err)
}
out.FromStat(&st)
}
return OK
}
// NewLoopback returns a root node for a loopback file system whose
// root is at the given root. This node implements all NodeXxxxer
// operations available.
func NewLoopbackRoot(root string) (InodeEmbedder, error) {
var st syscall.Stat_t
err := syscall.Stat(root, &st)
if err != nil {
return nil, err
}
n := &loopbackRoot{
rootPath: root,
rootDev: uint64(st.Dev),
}
return n, nil
}

View File

@ -0,0 +1,118 @@
// +build darwin
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"context"
"syscall"
"time"
"unsafe"
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/internal/utimens"
)
func (n *loopbackNode) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
return 0, syscall.ENOSYS
}
func (n *loopbackNode) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno {
return syscall.ENOSYS
}
func (n *loopbackNode) Removexattr(ctx context.Context, attr string) syscall.Errno {
return syscall.ENOSYS
}
func (n *loopbackNode) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
return 0, syscall.ENOSYS
}
func (n *loopbackNode) renameExchange(name string, newparent *loopbackNode, newName string) syscall.Errno {
return syscall.ENOSYS
}
func (f *loopbackFile) Allocate(ctx context.Context, off uint64, sz uint64, mode uint32) syscall.Errno {
// TODO: Handle `mode` parameter.
// From `man fcntl` on OSX:
// The F_PREALLOCATE command operates on the following structure:
//
// typedef struct fstore {
// u_int32_t fst_flags; /* IN: flags word */
// int fst_posmode; /* IN: indicates offset field */
// off_t fst_offset; /* IN: start of the region */
// off_t fst_length; /* IN: size of the region */
// off_t fst_bytesalloc; /* OUT: number of bytes allocated */
// } fstore_t;
//
// The flags (fst_flags) for the F_PREALLOCATE command are as follows:
//
// F_ALLOCATECONTIG Allocate contiguous space.
//
// F_ALLOCATEALL Allocate all requested space or no space at all.
//
// The position modes (fst_posmode) for the F_PREALLOCATE command indicate how to use the offset field. The modes are as fol-
// lows:
//
// F_PEOFPOSMODE Allocate from the physical end of file.
//
// F_VOLPOSMODE Allocate from the volume offset.
k := struct {
Flags uint32 // u_int32_t
Posmode int64 // int
Offset int64 // off_t
Length int64 // off_t
Bytesalloc int64 // off_t
}{
0,
0,
int64(off),
int64(sz),
0,
}
// Linux version for reference:
// err := syscall.Fallocate(int(f.File.Fd()), mode, int64(off), int64(sz))
_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(f.fd), uintptr(syscall.F_PREALLOCATE), uintptr(unsafe.Pointer(&k)))
return errno
}
// timeToTimeval - Convert time.Time to syscall.Timeval
//
// Note: This does not use syscall.NsecToTimespec because
// that does not work properly for times before 1970,
// see https://github.com/golang/go/issues/12777
func timeToTimeval(t *time.Time) syscall.Timeval {
var tv syscall.Timeval
tv.Usec = int32(t.Nanosecond() / 1000)
tv.Sec = t.Unix()
return tv
}
// MacOS before High Sierra lacks utimensat() and UTIME_OMIT.
// We emulate using utimes() and extra Getattr() calls.
func (f *loopbackFile) utimens(a *time.Time, m *time.Time) syscall.Errno {
var attr fuse.AttrOut
if a == nil || m == nil {
errno := f.Getattr(context.Background(), &attr)
if errno != 0 {
return errno
}
}
tv := utimens.Fill(a, m, &attr.Attr)
err := syscall.Futimes(int(f.fd), tv)
return ToErrno(err)
}
func (n *loopbackNode) CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, syscall.Errno) {
return 0, syscall.ENOSYS
}

View File

@ -0,0 +1,86 @@
// +build linux
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"context"
"syscall"
"golang.org/x/sys/unix"
)
func (n *loopbackNode) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
sz, err := syscall.Getxattr(n.path(), attr, dest)
return uint32(sz), ToErrno(err)
}
func (n *loopbackNode) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno {
err := syscall.Setxattr(n.path(), attr, data, int(flags))
return ToErrno(err)
}
func (n *loopbackNode) Removexattr(ctx context.Context, attr string) syscall.Errno {
err := syscall.Removexattr(n.path(), attr)
return ToErrno(err)
}
func (n *loopbackNode) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
sz, err := syscall.Listxattr(n.path(), dest)
return uint32(sz), ToErrno(err)
}
func (n *loopbackNode) renameExchange(name string, newparent *loopbackNode, newName string) syscall.Errno {
fd1, err := syscall.Open(n.path(), syscall.O_DIRECTORY, 0)
if err != nil {
return ToErrno(err)
}
defer syscall.Close(fd1)
fd2, err := syscall.Open(newparent.path(), syscall.O_DIRECTORY, 0)
defer syscall.Close(fd2)
if err != nil {
return ToErrno(err)
}
var st syscall.Stat_t
if err := syscall.Fstat(fd1, &st); err != nil {
return ToErrno(err)
}
// Double check that nodes didn't change from under us.
inode := &n.Inode
if inode.Root() != inode && inode.StableAttr().Ino != n.root().idFromStat(&st).Ino {
return syscall.EBUSY
}
if err := syscall.Fstat(fd2, &st); err != nil {
return ToErrno(err)
}
newinode := &newparent.Inode
if newinode.Root() != newinode && newinode.StableAttr().Ino != n.root().idFromStat(&st).Ino {
return syscall.EBUSY
}
return ToErrno(unix.Renameat2(fd1, name, fd2, newName, unix.RENAME_EXCHANGE))
}
func (n *loopbackNode) CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, syscall.Errno) {
lfIn, ok := fhIn.(*loopbackFile)
if !ok {
return 0, syscall.ENOTSUP
}
lfOut, ok := fhOut.(*loopbackFile)
if !ok {
return 0, syscall.ENOTSUP
}
signedOffIn := int64(offIn)
signedOffOut := int64(offOut)
count, err := unix.CopyFileRange(lfIn.fd, &signedOffIn, lfOut.fd, &signedOffOut, int(len), int(flags))
return uint32(count), ToErrno(err)
}

103
vendor/github.com/hanwen/go-fuse/v2/fs/mem.go generated vendored Normal file
View File

@ -0,0 +1,103 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"context"
"sync"
"syscall"
"github.com/hanwen/go-fuse/v2/fuse"
)
// MemRegularFile is a filesystem node that holds a read-only data
// slice in memory.
type MemRegularFile struct {
Inode
mu sync.Mutex
Data []byte
Attr fuse.Attr
}
var _ = (NodeOpener)((*MemRegularFile)(nil))
var _ = (NodeReader)((*MemRegularFile)(nil))
var _ = (NodeWriter)((*MemRegularFile)(nil))
var _ = (NodeSetattrer)((*MemRegularFile)(nil))
var _ = (NodeFlusher)((*MemRegularFile)(nil))
func (f *MemRegularFile) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
return nil, fuse.FOPEN_KEEP_CACHE, OK
}
func (f *MemRegularFile) Write(ctx context.Context, fh FileHandle, data []byte, off int64) (uint32, syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
end := int64(len(data)) + off
if int64(len(f.Data)) < end {
n := make([]byte, end)
copy(n, f.Data)
f.Data = n
}
copy(f.Data[off:off+int64(len(data))], data)
return uint32(len(data)), 0
}
var _ = (NodeGetattrer)((*MemRegularFile)(nil))
func (f *MemRegularFile) Getattr(ctx context.Context, fh FileHandle, out *fuse.AttrOut) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
out.Attr = f.Attr
out.Attr.Size = uint64(len(f.Data))
return OK
}
func (f *MemRegularFile) Setattr(ctx context.Context, fh FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
if sz, ok := in.GetSize(); ok {
f.Data = f.Data[:sz]
}
out.Attr = f.Attr
out.Size = uint64(len(f.Data))
return OK
}
func (f *MemRegularFile) Flush(ctx context.Context, fh FileHandle) syscall.Errno {
return 0
}
func (f *MemRegularFile) Read(ctx context.Context, fh FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
end := int(off) + len(dest)
if end > len(f.Data) {
end = len(f.Data)
}
return fuse.ReadResultData(f.Data[off:end]), OK
}
// MemSymlink is an inode holding a symlink in memory.
type MemSymlink struct {
Inode
Attr fuse.Attr
Data []byte
}
var _ = (NodeReadlinker)((*MemSymlink)(nil))
func (l *MemSymlink) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
return l.Data, OK
}
var _ = (NodeGetattrer)((*MemSymlink)(nil))
func (l *MemSymlink) Getattr(ctx context.Context, fh FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Attr = l.Attr
return OK
}

40
vendor/github.com/hanwen/go-fuse/v2/fs/mount.go generated vendored Normal file
View File

@ -0,0 +1,40 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
// Mount mounts the given NodeFS on the directory, and starts serving
// requests. This is a convenience wrapper around NewNodeFS and
// fuse.NewServer. If nil is given as options, default settings are
// applied, which are 1 second entry and attribute timeout.
func Mount(dir string, root InodeEmbedder, options *Options) (*fuse.Server, error) {
if options == nil {
oneSec := time.Second
options = &Options{
EntryTimeout: &oneSec,
AttrTimeout: &oneSec,
}
}
rawFS := NewNodeFS(root, options)
server, err := fuse.NewServer(rawFS, dir, &options.MountOptions)
if err != nil {
return nil, err
}
go server.Serve()
if err := server.WaitMount(); err != nil {
// we don't shutdown the serve loop. If the mount does
// not succeed, the loop won't work and exit.
return nil, err
}
return server, nil
}

View File

@ -0,0 +1,20 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"syscall"
"unsafe"
)
// futimens - futimens(3) calls utimensat(2) with "pathname" set to null and
// "flags" set to zero
func futimens(fd int, times *[2]syscall.Timespec) (err error) {
_, _, e1 := syscall.Syscall6(syscall.SYS_UTIMENSAT, uintptr(fd), 0, uintptr(unsafe.Pointer(times)), uintptr(0), 0, 0)
if e1 != 0 {
err = syscall.Errno(e1)
}
return
}

1
vendor/github.com/hanwen/go-fuse/v2/fuse/.gitignore generated vendored Normal file
View File

@ -0,0 +1 @@
version.gen.go

274
vendor/github.com/hanwen/go-fuse/v2/fuse/api.go generated vendored Normal file
View File

@ -0,0 +1,274 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package fuse provides APIs to implement filesystems in
// userspace in terms of raw FUSE protocol.
//
// A filesystem is implemented by implementing its server that provides a
// RawFileSystem interface. Typically the server embeds
// NewDefaultRawFileSystem() and implements only subset of filesystem methods:
//
// type MyFS struct {
// fuse.RawFileSystem
// ...
// }
//
// func NewMyFS() *MyFS {
// return &MyFS{
// RawFileSystem: fuse.NewDefaultRawFileSystem(),
// ...
// }
// }
//
// // Mkdir implements "mkdir" request handler.
// //
// // For other requests - not explicitly implemented by MyFS - ENOSYS
// // will be typically returned to client.
// func (fs *MyFS) Mkdir(...) {
// ...
// }
//
// Then the filesystem can be mounted and served to a client (typically OS
// kernel) by creating Server:
//
// fs := NewMyFS() // implements RawFileSystem
// fssrv, err := fuse.NewServer(fs, mountpoint, &fuse.MountOptions{...})
// if err != nil {
// ...
// }
//
// and letting the server do its work:
//
// // either synchronously - .Serve() blocks until the filesystem is unmounted.
// fssrv.Serve()
//
// // or in the background - .Serve() is spawned in another goroutine, but
// // before interacting with fssrv from current context we have to wait
// // until the filesystem mounting is complete.
// go fssrv.Serve()
// err = fssrv.WaitMount()
// if err != nil {
// ...
// }
//
// The server will serve clients by dispatching their requests to the
// filesystem implementation and conveying responses back. For example "mkdir"
// FUSE request dispatches to call
//
// fs.Mkdir(*MkdirIn, ..., *EntryOut)
//
// "stat" to call
//
// fs.GetAttr(*GetAttrIn, *AttrOut)
//
// etc. Please refer to RawFileSystem documentation for details.
//
// Typically, each call of the API happens in its own
// goroutine, so take care to make the file system thread-safe.
//
//
// Higher level interfaces
//
// As said above this packages provides way to implement filesystems in terms of
// raw FUSE protocol. Additionally packages nodefs and pathfs provide ways to
// implement filesystem at higher levels:
//
// Package github.com/hanwen/go-fuse/fuse/nodefs provides way to implement
// filesystems in terms of inodes. This resembles kernel's idea of what a
// filesystem looks like.
//
// Package github.com/hanwen/go-fuse/fuse/pathfs provides way to implement
// filesystems in terms of path names. Working with path names is somewhat
// easier compared to inodes, however renames can be racy. Do not use pathfs if
// you care about correctness.
package fuse
// Types for users to implement.
// The result of Read is an array of bytes, but for performance
// reasons, we can also return data as a file-descriptor/offset/size
// tuple. If the backing store for a file is another filesystem, this
// reduces the amount of copying between the kernel and the FUSE
// server. The ReadResult interface captures both cases.
type ReadResult interface {
// Returns the raw bytes for the read, possibly using the
// passed buffer. The buffer should be larger than the return
// value from Size.
Bytes(buf []byte) ([]byte, Status)
// Size returns how many bytes this return value takes at most.
Size() int
// Done() is called after sending the data to the kernel.
Done()
}
type MountOptions struct {
AllowOther bool
// Options are passed as -o string to fusermount.
Options []string
// Default is _DEFAULT_BACKGROUND_TASKS, 12. This numbers
// controls the allowed number of requests that relate to
// async I/O. Concurrency for synchronous I/O is not limited.
MaxBackground int
// Write size to use. If 0, use default. This number is
// capped at the kernel maximum.
MaxWrite int
// Max read ahead to use. If 0, use default. This number is
// capped at the kernel maximum.
MaxReadAhead int
// If IgnoreSecurityLabels is set, all security related xattr
// requests will return NO_DATA without passing through the
// user defined filesystem. You should only set this if you
// file system implements extended attributes, and you are not
// interested in security labels.
IgnoreSecurityLabels bool // ignoring labels should be provided as a fusermount mount option.
// If RememberInodes is set, we will never forget inodes.
// This may be useful for NFS.
RememberInodes bool
// Values shown in "df -T" and friends
// First column, "Filesystem"
FsName string
// Second column, "Type", will be shown as "fuse." + Name
Name string
// If set, wrap the file system in a single-threaded locking wrapper.
SingleThreaded bool
// If set, return ENOSYS for Getxattr calls, so the kernel does not issue any
// Xattr operations at all.
DisableXAttrs bool
// If set, print debugging information.
Debug bool
// If set, ask kernel to forward file locks to FUSE. If using,
// you must implement the GetLk/SetLk/SetLkw methods.
EnableLocks bool
// If set, ask kernel not to do automatic data cache invalidation.
// The filesystem is fully responsible for invalidating data cache.
ExplicitDataCacheControl bool
}
// RawFileSystem is an interface close to the FUSE wire protocol.
//
// Unless you really know what you are doing, you should not implement
// this, but rather the nodefs.Node or pathfs.FileSystem interfaces; the
// details of getting interactions with open files, renames, and threading
// right etc. are somewhat tricky and not very interesting.
//
// Each FUSE request results in a corresponding method called by Server.
// Several calls may be made simultaneously, because the server typically calls
// each method in separate goroutine.
//
// A null implementation is provided by NewDefaultRawFileSystem.
//
// After a successful FUSE API call returns, you may not read input or
// write output data: for performance reasons, memory is reused for
// following requests, and reading/writing the request data will lead
// to race conditions. If you spawn a background routine from a FUSE
// API call, any incoming request data it wants to reference should be
// copied over.
//
// If a FUSE API call is canceled (which is signaled by closing the
// `cancel` channel), the API call should return EINTR. In this case,
// the outstanding request data is not reused, so the API call may
// return EINTR without ensuring that child contexts have successfully
// completed.
type RawFileSystem interface {
String() string
// If called, provide debug output through the log package.
SetDebug(debug bool)
// Lookup is called by the kernel when the VFS wants to know
// about a file inside a directory. Many lookup calls can
// occur in parallel, but only one call happens for each (dir,
// name) pair.
Lookup(cancel <-chan struct{}, header *InHeader, name string, out *EntryOut) (status Status)
// Forget is called when the kernel discards entries from its
// dentry cache. This happens on unmount, and when the kernel
// is short on memory. Since it is not guaranteed to occur at
// any moment, and since there is no return value, Forget
// should not do I/O, as there is no channel to report back
// I/O errors.
Forget(nodeid, nlookup uint64)
// Attributes.
GetAttr(cancel <-chan struct{}, input *GetAttrIn, out *AttrOut) (code Status)
SetAttr(cancel <-chan struct{}, input *SetAttrIn, out *AttrOut) (code Status)
// Modifying structure.
Mknod(cancel <-chan struct{}, input *MknodIn, name string, out *EntryOut) (code Status)
Mkdir(cancel <-chan struct{}, input *MkdirIn, name string, out *EntryOut) (code Status)
Unlink(cancel <-chan struct{}, header *InHeader, name string) (code Status)
Rmdir(cancel <-chan struct{}, header *InHeader, name string) (code Status)
Rename(cancel <-chan struct{}, input *RenameIn, oldName string, newName string) (code Status)
Link(cancel <-chan struct{}, input *LinkIn, filename string, out *EntryOut) (code Status)
Symlink(cancel <-chan struct{}, header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status)
Readlink(cancel <-chan struct{}, header *InHeader) (out []byte, code Status)
Access(cancel <-chan struct{}, input *AccessIn) (code Status)
// Extended attributes.
// GetXAttr reads an extended attribute, and should return the
// number of bytes. If the buffer is too small, return ERANGE,
// with the required buffer size.
GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (sz uint32, code Status)
// ListXAttr lists extended attributes as '\0' delimited byte
// slice, and return the number of bytes. If the buffer is too
// small, return ERANGE, with the required buffer size.
ListXAttr(cancel <-chan struct{}, header *InHeader, dest []byte) (uint32, Status)
// SetAttr writes an extended attribute.
SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr string, data []byte) Status
// RemoveXAttr removes an extended attribute.
RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) (code Status)
// File handling.
Create(cancel <-chan struct{}, input *CreateIn, name string, out *CreateOut) (code Status)
Open(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status)
Read(cancel <-chan struct{}, input *ReadIn, buf []byte) (ReadResult, Status)
Lseek(cancel <-chan struct{}, in *LseekIn, out *LseekOut) Status
// File locking
GetLk(cancel <-chan struct{}, input *LkIn, out *LkOut) (code Status)
SetLk(cancel <-chan struct{}, input *LkIn) (code Status)
SetLkw(cancel <-chan struct{}, input *LkIn) (code Status)
Release(cancel <-chan struct{}, input *ReleaseIn)
Write(cancel <-chan struct{}, input *WriteIn, data []byte) (written uint32, code Status)
CopyFileRange(cancel <-chan struct{}, input *CopyFileRangeIn) (written uint32, code Status)
Flush(cancel <-chan struct{}, input *FlushIn) Status
Fsync(cancel <-chan struct{}, input *FsyncIn) (code Status)
Fallocate(cancel <-chan struct{}, input *FallocateIn) (code Status)
// Directory handling
OpenDir(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status)
ReadDir(cancel <-chan struct{}, input *ReadIn, out *DirEntryList) Status
ReadDirPlus(cancel <-chan struct{}, input *ReadIn, out *DirEntryList) Status
ReleaseDir(input *ReleaseIn)
FsyncDir(cancel <-chan struct{}, input *FsyncIn) (code Status)
StatFs(cancel <-chan struct{}, input *InHeader, out *StatfsOut) (code Status)
// This is called on processing the first request. The
// filesystem implementation can use the server argument to
// talk back to the kernel (through notify methods).
Init(*Server)
}

79
vendor/github.com/hanwen/go-fuse/v2/fuse/attr.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"os"
"syscall"
"time"
)
func (a *Attr) IsFifo() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFIFO }
// IsChar reports whether the FileInfo describes a character special file.
func (a *Attr) IsChar() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFCHR }
// IsDir reports whether the FileInfo describes a directory.
func (a *Attr) IsDir() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFDIR }
// IsBlock reports whether the FileInfo describes a block special file.
func (a *Attr) IsBlock() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFBLK }
// IsRegular reports whether the FileInfo describes a regular file.
func (a *Attr) IsRegular() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFREG }
// IsSymlink reports whether the FileInfo describes a symbolic link.
func (a *Attr) IsSymlink() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFLNK }
// IsSocket reports whether the FileInfo describes a socket.
func (a *Attr) IsSocket() bool { return (uint32(a.Mode) & syscall.S_IFMT) == syscall.S_IFSOCK }
func (a *Attr) SetTimes(access *time.Time, mod *time.Time, chstatus *time.Time) {
if access != nil {
a.Atime = uint64(access.Unix())
a.Atimensec = uint32(access.Nanosecond())
}
if mod != nil {
a.Mtime = uint64(mod.Unix())
a.Mtimensec = uint32(mod.Nanosecond())
}
if chstatus != nil {
a.Ctime = uint64(chstatus.Unix())
a.Ctimensec = uint32(chstatus.Nanosecond())
}
}
func (a *Attr) ChangeTime() time.Time {
return time.Unix(int64(a.Ctime), int64(a.Ctimensec))
}
func (a *Attr) AccessTime() time.Time {
return time.Unix(int64(a.Atime), int64(a.Atimensec))
}
func (a *Attr) ModTime() time.Time {
return time.Unix(int64(a.Mtime), int64(a.Mtimensec))
}
func ToStatT(f os.FileInfo) *syscall.Stat_t {
s, _ := f.Sys().(*syscall.Stat_t)
if s != nil {
return s
}
return nil
}
func ToAttr(f os.FileInfo) *Attr {
if f == nil {
return nil
}
s := ToStatT(f)
if s != nil {
a := &Attr{}
a.FromStat(s)
return a
}
return nil
}

View File

@ -0,0 +1,26 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"syscall"
)
func (a *Attr) FromStat(s *syscall.Stat_t) {
a.Ino = uint64(s.Ino)
a.Size = uint64(s.Size)
a.Blocks = uint64(s.Blocks)
a.Atime = uint64(s.Atimespec.Sec)
a.Atimensec = uint32(s.Atimespec.Nsec)
a.Mtime = uint64(s.Mtimespec.Sec)
a.Mtimensec = uint32(s.Mtimespec.Nsec)
a.Ctime = uint64(s.Ctimespec.Sec)
a.Ctimensec = uint32(s.Ctimespec.Nsec)
a.Mode = uint32(s.Mode)
a.Nlink = uint32(s.Nlink)
a.Uid = uint32(s.Uid)
a.Gid = uint32(s.Gid)
a.Rdev = uint32(s.Rdev)
}

27
vendor/github.com/hanwen/go-fuse/v2/fuse/attr_linux.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"syscall"
)
func (a *Attr) FromStat(s *syscall.Stat_t) {
a.Ino = uint64(s.Ino)
a.Size = uint64(s.Size)
a.Blocks = uint64(s.Blocks)
a.Atime = uint64(s.Atim.Sec)
a.Atimensec = uint32(s.Atim.Nsec)
a.Mtime = uint64(s.Mtim.Sec)
a.Mtimensec = uint32(s.Mtim.Nsec)
a.Ctime = uint64(s.Ctim.Sec)
a.Ctimensec = uint32(s.Ctim.Nsec)
a.Mode = s.Mode
a.Nlink = uint32(s.Nlink)
a.Uid = uint32(s.Uid)
a.Gid = uint32(s.Gid)
a.Rdev = uint32(s.Rdev)
a.Blksize = uint32(s.Blksize)
}

69
vendor/github.com/hanwen/go-fuse/v2/fuse/bufferpool.go generated vendored Normal file
View File

@ -0,0 +1,69 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"os"
"sync"
)
// bufferPool implements explicit memory management. It is used for
// minimizing the GC overhead of communicating with the kernel.
type bufferPool struct {
lock sync.Mutex
// For each page size multiple a list of slice pointers.
buffersBySize []*sync.Pool
}
var pageSize = os.Getpagesize()
func (p *bufferPool) getPool(pageCount int) *sync.Pool {
p.lock.Lock()
for len(p.buffersBySize) < pageCount+1 {
p.buffersBySize = append(p.buffersBySize, nil)
}
if p.buffersBySize[pageCount] == nil {
p.buffersBySize[pageCount] = &sync.Pool{
New: func() interface{} { return make([]byte, pageSize*pageCount) },
}
}
pool := p.buffersBySize[pageCount]
p.lock.Unlock()
return pool
}
// AllocBuffer creates a buffer of at least the given size. After use,
// it should be deallocated with FreeBuffer().
func (p *bufferPool) AllocBuffer(size uint32) []byte {
sz := int(size)
if sz < pageSize {
sz = pageSize
}
if sz%pageSize != 0 {
sz += pageSize
}
pages := sz / pageSize
b := p.getPool(pages).Get().([]byte)
return b[:size]
}
// FreeBuffer takes back a buffer if it was allocated through
// AllocBuffer. It is not an error to call FreeBuffer() on a slice
// obtained elsewhere.
func (p *bufferPool) FreeBuffer(slice []byte) {
if slice == nil {
return
}
if cap(slice)%pageSize != 0 || cap(slice) == 0 {
return
}
pages := cap(slice) / pageSize
slice = slice[:cap(slice)]
p.getPool(pages).Put(slice)
}

37
vendor/github.com/hanwen/go-fuse/v2/fuse/constants.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"os"
"syscall"
)
const (
FUSE_ROOT_ID = 1
FUSE_UNKNOWN_INO = 0xffffffff
CUSE_UNRESTRICTED_IOCTL = (1 << 0)
FUSE_LK_FLOCK = (1 << 0)
FUSE_IOCTL_MAX_IOV = 256
FUSE_POLL_SCHEDULE_NOTIFY = (1 << 0)
CUSE_INIT_INFO_MAX = 4096
S_IFDIR = syscall.S_IFDIR
S_IFREG = syscall.S_IFREG
S_IFLNK = syscall.S_IFLNK
S_IFIFO = syscall.S_IFIFO
CUSE_INIT = 4096
O_ANYWRITE = uint32(os.O_WRONLY | os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_TRUNC)
logicalBlockSize = 512
)

View File

@ -0,0 +1,9 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
// arbitrary values
const syscall_O_LARGEFILE = 1 << 29
const syscall_O_NOATIME = 1 << 30

View File

@ -0,0 +1,12 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"syscall"
)
const syscall_O_LARGEFILE = syscall.O_LARGEFILE
const syscall_O_NOATIME = syscall.O_NOATIME

61
vendor/github.com/hanwen/go-fuse/v2/fuse/context.go generated vendored Normal file
View File

@ -0,0 +1,61 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"context"
"time"
)
// Context passes along cancelation signal and request data (PID, GID,
// UID). The name of this class predates the standard "context"
// package from Go, but it does implement the context.Context
// interface.
//
// When a FUSE request is canceled, the API routine should respond by
// returning the EINTR status code.
type Context struct {
Caller
Cancel <-chan struct{}
}
func (c *Context) Deadline() (time.Time, bool) {
return time.Time{}, false
}
func (c *Context) Done() <-chan struct{} {
return c.Cancel
}
func (c *Context) Err() error {
select {
case <-c.Cancel:
return context.Canceled
default:
return nil
}
}
type callerKeyType struct{}
var callerKey callerKeyType
func FromContext(ctx context.Context) (*Caller, bool) {
v, ok := ctx.Value(callerKey).(*Caller)
return v, ok
}
func NewContext(ctx context.Context, caller *Caller) context.Context {
return context.WithValue(ctx, callerKey, caller)
}
func (c *Context) Value(key interface{}) interface{} {
if key == callerKey {
return &c.Caller
}
return nil
}
var _ = context.Context((*Context)(nil))

168
vendor/github.com/hanwen/go-fuse/v2/fuse/defaultraw.go generated vendored Normal file
View File

@ -0,0 +1,168 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"os"
)
// NewDefaultRawFileSystem returns ENOSYS (not implemented) for all
// operations.
func NewDefaultRawFileSystem() RawFileSystem {
return (*defaultRawFileSystem)(nil)
}
type defaultRawFileSystem struct{}
func (fs *defaultRawFileSystem) Init(*Server) {
}
func (fs *defaultRawFileSystem) String() string {
return os.Args[0]
}
func (fs *defaultRawFileSystem) SetDebug(dbg bool) {
}
func (fs *defaultRawFileSystem) StatFs(cancel <-chan struct{}, header *InHeader, out *StatfsOut) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) Lookup(cancel <-chan struct{}, header *InHeader, name string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Forget(nodeID, nlookup uint64) {
}
func (fs *defaultRawFileSystem) GetAttr(cancel <-chan struct{}, input *GetAttrIn, out *AttrOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Open(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status) {
return OK
}
func (fs *defaultRawFileSystem) SetAttr(cancel <-chan struct{}, input *SetAttrIn, out *AttrOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Readlink(cancel <-chan struct{}, header *InHeader) (out []byte, code Status) {
return nil, ENOSYS
}
func (fs *defaultRawFileSystem) Mknod(cancel <-chan struct{}, input *MknodIn, name string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Mkdir(cancel <-chan struct{}, input *MkdirIn, name string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Unlink(cancel <-chan struct{}, header *InHeader, name string) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Rmdir(cancel <-chan struct{}, header *InHeader, name string) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Symlink(cancel <-chan struct{}, header *InHeader, pointedTo string, linkName string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Rename(cancel <-chan struct{}, input *RenameIn, oldName string, newName string) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Link(cancel <-chan struct{}, input *LinkIn, name string, out *EntryOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (size uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr string, data []byte) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader, dest []byte) (n uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) Access(cancel <-chan struct{}, input *AccessIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Create(cancel <-chan struct{}, input *CreateIn, name string, out *CreateOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) OpenDir(cancel <-chan struct{}, input *OpenIn, out *OpenOut) (status Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Read(cancel <-chan struct{}, input *ReadIn, buf []byte) (ReadResult, Status) {
return nil, ENOSYS
}
func (fs *defaultRawFileSystem) GetLk(cancel <-chan struct{}, in *LkIn, out *LkOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) SetLk(cancel <-chan struct{}, in *LkIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) SetLkw(cancel <-chan struct{}, in *LkIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Release(cancel <-chan struct{}, input *ReleaseIn) {
}
func (fs *defaultRawFileSystem) Write(cancel <-chan struct{}, input *WriteIn, data []byte) (written uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) Flush(cancel <-chan struct{}, input *FlushIn) Status {
return OK
}
func (fs *defaultRawFileSystem) Fsync(cancel <-chan struct{}, input *FsyncIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) ReadDir(cancel <-chan struct{}, input *ReadIn, l *DirEntryList) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) ReadDirPlus(cancel <-chan struct{}, input *ReadIn, l *DirEntryList) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) ReleaseDir(input *ReleaseIn) {
}
func (fs *defaultRawFileSystem) FsyncDir(cancel <-chan struct{}, input *FsyncIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) Fallocate(cancel <-chan struct{}, in *FallocateIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) CopyFileRange(cancel <-chan struct{}, input *CopyFileRangeIn) (written uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) Lseek(cancel <-chan struct{}, in *LseekIn, out *LseekOut) Status {
return ENOSYS
}

118
vendor/github.com/hanwen/go-fuse/v2/fuse/direntry.go generated vendored Normal file
View File

@ -0,0 +1,118 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
// all of the code for DirEntryList.
import (
"fmt"
"unsafe"
)
var eightPadding [8]byte
const direntSize = int(unsafe.Sizeof(_Dirent{}))
// DirEntry is a type for PathFileSystem and NodeFileSystem to return
// directory contents in.
type DirEntry struct {
// Mode is the file's mode. Only the high bits (eg. S_IFDIR)
// are considered.
Mode uint32
// Name is the basename of the file in the directory.
Name string
// Ino is the inode number.
Ino uint64
}
func (d DirEntry) String() string {
return fmt.Sprintf("%o: %q ino=%d", d.Mode, d.Name, d.Ino)
}
// DirEntryList holds the return value for READDIR and READDIRPLUS
// opcodes.
type DirEntryList struct {
buf []byte
size int
offset uint64
}
// NewDirEntryList creates a DirEntryList with the given data buffer
// and offset.
func NewDirEntryList(data []byte, off uint64) *DirEntryList {
return &DirEntryList{
buf: data[:0],
size: len(data),
offset: off,
}
}
// AddDirEntry tries to add an entry, and reports whether it
// succeeded.
func (l *DirEntryList) AddDirEntry(e DirEntry) bool {
return l.Add(0, e.Name, e.Ino, e.Mode)
}
// Add adds a direntry to the DirEntryList, returning whether it
// succeeded.
func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) bool {
if inode == 0 {
inode = FUSE_UNKNOWN_INO
}
padding := (8 - len(name)&7) & 7
delta := padding + direntSize + len(name) + prefix
oldLen := len(l.buf)
newLen := delta + oldLen
if newLen > l.size {
return false
}
l.buf = l.buf[:newLen]
oldLen += prefix
dirent := (*_Dirent)(unsafe.Pointer(&l.buf[oldLen]))
dirent.Off = l.offset + 1
dirent.Ino = inode
dirent.NameLen = uint32(len(name))
dirent.Typ = (mode & 0170000) >> 12
oldLen += direntSize
copy(l.buf[oldLen:], name)
oldLen += len(name)
if padding > 0 {
copy(l.buf[oldLen:], eightPadding[:padding])
}
l.offset = dirent.Off
return true
}
// AddDirLookupEntry is used for ReadDirPlus. It serializes a DirEntry
// and returns the space for entry. If no space is left, returns a nil
// pointer.
func (l *DirEntryList) AddDirLookupEntry(e DirEntry) *EntryOut {
lastStart := len(l.buf)
ok := l.Add(int(unsafe.Sizeof(EntryOut{})), e.Name,
e.Ino, e.Mode)
if !ok {
return nil
}
result := (*EntryOut)(unsafe.Pointer(&l.buf[lastStart]))
*result = EntryOut{}
return result
}
// FixMode overrides the mode of the last direntry that was added. This can
// be needed when a directory changes while READDIRPLUS is running.
func (l *DirEntryList) FixMode(mode uint32) {
oldLen := len(l.buf) - int(unsafe.Sizeof(_Dirent{}))
dirent := (*_Dirent)(unsafe.Pointer(&l.buf[oldLen]))
dirent.Typ = (mode & 0170000) >> 12
}
func (l *DirEntryList) bytes() []byte {
return l.buf
}

100
vendor/github.com/hanwen/go-fuse/v2/fuse/misc.go generated vendored Normal file
View File

@ -0,0 +1,100 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Random odds and ends.
package fuse
import (
"fmt"
"log"
"os"
"reflect"
"syscall"
"time"
"unsafe"
)
func (code Status) String() string {
if code <= 0 {
return []string{
"OK",
"NOTIFY_POLL",
"NOTIFY_INVAL_INODE",
"NOTIFY_INVAL_ENTRY",
"NOTIFY_STORE_CACHE",
"NOTIFY_RETRIEVE_CACHE",
"NOTIFY_DELETE",
}[-code]
}
return fmt.Sprintf("%d=%v", int(code), syscall.Errno(code))
}
func (code Status) Ok() bool {
return code == OK
}
// ToStatus extracts an errno number from Go error objects. If it
// fails, it logs an error and returns ENOSYS.
func ToStatus(err error) Status {
switch err {
case nil:
return OK
case os.ErrPermission:
return EPERM
case os.ErrExist:
return Status(syscall.EEXIST)
case os.ErrNotExist:
return ENOENT
case os.ErrInvalid:
return EINVAL
}
switch t := err.(type) {
case syscall.Errno:
return Status(t)
case *os.SyscallError:
return Status(t.Err.(syscall.Errno))
case *os.PathError:
return ToStatus(t.Err)
case *os.LinkError:
return ToStatus(t.Err)
}
log.Println("can't convert error type:", err)
return ENOSYS
}
func toSlice(dest *[]byte, ptr unsafe.Pointer, byteCount uintptr) {
h := (*reflect.SliceHeader)(unsafe.Pointer(dest))
*h = reflect.SliceHeader{
Data: uintptr(ptr),
Len: int(byteCount),
Cap: int(byteCount),
}
}
func CurrentOwner() *Owner {
return &Owner{
Uid: uint32(os.Getuid()),
Gid: uint32(os.Getgid()),
}
}
const _UTIME_OMIT = ((1 << 30) - 2)
// UtimeToTimespec converts a "Time" pointer as passed to Utimens to a
// "Timespec" that can be passed to the utimensat syscall.
// A nil pointer is converted to the special UTIME_OMIT value.
func UtimeToTimespec(t *time.Time) (ts syscall.Timespec) {
if t == nil {
ts.Nsec = _UTIME_OMIT
} else {
ts = syscall.NsecToTimespec(t.UnixNano())
// Go bug https://github.com/golang/go/issues/12777
if ts.Nsec < 0 {
ts.Nsec = 0
}
}
return ts
}

View File

@ -0,0 +1,97 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
)
func openFUSEDevice() (*os.File, error) {
fs, err := filepath.Glob("/dev/osxfuse*")
if err != nil {
return nil, err
}
if len(fs) == 0 {
bin := oldLoadBin
if _, err := os.Stat(newLoadBin); err == nil {
bin = newLoadBin
}
cmd := exec.Command(bin)
if err := cmd.Run(); err != nil {
return nil, err
}
fs, err = filepath.Glob("/dev/osxfuse*")
if err != nil {
return nil, err
}
}
for _, fn := range fs {
f, err := os.OpenFile(fn, os.O_RDWR, 0)
if err != nil {
continue
}
return f, nil
}
return nil, fmt.Errorf("all FUSE devices busy")
}
const oldLoadBin = "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs"
const newLoadBin = "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse"
const oldMountBin = "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
const newMountBin = "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse"
func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) {
f, err := openFUSEDevice()
if err != nil {
return 0, err
}
bin := oldMountBin
if _, err := os.Stat(newMountBin); err == nil {
bin = newMountBin
}
cmd := exec.Command(bin, "-o", strings.Join(opts.optionsStrings(), ","), "-o", fmt.Sprintf("iosize=%d", opts.MaxWrite), "3", mountPoint)
cmd.ExtraFiles = []*os.File{f}
cmd.Env = append(os.Environ(), "MOUNT_FUSEFS_CALL_BY_LIB=", "MOUNT_OSXFUSE_CALL_BY_LIB=",
"MOUNT_OSXFUSE_DAEMON_PATH="+os.Args[0],
"MOUNT_FUSEFS_DAEMON_PATH="+os.Args[0])
var out, errOut bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &errOut
if err := cmd.Start(); err != nil {
f.Close()
return 0, err
}
go func() {
err := cmd.Wait()
if err != nil {
err = fmt.Errorf("mount_osxfusefs failed: %v. Stderr: %s, Stdout: %s", err, errOut.String(), out.String())
}
ready <- err
close(ready)
}()
// The finalizer for f will close its fd so we return a dup.
defer f.Close()
return syscall.Dup(int(f.Fd()))
}
func unmount(dir string) error {
return syscall.Unmount(dir, 0)
}

143
vendor/github.com/hanwen/go-fuse/v2/fuse/mount_linux.go generated vendored Normal file
View File

@ -0,0 +1,143 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"bytes"
"fmt"
"os"
"os/exec"
"path"
"strings"
"syscall"
"unsafe"
)
func unixgramSocketpair() (l, r *os.File, err error) {
fd, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
if err != nil {
return nil, nil, os.NewSyscallError("socketpair",
err.(syscall.Errno))
}
l = os.NewFile(uintptr(fd[0]), "socketpair-half1")
r = os.NewFile(uintptr(fd[1]), "socketpair-half2")
return
}
// Create a FUSE FS on the specified mount point. The returned
// mount point is always absolute.
func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) {
local, remote, err := unixgramSocketpair()
if err != nil {
return
}
defer local.Close()
defer remote.Close()
bin, err := fusermountBinary()
if err != nil {
return 0, err
}
cmd := []string{bin, mountPoint}
if s := opts.optionsStrings(); len(s) > 0 {
cmd = append(cmd, "-o", strings.Join(s, ","))
}
proc, err := os.StartProcess(bin,
cmd,
&os.ProcAttr{
Env: []string{"_FUSE_COMMFD=3"},
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr, remote}})
if err != nil {
return
}
w, err := proc.Wait()
if err != nil {
return
}
if !w.Success() {
err = fmt.Errorf("fusermount exited with code %v\n", w.Sys())
return
}
fd, err = getConnection(local)
if err != nil {
return -1, err
}
// golang sets CLOEXEC on file descriptors when they are
// acquired through normal operations (e.g. open).
// Buf for fd, we have to set CLOEXEC manually
syscall.CloseOnExec(fd)
close(ready)
return fd, err
}
func unmount(mountPoint string) (err error) {
bin, err := fusermountBinary()
if err != nil {
return err
}
errBuf := bytes.Buffer{}
cmd := exec.Command(bin, "-u", mountPoint)
cmd.Stderr = &errBuf
err = cmd.Run()
if errBuf.Len() > 0 {
return fmt.Errorf("%s (code %v)\n",
errBuf.String(), err)
}
return err
}
func getConnection(local *os.File) (int, error) {
var data [4]byte
control := make([]byte, 4*256)
// n, oobn, recvflags, from, errno - todo: error checking.
_, oobn, _, _,
err := syscall.Recvmsg(
int(local.Fd()), data[:], control[:], 0)
if err != nil {
return 0, err
}
message := *(*syscall.Cmsghdr)(unsafe.Pointer(&control[0]))
fd := *(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(&control[0])) + syscall.SizeofCmsghdr))
if message.Type != 1 {
return 0, fmt.Errorf("getConnection: recvmsg returned wrong control type: %d", message.Type)
}
if oobn <= syscall.SizeofCmsghdr {
return 0, fmt.Errorf("getConnection: too short control message. Length: %d", oobn)
}
if fd < 0 {
return 0, fmt.Errorf("getConnection: fd < 0: %d", fd)
}
return int(fd), nil
}
// lookPathFallback - search binary in PATH and, if that fails,
// in fallbackDir. This is useful if PATH is possible empty.
func lookPathFallback(file string, fallbackDir string) (string, error) {
binPath, err := exec.LookPath(file)
if err == nil {
return binPath, nil
}
abs := path.Join(fallbackDir, file)
return exec.LookPath(abs)
}
func fusermountBinary() (string, error) {
return lookPathFallback("fusermount", "/bin")
}
func umountBinary() (string, error) {
return lookPathFallback("umount", "/bin")
}

804
vendor/github.com/hanwen/go-fuse/v2/fuse/opcode.go generated vendored Normal file
View File

@ -0,0 +1,804 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"bytes"
"fmt"
"log"
"reflect"
"runtime"
"time"
"unsafe"
)
const (
_OP_LOOKUP = uint32(1)
_OP_FORGET = uint32(2)
_OP_GETATTR = uint32(3)
_OP_SETATTR = uint32(4)
_OP_READLINK = uint32(5)
_OP_SYMLINK = uint32(6)
_OP_MKNOD = uint32(8)
_OP_MKDIR = uint32(9)
_OP_UNLINK = uint32(10)
_OP_RMDIR = uint32(11)
_OP_RENAME = uint32(12)
_OP_LINK = uint32(13)
_OP_OPEN = uint32(14)
_OP_READ = uint32(15)
_OP_WRITE = uint32(16)
_OP_STATFS = uint32(17)
_OP_RELEASE = uint32(18)
_OP_FSYNC = uint32(20)
_OP_SETXATTR = uint32(21)
_OP_GETXATTR = uint32(22)
_OP_LISTXATTR = uint32(23)
_OP_REMOVEXATTR = uint32(24)
_OP_FLUSH = uint32(25)
_OP_INIT = uint32(26)
_OP_OPENDIR = uint32(27)
_OP_READDIR = uint32(28)
_OP_RELEASEDIR = uint32(29)
_OP_FSYNCDIR = uint32(30)
_OP_GETLK = uint32(31)
_OP_SETLK = uint32(32)
_OP_SETLKW = uint32(33)
_OP_ACCESS = uint32(34)
_OP_CREATE = uint32(35)
_OP_INTERRUPT = uint32(36)
_OP_BMAP = uint32(37)
_OP_DESTROY = uint32(38)
_OP_IOCTL = uint32(39)
_OP_POLL = uint32(40)
_OP_NOTIFY_REPLY = uint32(41)
_OP_BATCH_FORGET = uint32(42)
_OP_FALLOCATE = uint32(43) // protocol version 19.
_OP_READDIRPLUS = uint32(44) // protocol version 21.
_OP_RENAME2 = uint32(45) // protocol version 23.
_OP_LSEEK = uint32(46) // protocol version 24
_OP_COPY_FILE_RANGE = uint32(47) // protocol version 28.
// The following entries don't have to be compatible across Go-FUSE versions.
_OP_NOTIFY_INVAL_ENTRY = uint32(100)
_OP_NOTIFY_INVAL_INODE = uint32(101)
_OP_NOTIFY_STORE_CACHE = uint32(102)
_OP_NOTIFY_RETRIEVE_CACHE = uint32(103)
_OP_NOTIFY_DELETE = uint32(104) // protocol version 18
_OPCODE_COUNT = uint32(105)
)
////////////////////////////////////////////////////////////////
func doInit(server *Server, req *request) {
input := (*InitIn)(req.inData)
if input.Major != _FUSE_KERNEL_VERSION {
log.Printf("Major versions does not match. Given %d, want %d\n", input.Major, _FUSE_KERNEL_VERSION)
req.status = EIO
return
}
if input.Minor < _MINIMUM_MINOR_VERSION {
log.Printf("Minor version is less than we support. Given %d, want at least %d\n", input.Minor, _MINIMUM_MINOR_VERSION)
req.status = EIO
return
}
server.reqMu.Lock()
server.kernelSettings = *input
server.kernelSettings.Flags = input.Flags & (CAP_ASYNC_READ | CAP_BIG_WRITES | CAP_FILE_OPS |
CAP_READDIRPLUS | CAP_NO_OPEN_SUPPORT | CAP_PARALLEL_DIROPS)
if server.opts.EnableLocks {
server.kernelSettings.Flags |= CAP_FLOCK_LOCKS | CAP_POSIX_LOCKS
}
dataCacheMode := input.Flags & CAP_AUTO_INVAL_DATA
if server.opts.ExplicitDataCacheControl {
// we don't want CAP_AUTO_INVAL_DATA even if we cannot go into fully explicit mode
dataCacheMode = 0
explicit := input.Flags & CAP_EXPLICIT_INVAL_DATA
if explicit != 0 {
dataCacheMode = explicit
}
}
server.kernelSettings.Flags |= dataCacheMode
if input.Minor >= 13 {
server.setSplice()
}
server.reqMu.Unlock()
out := (*InitOut)(req.outData())
*out = InitOut{
Major: _FUSE_KERNEL_VERSION,
Minor: _OUR_MINOR_VERSION,
MaxReadAhead: input.MaxReadAhead,
Flags: server.kernelSettings.Flags,
MaxWrite: uint32(server.opts.MaxWrite),
CongestionThreshold: uint16(server.opts.MaxBackground * 3 / 4),
MaxBackground: uint16(server.opts.MaxBackground),
}
if server.opts.MaxReadAhead != 0 && uint32(server.opts.MaxReadAhead) < out.MaxReadAhead {
out.MaxReadAhead = uint32(server.opts.MaxReadAhead)
}
if out.Minor > input.Minor {
out.Minor = input.Minor
}
if out.Minor <= 22 {
tweaked := *req.handler
// v8-v22 don't have TimeGran and further fields.
tweaked.OutputSize = 24
req.handler = &tweaked
}
req.status = OK
}
func doOpen(server *Server, req *request) {
out := (*OpenOut)(req.outData())
status := server.fileSystem.Open(req.cancel, (*OpenIn)(req.inData), out)
req.status = status
if status != OK {
return
}
}
func doCreate(server *Server, req *request) {
out := (*CreateOut)(req.outData())
status := server.fileSystem.Create(req.cancel, (*CreateIn)(req.inData), req.filenames[0], out)
req.status = status
}
func doReadDir(server *Server, req *request) {
in := (*ReadIn)(req.inData)
buf := server.allocOut(req, in.Size)
out := NewDirEntryList(buf, uint64(in.Offset))
code := server.fileSystem.ReadDir(req.cancel, in, out)
req.flatData = out.bytes()
req.status = code
}
func doReadDirPlus(server *Server, req *request) {
in := (*ReadIn)(req.inData)
buf := server.allocOut(req, in.Size)
out := NewDirEntryList(buf, uint64(in.Offset))
code := server.fileSystem.ReadDirPlus(req.cancel, in, out)
req.flatData = out.bytes()
req.status = code
}
func doOpenDir(server *Server, req *request) {
out := (*OpenOut)(req.outData())
status := server.fileSystem.OpenDir(req.cancel, (*OpenIn)(req.inData), out)
req.status = status
}
func doSetattr(server *Server, req *request) {
out := (*AttrOut)(req.outData())
req.status = server.fileSystem.SetAttr(req.cancel, (*SetAttrIn)(req.inData), out)
}
func doWrite(server *Server, req *request) {
n, status := server.fileSystem.Write(req.cancel, (*WriteIn)(req.inData), req.arg)
o := (*WriteOut)(req.outData())
o.Size = n
req.status = status
}
func doNotifyReply(server *Server, req *request) {
reply := (*NotifyRetrieveIn)(req.inData)
server.retrieveMu.Lock()
reading := server.retrieveTab[reply.Unique]
delete(server.retrieveTab, reply.Unique)
server.retrieveMu.Unlock()
badf := func(format string, argv ...interface{}) {
log.Printf("notify reply: "+format, argv...)
}
if reading == nil {
badf("unexpected unique - ignoring")
return
}
reading.n = 0
reading.st = EIO
defer close(reading.ready)
if reading.nodeid != reply.NodeId {
badf("inode mismatch: expected %s, got %s", reading.nodeid, reply.NodeId)
return
}
if reading.offset != reply.Offset {
badf("offset mismatch: expected @%d, got @%d", reading.offset, reply.Offset)
return
}
if len(reading.dest) < len(req.arg) {
badf("too much data: requested %db, got %db (will use only %db)", len(reading.dest), len(req.arg), len(reading.dest))
}
reading.n = copy(reading.dest, req.arg)
reading.st = OK
}
const _SECURITY_CAPABILITY = "security.capability"
const _SECURITY_ACL = "system.posix_acl_access"
const _SECURITY_ACL_DEFAULT = "system.posix_acl_default"
func doGetXAttr(server *Server, req *request) {
if server.opts.DisableXAttrs {
req.status = ENOSYS
return
}
if server.opts.IgnoreSecurityLabels && req.inHeader.Opcode == _OP_GETXATTR {
fn := req.filenames[0]
if fn == _SECURITY_CAPABILITY || fn == _SECURITY_ACL_DEFAULT ||
fn == _SECURITY_ACL {
req.status = ENOATTR
return
}
}
input := (*GetXAttrIn)(req.inData)
req.flatData = server.allocOut(req, input.Size)
out := (*GetXAttrOut)(req.outData())
var n uint32
switch req.inHeader.Opcode {
case _OP_GETXATTR:
n, req.status = server.fileSystem.GetXAttr(req.cancel, req.inHeader, req.filenames[0], req.flatData)
case _OP_LISTXATTR:
n, req.status = server.fileSystem.ListXAttr(req.cancel, req.inHeader, req.flatData)
default:
req.status = ENOSYS
}
if input.Size == 0 && req.status == ERANGE {
// For input.size==0, returning ERANGE is an error.
req.status = OK
out.Size = n
} else if req.status.Ok() {
req.flatData = req.flatData[:n]
out.Size = n
} else {
req.flatData = req.flatData[:0]
}
}
func doGetAttr(server *Server, req *request) {
out := (*AttrOut)(req.outData())
s := server.fileSystem.GetAttr(req.cancel, (*GetAttrIn)(req.inData), out)
req.status = s
}
// doForget - forget one NodeId
func doForget(server *Server, req *request) {
if !server.opts.RememberInodes {
server.fileSystem.Forget(req.inHeader.NodeId, (*ForgetIn)(req.inData).Nlookup)
}
}
// doBatchForget - forget a list of NodeIds
func doBatchForget(server *Server, req *request) {
in := (*_BatchForgetIn)(req.inData)
wantBytes := uintptr(in.Count) * unsafe.Sizeof(_ForgetOne{})
if uintptr(len(req.arg)) < wantBytes {
// We have no return value to complain, so log an error.
log.Printf("Too few bytes for batch forget. Got %d bytes, want %d (%d entries)",
len(req.arg), wantBytes, in.Count)
}
h := &reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(&req.arg[0])),
Len: int(in.Count),
Cap: int(in.Count),
}
forgets := *(*[]_ForgetOne)(unsafe.Pointer(h))
for i, f := range forgets {
if server.opts.Debug {
log.Printf("doBatchForget: rx %d %d/%d: FORGET i%d {Nlookup=%d}",
req.inHeader.Unique, i+1, len(forgets), f.NodeId, f.Nlookup)
}
if f.NodeId == pollHackInode {
continue
}
server.fileSystem.Forget(f.NodeId, f.Nlookup)
}
}
func doReadlink(server *Server, req *request) {
req.flatData, req.status = server.fileSystem.Readlink(req.cancel, req.inHeader)
}
func doLookup(server *Server, req *request) {
out := (*EntryOut)(req.outData())
s := server.fileSystem.Lookup(req.cancel, req.inHeader, req.filenames[0], out)
req.status = s
}
func doMknod(server *Server, req *request) {
out := (*EntryOut)(req.outData())
req.status = server.fileSystem.Mknod(req.cancel, (*MknodIn)(req.inData), req.filenames[0], out)
}
func doMkdir(server *Server, req *request) {
out := (*EntryOut)(req.outData())
req.status = server.fileSystem.Mkdir(req.cancel, (*MkdirIn)(req.inData), req.filenames[0], out)
}
func doUnlink(server *Server, req *request) {
req.status = server.fileSystem.Unlink(req.cancel, req.inHeader, req.filenames[0])
}
func doRmdir(server *Server, req *request) {
req.status = server.fileSystem.Rmdir(req.cancel, req.inHeader, req.filenames[0])
}
func doLink(server *Server, req *request) {
out := (*EntryOut)(req.outData())
req.status = server.fileSystem.Link(req.cancel, (*LinkIn)(req.inData), req.filenames[0], out)
}
func doRead(server *Server, req *request) {
in := (*ReadIn)(req.inData)
buf := server.allocOut(req, in.Size)
req.readResult, req.status = server.fileSystem.Read(req.cancel, in, buf)
if fd, ok := req.readResult.(*readResultFd); ok {
req.fdData = fd
req.flatData = nil
} else if req.readResult != nil && req.status.Ok() {
req.flatData, req.status = req.readResult.Bytes(buf)
}
}
func doFlush(server *Server, req *request) {
req.status = server.fileSystem.Flush(req.cancel, (*FlushIn)(req.inData))
}
func doRelease(server *Server, req *request) {
server.fileSystem.Release(req.cancel, (*ReleaseIn)(req.inData))
}
func doFsync(server *Server, req *request) {
req.status = server.fileSystem.Fsync(req.cancel, (*FsyncIn)(req.inData))
}
func doReleaseDir(server *Server, req *request) {
server.fileSystem.ReleaseDir((*ReleaseIn)(req.inData))
}
func doFsyncDir(server *Server, req *request) {
req.status = server.fileSystem.FsyncDir(req.cancel, (*FsyncIn)(req.inData))
}
func doSetXAttr(server *Server, req *request) {
splits := bytes.SplitN(req.arg, []byte{0}, 2)
req.status = server.fileSystem.SetXAttr(req.cancel, (*SetXAttrIn)(req.inData), string(splits[0]), splits[1])
}
func doRemoveXAttr(server *Server, req *request) {
req.status = server.fileSystem.RemoveXAttr(req.cancel, req.inHeader, req.filenames[0])
}
func doAccess(server *Server, req *request) {
req.status = server.fileSystem.Access(req.cancel, (*AccessIn)(req.inData))
}
func doSymlink(server *Server, req *request) {
out := (*EntryOut)(req.outData())
req.status = server.fileSystem.Symlink(req.cancel, req.inHeader, req.filenames[1], req.filenames[0], out)
}
func doRename(server *Server, req *request) {
in1 := (*Rename1In)(req.inData)
in := RenameIn{
InHeader: in1.InHeader,
Newdir: in1.Newdir,
}
req.status = server.fileSystem.Rename(req.cancel, &in, req.filenames[0], req.filenames[1])
}
func doRename2(server *Server, req *request) {
req.status = server.fileSystem.Rename(req.cancel, (*RenameIn)(req.inData), req.filenames[0], req.filenames[1])
}
func doStatFs(server *Server, req *request) {
out := (*StatfsOut)(req.outData())
req.status = server.fileSystem.StatFs(req.cancel, req.inHeader, out)
if req.status == ENOSYS && runtime.GOOS == "darwin" {
// OSX FUSE requires Statfs to be implemented for the
// mount to succeed.
*out = StatfsOut{}
req.status = OK
}
}
func doIoctl(server *Server, req *request) {
req.status = ENOSYS
}
func doDestroy(server *Server, req *request) {
req.status = OK
}
func doFallocate(server *Server, req *request) {
req.status = server.fileSystem.Fallocate(req.cancel, (*FallocateIn)(req.inData))
}
func doGetLk(server *Server, req *request) {
req.status = server.fileSystem.GetLk(req.cancel, (*LkIn)(req.inData), (*LkOut)(req.outData()))
}
func doSetLk(server *Server, req *request) {
req.status = server.fileSystem.SetLk(req.cancel, (*LkIn)(req.inData))
}
func doSetLkw(server *Server, req *request) {
req.status = server.fileSystem.SetLkw(req.cancel, (*LkIn)(req.inData))
}
func doLseek(server *Server, req *request) {
in := (*LseekIn)(req.inData)
out := (*LseekOut)(req.outData())
req.status = server.fileSystem.Lseek(req.cancel, in, out)
}
func doCopyFileRange(server *Server, req *request) {
in := (*CopyFileRangeIn)(req.inData)
out := (*WriteOut)(req.outData())
out.Size, req.status = server.fileSystem.CopyFileRange(req.cancel, in)
}
func doInterrupt(server *Server, req *request) {
input := (*InterruptIn)(req.inData)
server.reqMu.Lock()
defer server.reqMu.Unlock()
// This is slow, but this operation is rare.
for _, inflight := range server.reqInflight {
if input.Unique == inflight.inHeader.Unique && !inflight.interrupted {
close(inflight.cancel)
inflight.interrupted = true
req.status = OK
return
}
}
// not found; wait for a bit
time.Sleep(10 * time.Microsecond)
req.status = EAGAIN
}
////////////////////////////////////////////////////////////////
type operationFunc func(*Server, *request)
type castPointerFunc func(unsafe.Pointer) interface{}
type operationHandler struct {
Name string
Func operationFunc
InputSize uintptr
OutputSize uintptr
DecodeIn castPointerFunc
DecodeOut castPointerFunc
FileNames int
FileNameOut bool
}
var operationHandlers []*operationHandler
func operationName(op uint32) string {
h := getHandler(op)
if h == nil {
return "unknown"
}
return h.Name
}
func getHandler(o uint32) *operationHandler {
if o >= _OPCODE_COUNT {
return nil
}
return operationHandlers[o]
}
var maxInputSize uintptr
func init() {
operationHandlers = make([]*operationHandler, _OPCODE_COUNT)
for i := range operationHandlers {
operationHandlers[i] = &operationHandler{Name: fmt.Sprintf("OPCODE-%d", i)}
}
fileOps := []uint32{_OP_READLINK, _OP_NOTIFY_INVAL_ENTRY, _OP_NOTIFY_DELETE}
for _, op := range fileOps {
operationHandlers[op].FileNameOut = true
}
maxInputSize = 0
for op, sz := range map[uint32]uintptr{
_OP_FORGET: unsafe.Sizeof(ForgetIn{}),
_OP_BATCH_FORGET: unsafe.Sizeof(_BatchForgetIn{}),
_OP_GETATTR: unsafe.Sizeof(GetAttrIn{}),
_OP_SETATTR: unsafe.Sizeof(SetAttrIn{}),
_OP_MKNOD: unsafe.Sizeof(MknodIn{}),
_OP_MKDIR: unsafe.Sizeof(MkdirIn{}),
_OP_RENAME: unsafe.Sizeof(Rename1In{}),
_OP_LINK: unsafe.Sizeof(LinkIn{}),
_OP_OPEN: unsafe.Sizeof(OpenIn{}),
_OP_READ: unsafe.Sizeof(ReadIn{}),
_OP_WRITE: unsafe.Sizeof(WriteIn{}),
_OP_RELEASE: unsafe.Sizeof(ReleaseIn{}),
_OP_FSYNC: unsafe.Sizeof(FsyncIn{}),
_OP_SETXATTR: unsafe.Sizeof(SetXAttrIn{}),
_OP_GETXATTR: unsafe.Sizeof(GetXAttrIn{}),
_OP_LISTXATTR: unsafe.Sizeof(GetXAttrIn{}),
_OP_FLUSH: unsafe.Sizeof(FlushIn{}),
_OP_INIT: unsafe.Sizeof(InitIn{}),
_OP_OPENDIR: unsafe.Sizeof(OpenIn{}),
_OP_READDIR: unsafe.Sizeof(ReadIn{}),
_OP_RELEASEDIR: unsafe.Sizeof(ReleaseIn{}),
_OP_FSYNCDIR: unsafe.Sizeof(FsyncIn{}),
_OP_GETLK: unsafe.Sizeof(LkIn{}),
_OP_SETLK: unsafe.Sizeof(LkIn{}),
_OP_SETLKW: unsafe.Sizeof(LkIn{}),
_OP_ACCESS: unsafe.Sizeof(AccessIn{}),
_OP_CREATE: unsafe.Sizeof(CreateIn{}),
_OP_INTERRUPT: unsafe.Sizeof(InterruptIn{}),
_OP_BMAP: unsafe.Sizeof(_BmapIn{}),
_OP_IOCTL: unsafe.Sizeof(_IoctlIn{}),
_OP_POLL: unsafe.Sizeof(_PollIn{}),
_OP_NOTIFY_REPLY: unsafe.Sizeof(NotifyRetrieveIn{}),
_OP_FALLOCATE: unsafe.Sizeof(FallocateIn{}),
_OP_READDIRPLUS: unsafe.Sizeof(ReadIn{}),
_OP_RENAME2: unsafe.Sizeof(RenameIn{}),
_OP_LSEEK: unsafe.Sizeof(LseekIn{}),
_OP_COPY_FILE_RANGE: unsafe.Sizeof(CopyFileRangeIn{}),
} {
operationHandlers[op].InputSize = sz
if sz > maxInputSize {
maxInputSize = sz
}
}
for op, sz := range map[uint32]uintptr{
_OP_LOOKUP: unsafe.Sizeof(EntryOut{}),
_OP_GETATTR: unsafe.Sizeof(AttrOut{}),
_OP_SETATTR: unsafe.Sizeof(AttrOut{}),
_OP_SYMLINK: unsafe.Sizeof(EntryOut{}),
_OP_MKNOD: unsafe.Sizeof(EntryOut{}),
_OP_MKDIR: unsafe.Sizeof(EntryOut{}),
_OP_LINK: unsafe.Sizeof(EntryOut{}),
_OP_OPEN: unsafe.Sizeof(OpenOut{}),
_OP_WRITE: unsafe.Sizeof(WriteOut{}),
_OP_STATFS: unsafe.Sizeof(StatfsOut{}),
_OP_GETXATTR: unsafe.Sizeof(GetXAttrOut{}),
_OP_LISTXATTR: unsafe.Sizeof(GetXAttrOut{}),
_OP_INIT: unsafe.Sizeof(InitOut{}),
_OP_OPENDIR: unsafe.Sizeof(OpenOut{}),
_OP_GETLK: unsafe.Sizeof(LkOut{}),
_OP_CREATE: unsafe.Sizeof(CreateOut{}),
_OP_BMAP: unsafe.Sizeof(_BmapOut{}),
_OP_IOCTL: unsafe.Sizeof(_IoctlOut{}),
_OP_POLL: unsafe.Sizeof(_PollOut{}),
_OP_NOTIFY_INVAL_ENTRY: unsafe.Sizeof(NotifyInvalEntryOut{}),
_OP_NOTIFY_INVAL_INODE: unsafe.Sizeof(NotifyInvalInodeOut{}),
_OP_NOTIFY_STORE_CACHE: unsafe.Sizeof(NotifyStoreOut{}),
_OP_NOTIFY_RETRIEVE_CACHE: unsafe.Sizeof(NotifyRetrieveOut{}),
_OP_NOTIFY_DELETE: unsafe.Sizeof(NotifyInvalDeleteOut{}),
_OP_LSEEK: unsafe.Sizeof(LseekOut{}),
_OP_COPY_FILE_RANGE: unsafe.Sizeof(WriteOut{}),
} {
operationHandlers[op].OutputSize = sz
}
for op, v := range map[uint32]string{
_OP_LOOKUP: "LOOKUP",
_OP_FORGET: "FORGET",
_OP_BATCH_FORGET: "BATCH_FORGET",
_OP_GETATTR: "GETATTR",
_OP_SETATTR: "SETATTR",
_OP_READLINK: "READLINK",
_OP_SYMLINK: "SYMLINK",
_OP_MKNOD: "MKNOD",
_OP_MKDIR: "MKDIR",
_OP_UNLINK: "UNLINK",
_OP_RMDIR: "RMDIR",
_OP_RENAME: "RENAME",
_OP_LINK: "LINK",
_OP_OPEN: "OPEN",
_OP_READ: "READ",
_OP_WRITE: "WRITE",
_OP_STATFS: "STATFS",
_OP_RELEASE: "RELEASE",
_OP_FSYNC: "FSYNC",
_OP_SETXATTR: "SETXATTR",
_OP_GETXATTR: "GETXATTR",
_OP_LISTXATTR: "LISTXATTR",
_OP_REMOVEXATTR: "REMOVEXATTR",
_OP_FLUSH: "FLUSH",
_OP_INIT: "INIT",
_OP_OPENDIR: "OPENDIR",
_OP_READDIR: "READDIR",
_OP_RELEASEDIR: "RELEASEDIR",
_OP_FSYNCDIR: "FSYNCDIR",
_OP_GETLK: "GETLK",
_OP_SETLK: "SETLK",
_OP_SETLKW: "SETLKW",
_OP_ACCESS: "ACCESS",
_OP_CREATE: "CREATE",
_OP_INTERRUPT: "INTERRUPT",
_OP_BMAP: "BMAP",
_OP_DESTROY: "DESTROY",
_OP_IOCTL: "IOCTL",
_OP_POLL: "POLL",
_OP_NOTIFY_REPLY: "NOTIFY_REPLY",
_OP_NOTIFY_INVAL_ENTRY: "NOTIFY_INVAL_ENTRY",
_OP_NOTIFY_INVAL_INODE: "NOTIFY_INVAL_INODE",
_OP_NOTIFY_STORE_CACHE: "NOTIFY_STORE",
_OP_NOTIFY_RETRIEVE_CACHE: "NOTIFY_RETRIEVE",
_OP_NOTIFY_DELETE: "NOTIFY_DELETE",
_OP_FALLOCATE: "FALLOCATE",
_OP_READDIRPLUS: "READDIRPLUS",
_OP_RENAME2: "RENAME2",
_OP_LSEEK: "LSEEK",
_OP_COPY_FILE_RANGE: "COPY_FILE_RANGE",
} {
operationHandlers[op].Name = v
}
for op, v := range map[uint32]operationFunc{
_OP_OPEN: doOpen,
_OP_READDIR: doReadDir,
_OP_WRITE: doWrite,
_OP_OPENDIR: doOpenDir,
_OP_CREATE: doCreate,
_OP_SETATTR: doSetattr,
_OP_GETXATTR: doGetXAttr,
_OP_LISTXATTR: doGetXAttr,
_OP_GETATTR: doGetAttr,
_OP_FORGET: doForget,
_OP_BATCH_FORGET: doBatchForget,
_OP_READLINK: doReadlink,
_OP_INIT: doInit,
_OP_LOOKUP: doLookup,
_OP_MKNOD: doMknod,
_OP_MKDIR: doMkdir,
_OP_UNLINK: doUnlink,
_OP_RMDIR: doRmdir,
_OP_LINK: doLink,
_OP_READ: doRead,
_OP_FLUSH: doFlush,
_OP_RELEASE: doRelease,
_OP_FSYNC: doFsync,
_OP_RELEASEDIR: doReleaseDir,
_OP_FSYNCDIR: doFsyncDir,
_OP_SETXATTR: doSetXAttr,
_OP_REMOVEXATTR: doRemoveXAttr,
_OP_GETLK: doGetLk,
_OP_SETLK: doSetLk,
_OP_SETLKW: doSetLkw,
_OP_ACCESS: doAccess,
_OP_SYMLINK: doSymlink,
_OP_RENAME: doRename,
_OP_STATFS: doStatFs,
_OP_IOCTL: doIoctl,
_OP_DESTROY: doDestroy,
_OP_NOTIFY_REPLY: doNotifyReply,
_OP_FALLOCATE: doFallocate,
_OP_READDIRPLUS: doReadDirPlus,
_OP_RENAME2: doRename2,
_OP_INTERRUPT: doInterrupt,
_OP_COPY_FILE_RANGE: doCopyFileRange,
_OP_LSEEK: doLseek,
} {
operationHandlers[op].Func = v
}
// Outputs.
for op, f := range map[uint32]castPointerFunc{
_OP_LOOKUP: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
_OP_OPEN: func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) },
_OP_OPENDIR: func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) },
_OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
_OP_CREATE: func(ptr unsafe.Pointer) interface{} { return (*CreateOut)(ptr) },
_OP_LINK: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
_OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
_OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitOut)(ptr) },
_OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
_OP_NOTIFY_INVAL_ENTRY: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalEntryOut)(ptr) },
_OP_NOTIFY_INVAL_INODE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalInodeOut)(ptr) },
_OP_NOTIFY_STORE_CACHE: func(ptr unsafe.Pointer) interface{} { return (*NotifyStoreOut)(ptr) },
_OP_NOTIFY_RETRIEVE_CACHE: func(ptr unsafe.Pointer) interface{} { return (*NotifyRetrieveOut)(ptr) },
_OP_NOTIFY_DELETE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalDeleteOut)(ptr) },
_OP_STATFS: func(ptr unsafe.Pointer) interface{} { return (*StatfsOut)(ptr) },
_OP_SYMLINK: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
_OP_GETLK: func(ptr unsafe.Pointer) interface{} { return (*LkOut)(ptr) },
_OP_LSEEK: func(ptr unsafe.Pointer) interface{} { return (*LseekOut)(ptr) },
_OP_COPY_FILE_RANGE: func(ptr unsafe.Pointer) interface{} { return (*WriteOut)(ptr) },
} {
operationHandlers[op].DecodeOut = f
}
// Inputs.
for op, f := range map[uint32]castPointerFunc{
_OP_FLUSH: func(ptr unsafe.Pointer) interface{} { return (*FlushIn)(ptr) },
_OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*GetAttrIn)(ptr) },
_OP_SETXATTR: func(ptr unsafe.Pointer) interface{} { return (*SetXAttrIn)(ptr) },
_OP_GETXATTR: func(ptr unsafe.Pointer) interface{} { return (*GetXAttrIn)(ptr) },
_OP_LISTXATTR: func(ptr unsafe.Pointer) interface{} { return (*GetXAttrIn)(ptr) },
_OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*SetAttrIn)(ptr) },
_OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitIn)(ptr) },
_OP_IOCTL: func(ptr unsafe.Pointer) interface{} { return (*_IoctlIn)(ptr) },
_OP_OPEN: func(ptr unsafe.Pointer) interface{} { return (*OpenIn)(ptr) },
_OP_MKNOD: func(ptr unsafe.Pointer) interface{} { return (*MknodIn)(ptr) },
_OP_CREATE: func(ptr unsafe.Pointer) interface{} { return (*CreateIn)(ptr) },
_OP_READ: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
_OP_WRITE: func(ptr unsafe.Pointer) interface{} { return (*WriteIn)(ptr) },
_OP_READDIR: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
_OP_ACCESS: func(ptr unsafe.Pointer) interface{} { return (*AccessIn)(ptr) },
_OP_FORGET: func(ptr unsafe.Pointer) interface{} { return (*ForgetIn)(ptr) },
_OP_BATCH_FORGET: func(ptr unsafe.Pointer) interface{} { return (*_BatchForgetIn)(ptr) },
_OP_LINK: func(ptr unsafe.Pointer) interface{} { return (*LinkIn)(ptr) },
_OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*MkdirIn)(ptr) },
_OP_RELEASE: func(ptr unsafe.Pointer) interface{} { return (*ReleaseIn)(ptr) },
_OP_RELEASEDIR: func(ptr unsafe.Pointer) interface{} { return (*ReleaseIn)(ptr) },
_OP_FALLOCATE: func(ptr unsafe.Pointer) interface{} { return (*FallocateIn)(ptr) },
_OP_NOTIFY_REPLY: func(ptr unsafe.Pointer) interface{} { return (*NotifyRetrieveIn)(ptr) },
_OP_READDIRPLUS: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
_OP_RENAME: func(ptr unsafe.Pointer) interface{} { return (*Rename1In)(ptr) },
_OP_GETLK: func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
_OP_SETLK: func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
_OP_SETLKW: func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
_OP_RENAME2: func(ptr unsafe.Pointer) interface{} { return (*RenameIn)(ptr) },
_OP_INTERRUPT: func(ptr unsafe.Pointer) interface{} { return (*InterruptIn)(ptr) },
_OP_LSEEK: func(ptr unsafe.Pointer) interface{} { return (*LseekIn)(ptr) },
_OP_COPY_FILE_RANGE: func(ptr unsafe.Pointer) interface{} { return (*CopyFileRangeIn)(ptr) },
} {
operationHandlers[op].DecodeIn = f
}
// File name args.
for op, count := range map[uint32]int{
_OP_CREATE: 1,
_OP_SETXATTR: 1,
_OP_GETXATTR: 1,
_OP_LINK: 1,
_OP_LOOKUP: 1,
_OP_MKDIR: 1,
_OP_MKNOD: 1,
_OP_REMOVEXATTR: 1,
_OP_RENAME: 2,
_OP_RENAME2: 2,
_OP_RMDIR: 1,
_OP_SYMLINK: 2,
_OP_UNLINK: 1,
} {
operationHandlers[op].FileNames = count
}
var r request
sizeOfOutHeader := unsafe.Sizeof(OutHeader{})
for code, h := range operationHandlers {
if h.OutputSize+sizeOfOutHeader > unsafe.Sizeof(r.outBuf) {
log.Panicf("request output buffer too small: code %v, sz %d + %d %v", code, h.OutputSize, sizeOfOutHeader, h)
}
}
}

34
vendor/github.com/hanwen/go-fuse/v2/fuse/poll.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
package fuse
// Go 1.9 introduces polling for file I/O. The implementation causes
// the runtime's epoll to take up the last GOMAXPROCS slot, and if
// that happens, we won't have any threads left to service FUSE's
// _OP_POLL request. Prevent this by forcing _OP_POLL to happen, so we
// can say ENOSYS and prevent further _OP_POLL requests.
const pollHackName = ".go-fuse-epoll-hack"
const pollHackInode = ^uint64(0)
func doPollHackLookup(ms *Server, req *request) {
switch req.inHeader.Opcode {
case _OP_CREATE:
out := (*CreateOut)(req.outData())
out.EntryOut = EntryOut{
NodeId: pollHackInode,
Attr: Attr{
Ino: pollHackInode,
Mode: S_IFREG | 0644,
Nlink: 1,
},
}
out.OpenOut = OpenOut{
Fh: pollHackInode,
}
req.status = OK
case _OP_LOOKUP:
out := (*EntryOut)(req.outData())
*out = EntryOut{}
req.status = ENOENT
default:
req.status = EIO
}
}

View File

@ -0,0 +1,49 @@
package fuse
import (
"path/filepath"
"syscall"
"unsafe"
)
type pollFd struct {
Fd int32
Events int16
Revents int16
}
func sysPoll(fds []pollFd, timeout int) (n int, err error) {
r0, _, e1 := syscall.Syscall(syscall.SYS_POLL, uintptr(unsafe.Pointer(&fds[0])),
uintptr(len(fds)), uintptr(timeout))
n = int(r0)
if e1 != 0 {
err = syscall.Errno(e1)
}
return n, err
}
func pollHack(mountPoint string) error {
const (
POLLIN = 0x1
POLLPRI = 0x2
POLLOUT = 0x4
POLLRDHUP = 0x2000
POLLERR = 0x8
POLLHUP = 0x10
)
fd, err := syscall.Open(filepath.Join(mountPoint, pollHackName), syscall.O_CREAT|syscall.O_TRUNC|syscall.O_RDWR, 0644)
if err != nil {
return err
}
pollData := []pollFd{{
Fd: int32(fd),
Events: POLLIN | POLLPRI | POLLOUT,
}}
// Trigger _OP_POLL, so we can say ENOSYS. We don't care about
// the return value.
sysPoll(pollData, 0)
syscall.Close(fd)
return nil
}

25
vendor/github.com/hanwen/go-fuse/v2/fuse/poll_linux.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
package fuse
import (
"path/filepath"
"syscall"
"golang.org/x/sys/unix"
)
func pollHack(mountPoint string) error {
fd, err := syscall.Creat(filepath.Join(mountPoint, pollHackName), syscall.O_CREAT)
if err != nil {
return err
}
pollData := []unix.PollFd{{
Fd: int32(fd),
Events: unix.POLLIN | unix.POLLPRI | unix.POLLOUT,
}}
// Trigger _OP_POLL, so we can say ENOSYS. We don't care about
// the return value.
unix.Poll(pollData, 0)
syscall.Close(fd)
return nil
}

305
vendor/github.com/hanwen/go-fuse/v2/fuse/print.go generated vendored Normal file
View File

@ -0,0 +1,305 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"fmt"
"os"
"strings"
"syscall"
)
var (
writeFlagNames = map[int64]string{
WRITE_CACHE: "CACHE",
WRITE_LOCKOWNER: "LOCKOWNER",
}
readFlagNames = map[int64]string{
READ_LOCKOWNER: "LOCKOWNER",
}
initFlagNames = map[int64]string{
CAP_ASYNC_READ: "ASYNC_READ",
CAP_POSIX_LOCKS: "POSIX_LOCKS",
CAP_FILE_OPS: "FILE_OPS",
CAP_ATOMIC_O_TRUNC: "ATOMIC_O_TRUNC",
CAP_EXPORT_SUPPORT: "EXPORT_SUPPORT",
CAP_BIG_WRITES: "BIG_WRITES",
CAP_DONT_MASK: "DONT_MASK",
CAP_SPLICE_WRITE: "SPLICE_WRITE",
CAP_SPLICE_MOVE: "SPLICE_MOVE",
CAP_SPLICE_READ: "SPLICE_READ",
CAP_FLOCK_LOCKS: "FLOCK_LOCKS",
CAP_IOCTL_DIR: "IOCTL_DIR",
CAP_AUTO_INVAL_DATA: "AUTO_INVAL_DATA",
CAP_READDIRPLUS: "READDIRPLUS",
CAP_READDIRPLUS_AUTO: "READDIRPLUS_AUTO",
CAP_ASYNC_DIO: "ASYNC_DIO",
CAP_WRITEBACK_CACHE: "WRITEBACK_CACHE",
CAP_NO_OPEN_SUPPORT: "NO_OPEN_SUPPORT",
CAP_PARALLEL_DIROPS: "PARALLEL_DIROPS",
CAP_POSIX_ACL: "POSIX_ACL",
CAP_HANDLE_KILLPRIV: "HANDLE_KILLPRIV",
CAP_ABORT_ERROR: "ABORT_ERROR",
CAP_MAX_PAGES: "MAX_PAGES",
CAP_CACHE_SYMLINKS: "CACHE_SYMLINKS",
CAP_NO_OPENDIR_SUPPORT: "NO_OPENDIR_SUPPORT",
CAP_EXPLICIT_INVAL_DATA: "EXPLICIT_INVAL_DATA",
}
releaseFlagNames = map[int64]string{
RELEASE_FLUSH: "FLUSH",
}
openFlagNames = map[int64]string{
int64(os.O_WRONLY): "WRONLY",
int64(os.O_RDWR): "RDWR",
int64(os.O_APPEND): "APPEND",
int64(syscall.O_ASYNC): "ASYNC",
int64(os.O_CREATE): "CREAT",
int64(os.O_EXCL): "EXCL",
int64(syscall.O_NOCTTY): "NOCTTY",
int64(syscall.O_NONBLOCK): "NONBLOCK",
int64(os.O_SYNC): "SYNC",
int64(os.O_TRUNC): "TRUNC",
int64(syscall.O_CLOEXEC): "CLOEXEC",
int64(syscall.O_DIRECTORY): "DIRECTORY",
}
fuseOpenFlagNames = map[int64]string{
FOPEN_DIRECT_IO: "DIRECT",
FOPEN_KEEP_CACHE: "CACHE",
FOPEN_NONSEEKABLE: "NONSEEK",
FOPEN_CACHE_DIR: "CACHE_DIR",
FOPEN_STREAM: "STREAM",
}
accessFlagName = map[int64]string{
X_OK: "x",
W_OK: "w",
R_OK: "r",
}
)
func flagString(names map[int64]string, fl int64, def string) string {
s := []string{}
for k, v := range names {
if fl&k != 0 {
s = append(s, v)
fl ^= k
}
}
if len(s) == 0 && def != "" {
s = []string{def}
}
if fl != 0 {
s = append(s, fmt.Sprintf("0x%x", fl))
}
return strings.Join(s, ",")
}
func (in *ForgetIn) string() string {
return fmt.Sprintf("{Nlookup=%d}", in.Nlookup)
}
func (in *_BatchForgetIn) string() string {
return fmt.Sprintf("{Count=%d}", in.Count)
}
func (in *MkdirIn) string() string {
return fmt.Sprintf("{0%o (0%o)}", in.Mode, in.Umask)
}
func (in *Rename1In) string() string {
return fmt.Sprintf("{i%d}", in.Newdir)
}
func (in *RenameIn) string() string {
return fmt.Sprintf("{i%d %x}", in.Newdir, in.Flags)
}
func (in *SetAttrIn) string() string {
s := []string{}
if in.Valid&FATTR_MODE != 0 {
s = append(s, fmt.Sprintf("mode 0%o", in.Mode))
}
if in.Valid&FATTR_UID != 0 {
s = append(s, fmt.Sprintf("uid %d", in.Uid))
}
if in.Valid&FATTR_GID != 0 {
s = append(s, fmt.Sprintf("gid %d", in.Gid))
}
if in.Valid&FATTR_SIZE != 0 {
s = append(s, fmt.Sprintf("size %d", in.Size))
}
if in.Valid&FATTR_ATIME != 0 {
s = append(s, fmt.Sprintf("atime %d.%09d", in.Atime, in.Atimensec))
}
if in.Valid&FATTR_MTIME != 0 {
s = append(s, fmt.Sprintf("mtime %d.%09d", in.Mtime, in.Mtimensec))
}
if in.Valid&FATTR_FH != 0 {
s = append(s, fmt.Sprintf("fh %d", in.Fh))
}
// TODO - FATTR_ATIME_NOW = (1 << 7), FATTR_MTIME_NOW = (1 << 8), FATTR_LOCKOWNER = (1 << 9)
return fmt.Sprintf("{%s}", strings.Join(s, ", "))
}
func (in *ReleaseIn) string() string {
return fmt.Sprintf("{Fh %d %s %s L%d}",
in.Fh, flagString(openFlagNames, int64(in.Flags), ""),
flagString(releaseFlagNames, int64(in.ReleaseFlags), ""),
in.LockOwner)
}
func (in *OpenIn) string() string {
return fmt.Sprintf("{%s}", flagString(openFlagNames, int64(in.Flags), "O_RDONLY"))
}
func (in *OpenOut) string() string {
return fmt.Sprintf("{Fh %d %s}", in.Fh,
flagString(fuseOpenFlagNames, int64(in.OpenFlags), ""))
}
func (in *InitIn) string() string {
return fmt.Sprintf("{%d.%d Ra 0x%x %s}",
in.Major, in.Minor, in.MaxReadAhead,
flagString(initFlagNames, int64(in.Flags), ""))
}
func (o *InitOut) string() string {
return fmt.Sprintf("{%d.%d Ra 0x%x %s %d/%d Wr 0x%x Tg 0x%x}",
o.Major, o.Minor, o.MaxReadAhead,
flagString(initFlagNames, int64(o.Flags), ""),
o.CongestionThreshold, o.MaxBackground, o.MaxWrite,
o.TimeGran)
}
func (s *FsyncIn) string() string {
return fmt.Sprintf("{Fh %d Flags %x}", s.Fh, s.FsyncFlags)
}
func (in *SetXAttrIn) string() string {
return fmt.Sprintf("{sz %d f%o}", in.Size, in.Flags)
}
func (in *GetXAttrIn) string() string {
return fmt.Sprintf("{sz %d}", in.Size)
}
func (o *GetXAttrOut) string() string {
return fmt.Sprintf("{sz %d}", o.Size)
}
func (in *AccessIn) string() string {
return fmt.Sprintf("{u=%d g=%d %s}",
in.Uid,
in.Gid,
flagString(accessFlagName, int64(in.Mask), ""))
}
func (in *FlushIn) string() string {
return fmt.Sprintf("{Fh %d}", in.Fh)
}
func (o *AttrOut) string() string {
return fmt.Sprintf(
"{tA=%gs %v}",
ft(o.AttrValid, o.AttrValidNsec), &o.Attr)
}
// ft converts (seconds , nanoseconds) -> float(seconds)
func ft(tsec uint64, tnsec uint32) float64 {
return float64(tsec) + float64(tnsec)*1E-9
}
// Returned by LOOKUP
func (o *EntryOut) string() string {
return fmt.Sprintf("{i%d g%d tE=%gs tA=%gs %v}",
o.NodeId, o.Generation, ft(o.EntryValid, o.EntryValidNsec),
ft(o.AttrValid, o.AttrValidNsec), &o.Attr)
}
func (o *CreateOut) string() string {
return fmt.Sprintf("{i%d g%d %v %v}", o.NodeId, o.Generation, &o.EntryOut, &o.OpenOut)
}
func (o *StatfsOut) string() string {
return fmt.Sprintf(
"{blocks (%d,%d)/%d files %d/%d bs%d nl%d frs%d}",
o.Bfree, o.Bavail, o.Blocks, o.Ffree, o.Files,
o.Bsize, o.NameLen, o.Frsize)
}
func (o *NotifyInvalEntryOut) string() string {
return fmt.Sprintf("{parent i%d sz %d}", o.Parent, o.NameLen)
}
func (o *NotifyInvalInodeOut) string() string {
return fmt.Sprintf("{i%d [%d +%d)}", o.Ino, o.Off, o.Length)
}
func (o *NotifyInvalDeleteOut) string() string {
return fmt.Sprintf("{parent i%d ch i%d sz %d}", o.Parent, o.Child, o.NameLen)
}
func (o *NotifyStoreOut) string() string {
return fmt.Sprintf("{i%d [%d +%d)}", o.Nodeid, o.Offset, o.Size)
}
func (o *NotifyRetrieveOut) string() string {
return fmt.Sprintf("{> %d: i%d [%d +%d)}", o.NotifyUnique, o.Nodeid, o.Offset, o.Size)
}
func (i *NotifyRetrieveIn) string() string {
return fmt.Sprintf("{[%d +%d)}", i.Offset, i.Size)
}
func (f *FallocateIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) mod 0%o}",
f.Fh, f.Offset, f.Length, f.Mode)
}
func (f *LinkIn) string() string {
return fmt.Sprintf("{Oldnodeid: %d}", f.Oldnodeid)
}
func (o *WriteOut) string() string {
return fmt.Sprintf("{%db }", o.Size)
}
func (i *CopyFileRangeIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) => i%d Fh %d [%d, %d)}",
i.FhIn, i.OffIn, i.Len, i.NodeIdOut, i.FhOut, i.OffOut, i.Len)
}
func (in *InterruptIn) string() string {
return fmt.Sprintf("{ix %d}", in.Unique)
}
var seekNames = map[uint32]string{
0: "SET",
1: "CUR",
2: "END",
3: "DATA",
4: "HOLE",
}
func (in *LseekIn) string() string {
return fmt.Sprintf("{Fh %d [%s +%d)}", in.Fh,
seekNames[in.Whence], in.Offset)
}
func (o *LseekOut) string() string {
return fmt.Sprintf("{%d}", o.Offset)
}
// Print pretty prints FUSE data types for kernel communication
func Print(obj interface{}) string {
t, ok := obj.(interface {
string() string
})
if ok {
return t.string()
}
return fmt.Sprintf("%T: %v", obj, obj)
}

View File

@ -0,0 +1,54 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"fmt"
)
func init() {
initFlagNames[CAP_XTIMES] = "XTIMES"
initFlagNames[CAP_VOL_RENAME] = "VOL_RENAME"
initFlagNames[CAP_CASE_INSENSITIVE] = "CASE_INSENSITIVE"
}
func (a *Attr) string() string {
return fmt.Sprintf(
"{M0%o SZ=%d L=%d "+
"%d:%d "+
"%d %d:%d "+
"A %f "+
"M %f "+
"C %f}",
a.Mode, a.Size, a.Nlink,
a.Uid, a.Gid,
a.Blocks,
a.Rdev, a.Ino, ft(a.Atime, a.Atimensec), ft(a.Mtime, a.Mtimensec),
ft(a.Ctime, a.Ctimensec))
}
func (me *CreateIn) string() string {
return fmt.Sprintf(
"{0%o [%s]}", me.Mode,
flagString(openFlagNames, int64(me.Flags), "O_RDONLY"))
}
func (me *GetAttrIn) string() string { return "" }
func (me *MknodIn) string() string {
return fmt.Sprintf("{0%o, %d}", me.Mode, me.Rdev)
}
func (me *ReadIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s}",
me.Fh, me.Offset, me.Size,
flagString(readFlagNames, int64(me.ReadFlags), ""))
}
func (me *WriteIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s}",
me.Fh, me.Offset, me.Size,
flagString(writeFlagNames, int64(me.WriteFlags), ""))
}

View File

@ -0,0 +1,61 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"fmt"
"syscall"
)
func init() {
openFlagNames[syscall.O_DIRECT] = "DIRECT"
openFlagNames[syscall.O_LARGEFILE] = "LARGEFILE"
openFlagNames[syscall_O_NOATIME] = "NOATIME"
}
func (a *Attr) string() string {
return fmt.Sprintf(
"{M0%o SZ=%d L=%d "+
"%d:%d "+
"B%d*%d i%d:%d "+
"A %f "+
"M %f "+
"C %f}",
a.Mode, a.Size, a.Nlink,
a.Uid, a.Gid,
a.Blocks, a.Blksize,
a.Rdev, a.Ino, ft(a.Atime, a.Atimensec), ft(a.Mtime, a.Mtimensec),
ft(a.Ctime, a.Ctimensec))
}
func (in *CreateIn) string() string {
return fmt.Sprintf(
"{0%o [%s] (0%o)}", in.Mode,
flagString(openFlagNames, int64(in.Flags), "O_RDONLY"), in.Umask)
}
func (in *GetAttrIn) string() string {
return fmt.Sprintf("{Fh %d}", in.Fh_)
}
func (in *MknodIn) string() string {
return fmt.Sprintf("{0%o (0%o), %d}", in.Mode, in.Umask, in.Rdev)
}
func (in *ReadIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s L %d %s}",
in.Fh, in.Offset, in.Size,
flagString(readFlagNames, int64(in.ReadFlags), ""),
in.LockOwner,
flagString(openFlagNames, int64(in.Flags), "RDONLY"))
}
func (in *WriteIn) string() string {
return fmt.Sprintf("{Fh %d [%d +%d) %s L %d %s}",
in.Fh, in.Offset, in.Size,
flagString(writeFlagNames, int64(in.WriteFlags), ""),
in.LockOwner,
flagString(openFlagNames, int64(in.Flags), "RDONLY"))
}

75
vendor/github.com/hanwen/go-fuse/v2/fuse/read.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"io"
"syscall"
)
// ReadResultData is the read return for returning bytes directly.
type readResultData struct {
// Raw bytes for the read.
Data []byte
}
func (r *readResultData) Size() int {
return len(r.Data)
}
func (r *readResultData) Done() {
}
func (r *readResultData) Bytes(buf []byte) ([]byte, Status) {
return r.Data, OK
}
func ReadResultData(b []byte) ReadResult {
return &readResultData{b}
}
func ReadResultFd(fd uintptr, off int64, sz int) ReadResult {
return &readResultFd{fd, off, sz}
}
// ReadResultFd is the read return for zero-copy file data.
type readResultFd struct {
// Splice from the following file.
Fd uintptr
// Offset within Fd, or -1 to use current offset.
Off int64
// Size of data to be loaded. Actual data available may be
// less at the EOF.
Sz int
}
// Reads raw bytes from file descriptor if necessary, using the passed
// buffer as storage.
func (r *readResultFd) Bytes(buf []byte) ([]byte, Status) {
sz := r.Sz
if len(buf) < sz {
sz = len(buf)
}
n, err := syscall.Pread(int(r.Fd), buf[:sz], r.Off)
if err == io.EOF {
err = nil
}
if n < 0 {
n = 0
}
return buf[:n], ToStatus(err)
}
func (r *readResultFd) Size() int {
return r.Sz
}
func (r *readResultFd) Done() {
}

254
vendor/github.com/hanwen/go-fuse/v2/fuse/request.go generated vendored Normal file
View File

@ -0,0 +1,254 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"bytes"
"fmt"
"log"
"strings"
"time"
"unsafe"
)
var sizeOfOutHeader = unsafe.Sizeof(OutHeader{})
var zeroOutBuf [outputHeaderSize]byte
type request struct {
inflightIndex int
cancel chan struct{}
// written under Server.reqMu
interrupted bool
inputBuf []byte
// These split up inputBuf.
inHeader *InHeader // generic header
inData unsafe.Pointer // per op data
arg []byte // flat data.
filenames []string // filename arguments
// Output data.
status Status
flatData []byte
fdData *readResultFd
// In case of read, keep read result here so we can call
// Done() on it.
readResult ReadResult
// Start timestamp for timing info.
startTime time.Time
// All information pertaining to opcode of this request.
handler *operationHandler
// Request storage. For large inputs and outputs, use data
// obtained through bufferpool.
bufferPoolInputBuf []byte
bufferPoolOutputBuf []byte
// For small pieces of data, we use the following inlines
// arrays:
//
// Output header and structured data.
outBuf [outputHeaderSize]byte
// Input, if small enough to fit here.
smallInputBuf [128]byte
}
func (r *request) clear() {
r.inputBuf = nil
r.inHeader = nil
r.inData = nil
r.arg = nil
r.filenames = nil
r.status = OK
r.flatData = nil
r.fdData = nil
r.startTime = time.Time{}
r.handler = nil
r.readResult = nil
}
func (r *request) InputDebug() string {
val := ""
if r.handler != nil && r.handler.DecodeIn != nil {
val = fmt.Sprintf("%v ", Print(r.handler.DecodeIn(r.inData)))
}
names := ""
if r.filenames != nil {
names = fmt.Sprintf("%q", r.filenames)
}
if len(r.arg) > 0 {
names += fmt.Sprintf(" %db", len(r.arg))
}
return fmt.Sprintf("rx %d: %s i%d %s%s",
r.inHeader.Unique, operationName(r.inHeader.Opcode), r.inHeader.NodeId,
val, names)
}
func (r *request) OutputDebug() string {
var dataStr string
if r.handler != nil && r.handler.DecodeOut != nil && r.handler.OutputSize > 0 {
dataStr = Print(r.handler.DecodeOut(r.outData()))
}
max := 1024
if len(dataStr) > max {
dataStr = dataStr[:max] + fmt.Sprintf(" ...trimmed")
}
flatStr := ""
if r.flatDataSize() > 0 {
if r.handler != nil && r.handler.FileNameOut {
s := strings.TrimRight(string(r.flatData), "\x00")
flatStr = fmt.Sprintf(" %q", s)
} else {
spl := ""
if r.fdData != nil {
spl = " (fd data)"
} else {
l := len(r.flatData)
s := ""
if l > 8 {
l = 8
s = "..."
}
spl = fmt.Sprintf(" %q%s", r.flatData[:l], s)
}
flatStr = fmt.Sprintf(" %db data%s", r.flatDataSize(), spl)
}
}
extraStr := dataStr + flatStr
if extraStr != "" {
extraStr = ", " + extraStr
}
return fmt.Sprintf("tx %d: %v%s",
r.inHeader.Unique, r.status, extraStr)
}
// setInput returns true if it takes ownership of the argument, false if not.
func (r *request) setInput(input []byte) bool {
if len(input) < len(r.smallInputBuf) {
copy(r.smallInputBuf[:], input)
r.inputBuf = r.smallInputBuf[:len(input)]
return false
}
r.inputBuf = input
r.bufferPoolInputBuf = input[:cap(input)]
return true
}
func (r *request) parseHeader() Status {
if len(r.inputBuf) < int(unsafe.Sizeof(InHeader{})) {
log.Printf("Short read for input header: %v", r.inputBuf)
return EINVAL
}
r.inHeader = (*InHeader)(unsafe.Pointer(&r.inputBuf[0]))
return OK
}
func (r *request) parse() {
r.arg = r.inputBuf[:]
r.handler = getHandler(r.inHeader.Opcode)
if r.handler == nil {
log.Printf("Unknown opcode %d", r.inHeader.Opcode)
r.status = ENOSYS
return
}
if len(r.arg) < int(r.handler.InputSize) {
log.Printf("Short read for %v: %v", operationName(r.inHeader.Opcode), r.arg)
r.status = EIO
return
}
if r.handler.InputSize > 0 {
r.inData = unsafe.Pointer(&r.arg[0])
r.arg = r.arg[r.handler.InputSize:]
} else {
r.arg = r.arg[unsafe.Sizeof(InHeader{}):]
}
count := r.handler.FileNames
if count > 0 {
if count == 1 && r.inHeader.Opcode == _OP_SETXATTR {
// SETXATTR is special: the only opcode with a file name AND a
// binary argument.
splits := bytes.SplitN(r.arg, []byte{0}, 2)
r.filenames = []string{string(splits[0])}
} else if count == 1 {
r.filenames = []string{string(r.arg[:len(r.arg)-1])}
} else {
names := bytes.SplitN(r.arg[:len(r.arg)-1], []byte{0}, count)
r.filenames = make([]string, len(names))
for i, n := range names {
r.filenames[i] = string(n)
}
if len(names) != count {
log.Println("filename argument mismatch", names, count)
r.status = EIO
}
}
}
copy(r.outBuf[:r.handler.OutputSize+sizeOfOutHeader],
zeroOutBuf[:r.handler.OutputSize+sizeOfOutHeader])
}
func (r *request) outData() unsafe.Pointer {
return unsafe.Pointer(&r.outBuf[sizeOfOutHeader])
}
// serializeHeader serializes the response header. The header points
// to an internal buffer of the receiver.
func (r *request) serializeHeader(flatDataSize int) (header []byte) {
var dataLength uintptr
if r.handler != nil {
dataLength = r.handler.OutputSize
}
if r.status > OK {
// only do this for positive status; negative status
// is used for notification.
dataLength = 0
}
// [GET|LIST]XATTR is two opcodes in one: get/list xattr size (return
// structured GetXAttrOut, no flat data) and get/list xattr data
// (return no structured data, but only flat data)
if r.inHeader.Opcode == _OP_GETXATTR || r.inHeader.Opcode == _OP_LISTXATTR {
if (*GetXAttrIn)(r.inData).Size != 0 {
dataLength = 0
}
}
header = r.outBuf[:sizeOfOutHeader+dataLength]
o := (*OutHeader)(unsafe.Pointer(&header[0]))
o.Unique = r.inHeader.Unique
o.Status = int32(-r.status)
o.Length = uint32(
int(sizeOfOutHeader) + int(dataLength) + flatDataSize)
return header
}
func (r *request) flatDataSize() int {
if r.fdData != nil {
return r.fdData.Size()
}
return len(r.flatData)
}

View File

@ -0,0 +1,13 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
const outputHeaderSize = 200
const (
_FUSE_KERNEL_VERSION = 7
_MINIMUM_MINOR_VERSION = 8
_OUR_MINOR_VERSION = 8
)

View File

@ -0,0 +1,13 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
const outputHeaderSize = 160
const (
_FUSE_KERNEL_VERSION = 7
_MINIMUM_MINOR_VERSION = 12
_OUR_MINOR_VERSION = 28
)

858
vendor/github.com/hanwen/go-fuse/v2/fuse/server.go generated vendored Normal file
View File

@ -0,0 +1,858 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"fmt"
"log"
"math"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"syscall"
"time"
"unsafe"
)
const (
// The kernel caps writes at 128k.
MAX_KERNEL_WRITE = 128 * 1024
)
// Server contains the logic for reading from the FUSE device and
// translating it to RawFileSystem interface calls.
type Server struct {
// Empty if unmounted.
mountPoint string
fileSystem RawFileSystem
// writeMu serializes close and notify writes
writeMu sync.Mutex
// I/O with kernel and daemon.
mountFd int
latencies LatencyMap
opts *MountOptions
// Pools for []byte
buffers bufferPool
// Pool for request structs.
reqPool sync.Pool
// Pool for raw requests data
readPool sync.Pool
reqMu sync.Mutex
reqReaders int
reqInflight []*request
kernelSettings InitIn
// in-flight notify-retrieve queries
retrieveMu sync.Mutex
retrieveNext uint64
retrieveTab map[uint64]*retrieveCacheRequest // notifyUnique -> retrieve request
singleReader bool
canSplice bool
loops sync.WaitGroup
ready chan error
// for implementing single threaded processing.
requestProcessingMu sync.Mutex
}
// SetDebug is deprecated. Use MountOptions.Debug instead.
func (ms *Server) SetDebug(dbg bool) {
// This will typically trigger the race detector.
ms.opts.Debug = dbg
}
// KernelSettings returns the Init message from the kernel, so
// filesystems can adapt to availability of features of the kernel
// driver. The message should not be altered.
func (ms *Server) KernelSettings() *InitIn {
ms.reqMu.Lock()
s := ms.kernelSettings
ms.reqMu.Unlock()
return &s
}
const _MAX_NAME_LEN = 20
// This type may be provided for recording latencies of each FUSE
// operation.
type LatencyMap interface {
Add(name string, dt time.Duration)
}
// RecordLatencies switches on collection of timing for each request
// coming from the kernel.P assing a nil argument switches off the
func (ms *Server) RecordLatencies(l LatencyMap) {
ms.latencies = l
}
// Unmount calls fusermount -u on the mount. This has the effect of
// shutting down the filesystem. After the Server is unmounted, it
// should be discarded.
func (ms *Server) Unmount() (err error) {
if ms.mountPoint == "" {
return nil
}
delay := time.Duration(0)
for try := 0; try < 5; try++ {
err = unmount(ms.mountPoint)
if err == nil {
break
}
// Sleep for a bit. This is not pretty, but there is
// no way we can be certain that the kernel thinks all
// open files have already been closed.
delay = 2*delay + 5*time.Millisecond
time.Sleep(delay)
}
if err != nil {
return
}
// Wait for event loops to exit.
ms.loops.Wait()
ms.mountPoint = ""
return err
}
// NewServer creates a server and attaches it to the given directory.
func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server, error) {
if opts == nil {
opts = &MountOptions{
MaxBackground: _DEFAULT_BACKGROUND_TASKS,
}
}
o := *opts
if o.MaxWrite < 0 {
o.MaxWrite = 0
}
if o.MaxWrite == 0 {
o.MaxWrite = 1 << 16
}
if o.MaxWrite > MAX_KERNEL_WRITE {
o.MaxWrite = MAX_KERNEL_WRITE
}
if o.Name == "" {
name := fs.String()
l := len(name)
if l > _MAX_NAME_LEN {
l = _MAX_NAME_LEN
}
o.Name = strings.Replace(name[:l], ",", ";", -1)
}
for _, s := range o.optionsStrings() {
if strings.Contains(s, ",") {
return nil, fmt.Errorf("found ',' in option string %q", s)
}
}
ms := &Server{
fileSystem: fs,
opts: &o,
retrieveTab: make(map[uint64]*retrieveCacheRequest),
// OSX has races when multiple routines read from the
// FUSE device: on unmount, sometime some reads do not
// error-out, meaning that unmount will hang.
singleReader: runtime.GOOS == "darwin",
ready: make(chan error, 1),
}
ms.reqPool.New = func() interface{} {
return &request{
cancel: make(chan struct{}),
}
}
ms.readPool.New = func() interface{} {
buf := make([]byte, o.MaxWrite+int(maxInputSize)+logicalBlockSize)
buf = alignSlice(buf, unsafe.Sizeof(WriteIn{}), logicalBlockSize, uintptr(o.MaxWrite)+maxInputSize)
return buf
}
mountPoint = filepath.Clean(mountPoint)
if !filepath.IsAbs(mountPoint) {
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
mountPoint = filepath.Clean(filepath.Join(cwd, mountPoint))
}
fd, err := mount(mountPoint, &o, ms.ready)
if err != nil {
return nil, err
}
ms.mountPoint = mountPoint
ms.mountFd = fd
if code := ms.handleInit(); !code.Ok() {
syscall.Close(fd)
// TODO - unmount as well?
return nil, fmt.Errorf("init: %s", code)
}
return ms, nil
}
func (o *MountOptions) optionsStrings() []string {
var r []string
r = append(r, o.Options...)
if o.AllowOther {
r = append(r, "allow_other")
}
if o.FsName != "" {
r = append(r, "fsname="+o.FsName)
}
if o.Name != "" {
r = append(r, "subtype="+o.Name)
}
return r
}
// DebugData returns internal status information for debugging
// purposes.
func (ms *Server) DebugData() string {
var r int
ms.reqMu.Lock()
r = ms.reqReaders
ms.reqMu.Unlock()
return fmt.Sprintf("readers: %d", r)
}
// What is a good number? Maybe the number of CPUs?
const _MAX_READERS = 2
// handleEINTR retries the given function until it doesn't return syscall.EINTR.
// This is similar to the HANDLE_EINTR() macro from Chromium ( see
// https://code.google.com/p/chromium/codesearch#chromium/src/base/posix/eintr_wrapper.h
// ) and the TEMP_FAILURE_RETRY() from glibc (see
// https://www.gnu.org/software/libc/manual/html_node/Interrupted-Primitives.html
// ).
//
// Don't use handleEINTR() with syscall.Close(); see
// https://code.google.com/p/chromium/issues/detail?id=269623 .
func handleEINTR(fn func() error) (err error) {
for {
err = fn()
if err != syscall.EINTR {
break
}
}
return
}
// Returns a new request, or error. In case exitIdle is given, returns
// nil, OK if we have too many readers already.
func (ms *Server) readRequest(exitIdle bool) (req *request, code Status) {
req = ms.reqPool.Get().(*request)
dest := ms.readPool.Get().([]byte)
ms.reqMu.Lock()
if ms.reqReaders > _MAX_READERS {
ms.reqMu.Unlock()
return nil, OK
}
ms.reqReaders++
ms.reqMu.Unlock()
var n int
err := handleEINTR(func() error {
var err error
n, err = syscall.Read(ms.mountFd, dest)
return err
})
if err != nil {
code = ToStatus(err)
ms.reqPool.Put(req)
ms.reqMu.Lock()
ms.reqReaders--
ms.reqMu.Unlock()
return nil, code
}
if ms.latencies != nil {
req.startTime = time.Now()
}
gobbled := req.setInput(dest[:n])
ms.reqMu.Lock()
defer ms.reqMu.Unlock()
// Must parse request.Unique under lock
if status := req.parseHeader(); !status.Ok() {
return nil, status
}
req.inflightIndex = len(ms.reqInflight)
ms.reqInflight = append(ms.reqInflight, req)
if !gobbled {
ms.readPool.Put(dest)
dest = nil
}
ms.reqReaders--
if !ms.singleReader && ms.reqReaders <= 0 {
ms.loops.Add(1)
go ms.loop(true)
}
return req, OK
}
// returnRequest returns a request to the pool of unused requests.
func (ms *Server) returnRequest(req *request) {
ms.reqMu.Lock()
this := req.inflightIndex
last := len(ms.reqInflight) - 1
if last != this {
ms.reqInflight[this] = ms.reqInflight[last]
ms.reqInflight[this].inflightIndex = this
}
ms.reqInflight = ms.reqInflight[:last]
interrupted := req.interrupted
ms.reqMu.Unlock()
ms.recordStats(req)
if interrupted {
// Don't reposses data, because someone might still
// be looking at it
return
}
if req.bufferPoolOutputBuf != nil {
ms.buffers.FreeBuffer(req.bufferPoolOutputBuf)
req.bufferPoolOutputBuf = nil
}
req.clear()
if p := req.bufferPoolInputBuf; p != nil {
req.bufferPoolInputBuf = nil
ms.readPool.Put(p)
}
ms.reqPool.Put(req)
}
func (ms *Server) recordStats(req *request) {
if ms.latencies != nil {
dt := time.Now().Sub(req.startTime)
opname := operationName(req.inHeader.Opcode)
ms.latencies.Add(opname, dt)
}
}
// Serve initiates the FUSE loop. Normally, callers should run Serve()
// and wait for it to exit, but tests will want to run this in a
// goroutine.
//
// Each filesystem operation executes in a separate goroutine.
func (ms *Server) Serve() {
ms.loops.Add(1)
ms.loop(false)
ms.loops.Wait()
ms.writeMu.Lock()
syscall.Close(ms.mountFd)
ms.writeMu.Unlock()
// shutdown in-flight cache retrieves.
//
// It is possible that umount comes in the middle - after retrieve
// request was sent to kernel, but corresponding kernel reply has not
// yet been read. We unblock all such readers and wake them up with ENODEV.
ms.retrieveMu.Lock()
rtab := ms.retrieveTab
// retrieve attempts might be erroneously tried even after close
// we have to keep retrieveTab !nil not to panic.
ms.retrieveTab = make(map[uint64]*retrieveCacheRequest)
ms.retrieveMu.Unlock()
for _, reading := range rtab {
reading.n = 0
reading.st = ENODEV
close(reading.ready)
}
}
// Wait waits for the serve loop to exit
func (ms *Server) Wait() {
ms.loops.Wait()
}
func (ms *Server) handleInit() Status {
// The first request should be INIT; read it synchronously,
// and don't spawn new readers.
orig := ms.singleReader
ms.singleReader = true
req, errNo := ms.readRequest(false)
ms.singleReader = orig
if errNo != OK || req == nil {
return errNo
}
if code := ms.handleRequest(req); !code.Ok() {
return code
}
// INIT is handled. Init the file system, but don't accept
// incoming requests, so the file system can setup itself.
ms.fileSystem.Init(ms)
return OK
}
func (ms *Server) loop(exitIdle bool) {
defer ms.loops.Done()
exit:
for {
req, errNo := ms.readRequest(exitIdle)
switch errNo {
case OK:
if req == nil {
break exit
}
case ENOENT:
continue
case ENODEV:
// unmount
if ms.opts.Debug {
log.Printf("received ENODEV (unmount request), thread exiting")
}
break exit
default: // some other error?
log.Printf("Failed to read from fuse conn: %v", errNo)
break exit
}
if ms.singleReader {
go ms.handleRequest(req)
} else {
ms.handleRequest(req)
}
}
}
func (ms *Server) handleRequest(req *request) Status {
if ms.opts.SingleThreaded {
ms.requestProcessingMu.Lock()
defer ms.requestProcessingMu.Unlock()
}
req.parse()
if req.handler == nil {
req.status = ENOSYS
}
if req.status.Ok() && ms.opts.Debug {
log.Println(req.InputDebug())
}
if req.inHeader.NodeId == pollHackInode {
// We want to avoid switching off features through our
// poll hack, so don't use ENOSYS
req.status = EIO
if req.inHeader.Opcode == _OP_POLL {
req.status = ENOSYS
}
} else if req.inHeader.NodeId == FUSE_ROOT_ID && len(req.filenames) > 0 && req.filenames[0] == pollHackName {
doPollHackLookup(ms, req)
} else if req.status.Ok() && req.handler.Func == nil {
log.Printf("Unimplemented opcode %v", operationName(req.inHeader.Opcode))
req.status = ENOSYS
} else if req.status.Ok() {
req.handler.Func(ms, req)
}
errNo := ms.write(req)
if errNo != 0 {
log.Printf("writer: Write/Writev failed, err: %v. opcode: %v",
errNo, operationName(req.inHeader.Opcode))
}
ms.returnRequest(req)
return Status(errNo)
}
// alignSlice ensures that the byte at alignedByte is aligned with the
// given logical block size. The input slice should be at least (size
// + blockSize)
func alignSlice(buf []byte, alignedByte, blockSize, size uintptr) []byte {
misaligned := uintptr(unsafe.Pointer(&buf[alignedByte])) & (blockSize - 1)
buf = buf[blockSize-misaligned:]
return buf[:size]
}
func (ms *Server) allocOut(req *request, size uint32) []byte {
if cap(req.bufferPoolOutputBuf) >= int(size) {
req.bufferPoolOutputBuf = req.bufferPoolOutputBuf[:size]
return req.bufferPoolOutputBuf
}
if req.bufferPoolOutputBuf != nil {
ms.buffers.FreeBuffer(req.bufferPoolOutputBuf)
req.bufferPoolOutputBuf = nil
}
// As this allocated a multiple of the page size, very likely
// this is aligned to logicalBlockSize too, which is smaller.
req.bufferPoolOutputBuf = ms.buffers.AllocBuffer(size)
return req.bufferPoolOutputBuf
}
func (ms *Server) write(req *request) Status {
// Forget/NotifyReply do not wait for reply from filesystem server.
switch req.inHeader.Opcode {
case _OP_FORGET, _OP_BATCH_FORGET, _OP_NOTIFY_REPLY:
return OK
case _OP_INTERRUPT:
if req.status.Ok() {
return OK
}
}
header := req.serializeHeader(req.flatDataSize())
if ms.opts.Debug {
log.Println(req.OutputDebug())
}
if header == nil {
return OK
}
s := ms.systemWrite(req, header)
return s
}
// InodeNotify invalidates the information associated with the inode
// (ie. data cache, attributes, etc.)
func (ms *Server) InodeNotify(node uint64, off int64, length int64) Status {
if !ms.kernelSettings.SupportsNotify(NOTIFY_INVAL_INODE) {
return ENOSYS
}
req := request{
inHeader: &InHeader{
Opcode: _OP_NOTIFY_INVAL_INODE,
},
handler: operationHandlers[_OP_NOTIFY_INVAL_INODE],
status: NOTIFY_INVAL_INODE,
}
entry := (*NotifyInvalInodeOut)(req.outData())
entry.Ino = node
entry.Off = off
entry.Length = length
// Protect against concurrent close.
ms.writeMu.Lock()
result := ms.write(&req)
ms.writeMu.Unlock()
if ms.opts.Debug {
log.Println("Response: INODE_NOTIFY", result)
}
return result
}
// InodeNotifyStoreCache tells kernel to store data into inode's cache.
//
// This call is similar to InodeNotify, but instead of only invalidating a data
// region, it gives updated data directly to the kernel.
func (ms *Server) InodeNotifyStoreCache(node uint64, offset int64, data []byte) Status {
if !ms.kernelSettings.SupportsNotify(NOTIFY_STORE_CACHE) {
return ENOSYS
}
for len(data) > 0 {
size := len(data)
if size > math.MaxInt32 {
// NotifyStoreOut has only uint32 for size.
// we check for max(int32), not max(uint32), because on 32-bit
// platforms int has only 31-bit for positive range.
size = math.MaxInt32
}
st := ms.inodeNotifyStoreCache32(node, offset, data[:size])
if st != OK {
return st
}
data = data[size:]
offset += int64(size)
}
return OK
}
// inodeNotifyStoreCache32 is internal worker for InodeNotifyStoreCache which
// handles data chunks not larger than 2GB.
func (ms *Server) inodeNotifyStoreCache32(node uint64, offset int64, data []byte) Status {
req := request{
inHeader: &InHeader{
Opcode: _OP_NOTIFY_STORE_CACHE,
},
handler: operationHandlers[_OP_NOTIFY_STORE_CACHE],
status: NOTIFY_STORE_CACHE,
}
store := (*NotifyStoreOut)(req.outData())
store.Nodeid = node
store.Offset = uint64(offset) // NOTE not int64, as it is e.g. in NotifyInvalInodeOut
store.Size = uint32(len(data))
req.flatData = data
// Protect against concurrent close.
ms.writeMu.Lock()
result := ms.write(&req)
ms.writeMu.Unlock()
if ms.opts.Debug {
log.Printf("Response: INODE_NOTIFY_STORE_CACHE: %v", result)
}
return result
}
// InodeRetrieveCache retrieves data from kernel's inode cache.
//
// InodeRetrieveCache asks kernel to return data from its cache for inode at
// [offset:offset+len(dest)) and waits for corresponding reply. If kernel cache
// has fewer consecutive data starting at offset, that fewer amount is returned.
// In particular if inode data at offset is not cached (0, OK) is returned.
//
// The kernel returns ENOENT if it does not currently have entry for this inode
// in its dentry cache.
func (ms *Server) InodeRetrieveCache(node uint64, offset int64, dest []byte) (n int, st Status) {
// the kernel won't send us in one go more then what we negotiated as MaxWrite.
// retrieve the data in chunks.
// TODO spawn some number of readahead retrievers in parallel.
ntotal := 0
for {
chunkSize := len(dest)
if chunkSize > ms.opts.MaxWrite {
chunkSize = ms.opts.MaxWrite
}
n, st = ms.inodeRetrieveCache1(node, offset, dest[:chunkSize])
if st != OK || n == 0 {
break
}
ntotal += n
offset += int64(n)
dest = dest[n:]
}
// if we could retrieve at least something - it is ok.
// if ntotal=0 - st will be st returned from first inodeRetrieveCache1.
if ntotal > 0 {
st = OK
}
return ntotal, st
}
// inodeRetrieveCache1 is internal worker for InodeRetrieveCache which
// actually talks to kernel and retrieves chunks not larger than ms.opts.MaxWrite.
func (ms *Server) inodeRetrieveCache1(node uint64, offset int64, dest []byte) (n int, st Status) {
if !ms.kernelSettings.SupportsNotify(NOTIFY_RETRIEVE_CACHE) {
return 0, ENOSYS
}
req := request{
inHeader: &InHeader{
Opcode: _OP_NOTIFY_RETRIEVE_CACHE,
},
handler: operationHandlers[_OP_NOTIFY_RETRIEVE_CACHE],
status: NOTIFY_RETRIEVE_CACHE,
}
// retrieve up to 2GB not to overflow uint32 size in NotifyRetrieveOut.
// see InodeNotifyStoreCache in similar place for why it is only 2GB, not 4GB.
//
// ( InodeRetrieveCache calls us with chunks not larger than
// ms.opts.MaxWrite, but MaxWrite is int, so let's be extra cautious )
size := len(dest)
if size > math.MaxInt32 {
size = math.MaxInt32
}
dest = dest[:size]
q := (*NotifyRetrieveOut)(req.outData())
q.Nodeid = node
q.Offset = uint64(offset) // not int64, as it is e.g. in NotifyInvalInodeOut
q.Size = uint32(len(dest))
reading := &retrieveCacheRequest{
nodeid: q.Nodeid,
offset: q.Offset,
dest: dest,
ready: make(chan struct{}),
}
ms.retrieveMu.Lock()
q.NotifyUnique = ms.retrieveNext
ms.retrieveNext++
ms.retrieveTab[q.NotifyUnique] = reading
ms.retrieveMu.Unlock()
// Protect against concurrent close.
ms.writeMu.Lock()
result := ms.write(&req)
ms.writeMu.Unlock()
if ms.opts.Debug {
log.Printf("Response: NOTIFY_RETRIEVE_CACHE: %v", result)
}
if result != OK {
ms.retrieveMu.Lock()
r := ms.retrieveTab[q.NotifyUnique]
if r == reading {
delete(ms.retrieveTab, q.NotifyUnique)
} else if r == nil {
// ok - might be dequeued by umount
} else {
// although very unlikely, it is possible that kernel sends
// unexpected NotifyReply with our notifyUnique, then
// retrieveNext wraps, makes full cycle, and another
// retrieve request is made with the same notifyUnique.
log.Printf("W: INODE_RETRIEVE_CACHE: request with notifyUnique=%d mutated", q.NotifyUnique)
}
ms.retrieveMu.Unlock()
return 0, result
}
// NotifyRetrieveOut sent to the kernel successfully. Now the kernel
// have to return data in a separate write-style NotifyReply request.
// Wait for the result.
<-reading.ready
return reading.n, reading.st
}
// retrieveCacheRequest represents in-flight cache retrieve request.
type retrieveCacheRequest struct {
nodeid uint64
offset uint64
dest []byte
// reply status
n int
st Status
ready chan struct{}
}
// DeleteNotify notifies the kernel that an entry is removed from a
// directory. In many cases, this is equivalent to EntryNotify,
// except when the directory is in use, eg. as working directory of
// some process. You should not hold any FUSE filesystem locks, as that
// can lead to deadlock.
func (ms *Server) DeleteNotify(parent uint64, child uint64, name string) Status {
if ms.kernelSettings.Minor < 18 {
return ms.EntryNotify(parent, name)
}
req := request{
inHeader: &InHeader{
Opcode: _OP_NOTIFY_DELETE,
},
handler: operationHandlers[_OP_NOTIFY_DELETE],
status: NOTIFY_DELETE,
}
entry := (*NotifyInvalDeleteOut)(req.outData())
entry.Parent = parent
entry.Child = child
entry.NameLen = uint32(len(name))
// Many versions of FUSE generate stacktraces if the
// terminating null byte is missing.
nameBytes := make([]byte, len(name)+1)
copy(nameBytes, name)
nameBytes[len(nameBytes)-1] = '\000'
req.flatData = nameBytes
// Protect against concurrent close.
ms.writeMu.Lock()
result := ms.write(&req)
ms.writeMu.Unlock()
if ms.opts.Debug {
log.Printf("Response: DELETE_NOTIFY: %v", result)
}
return result
}
// EntryNotify should be used if the existence status of an entry
// within a directory changes. You should not hold any FUSE filesystem
// locks, as that can lead to deadlock.
func (ms *Server) EntryNotify(parent uint64, name string) Status {
if !ms.kernelSettings.SupportsNotify(NOTIFY_INVAL_ENTRY) {
return ENOSYS
}
req := request{
inHeader: &InHeader{
Opcode: _OP_NOTIFY_INVAL_ENTRY,
},
handler: operationHandlers[_OP_NOTIFY_INVAL_ENTRY],
status: NOTIFY_INVAL_ENTRY,
}
entry := (*NotifyInvalEntryOut)(req.outData())
entry.Parent = parent
entry.NameLen = uint32(len(name))
// Many versions of FUSE generate stacktraces if the
// terminating null byte is missing.
nameBytes := make([]byte, len(name)+1)
copy(nameBytes, name)
nameBytes[len(nameBytes)-1] = '\000'
req.flatData = nameBytes
// Protect against concurrent close.
ms.writeMu.Lock()
result := ms.write(&req)
ms.writeMu.Unlock()
if ms.opts.Debug {
log.Printf("Response: ENTRY_NOTIFY: %v", result)
}
return result
}
// SupportsVersion returns true if the kernel supports the given
// protocol version or newer.
func (in *InitIn) SupportsVersion(maj, min uint32) bool {
return in.Major > maj || (in.Major == maj && in.Minor >= min)
}
// SupportsNotify returns whether a certain notification type is
// supported. Pass any of the NOTIFY_* types as argument.
func (in *InitIn) SupportsNotify(notifyType int) bool {
switch notifyType {
case NOTIFY_INVAL_ENTRY:
return in.SupportsVersion(7, 12)
case NOTIFY_INVAL_INODE:
return in.SupportsVersion(7, 12)
case NOTIFY_STORE_CACHE, NOTIFY_RETRIEVE_CACHE:
return in.SupportsVersion(7, 15)
case NOTIFY_DELETE:
return in.SupportsVersion(7, 18)
}
return false
}
// WaitMount waits for the first request to be served. Use this to
// avoid racing between accessing the (empty or not yet mounted)
// mountpoint, and the OS trying to setup the user-space mount.
func (ms *Server) WaitMount() error {
err := <-ms.ready
if err != nil {
return err
}
return pollHack(ms.mountPoint)
}

View File

@ -0,0 +1,32 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"syscall"
)
func (ms *Server) systemWrite(req *request, header []byte) Status {
if req.flatDataSize() == 0 {
err := handleEINTR(func() error {
_, err := syscall.Write(ms.mountFd, header)
return err
})
return ToStatus(err)
}
if req.fdData != nil {
sz := req.flatDataSize()
buf := ms.allocOut(req, uint32(sz))
req.flatData, req.status = req.fdData.Bytes(buf)
header = req.serializeHeader(len(req.flatData))
}
_, err := writev(int(ms.mountFd), [][]byte{header, req.flatData})
if req.readResult != nil {
req.readResult.Done()
}
return ToStatus(err)
}

View File

@ -0,0 +1,42 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"log"
"syscall"
)
func (ms *Server) systemWrite(req *request, header []byte) Status {
if req.flatDataSize() == 0 {
err := handleEINTR(func() error {
_, err := syscall.Write(ms.mountFd, header)
return err
})
return ToStatus(err)
}
if req.fdData != nil {
if ms.canSplice {
err := ms.trySplice(header, req, req.fdData)
if err == nil {
req.readResult.Done()
return OK
}
log.Println("trySplice:", err)
}
sz := req.flatDataSize()
buf := ms.allocOut(req, uint32(sz))
req.flatData, req.status = req.fdData.Bytes(buf)
header = req.serializeHeader(len(req.flatData))
}
_, err := writev(ms.mountFd, [][]byte{header, req.flatData})
if req.readResult != nil {
req.readResult.Done()
}
return ToStatus(err)
}

View File

@ -0,0 +1,17 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"fmt"
)
func (s *Server) setSplice() {
s.canSplice = false
}
func (ms *Server) trySplice(header []byte, req *request, fdData *readResultFd) error {
return fmt.Errorf("unimplemented")
}

View File

@ -0,0 +1,97 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"fmt"
"os"
"github.com/hanwen/go-fuse/v2/splice"
)
func (s *Server) setSplice() {
s.canSplice = splice.Resizable()
}
// trySplice: Zero-copy read from fdData.Fd into /dev/fuse
//
// This is a four-step process:
//
// 1) Splice data form fdData.Fd into the "pair1" pipe buffer --> pair1: [payload]
// Now we know the actual payload length and can
// construct the reply header
// 2) Write header into the "pair2" pipe buffer --> pair2: [header]
// 4) Splice data from "pair1" into "pair2" --> pair2: [header][payload]
// 3) Splice the data from "pair2" into /dev/fuse
//
// This dance is neccessary because header and payload cannot be split across
// two splices and we cannot seek in a pipe buffer.
func (ms *Server) trySplice(header []byte, req *request, fdData *readResultFd) error {
var err error
// Get a pair of connected pipes
pair1, err := splice.Get()
if err != nil {
return err
}
defer splice.Done(pair1)
// Grow buffer pipe to requested size + one extra page
// Without the extra page the kernel will block once the pipe is almost full
pair1Sz := fdData.Size() + os.Getpagesize()
if err := pair1.Grow(pair1Sz); err != nil {
return err
}
// Read data from file
payloadLen, err := pair1.LoadFromAt(fdData.Fd, fdData.Size(), fdData.Off)
if err != nil {
// TODO - extract the data from splice.
return err
}
// Get another pair of connected pipes
pair2, err := splice.Get()
if err != nil {
return err
}
defer splice.Done(pair2)
// Grow pipe to header + actually read size + one extra page
// Without the extra page the kernel will block once the pipe is almost full
header = req.serializeHeader(payloadLen)
total := len(header) + payloadLen
pair2Sz := total + os.Getpagesize()
if err := pair2.Grow(pair2Sz); err != nil {
return err
}
// Write header into pair2
n, err := pair2.Write(header)
if err != nil {
return err
}
if n != len(header) {
return fmt.Errorf("Short write into splice: wrote %d, want %d", n, len(header))
}
// Write data into pair2
n, err = pair2.LoadFrom(pair1.ReadFd(), payloadLen)
if err != nil {
return err
}
if n != payloadLen {
return fmt.Errorf("Short splice: wrote %d, want %d", n, payloadLen)
}
// Write header + data to /dev/fuse
_, err = pair2.WriteTo(uintptr(ms.mountFd), total)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,139 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"bytes"
"os"
"syscall"
"unsafe"
)
// TODO - move these into Go's syscall package.
func sys_writev(fd int, iovecs *syscall.Iovec, cnt int) (n int, err error) {
n1, _, e1 := syscall.Syscall(
syscall.SYS_WRITEV,
uintptr(fd), uintptr(unsafe.Pointer(iovecs)), uintptr(cnt))
n = int(n1)
if e1 != 0 {
err = syscall.Errno(e1)
}
return
}
func writev(fd int, packet [][]byte) (n int, err error) {
iovecs := make([]syscall.Iovec, 0, len(packet))
for _, v := range packet {
if len(v) == 0 {
continue
}
vec := syscall.Iovec{
Base: &v[0],
}
vec.SetLen(len(v))
iovecs = append(iovecs, vec)
}
sysErr := handleEINTR(func() error {
var err error
n, err = sys_writev(fd, &iovecs[0], len(iovecs))
return err
})
if sysErr != nil {
err = os.NewSyscallError("writev", sysErr)
}
return n, err
}
func getxattr(path string, attr string, dest []byte) (sz int, errno int) {
pathBs := syscall.StringBytePtr(path)
attrBs := syscall.StringBytePtr(attr)
size, _, errNo := syscall.Syscall6(
syscall.SYS_GETXATTR,
uintptr(unsafe.Pointer(pathBs)),
uintptr(unsafe.Pointer(attrBs)),
uintptr(unsafe.Pointer(&dest[0])),
uintptr(len(dest)),
0, 0)
return int(size), int(errNo)
}
func GetXAttr(path string, attr string, dest []byte) (value []byte, errno int) {
sz, errno := getxattr(path, attr, dest)
for sz > cap(dest) && errno == 0 {
dest = make([]byte, sz)
sz, errno = getxattr(path, attr, dest)
}
if errno != 0 {
return nil, errno
}
return dest[:sz], errno
}
func listxattr(path string, dest []byte) (sz int, errno int) {
pathbs := syscall.StringBytePtr(path)
var destPointer unsafe.Pointer
if len(dest) > 0 {
destPointer = unsafe.Pointer(&dest[0])
}
size, _, errNo := syscall.Syscall(
syscall.SYS_LISTXATTR,
uintptr(unsafe.Pointer(pathbs)),
uintptr(destPointer),
uintptr(len(dest)))
return int(size), int(errNo)
}
func ListXAttr(path string) (attributes []string, errno int) {
dest := make([]byte, 0)
sz, errno := listxattr(path, dest)
if errno != 0 {
return nil, errno
}
for sz > cap(dest) && errno == 0 {
dest = make([]byte, sz)
sz, errno = listxattr(path, dest)
}
// -1 to drop the final empty slice.
dest = dest[:sz-1]
attributesBytes := bytes.Split(dest, []byte{0})
attributes = make([]string, len(attributesBytes))
for i, v := range attributesBytes {
attributes[i] = string(v)
}
return attributes, errno
}
func Setxattr(path string, attr string, data []byte, flags int) (errno int) {
pathbs := syscall.StringBytePtr(path)
attrbs := syscall.StringBytePtr(attr)
_, _, errNo := syscall.Syscall6(
syscall.SYS_SETXATTR,
uintptr(unsafe.Pointer(pathbs)),
uintptr(unsafe.Pointer(attrbs)),
uintptr(unsafe.Pointer(&data[0])),
uintptr(len(data)),
uintptr(flags), 0)
return int(errNo)
}
func Removexattr(path string, attr string) (errno int) {
pathbs := syscall.StringBytePtr(path)
attrbs := syscall.StringBytePtr(attr)
_, _, errNo := syscall.Syscall(
syscall.SYS_REMOVEXATTR,
uintptr(unsafe.Pointer(pathbs)),
uintptr(unsafe.Pointer(attrbs)), 0)
return int(errNo)
}

View File

@ -0,0 +1,49 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"os"
"syscall"
"unsafe"
)
// TODO - move these into Go's syscall package.
func sys_writev(fd int, iovecs *syscall.Iovec, cnt int) (n int, err error) {
n1, _, e1 := syscall.Syscall(
syscall.SYS_WRITEV,
uintptr(fd), uintptr(unsafe.Pointer(iovecs)), uintptr(cnt))
n = int(n1)
if e1 != 0 {
err = syscall.Errno(e1)
}
return n, err
}
func writev(fd int, packet [][]byte) (n int, err error) {
iovecs := make([]syscall.Iovec, 0, len(packet))
for _, v := range packet {
if len(v) == 0 {
continue
}
vec := syscall.Iovec{
Base: &v[0],
}
vec.SetLen(len(v))
iovecs = append(iovecs, vec)
}
sysErr := handleEINTR(func() error {
var err error
n, err = sys_writev(fd, &iovecs[0], len(iovecs))
return err
})
if sysErr != nil {
err = os.NewSyscallError("writev", sysErr)
}
return n, err
}

View File

@ -0,0 +1,9 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
func (a *Attr) String() string {
return Print(a)
}

676
vendor/github.com/hanwen/go-fuse/v2/fuse/types.go generated vendored Normal file
View File

@ -0,0 +1,676 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"io"
"syscall"
"time"
)
const (
_DEFAULT_BACKGROUND_TASKS = 12
)
// Status is the errno number that a FUSE call returns to the kernel.
type Status int32
const (
OK = Status(0)
// EACCESS Permission denied
EACCES = Status(syscall.EACCES)
// EBUSY Device or resource busy
EBUSY = Status(syscall.EBUSY)
// EAGAIN Resource temporarily unavailable
EAGAIN = Status(syscall.EAGAIN)
// EINTR Call was interrupted
EINTR = Status(syscall.EINTR)
// EINVAL Invalid argument
EINVAL = Status(syscall.EINVAL)
// EIO I/O error
EIO = Status(syscall.EIO)
// ENOENT No such file or directory
ENOENT = Status(syscall.ENOENT)
// ENOSYS Function not implemented
ENOSYS = Status(syscall.ENOSYS)
// ENODATA No data available
ENODATA = Status(syscall.ENODATA)
// ENOTDIR Not a directory
ENOTDIR = Status(syscall.ENOTDIR)
// ENOTSUP Not supported
ENOTSUP = Status(syscall.ENOTSUP)
// EISDIR Is a directory
EISDIR = Status(syscall.EISDIR)
// EPERM Operation not permitted
EPERM = Status(syscall.EPERM)
// ERANGE Math result not representable
ERANGE = Status(syscall.ERANGE)
// EXDEV Cross-device link
EXDEV = Status(syscall.EXDEV)
// EBADF Bad file number
EBADF = Status(syscall.EBADF)
// ENODEV No such device
ENODEV = Status(syscall.ENODEV)
// EROFS Read-only file system
EROFS = Status(syscall.EROFS)
)
type ForgetIn struct {
InHeader
Nlookup uint64
}
// batch forget is handled internally.
type _ForgetOne struct {
NodeId uint64
Nlookup uint64
}
// batch forget is handled internally.
type _BatchForgetIn struct {
InHeader
Count uint32
Dummy uint32
}
type MkdirIn struct {
InHeader
// The mode for the new directory. The calling process' umask
// is already factored into the mode.
Mode uint32
Umask uint32
}
type Rename1In struct {
InHeader
Newdir uint64
}
type RenameIn struct {
InHeader
Newdir uint64
Flags uint32
Padding uint32
}
type LinkIn struct {
InHeader
Oldnodeid uint64
}
type Owner struct {
Uid uint32
Gid uint32
}
const ( // SetAttrIn.Valid
FATTR_MODE = (1 << 0)
FATTR_UID = (1 << 1)
FATTR_GID = (1 << 2)
FATTR_SIZE = (1 << 3)
FATTR_ATIME = (1 << 4)
FATTR_MTIME = (1 << 5)
FATTR_FH = (1 << 6)
FATTR_ATIME_NOW = (1 << 7)
FATTR_MTIME_NOW = (1 << 8)
FATTR_LOCKOWNER = (1 << 9)
FATTR_CTIME = (1 << 10)
)
type SetAttrInCommon struct {
InHeader
Valid uint32
Padding uint32
Fh uint64
Size uint64
LockOwner uint64
Atime uint64
Mtime uint64
Ctime uint64
Atimensec uint32
Mtimensec uint32
Ctimensec uint32
Mode uint32
Unused4 uint32
Owner
Unused5 uint32
}
// GetFh returns the file handle if available, or 0 if undefined.
func (s *SetAttrInCommon) GetFh() (uint64, bool) {
if s.Valid&FATTR_FH != 0 {
return s.Fh, true
}
return 0, false
}
func (s *SetAttrInCommon) GetMode() (uint32, bool) {
if s.Valid&FATTR_MODE != 0 {
return s.Mode & 07777, true
}
return 0, false
}
func (s *SetAttrInCommon) GetUID() (uint32, bool) {
if s.Valid&FATTR_UID != 0 {
return s.Uid, true
}
return ^uint32(0), false
}
func (s *SetAttrInCommon) GetGID() (uint32, bool) {
if s.Valid&FATTR_GID != 0 {
return s.Gid, true
}
return ^uint32(0), false
}
func (s *SetAttrInCommon) GetSize() (uint64, bool) {
if s.Valid&FATTR_SIZE != 0 {
return s.Size, true
}
return 0, false
}
func (s *SetAttrInCommon) GetMTime() (time.Time, bool) {
var t time.Time
if s.Valid&FATTR_MTIME != 0 {
if s.Valid&FATTR_MTIME_NOW != 0 {
t = time.Now()
} else {
t = time.Unix(int64(s.Mtime), int64(s.Mtimensec))
}
return t, true
}
return t, false
}
func (s *SetAttrInCommon) GetATime() (time.Time, bool) {
var t time.Time
if s.Valid&FATTR_ATIME != 0 {
if s.Valid&FATTR_ATIME_NOW != 0 {
t = time.Now()
} else {
t = time.Unix(int64(s.Atime), int64(s.Atimensec))
}
return t, true
}
return t, false
}
func (s *SetAttrInCommon) GetCTime() (time.Time, bool) {
var t time.Time
if s.Valid&FATTR_CTIME != 0 {
t = time.Unix(int64(s.Ctime), int64(s.Ctimensec))
return t, true
}
return t, false
}
const RELEASE_FLUSH = (1 << 0)
type ReleaseIn struct {
InHeader
Fh uint64
Flags uint32
ReleaseFlags uint32
LockOwner uint64
}
type OpenIn struct {
InHeader
Flags uint32
Mode uint32
}
const (
// OpenOut.Flags
FOPEN_DIRECT_IO = (1 << 0)
FOPEN_KEEP_CACHE = (1 << 1)
FOPEN_NONSEEKABLE = (1 << 2)
FOPEN_CACHE_DIR = (1 << 3)
FOPEN_STREAM = (1 << 4)
)
type OpenOut struct {
Fh uint64
OpenFlags uint32
Padding uint32
}
// To be set in InitIn/InitOut.Flags.
const (
CAP_ASYNC_READ = (1 << 0)
CAP_POSIX_LOCKS = (1 << 1)
CAP_FILE_OPS = (1 << 2)
CAP_ATOMIC_O_TRUNC = (1 << 3)
CAP_EXPORT_SUPPORT = (1 << 4)
CAP_BIG_WRITES = (1 << 5)
CAP_DONT_MASK = (1 << 6)
CAP_SPLICE_WRITE = (1 << 7)
CAP_SPLICE_MOVE = (1 << 8)
CAP_SPLICE_READ = (1 << 9)
CAP_FLOCK_LOCKS = (1 << 10)
CAP_IOCTL_DIR = (1 << 11)
CAP_AUTO_INVAL_DATA = (1 << 12)
CAP_READDIRPLUS = (1 << 13)
CAP_READDIRPLUS_AUTO = (1 << 14)
CAP_ASYNC_DIO = (1 << 15)
CAP_WRITEBACK_CACHE = (1 << 16)
CAP_NO_OPEN_SUPPORT = (1 << 17)
CAP_PARALLEL_DIROPS = (1 << 18)
CAP_HANDLE_KILLPRIV = (1 << 19)
CAP_POSIX_ACL = (1 << 20)
CAP_ABORT_ERROR = (1 << 21)
CAP_MAX_PAGES = (1 << 22)
CAP_CACHE_SYMLINKS = (1 << 23)
CAP_NO_OPENDIR_SUPPORT = (1 << 24)
CAP_EXPLICIT_INVAL_DATA = (1 << 25)
)
type InitIn struct {
InHeader
Major uint32
Minor uint32
MaxReadAhead uint32
Flags uint32
}
type InitOut struct {
Major uint32
Minor uint32
MaxReadAhead uint32
Flags uint32
MaxBackground uint16
CongestionThreshold uint16
MaxWrite uint32
TimeGran uint32
MaxPages uint16
Padding uint16
Unused [8]uint32
}
type _CuseInitIn struct {
InHeader
Major uint32
Minor uint32
Unused uint32
Flags uint32
}
type _CuseInitOut struct {
Major uint32
Minor uint32
Unused uint32
Flags uint32
MaxRead uint32
MaxWrite uint32
DevMajor uint32
DevMinor uint32
Spare [10]uint32
}
type InterruptIn struct {
InHeader
Unique uint64
}
type _BmapIn struct {
InHeader
Block uint64
Blocksize uint32
Padding uint32
}
type _BmapOut struct {
Block uint64
}
const (
FUSE_IOCTL_COMPAT = (1 << 0)
FUSE_IOCTL_UNRESTRICTED = (1 << 1)
FUSE_IOCTL_RETRY = (1 << 2)
)
type _IoctlIn struct {
InHeader
Fh uint64
Flags uint32
Cmd uint32
Arg uint64
InSize uint32
OutSize uint32
}
type _IoctlOut struct {
Result int32
Flags uint32
InIovs uint32
OutIovs uint32
}
type _PollIn struct {
InHeader
Fh uint64
Kh uint64
Flags uint32
Padding uint32
}
type _PollOut struct {
Revents uint32
Padding uint32
}
type _NotifyPollWakeupOut struct {
Kh uint64
}
type WriteOut struct {
Size uint32
Padding uint32
}
type GetXAttrOut struct {
Size uint32
Padding uint32
}
type FileLock struct {
Start uint64
End uint64
Typ uint32
Pid uint32
}
type LkIn struct {
InHeader
Fh uint64
Owner uint64
Lk FileLock
LkFlags uint32
Padding uint32
}
type LkOut struct {
Lk FileLock
}
// For AccessIn.Mask.
const (
X_OK = 1
W_OK = 2
R_OK = 4
F_OK = 0
)
type AccessIn struct {
InHeader
Mask uint32
Padding uint32
}
type FsyncIn struct {
InHeader
Fh uint64
FsyncFlags uint32
Padding uint32
}
type OutHeader struct {
Length uint32
Status int32
Unique uint64
}
type NotifyInvalInodeOut struct {
Ino uint64
Off int64
Length int64
}
type NotifyInvalEntryOut struct {
Parent uint64
NameLen uint32
Padding uint32
}
type NotifyInvalDeleteOut struct {
Parent uint64
Child uint64
NameLen uint32
Padding uint32
}
type NotifyStoreOut struct {
Nodeid uint64
Offset uint64
Size uint32
Padding uint32
}
type NotifyRetrieveOut struct {
NotifyUnique uint64
Nodeid uint64
Offset uint64
Size uint32
Padding uint32
}
type NotifyRetrieveIn struct {
InHeader
Dummy1 uint64
Offset uint64
Size uint32
Dummy2 uint32
Dummy3 uint64
Dummy4 uint64
}
const (
// NOTIFY_POLL = -1 // notify kernel that a poll waiting for IO on a file handle should wake up
NOTIFY_INVAL_INODE = -2 // notify kernel that an inode should be invalidated
NOTIFY_INVAL_ENTRY = -3 // notify kernel that a directory entry should be invalidated
NOTIFY_STORE_CACHE = -4 // store data into kernel cache of an inode
NOTIFY_RETRIEVE_CACHE = -5 // retrieve data from kernel cache of an inode
NOTIFY_DELETE = -6 // notify kernel that a directory entry has been deleted
// NOTIFY_CODE_MAX = -6
)
type FlushIn struct {
InHeader
Fh uint64
Unused uint32
Padding uint32
LockOwner uint64
}
type LseekIn struct {
InHeader
Fh uint64
Offset uint64
Whence uint32
Padding uint32
}
type LseekOut struct {
Offset uint64
}
type CopyFileRangeIn struct {
InHeader
FhIn uint64
OffIn uint64
NodeIdOut uint64
FhOut uint64
OffOut uint64
Len uint64
Flags uint64
}
// EntryOut holds the result of a (directory,name) lookup. It has two
// TTLs, one for the (directory, name) lookup itself, and one for the
// attributes (eg. size, mode). The entry TTL also applies if the
// lookup result is ENOENT ("negative entry lookup")
type EntryOut struct {
NodeId uint64
Generation uint64
EntryValid uint64
AttrValid uint64
EntryValidNsec uint32
AttrValidNsec uint32
Attr
}
// EntryTimeout returns entry timeout currently
func (o *EntryOut) EntryTimeout() time.Duration {
return time.Duration(uint64(o.EntryValidNsec) + o.EntryValid*1e9)
}
func (o *EntryOut) AttrTimeout() time.Duration {
return time.Duration(uint64(o.AttrValidNsec) + o.AttrValid*1e9)
}
func (o *EntryOut) SetEntryTimeout(dt time.Duration) {
ns := int64(dt)
o.EntryValidNsec = uint32(ns % 1e9)
o.EntryValid = uint64(ns / 1e9)
}
func (o *EntryOut) SetAttrTimeout(dt time.Duration) {
ns := int64(dt)
o.AttrValidNsec = uint32(ns % 1e9)
o.AttrValid = uint64(ns / 1e9)
}
type AttrOut struct {
AttrValid uint64
AttrValidNsec uint32
Dummy uint32
Attr
}
func (o *AttrOut) Timeout() time.Duration {
return time.Duration(uint64(o.AttrValidNsec) + o.AttrValid*1e9)
}
func (o *AttrOut) SetTimeout(dt time.Duration) {
ns := int64(dt)
o.AttrValidNsec = uint32(ns % 1e9)
o.AttrValid = uint64(ns / 1e9)
}
type CreateOut struct {
EntryOut
OpenOut
}
// Caller has data on the process making the FS call.
//
// The UID and GID are effective UID/GID, except for the ACCESS
// opcode, where UID and GID are the real UIDs
type Caller struct {
Owner
Pid uint32
}
type InHeader struct {
Length uint32
Opcode uint32
Unique uint64
NodeId uint64
Caller
Padding uint32
}
type StatfsOut struct {
Blocks uint64
Bfree uint64
Bavail uint64
Files uint64
Ffree uint64
Bsize uint32
NameLen uint32
Frsize uint32
Padding uint32
Spare [6]uint32
}
// _Dirent is what we send to the kernel, but we offer DirEntry and
// DirEntryList to the user.
type _Dirent struct {
Ino uint64
Off uint64
NameLen uint32
Typ uint32
}
const (
READ_LOCKOWNER = (1 << 1)
)
const (
WRITE_CACHE = (1 << 0)
WRITE_LOCKOWNER = (1 << 1)
)
type FallocateIn struct {
InHeader
Fh uint64
Offset uint64
Length uint64
Mode uint32
Padding uint32
}
func (lk *FileLock) ToFlockT(flockT *syscall.Flock_t) {
flockT.Start = int64(lk.Start)
if lk.End == (1<<63)-1 {
flockT.Len = 0
} else {
flockT.Len = int64(lk.End - lk.Start + 1)
}
flockT.Whence = int16(io.SeekStart)
flockT.Type = int16(lk.Typ)
}
func (lk *FileLock) FromFlockT(flockT *syscall.Flock_t) {
lk.Typ = uint32(flockT.Type)
if flockT.Type != syscall.F_UNLCK {
lk.Start = uint64(flockT.Start)
if flockT.Len == 0 {
lk.End = (1 << 63) - 1
} else {
lk.End = uint64(flockT.Start + flockT.Len - 1)
}
}
lk.Pid = uint32(flockT.Pid)
}

View File

@ -0,0 +1,166 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"syscall"
)
const (
ENOATTR = Status(syscall.ENOATTR) // ENOATTR is not defined for all GOOS.
// EREMOTEIO is not supported on Darwin.
EREMOTEIO = Status(syscall.EIO)
)
type Attr struct {
Ino uint64
Size uint64
Blocks uint64
Atime uint64
Mtime uint64
Ctime uint64
Crtime_ uint64 // OS X
Atimensec uint32
Mtimensec uint32
Ctimensec uint32
Crtimensec_ uint32 // OS X
Mode uint32
Nlink uint32
Owner
Rdev uint32
Flags_ uint32 // OS X
}
const (
FATTR_CRTIME = (1 << 28)
FATTR_CHGTIME = (1 << 29)
FATTR_BKUPTIME = (1 << 30)
FATTR_FLAGS = (1 << 31)
)
type SetAttrIn struct {
SetAttrInCommon
// OS X only
Bkuptime_ uint64
Chgtime_ uint64
Crtime uint64
BkuptimeNsec uint32
ChgtimeNsec uint32
CrtimeNsec uint32
Flags_ uint32 // see chflags(2)
}
const (
FOPEN_PURGE_ATTR = (1 << 30)
FOPEN_PURGE_UBC = (1 << 31)
)
// compat with linux.
const (
// Mask for GetAttrIn.Flags. If set, GetAttrIn has a file handle set.
FUSE_GETATTR_FH = (1 << 0)
)
type GetAttrIn struct {
InHeader
}
func (g *GetAttrIn) Flags() uint32 {
return 0
}
func (g *GetAttrIn) Fh() uint64 {
return 0
}
// Uses OpenIn struct for create.
type CreateIn struct {
InHeader
Flags uint32
Mode uint32
}
type MknodIn struct {
InHeader
Mode uint32
Rdev uint32
}
type ReadIn struct {
InHeader
Fh uint64
Offset uint64
Size uint32
ReadFlags uint32
}
type WriteIn struct {
InHeader
Fh uint64
Offset uint64
Size uint32
WriteFlags uint32
}
type SetXAttrIn struct {
InHeader
Size uint32
Flags uint32
Position uint32
Padding uint32
}
type GetXAttrIn struct {
InHeader
Size uint32
Padding uint32
Position uint32
Padding2 uint32
}
const (
CAP_CASE_INSENSITIVE = (1 << 29)
CAP_VOL_RENAME = (1 << 30)
CAP_XTIMES = (1 << 31)
)
type GetxtimesOut struct {
Bkuptime uint64
Crtime uint64
Bkuptimensec uint32
Crtimensec uint32
}
type ExchangeIn struct {
InHeader
Olddir uint64
Newdir uint64
Options uint64
}
func (s *StatfsOut) FromStatfsT(statfs *syscall.Statfs_t) {
s.Blocks = statfs.Blocks
s.Bfree = statfs.Bfree
s.Bavail = statfs.Bavail
s.Files = statfs.Files
s.Ffree = statfs.Ffree
s.Bsize = uint32(statfs.Iosize) // Iosize translates to Bsize: the optimal transfer size.
s.Frsize = s.Bsize // Bsize translates to Frsize: the minimum transfer size.
// The block counts are in units of statfs.Bsize.
// If s.Bsize != statfs.Bsize, we have to recalculate the block counts
// accordingly (s.Bsize is usually 256*statfs.Bsize).
if s.Bsize > statfs.Bsize {
adj := uint64(s.Bsize / statfs.Bsize)
s.Blocks /= adj
s.Bfree /= adj
s.Bavail /= adj
}
}

134
vendor/github.com/hanwen/go-fuse/v2/fuse/types_linux.go generated vendored Normal file
View File

@ -0,0 +1,134 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"syscall"
)
const (
ENOATTR = Status(syscall.ENODATA) // On Linux, ENOATTR is an alias for ENODATA.
// EREMOTEIO Remote I/O error
EREMOTEIO = Status(syscall.EREMOTEIO)
)
type Attr struct {
Ino uint64
Size uint64
// Blocks is the number of 512-byte blocks that the file occupies on disk.
Blocks uint64
Atime uint64
Mtime uint64
Ctime uint64
Atimensec uint32
Mtimensec uint32
Ctimensec uint32
Mode uint32
Nlink uint32
Owner
Rdev uint32
// Blksize is the preferred size for file system operations.
Blksize uint32
Padding uint32
}
type SetAttrIn struct {
SetAttrInCommon
}
const (
// Mask for GetAttrIn.Flags. If set, GetAttrIn has a file handle set.
FUSE_GETATTR_FH = (1 << 0)
)
type GetAttrIn struct {
InHeader
Flags_ uint32
Dummy uint32
Fh_ uint64
}
// Flags accesses the flags. This is a method, because OSXFuse does not
// have GetAttrIn flags.
func (g *GetAttrIn) Flags() uint32 {
return g.Flags_
}
// Fh accesses the file handle. This is a method, because OSXFuse does not
// have GetAttrIn flags.
func (g *GetAttrIn) Fh() uint64 {
return g.Fh_
}
type CreateIn struct {
InHeader
Flags uint32
// Mode for the new file; already takes Umask into account.
Mode uint32
// Umask used for this create call.
Umask uint32
Padding uint32
}
type MknodIn struct {
InHeader
// Mode to use, including the Umask value
Mode uint32
Rdev uint32
Umask uint32
Padding uint32
}
type ReadIn struct {
InHeader
Fh uint64
Offset uint64
Size uint32
ReadFlags uint32
LockOwner uint64
Flags uint32
Padding uint32
}
type WriteIn struct {
InHeader
Fh uint64
Offset uint64
Size uint32
WriteFlags uint32
LockOwner uint64
Flags uint32
Padding uint32
}
type SetXAttrIn struct {
InHeader
Size uint32
Flags uint32
}
type GetXAttrIn struct {
InHeader
Size uint32
Padding uint32
}
func (s *StatfsOut) FromStatfsT(statfs *syscall.Statfs_t) {
s.Blocks = statfs.Blocks
s.Bsize = uint32(statfs.Bsize)
s.Bfree = statfs.Bfree
s.Bavail = statfs.Bavail
s.Files = statfs.Files
s.Ffree = statfs.Ffree
s.Frsize = uint32(statfs.Frsize)
s.NameLen = uint32(statfs.Namelen)
}

60
vendor/github.com/hanwen/go-fuse/v2/internal/access.go generated vendored Normal file
View File

@ -0,0 +1,60 @@
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package internal
import (
"os/user"
"strconv"
)
// HasAccess tests if a caller can access a file with permissions
// `perm` in mode `mask`
func HasAccess(callerUid, callerGid, fileUid, fileGid uint32, perm uint32, mask uint32) bool {
if callerUid == 0 {
// root can do anything.
return true
}
mask = mask & 7
if mask == 0 {
return true
}
if callerUid == fileUid {
if perm&(mask<<6) != 0 {
return true
}
}
if callerGid == fileGid {
if perm&(mask<<3) != 0 {
return true
}
}
if perm&mask != 0 {
return true
}
// Check other groups.
if perm&(mask<<3) == 0 {
// avoid expensive lookup if it's not allowed anyway
return false
}
u, err := user.LookupId(strconv.Itoa(int(callerUid)))
if err != nil {
return false
}
gs, err := u.GroupIds()
if err != nil {
return false
}
fileGidStr := strconv.Itoa(int(fileGid))
for _, gidStr := range gs {
if gidStr == fileGidStr {
return true
}
}
return false
}

View File

@ -0,0 +1,40 @@
// Copyright 2018 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package utimens
import (
"syscall"
"time"
"github.com/hanwen/go-fuse/v2/fuse"
)
// timeToTimeval converts time.Time to syscall.Timeval
func timeToTimeval(t *time.Time) syscall.Timeval {
// Note: This does not use syscall.NsecToTimespec because
// that does not work properly for times before 1970,
// see https://github.com/golang/go/issues/12777
var tv syscall.Timeval
tv.Usec = int32(t.Nanosecond() / 1000)
tv.Sec = t.Unix()
return tv
}
// Fill converts a and m to a syscall.Timeval slice that can be passed
// to syscall.Utimes. Missing values (if any) are taken from attr
func Fill(a *time.Time, m *time.Time, attr *fuse.Attr) []syscall.Timeval {
if a == nil {
a2 := time.Unix(int64(attr.Atime), int64(attr.Atimensec))
a = &a2
}
if m == nil {
m2 := time.Unix(int64(attr.Mtime), int64(attr.Mtimensec))
m = &m2
}
tv := make([]syscall.Timeval, 2)
tv[0] = timeToTimeval(a)
tv[1] = timeToTimeval(m)
return tv
}

View File

@ -0,0 +1,7 @@
// Copyright 2018 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package utimens
// placeholder file so this package exists on all platforms.

70
vendor/github.com/hanwen/go-fuse/v2/splice/copy.go generated vendored Normal file
View File

@ -0,0 +1,70 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package splice
import (
"io"
"os"
)
func SpliceCopy(dst *os.File, src *os.File, p *Pair) (int64, error) {
total := int64(0)
for {
n, err := p.LoadFrom(src.Fd(), p.size)
if err != nil {
return total, err
}
if n == 0 {
break
}
m, err := p.WriteTo(dst.Fd(), n)
total += int64(m)
if err != nil {
return total, err
}
if m < n {
return total, err
}
if int(n) < p.size {
break
}
}
return total, nil
}
// Argument ordering follows io.Copy.
func CopyFile(dstName string, srcName string, mode int) error {
src, err := os.Open(srcName)
if err != nil {
return err
}
defer src.Close()
dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(mode))
if err != nil {
return err
}
defer dst.Close()
return CopyFds(dst, src)
}
func CopyFds(dst *os.File, src *os.File) (err error) {
p, err := splicePool.get()
if p != nil {
p.Grow(256 * 1024)
_, err := SpliceCopy(dst, src, p)
splicePool.done(p)
return err
} else {
_, err = io.Copy(dst, src)
}
if err == io.EOF {
err = nil
}
return err
}

68
vendor/github.com/hanwen/go-fuse/v2/splice/pair.go generated vendored Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package splice
import (
"fmt"
"syscall"
)
type Pair struct {
r, w int
size int
}
func (p *Pair) MaxGrow() {
for p.Grow(2*p.size) == nil {
}
}
func (p *Pair) Grow(n int) error {
if n <= p.size {
return nil
}
if !resizable {
return fmt.Errorf("splice: want %d bytes, but not resizable", n)
}
if n > maxPipeSize {
return fmt.Errorf("splice: want %d bytes, max pipe size %d", n, maxPipeSize)
}
newsize, errNo := fcntl(uintptr(p.r), F_SETPIPE_SZ, n)
if errNo != 0 {
return fmt.Errorf("splice: fcntl returned %v", errNo)
}
p.size = newsize
return nil
}
func (p *Pair) Cap() int {
return p.size
}
func (p *Pair) Close() error {
err1 := syscall.Close(p.r)
err2 := syscall.Close(p.w)
if err1 != nil {
return err1
}
return err2
}
func (p *Pair) Read(d []byte) (n int, err error) {
return syscall.Read(p.r, d)
}
func (p *Pair) Write(d []byte) (n int, err error) {
return syscall.Write(p.w, d)
}
func (p *Pair) ReadFd() uintptr {
return uintptr(p.r)
}
func (p *Pair) WriteFd() uintptr {
return uintptr(p.w)
}

View File

@ -0,0 +1,22 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package splice
import ()
func (p *Pair) LoadFromAt(fd uintptr, sz int, off int64) (int, error) {
panic("not implemented")
return 0, nil
}
func (p *Pair) LoadFrom(fd uintptr, sz int) (int, error) {
panic("not implemented")
return 0, nil
}
func (p *Pair) WriteTo(fd uintptr, n int) (int, error) {
panic("not implemented")
return 0, nil
}

View File

@ -0,0 +1,54 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package splice
import (
"fmt"
"log"
"os"
"syscall"
)
func (p *Pair) LoadFromAt(fd uintptr, sz int, off int64) (int, error) {
n, err := syscall.Splice(int(fd), &off, p.w, nil, sz, 0)
return int(n), err
}
func (p *Pair) LoadFrom(fd uintptr, sz int) (int, error) {
if sz > p.size {
return 0, fmt.Errorf("LoadFrom: not enough space %d, %d",
sz, p.size)
}
n, err := syscall.Splice(int(fd), nil, p.w, nil, sz, 0)
if err != nil {
err = os.NewSyscallError("Splice load from", err)
}
return int(n), err
}
func (p *Pair) WriteTo(fd uintptr, n int) (int, error) {
m, err := syscall.Splice(p.r, nil, int(fd), nil, int(n), 0)
if err != nil {
err = os.NewSyscallError("Splice write", err)
}
return int(m), err
}
const _SPLICE_F_NONBLOCK = 0x2
func (p *Pair) discard() {
_, err := syscall.Splice(p.r, nil, int(devNullFD), nil, int(p.size), _SPLICE_F_NONBLOCK)
if err == syscall.EAGAIN {
// all good.
} else if err != nil {
errR := syscall.Close(p.r)
errW := syscall.Close(p.w)
// This can happen if something closed our fd
// inadvertently (eg. double close)
log.Panicf("splicing into /dev/null: %v (close R %d '%v', close W %d '%v')", err, p.r, errR, p.w, errW)
}
}

105
vendor/github.com/hanwen/go-fuse/v2/splice/pool.go generated vendored Normal file
View File

@ -0,0 +1,105 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package splice
import (
"sync"
)
var splicePool *pairPool
type pairPool struct {
sync.Mutex
unused []*Pair
usedCount int
}
func ClearSplicePool() {
splicePool.clear()
}
func Get() (*Pair, error) {
return splicePool.get()
}
func Total() int {
return splicePool.total()
}
func Used() int {
return splicePool.used()
}
// Done returns the pipe pair to pool.
func Done(p *Pair) {
splicePool.done(p)
}
// Closes and discards pipe pair.
func Drop(p *Pair) {
splicePool.drop(p)
}
func newSplicePairPool() *pairPool {
return &pairPool{}
}
func (pp *pairPool) clear() {
pp.Lock()
for _, p := range pp.unused {
p.Close()
}
pp.unused = pp.unused[:0]
pp.Unlock()
}
func (pp *pairPool) used() (n int) {
pp.Lock()
n = pp.usedCount
pp.Unlock()
return n
}
func (pp *pairPool) total() int {
pp.Lock()
n := pp.usedCount + len(pp.unused)
pp.Unlock()
return n
}
func (pp *pairPool) drop(p *Pair) {
p.Close()
pp.Lock()
pp.usedCount--
pp.Unlock()
}
func (pp *pairPool) get() (p *Pair, err error) {
pp.Lock()
defer pp.Unlock()
pp.usedCount++
l := len(pp.unused)
if l > 0 {
p := pp.unused[l-1]
pp.unused = pp.unused[:l-1]
return p, nil
}
return newSplicePair()
}
func (pp *pairPool) done(p *Pair) {
p.discard()
pp.Lock()
pp.usedCount--
pp.unused = append(pp.unused, p)
pp.Unlock()
}
func init() {
splicePool = newSplicePairPool()
}

97
vendor/github.com/hanwen/go-fuse/v2/splice/splice.go generated vendored Normal file
View File

@ -0,0 +1,97 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package splice
// Routines for efficient file to file copying.
import (
"fmt"
"io/ioutil"
"log"
"os"
"syscall"
)
var maxPipeSize int
var resizable bool
func Resizable() bool {
return resizable
}
func MaxPipeSize() int {
return maxPipeSize
}
// From manpage on ubuntu Lucid:
//
// Since Linux 2.6.11, the pipe capacity is 65536 bytes.
const DefaultPipeSize = 16 * 4096
// We empty pipes by splicing to /dev/null.
var devNullFD uintptr
func init() {
content, err := ioutil.ReadFile("/proc/sys/fs/pipe-max-size")
if err != nil {
maxPipeSize = DefaultPipeSize
} else {
fmt.Sscan(string(content), &maxPipeSize)
}
r, w, err := os.Pipe()
if err != nil {
log.Panicf("cannot create pipe: %v", err)
}
sz, errNo := fcntl(r.Fd(), F_GETPIPE_SZ, 0)
resizable = (errNo == 0)
_, errNo = fcntl(r.Fd(), F_SETPIPE_SZ, 2*sz)
resizable = resizable && (errNo == 0)
r.Close()
w.Close()
fd, err := syscall.Open("/dev/null", os.O_WRONLY, 0)
if err != nil {
log.Panicf("splice: %v", err)
}
devNullFD = uintptr(fd)
}
// copy & paste from syscall.
func fcntl(fd uintptr, cmd int, arg int) (val int, errno syscall.Errno) {
r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, fd, uintptr(cmd), uintptr(arg))
val = int(r0)
errno = syscall.Errno(e1)
return
}
const F_SETPIPE_SZ = 1031
const F_GETPIPE_SZ = 1032
func osPipe() (int, int, error) {
var fds [2]int
err := syscall.Pipe2(fds[:], syscall.O_NONBLOCK)
return fds[0], fds[1], err
}
func newSplicePair() (p *Pair, err error) {
p = &Pair{}
p.r, p.w, err = osPipe()
if err != nil {
return nil, err
}
var errNo syscall.Errno
p.size, errNo = fcntl(uintptr(p.r), F_GETPIPE_SZ, 0)
if err == syscall.EINVAL {
p.size = DefaultPipeSize
return p, nil
}
if errNo != 0 {
p.Close()
return nil, fmt.Errorf("fcntl getsize: %v", errNo)
}
return p, nil
}

6
vendor/modules.txt vendored
View File

@ -96,6 +96,12 @@ github.com/golang/protobuf/ptypes/timestamp
github.com/google/go-querystring/query
# github.com/googleapis/gax-go/v2 v2.0.5
github.com/googleapis/gax-go/v2
# github.com/hanwen/go-fuse/v2 v2.0.3-0.20191108143333-152e6ac32d54
github.com/hanwen/go-fuse/v2/fs
github.com/hanwen/go-fuse/v2/fuse
github.com/hanwen/go-fuse/v2/internal
github.com/hanwen/go-fuse/v2/internal/utimens
github.com/hanwen/go-fuse/v2/splice
# github.com/inconshreveable/mousetrap v1.0.0
github.com/inconshreveable/mousetrap
# github.com/jlaffaye/ftp v0.0.0-20191025175106-a59fe673c9b2