forked from TrueCloudLab/restic
Vendor dependencies with dep
This commit is contained in:
parent
df8a5792f1
commit
91edebf1fe
1691 changed files with 466360 additions and 0 deletions
123
Gopkg.lock
generated
Normal file
123
Gopkg.lock
generated
Normal file
|
@ -0,0 +1,123 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "bazil.org/fuse"
|
||||
packages = [".","fs","fuseutil"]
|
||||
revision = "371fbbdaa8987b715bdd21d6adc4c9b20155f748"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/elithrar/simple-scrypt"
|
||||
packages = ["."]
|
||||
revision = "47767683c6c880a9f89e48d376c70de7f5268951"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-ini/ini"
|
||||
packages = ["."]
|
||||
revision = "d3de07a94d22b4a0972deb4b96d790c2c0ce8333"
|
||||
version = "v1.28.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kr/fs"
|
||||
packages = ["."]
|
||||
revision = "2788f0dbd16903de03cb8186e5c7d97b69ad387b"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kurin/blazer"
|
||||
packages = ["b2","base","internal/b2types","internal/blog"]
|
||||
revision = "612082ed2430716569f1ec816fc6ade849020816"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/minio/go-homedir"
|
||||
packages = ["."]
|
||||
revision = "21304a94172ae3a09dee2cd86a12fb6f842138c7"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/minio/minio-go"
|
||||
packages = [".","pkg/credentials","pkg/encrypt","pkg/policy","pkg/s3signer","pkg/s3utils","pkg/set"]
|
||||
revision = "5ca66c9a35ba1cd674484be99dc97aa0973afe12"
|
||||
version = "v3.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/ncw/swift"
|
||||
packages = ["."]
|
||||
revision = "5068c3506cf003c630c94b92a64e978115394f26"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/profile"
|
||||
packages = ["."]
|
||||
revision = "5b67d428864e92711fcbd2f8629456121a56d91f"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/pkg/sftp"
|
||||
packages = ["."]
|
||||
revision = "4f3e725e885c021085d2fb8a9cc26e30ea1a992f"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/xattr"
|
||||
packages = ["."]
|
||||
revision = "23c75e3f6c1d8b13b3dd905b011a7f38a06044b7"
|
||||
version = "v0.2.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/restic/chunker"
|
||||
packages = ["."]
|
||||
revision = "bb2ecf9a98e35a0b336ffc23fc515fb6e7961577"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = ["."]
|
||||
revision = "f20b4e9c32bb3e9d44773ca208db814f24dcd21b"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","pbkdf2","poly1305","scrypt","ssh","ssh/terminal"]
|
||||
revision = "6914964337150723782436d56b3f21610a74ce7b"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context","context/ctxhttp"]
|
||||
revision = "ab5485076ff3407ad2d02db054635913f017b0ed"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "c4489faa6e5ab84c0ef40d6ee878f7a030281f0f"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "b4de858790fd06b6d1df19baa8a3246f23b2fded9ab6eb85325ddb3ee91003b8"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
78
Gopkg.toml
Normal file
78
Gopkg.toml
Normal file
|
@ -0,0 +1,78 @@
|
|||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "bazil.org/fuse"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/elithrar/simple-scrypt"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/kurin/blazer"
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/minio/minio-go"
|
||||
version = "3.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/ncw/swift"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/errors"
|
||||
version = "0.8.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/profile"
|
||||
version = "1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/pkg/sftp"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/xattr"
|
||||
version = "0.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/restic/chunker"
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/cobra"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
2
vendor/bazil.org/fuse/.gitattributes
generated
vendored
Normal file
2
vendor/bazil.org/fuse/.gitattributes
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.go filter=gofmt
|
||||
*.cgo filter=gofmt
|
11
vendor/bazil.org/fuse/.gitignore
generated
vendored
Normal file
11
vendor/bazil.org/fuse/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
*~
|
||||
.#*
|
||||
## the next line needs to start with a backslash to avoid looking like
|
||||
## a comment
|
||||
\#*#
|
||||
.*.swp
|
||||
|
||||
*.test
|
||||
|
||||
/clockfs
|
||||
/hellofs
|
93
vendor/bazil.org/fuse/LICENSE
generated
vendored
Normal file
93
vendor/bazil.org/fuse/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
Copyright (c) 2013-2015 Tommi Virtanen.
|
||||
Copyright (c) 2009, 2011, 2012 The Go 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 Google Inc. 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.
|
||||
|
||||
|
||||
|
||||
The following included software components have additional copyright
|
||||
notices and license terms that may differ from the above.
|
||||
|
||||
|
||||
File fuse.go:
|
||||
|
||||
// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c,
|
||||
// which carries this notice:
|
||||
//
|
||||
// The files in this directory are subject to the following license.
|
||||
//
|
||||
// The author of this software is Russ Cox.
|
||||
//
|
||||
// Copyright (c) 2006 Russ Cox
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose without fee is hereby granted, provided that this entire notice
|
||||
// is included in all copies of any software which is or includes a copy
|
||||
// or modification of this software and in all copies of the supporting
|
||||
// documentation for such software.
|
||||
//
|
||||
// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
||||
// WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY
|
||||
// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
|
||||
// FITNESS FOR ANY PARTICULAR PURPOSE.
|
||||
|
||||
|
||||
File fuse_kernel.go:
|
||||
|
||||
// Derived from FUSE's fuse_kernel.h
|
||||
/*
|
||||
This file defines the kernel interface of FUSE
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
|
||||
This -- and only this -- header file may also be distributed under
|
||||
the terms of the BSD Licence as follows:
|
||||
|
||||
Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. 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.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
|
||||
*/
|
23
vendor/bazil.org/fuse/README.md
generated
vendored
Normal file
23
vendor/bazil.org/fuse/README.md
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
bazil.org/fuse -- Filesystems in Go
|
||||
===================================
|
||||
|
||||
`bazil.org/fuse` is a Go library for writing FUSE userspace
|
||||
filesystems.
|
||||
|
||||
It is a from-scratch implementation of the kernel-userspace
|
||||
communication protocol, and does not use the C library from the
|
||||
project called FUSE. `bazil.org/fuse` embraces Go fully for safety and
|
||||
ease of programming.
|
||||
|
||||
Here’s how to get going:
|
||||
|
||||
go get bazil.org/fuse
|
||||
|
||||
Website: http://bazil.org/fuse/
|
||||
|
||||
Github repository: https://github.com/bazil/fuse
|
||||
|
||||
API docs: http://godoc.org/bazil.org/fuse
|
||||
|
||||
Our thanks to Russ Cox for his fuse library, which this project is
|
||||
based on.
|
35
vendor/bazil.org/fuse/buffer.go
generated
vendored
Normal file
35
vendor/bazil.org/fuse/buffer.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
package fuse
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// buffer provides a mechanism for constructing a message from
|
||||
// multiple segments.
|
||||
type buffer []byte
|
||||
|
||||
// alloc allocates size bytes and returns a pointer to the new
|
||||
// segment.
|
||||
func (w *buffer) alloc(size uintptr) unsafe.Pointer {
|
||||
s := int(size)
|
||||
if len(*w)+s > cap(*w) {
|
||||
old := *w
|
||||
*w = make([]byte, len(*w), 2*cap(*w)+s)
|
||||
copy(*w, old)
|
||||
}
|
||||
l := len(*w)
|
||||
*w = (*w)[:l+s]
|
||||
return unsafe.Pointer(&(*w)[l])
|
||||
}
|
||||
|
||||
// reset clears out the contents of the buffer.
|
||||
func (w *buffer) reset() {
|
||||
for i := range (*w)[:cap(*w)] {
|
||||
(*w)[i] = 0
|
||||
}
|
||||
*w = (*w)[:0]
|
||||
}
|
||||
|
||||
func newBuffer(extra uintptr) buffer {
|
||||
const hdrSize = unsafe.Sizeof(outHeader{})
|
||||
buf := make(buffer, hdrSize, hdrSize+extra)
|
||||
return buf
|
||||
}
|
21
vendor/bazil.org/fuse/debug.go
generated
vendored
Normal file
21
vendor/bazil.org/fuse/debug.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func stack() string {
|
||||
buf := make([]byte, 1024)
|
||||
return string(buf[:runtime.Stack(buf, false)])
|
||||
}
|
||||
|
||||
func nop(msg interface{}) {}
|
||||
|
||||
// Debug is called to output debug messages, including protocol
|
||||
// traces. The default behavior is to do nothing.
|
||||
//
|
||||
// The messages have human-friendly string representations and are
|
||||
// safe to marshal to JSON.
|
||||
//
|
||||
// Implementations must not retain msg.
|
||||
var Debug func(msg interface{}) = nop
|
4
vendor/bazil.org/fuse/doc/.gitignore
generated
vendored
Normal file
4
vendor/bazil.org/fuse/doc/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/*.seq.svg
|
||||
|
||||
# not ignoring *.seq.png; we want those committed to the repo
|
||||
# for embedding on Github
|
6
vendor/bazil.org/fuse/doc/README.md
generated
vendored
Normal file
6
vendor/bazil.org/fuse/doc/README.md
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
# bazil.org/fuse documentation
|
||||
|
||||
See also API docs at http://godoc.org/bazil.org/fuse
|
||||
|
||||
- [The mount sequence](mount-sequence.md)
|
||||
- [Writing documentation](writing-docs.md)
|
32
vendor/bazil.org/fuse/doc/mount-linux-error-init.seq
generated
vendored
Normal file
32
vendor/bazil.org/fuse/doc/mount-linux-error-init.seq
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
seqdiag {
|
||||
app;
|
||||
fuse [label="bazil.org/fuse"];
|
||||
fusermount;
|
||||
kernel;
|
||||
mounts;
|
||||
|
||||
app;
|
||||
fuse [label="bazil.org/fuse"];
|
||||
fusermount;
|
||||
kernel;
|
||||
mounts;
|
||||
|
||||
app -> fuse [label="Mount"];
|
||||
fuse -> fusermount [label="spawn, pass socketpair fd"];
|
||||
fusermount -> kernel [label="open /dev/fuse"];
|
||||
fusermount -> kernel [label="mount(2)"];
|
||||
kernel ->> mounts [label="mount is visible"];
|
||||
fusermount <-- kernel [label="mount(2) returns"];
|
||||
fuse <<-- fusermount [diagonal, label="exit, receive /dev/fuse fd", leftnote="on Linux, successful exit here\nmeans the mount has happened,\nthough InitRequest might not have yet"];
|
||||
app <-- fuse [label="Mount returns\nConn.Ready is already closed"];
|
||||
|
||||
app -> fuse [label="fs.Serve"];
|
||||
fuse => kernel [label="read /dev/fuse fd", note="starts with InitRequest"];
|
||||
fuse -> app [label="Init"];
|
||||
fuse <-- app [color=red];
|
||||
fuse -> kernel [label="write /dev/fuse fd", color=red];
|
||||
kernel -> kernel [label="set connection\nstate to error", color=red];
|
||||
fuse <-- kernel;
|
||||
... conn.MountError == nil, so it is still mounted ...
|
||||
... call conn.Close to clean up ...
|
||||
}
|
BIN
vendor/bazil.org/fuse/doc/mount-linux-error-init.seq.png
generated
vendored
Normal file
BIN
vendor/bazil.org/fuse/doc/mount-linux-error-init.seq.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
41
vendor/bazil.org/fuse/doc/mount-linux.seq
generated
vendored
Normal file
41
vendor/bazil.org/fuse/doc/mount-linux.seq
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
seqdiag {
|
||||
// seqdiag -T svg -o doc/mount-osx.svg doc/mount-osx.seq
|
||||
app;
|
||||
fuse [label="bazil.org/fuse"];
|
||||
fusermount;
|
||||
kernel;
|
||||
mounts;
|
||||
|
||||
app -> fuse [label="Mount"];
|
||||
fuse -> fusermount [label="spawn, pass socketpair fd"];
|
||||
fusermount -> kernel [label="open /dev/fuse"];
|
||||
fusermount -> kernel [label="mount(2)"];
|
||||
kernel ->> mounts [label="mount is visible"];
|
||||
fusermount <-- kernel [label="mount(2) returns"];
|
||||
fuse <<-- fusermount [diagonal, label="exit, receive /dev/fuse fd", leftnote="on Linux, successful exit here\nmeans the mount has happened,\nthough InitRequest might not have yet"];
|
||||
app <-- fuse [label="Mount returns\nConn.Ready is already closed", rightnote="InitRequest and StatfsRequest\nmay or may not be seen\nbefore Conn.Ready,\ndepending on platform"];
|
||||
|
||||
app -> fuse [label="fs.Serve"];
|
||||
fuse => kernel [label="read /dev/fuse fd", note="starts with InitRequest"];
|
||||
fuse => app [label="FS/Node/Handle methods"];
|
||||
fuse => kernel [label="write /dev/fuse fd"];
|
||||
... repeat ...
|
||||
|
||||
... shutting down ...
|
||||
app -> fuse [label="Unmount"];
|
||||
fuse -> fusermount [label="fusermount -u"];
|
||||
fusermount -> kernel;
|
||||
kernel <<-- mounts;
|
||||
fusermount <-- kernel;
|
||||
fuse <<-- fusermount [diagonal];
|
||||
app <-- fuse [label="Unmount returns"];
|
||||
|
||||
// actually triggers before above
|
||||
fuse <<-- kernel [diagonal, label="/dev/fuse EOF"];
|
||||
app <-- fuse [label="fs.Serve returns"];
|
||||
|
||||
app -> fuse [label="conn.Close"];
|
||||
fuse -> kernel [label="close /dev/fuse fd"];
|
||||
fuse <-- kernel;
|
||||
app <-- fuse;
|
||||
}
|
BIN
vendor/bazil.org/fuse/doc/mount-linux.seq.png
generated
vendored
Normal file
BIN
vendor/bazil.org/fuse/doc/mount-linux.seq.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
32
vendor/bazil.org/fuse/doc/mount-osx-error-init.seq
generated
vendored
Normal file
32
vendor/bazil.org/fuse/doc/mount-osx-error-init.seq
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
seqdiag {
|
||||
app;
|
||||
fuse [label="bazil.org/fuse"];
|
||||
wait [label="callMount\nhelper goroutine"];
|
||||
mount_osxfusefs;
|
||||
kernel;
|
||||
|
||||
app -> fuse [label="Mount"];
|
||||
fuse -> kernel [label="open /dev/osxfuseN"];
|
||||
fuse -> mount_osxfusefs [label="spawn, pass fd"];
|
||||
fuse -> wait [label="goroutine", note="blocks on cmd.Wait"];
|
||||
app <-- fuse [label="Mount returns"];
|
||||
|
||||
mount_osxfusefs -> kernel [label="mount(2)"];
|
||||
|
||||
app -> fuse [label="fs.Serve"];
|
||||
fuse => kernel [label="read /dev/osxfuseN fd", note="starts with InitRequest,\nalso seen before mount exits:\ntwo StatfsRequest calls"];
|
||||
fuse -> app [label="Init"];
|
||||
fuse <-- app [color=red];
|
||||
fuse -> kernel [label="write /dev/osxfuseN fd", color=red];
|
||||
fuse <-- kernel;
|
||||
|
||||
mount_osxfusefs <-- kernel [label="mount(2) returns", color=red];
|
||||
wait <<-- mount_osxfusefs [diagonal, label="exit", color=red];
|
||||
app <<-- wait [diagonal, label="mount has failed,\nclose Conn.Ready", color=red];
|
||||
|
||||
// actually triggers before above
|
||||
fuse <<-- kernel [diagonal, label="/dev/osxfuseN EOF"];
|
||||
app <-- fuse [label="fs.Serve returns"];
|
||||
... conn.MountError != nil, so it was was never mounted ...
|
||||
... call conn.Close to clean up ...
|
||||
}
|
BIN
vendor/bazil.org/fuse/doc/mount-osx-error-init.seq.png
generated
vendored
Normal file
BIN
vendor/bazil.org/fuse/doc/mount-osx-error-init.seq.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
45
vendor/bazil.org/fuse/doc/mount-osx.seq
generated
vendored
Normal file
45
vendor/bazil.org/fuse/doc/mount-osx.seq
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
seqdiag {
|
||||
// seqdiag -T svg -o doc/mount-osx.svg doc/mount-osx.seq
|
||||
app;
|
||||
fuse [label="bazil.org/fuse"];
|
||||
wait [label="callMount\nhelper goroutine"];
|
||||
mount_osxfusefs;
|
||||
kernel;
|
||||
mounts;
|
||||
|
||||
app -> fuse [label="Mount"];
|
||||
fuse -> kernel [label="open /dev/osxfuseN"];
|
||||
fuse -> mount_osxfusefs [label="spawn, pass fd"];
|
||||
fuse -> wait [label="goroutine", note="blocks on cmd.Wait"];
|
||||
app <-- fuse [label="Mount returns"];
|
||||
|
||||
mount_osxfusefs -> kernel [label="mount(2)"];
|
||||
|
||||
app -> fuse [label="fs.Serve"];
|
||||
fuse => kernel [label="read /dev/osxfuseN fd", note="starts with InitRequest,\nalso seen before mount exits:\ntwo StatfsRequest calls"];
|
||||
fuse => app [label="FS/Node/Handle methods"];
|
||||
fuse => kernel [label="write /dev/osxfuseN fd"];
|
||||
... repeat ...
|
||||
|
||||
kernel ->> mounts [label="mount is visible"];
|
||||
mount_osxfusefs <-- kernel [label="mount(2) returns"];
|
||||
wait <<-- mount_osxfusefs [diagonal, label="exit", leftnote="on OS X, successful exit\nhere means we finally know\nthe mount has happened\n(can't trust InitRequest,\nkernel might have timed out\nwaiting for InitResponse)"];
|
||||
|
||||
app <<-- wait [diagonal, label="mount is ready,\nclose Conn.Ready", rightnote="InitRequest and StatfsRequest\nmay or may not be seen\nbefore Conn.Ready,\ndepending on platform"];
|
||||
|
||||
... shutting down ...
|
||||
app -> fuse [label="Unmount"];
|
||||
fuse -> kernel [label="umount(2)"];
|
||||
kernel <<-- mounts;
|
||||
fuse <-- kernel;
|
||||
app <-- fuse [label="Unmount returns"];
|
||||
|
||||
// actually triggers before above
|
||||
fuse <<-- kernel [diagonal, label="/dev/osxfuseN EOF"];
|
||||
app <-- fuse [label="fs.Serve returns"];
|
||||
|
||||
app -> fuse [label="conn.Close"];
|
||||
fuse -> kernel [label="close /dev/osxfuseN"];
|
||||
fuse <-- kernel;
|
||||
app <-- fuse;
|
||||
}
|
BIN
vendor/bazil.org/fuse/doc/mount-osx.seq.png
generated
vendored
Normal file
BIN
vendor/bazil.org/fuse/doc/mount-osx.seq.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
30
vendor/bazil.org/fuse/doc/mount-sequence.md
generated
vendored
Normal file
30
vendor/bazil.org/fuse/doc/mount-sequence.md
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
# The mount sequence
|
||||
|
||||
FUSE mounting is a little bit tricky. There's a userspace helper tool
|
||||
that performs the handshake with the kernel, and then steps out of the
|
||||
way. This helper behaves differently on different platforms, forcing a
|
||||
more complex API on us.
|
||||
|
||||
## Successful runs
|
||||
|
||||
On Linux, the mount is immediate and file system accesses wait until
|
||||
the requests are served.
|
||||
|
||||
![Diagram of Linux FUSE mount sequence](mount-linux.seq.png)
|
||||
|
||||
On OS X, the mount becomes visible only after `InitRequest` (and maybe
|
||||
more) have been served.
|
||||
|
||||
![Diagram of OSXFUSE mount sequence](mount-osx.seq.png)
|
||||
|
||||
|
||||
## Errors
|
||||
|
||||
Let's see what happens if `InitRequest` gets an error response. On
|
||||
Linux, the mountpoint is there but all operations will fail:
|
||||
|
||||
![Diagram of Linux error handling](mount-linux-error-init.seq.png)
|
||||
|
||||
On OS X, the mount never happened:
|
||||
|
||||
![Diagram of OS X error handling](mount-osx-error-init.seq.png)
|
16
vendor/bazil.org/fuse/doc/writing-docs.md
generated
vendored
Normal file
16
vendor/bazil.org/fuse/doc/writing-docs.md
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Writing documentation
|
||||
|
||||
## Sequence diagrams
|
||||
|
||||
The sequence diagrams are generated with `seqdiag`:
|
||||
http://blockdiag.com/en/seqdiag/index.html
|
||||
|
||||
An easy way to work on them is to automatically update the generated
|
||||
files with https://github.com/cespare/reflex :
|
||||
|
||||
reflex -g 'doc/[^.]*.seq' -- seqdiag -T svg -o '{}.svg' '{}' &
|
||||
|
||||
reflex -g 'doc/[^.]*.seq' -- seqdiag -T png -o '{}.png' '{}' &
|
||||
|
||||
The markdown files refer to PNG images because of Github limitations,
|
||||
but the SVG is generally more pleasant to view.
|
17
vendor/bazil.org/fuse/error_darwin.go
generated
vendored
Normal file
17
vendor/bazil.org/fuse/error_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
ENOATTR = Errno(syscall.ENOATTR)
|
||||
)
|
||||
|
||||
const (
|
||||
errNoXattr = ENOATTR
|
||||
)
|
||||
|
||||
func init() {
|
||||
errnoNames[errNoXattr] = "ENOATTR"
|
||||
}
|
15
vendor/bazil.org/fuse/error_freebsd.go
generated
vendored
Normal file
15
vendor/bazil.org/fuse/error_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
package fuse
|
||||
|
||||
import "syscall"
|
||||
|
||||
const (
|
||||
ENOATTR = Errno(syscall.ENOATTR)
|
||||
)
|
||||
|
||||
const (
|
||||
errNoXattr = ENOATTR
|
||||
)
|
||||
|
||||
func init() {
|
||||
errnoNames[errNoXattr] = "ENOATTR"
|
||||
}
|
17
vendor/bazil.org/fuse/error_linux.go
generated
vendored
Normal file
17
vendor/bazil.org/fuse/error_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
ENODATA = Errno(syscall.ENODATA)
|
||||
)
|
||||
|
||||
const (
|
||||
errNoXattr = ENODATA
|
||||
)
|
||||
|
||||
func init() {
|
||||
errnoNames[errNoXattr] = "ENODATA"
|
||||
}
|
31
vendor/bazil.org/fuse/error_std.go
generated
vendored
Normal file
31
vendor/bazil.org/fuse/error_std.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package fuse
|
||||
|
||||
// There is very little commonality in extended attribute errors
|
||||
// across platforms.
|
||||
//
|
||||
// getxattr return value for "extended attribute does not exist" is
|
||||
// ENOATTR on OS X, and ENODATA on Linux and apparently at least
|
||||
// NetBSD. There may be a #define ENOATTR on Linux too, but the value
|
||||
// is ENODATA in the actual syscalls. FreeBSD and OpenBSD have no
|
||||
// ENODATA, only ENOATTR. ENOATTR is not in any of the standards,
|
||||
// ENODATA exists but is only used for STREAMs.
|
||||
//
|
||||
// Each platform will define it a errNoXattr constant, and this file
|
||||
// will enforce that it implements the right interfaces and hide the
|
||||
// implementation.
|
||||
//
|
||||
// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/getxattr.2.html
|
||||
// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013090.html
|
||||
// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013097.html
|
||||
// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
|
||||
// http://www.freebsd.org/cgi/man.cgi?query=extattr_get_file&sektion=2
|
||||
// http://nixdoc.net/man-pages/openbsd/man2/extattr_get_file.2.html
|
||||
|
||||
// ErrNoXattr is a platform-independent error value meaning the
|
||||
// extended attribute was not found. It can be used to respond to
|
||||
// GetxattrRequest and such.
|
||||
const ErrNoXattr = errNoXattr
|
||||
|
||||
var _ error = ErrNoXattr
|
||||
var _ Errno = ErrNoXattr
|
||||
var _ ErrorNumber = ErrNoXattr
|
184
vendor/bazil.org/fuse/examples/clockfs/clockfs.go
generated
vendored
Normal file
184
vendor/bazil.org/fuse/examples/clockfs/clockfs.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
// Clockfs implements a file system with the current time in a file.
|
||||
// It was written to demonstrate kernel cache invalidation.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
_ "bazil.org/fuse/fs/fstestutil"
|
||||
"bazil.org/fuse/fuseutil"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, " %s MOUNTPOINT\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func run(mountpoint string) error {
|
||||
c, err := fuse.Mount(
|
||||
mountpoint,
|
||||
fuse.FSName("clock"),
|
||||
fuse.Subtype("clockfsfs"),
|
||||
fuse.LocalVolume(),
|
||||
fuse.VolumeName("Clock filesystem"),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if p := c.Protocol(); !p.HasInvalidate() {
|
||||
return fmt.Errorf("kernel FUSE support is too old to have invalidations: version %v", p)
|
||||
}
|
||||
|
||||
srv := fs.New(c, nil)
|
||||
filesys := &FS{
|
||||
// We pre-create the clock node so that it's always the same
|
||||
// object returned from all the Lookups. You could carefully
|
||||
// track its lifetime between Lookup&Forget, and have the
|
||||
// ticking & invalidation happen only when active, but let's
|
||||
// keep this example simple.
|
||||
clockFile: &File{
|
||||
fuse: srv,
|
||||
},
|
||||
}
|
||||
filesys.clockFile.tick()
|
||||
// This goroutine never exits. That's fine for this example.
|
||||
go filesys.clockFile.update()
|
||||
if err := srv.Serve(filesys); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the mount process has an error to report.
|
||||
<-c.Ready
|
||||
if err := c.MountError; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
mountpoint := flag.Arg(0)
|
||||
|
||||
if err := run(mountpoint); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type FS struct {
|
||||
clockFile *File
|
||||
}
|
||||
|
||||
var _ fs.FS = (*FS)(nil)
|
||||
|
||||
func (f *FS) Root() (fs.Node, error) {
|
||||
return &Dir{fs: f}, nil
|
||||
}
|
||||
|
||||
// Dir implements both Node and Handle for the root directory.
|
||||
type Dir struct {
|
||||
fs *FS
|
||||
}
|
||||
|
||||
var _ fs.Node = (*Dir)(nil)
|
||||
|
||||
func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Inode = 1
|
||||
a.Mode = os.ModeDir | 0555
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ fs.NodeStringLookuper = (*Dir)(nil)
|
||||
|
||||
func (d *Dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||
if name == "clock" {
|
||||
return d.fs.clockFile, nil
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
var dirDirs = []fuse.Dirent{
|
||||
{Inode: 2, Name: "clock", Type: fuse.DT_File},
|
||||
}
|
||||
|
||||
var _ fs.HandleReadDirAller = (*Dir)(nil)
|
||||
|
||||
func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
||||
return dirDirs, nil
|
||||
}
|
||||
|
||||
type File struct {
|
||||
fs.NodeRef
|
||||
fuse *fs.Server
|
||||
content atomic.Value
|
||||
count uint64
|
||||
}
|
||||
|
||||
var _ fs.Node = (*File)(nil)
|
||||
|
||||
func (f *File) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Inode = 2
|
||||
a.Mode = 0444
|
||||
t := f.content.Load().(string)
|
||||
a.Size = uint64(len(t))
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ fs.NodeOpener = (*File)(nil)
|
||||
|
||||
func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
|
||||
if !req.Flags.IsReadOnly() {
|
||||
return nil, fuse.Errno(syscall.EACCES)
|
||||
}
|
||||
resp.Flags |= fuse.OpenKeepCache
|
||||
return f, nil
|
||||
}
|
||||
|
||||
var _ fs.Handle = (*File)(nil)
|
||||
|
||||
var _ fs.HandleReader = (*File)(nil)
|
||||
|
||||
func (f *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
||||
t := f.content.Load().(string)
|
||||
fuseutil.HandleRead(req, resp, []byte(t))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) tick() {
|
||||
// Intentionally a variable-length format, to demonstrate size changes.
|
||||
f.count++
|
||||
s := fmt.Sprintf("%d\t%s\n", f.count, time.Now())
|
||||
f.content.Store(s)
|
||||
|
||||
// For simplicity, this example tries to send invalidate
|
||||
// notifications even when the kernel does not hold a reference to
|
||||
// the node, so be extra sure to ignore ErrNotCached.
|
||||
if err := f.fuse.InvalidateNodeData(f); err != nil && err != fuse.ErrNotCached {
|
||||
log.Printf("invalidate error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *File) update() {
|
||||
tick := time.NewTicker(1 * time.Second)
|
||||
defer tick.Stop()
|
||||
for range tick.C {
|
||||
f.tick()
|
||||
}
|
||||
}
|
101
vendor/bazil.org/fuse/examples/hellofs/hello.go
generated
vendored
Normal file
101
vendor/bazil.org/fuse/examples/hellofs/hello.go
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
// Hellofs implements a simple "hello world" file system.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
_ "bazil.org/fuse/fs/fstestutil"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, " %s MOUNTPOINT\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
mountpoint := flag.Arg(0)
|
||||
|
||||
c, err := fuse.Mount(
|
||||
mountpoint,
|
||||
fuse.FSName("helloworld"),
|
||||
fuse.Subtype("hellofs"),
|
||||
fuse.LocalVolume(),
|
||||
fuse.VolumeName("Hello world!"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
err = fs.Serve(c, FS{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// check if the mount process has an error to report
|
||||
<-c.Ready
|
||||
if err := c.MountError; err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// FS implements the hello world file system.
|
||||
type FS struct{}
|
||||
|
||||
func (FS) Root() (fs.Node, error) {
|
||||
return Dir{}, nil
|
||||
}
|
||||
|
||||
// Dir implements both Node and Handle for the root directory.
|
||||
type Dir struct{}
|
||||
|
||||
func (Dir) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Inode = 1
|
||||
a.Mode = os.ModeDir | 0555
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||
if name == "hello" {
|
||||
return File{}, nil
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
var dirDirs = []fuse.Dirent{
|
||||
{Inode: 2, Name: "hello", Type: fuse.DT_File},
|
||||
}
|
||||
|
||||
func (Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
||||
return dirDirs, nil
|
||||
}
|
||||
|
||||
// File implements both Node and Handle for the hello file.
|
||||
type File struct{}
|
||||
|
||||
const greeting = "hello, world\n"
|
||||
|
||||
func (File) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Inode = 2
|
||||
a.Mode = 0444
|
||||
a.Size = uint64(len(greeting))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (File) ReadAll(ctx context.Context) ([]byte, error) {
|
||||
return []byte(greeting), nil
|
||||
}
|
54
vendor/bazil.org/fuse/fs/bench/bench_create_test.go
generated
vendored
Normal file
54
vendor/bazil.org/fuse/fs/bench/bench_create_test.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package bench_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"bazil.org/fuse/fs/fstestutil"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type dummyFile struct {
|
||||
fstestutil.File
|
||||
}
|
||||
|
||||
type benchCreateDir struct {
|
||||
fstestutil.Dir
|
||||
}
|
||||
|
||||
var _ fs.NodeCreater = (*benchCreateDir)(nil)
|
||||
|
||||
func (f *benchCreateDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
|
||||
child := &dummyFile{}
|
||||
return child, child, nil
|
||||
}
|
||||
|
||||
func BenchmarkCreate(b *testing.B) {
|
||||
f := &benchCreateDir{}
|
||||
mnt, err := fstestutil.MountedT(b, fstestutil.SimpleFS{f}, nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
// prepare file names to decrease test overhead
|
||||
names := make([]string, 0, b.N)
|
||||
for i := 0; i < b.N; i++ {
|
||||
// zero-padded so cost stays the same on every iteration
|
||||
names = append(names, mnt.Dir+"/"+fmt.Sprintf("%08x", i))
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
f, err := os.Create(names[i])
|
||||
if err != nil {
|
||||
b.Fatalf("WriteFile: %v", err)
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
}
|
42
vendor/bazil.org/fuse/fs/bench/bench_lookup_test.go
generated
vendored
Normal file
42
vendor/bazil.org/fuse/fs/bench/bench_lookup_test.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package bench_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"bazil.org/fuse/fs/fstestutil"
|
||||
)
|
||||
|
||||
type benchLookupDir struct {
|
||||
fstestutil.Dir
|
||||
}
|
||||
|
||||
var _ fs.NodeRequestLookuper = (*benchLookupDir)(nil)
|
||||
|
||||
func (f *benchLookupDir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func BenchmarkLookup(b *testing.B) {
|
||||
f := &benchLookupDir{}
|
||||
mnt, err := fstestutil.MountedT(b, fstestutil.SimpleFS{f}, nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
name := mnt.Dir + "/does-not-exist"
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := os.Stat(name); !os.IsNotExist(err) {
|
||||
b.Fatalf("Stat: wrong error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
}
|
268
vendor/bazil.org/fuse/fs/bench/bench_readwrite_test.go
generated
vendored
Normal file
268
vendor/bazil.org/fuse/fs/bench/bench_readwrite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,268 @@
|
|||
package bench_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"bazil.org/fuse/fs/fstestutil"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type benchConfig struct {
|
||||
directIO bool
|
||||
}
|
||||
|
||||
type benchFS struct {
|
||||
conf *benchConfig
|
||||
}
|
||||
|
||||
var _ = fs.FS(benchFS{})
|
||||
|
||||
func (f benchFS) Root() (fs.Node, error) {
|
||||
return benchDir{conf: f.conf}, nil
|
||||
}
|
||||
|
||||
type benchDir struct {
|
||||
conf *benchConfig
|
||||
}
|
||||
|
||||
var _ = fs.Node(benchDir{})
|
||||
var _ = fs.NodeStringLookuper(benchDir{})
|
||||
var _ = fs.Handle(benchDir{})
|
||||
var _ = fs.HandleReadDirAller(benchDir{})
|
||||
|
||||
func (benchDir) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Inode = 1
|
||||
a.Mode = os.ModeDir | 0555
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d benchDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||
if name == "bench" {
|
||||
return benchFile{conf: d.conf}, nil
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (benchDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
||||
l := []fuse.Dirent{
|
||||
{Inode: 2, Name: "bench", Type: fuse.DT_File},
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
type benchFile struct {
|
||||
conf *benchConfig
|
||||
}
|
||||
|
||||
var _ = fs.Node(benchFile{})
|
||||
var _ = fs.NodeOpener(benchFile{})
|
||||
var _ = fs.NodeFsyncer(benchFile{})
|
||||
var _ = fs.Handle(benchFile{})
|
||||
var _ = fs.HandleReader(benchFile{})
|
||||
var _ = fs.HandleWriter(benchFile{})
|
||||
|
||||
func (benchFile) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Inode = 2
|
||||
a.Mode = 0644
|
||||
a.Size = 9999999999999999
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f benchFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
|
||||
if f.conf.directIO {
|
||||
resp.Flags |= fuse.OpenDirectIO
|
||||
}
|
||||
// TODO configurable?
|
||||
resp.Flags |= fuse.OpenKeepCache
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (benchFile) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
||||
resp.Data = resp.Data[:cap(resp.Data)]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (benchFile) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
|
||||
resp.Size = len(req.Data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (benchFile) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func benchmark(b *testing.B, fn func(b *testing.B, mnt string), conf *benchConfig) {
|
||||
filesys := benchFS{
|
||||
conf: conf,
|
||||
}
|
||||
mnt, err := fstestutil.Mounted(filesys, nil,
|
||||
fuse.MaxReadahead(64*1024*1024),
|
||||
fuse.AsyncRead(),
|
||||
fuse.WritebackCache(),
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
fn(b, mnt.Dir)
|
||||
}
|
||||
|
||||
type zero struct{}
|
||||
|
||||
func (zero) Read(p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
var Zero io.Reader = zero{}
|
||||
|
||||
func doWrites(size int64) func(b *testing.B, mnt string) {
|
||||
return func(b *testing.B, mnt string) {
|
||||
p := path.Join(mnt, "bench")
|
||||
|
||||
f, err := os.Create(p)
|
||||
if err != nil {
|
||||
b.Fatalf("create: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
b.ResetTimer()
|
||||
b.SetBytes(size)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err = io.CopyN(f, Zero, size)
|
||||
if err != nil {
|
||||
b.Fatalf("write: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWrite100(b *testing.B) {
|
||||
benchmark(b, doWrites(100), &benchConfig{})
|
||||
}
|
||||
|
||||
func BenchmarkWrite10MB(b *testing.B) {
|
||||
benchmark(b, doWrites(10*1024*1024), &benchConfig{})
|
||||
}
|
||||
|
||||
func BenchmarkWrite100MB(b *testing.B) {
|
||||
benchmark(b, doWrites(100*1024*1024), &benchConfig{})
|
||||
}
|
||||
|
||||
func BenchmarkDirectWrite100(b *testing.B) {
|
||||
benchmark(b, doWrites(100), &benchConfig{
|
||||
directIO: true,
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDirectWrite10MB(b *testing.B) {
|
||||
benchmark(b, doWrites(10*1024*1024), &benchConfig{
|
||||
directIO: true,
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDirectWrite100MB(b *testing.B) {
|
||||
benchmark(b, doWrites(100*1024*1024), &benchConfig{
|
||||
directIO: true,
|
||||
})
|
||||
}
|
||||
|
||||
func doWritesSync(size int64) func(b *testing.B, mnt string) {
|
||||
return func(b *testing.B, mnt string) {
|
||||
p := path.Join(mnt, "bench")
|
||||
|
||||
f, err := os.Create(p)
|
||||
if err != nil {
|
||||
b.Fatalf("create: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
b.ResetTimer()
|
||||
b.SetBytes(size)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err = io.CopyN(f, Zero, size)
|
||||
if err != nil {
|
||||
b.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
if err := f.Sync(); err != nil {
|
||||
b.Fatalf("sync: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWriteSync100(b *testing.B) {
|
||||
benchmark(b, doWritesSync(100), &benchConfig{})
|
||||
}
|
||||
|
||||
func BenchmarkWriteSync10MB(b *testing.B) {
|
||||
benchmark(b, doWritesSync(10*1024*1024), &benchConfig{})
|
||||
}
|
||||
|
||||
func BenchmarkWriteSync100MB(b *testing.B) {
|
||||
benchmark(b, doWritesSync(100*1024*1024), &benchConfig{})
|
||||
}
|
||||
|
||||
func doReads(size int64) func(b *testing.B, mnt string) {
|
||||
return func(b *testing.B, mnt string) {
|
||||
p := path.Join(mnt, "bench")
|
||||
|
||||
f, err := os.Open(p)
|
||||
if err != nil {
|
||||
b.Fatalf("close: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
b.ResetTimer()
|
||||
b.SetBytes(size)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
n, err := io.CopyN(ioutil.Discard, f, size)
|
||||
if err != nil {
|
||||
b.Fatalf("read: %v", err)
|
||||
}
|
||||
if n != size {
|
||||
b.Errorf("unexpected size: %d != %d", n, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRead100(b *testing.B) {
|
||||
benchmark(b, doReads(100), &benchConfig{})
|
||||
}
|
||||
|
||||
func BenchmarkRead10MB(b *testing.B) {
|
||||
benchmark(b, doReads(10*1024*1024), &benchConfig{})
|
||||
}
|
||||
|
||||
func BenchmarkRead100MB(b *testing.B) {
|
||||
benchmark(b, doReads(100*1024*1024), &benchConfig{})
|
||||
}
|
||||
|
||||
func BenchmarkDirectRead100(b *testing.B) {
|
||||
benchmark(b, doReads(100), &benchConfig{
|
||||
directIO: true,
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDirectRead10MB(b *testing.B) {
|
||||
benchmark(b, doReads(10*1024*1024), &benchConfig{
|
||||
directIO: true,
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDirectRead100MB(b *testing.B) {
|
||||
benchmark(b, doReads(100*1024*1024), &benchConfig{
|
||||
directIO: true,
|
||||
})
|
||||
}
|
5
vendor/bazil.org/fuse/fs/bench/doc.go
generated
vendored
Normal file
5
vendor/bazil.org/fuse/fs/bench/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Package bench contains benchmarks.
|
||||
//
|
||||
// It is kept in a separate package to avoid conflicting with the
|
||||
// debug-heavy defaults for the actual tests.
|
||||
package bench
|
70
vendor/bazil.org/fuse/fs/fstestutil/checkdir.go
generated
vendored
Normal file
70
vendor/bazil.org/fuse/fs/fstestutil/checkdir.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
package fstestutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// FileInfoCheck is a function that validates an os.FileInfo according
|
||||
// to some criteria.
|
||||
type FileInfoCheck func(fi os.FileInfo) error
|
||||
|
||||
type checkDirError struct {
|
||||
missing map[string]struct{}
|
||||
extra map[string]os.FileMode
|
||||
}
|
||||
|
||||
func (e *checkDirError) Error() string {
|
||||
return fmt.Sprintf("wrong directory contents: missing %v, extra %v", e.missing, e.extra)
|
||||
}
|
||||
|
||||
// CheckDir checks the contents of the directory at path, making sure
|
||||
// every directory entry listed in want is present. If the check is
|
||||
// not nil, it must also pass.
|
||||
//
|
||||
// If want contains the impossible filename "", unexpected files are
|
||||
// checked with that. If the key is not in want, unexpected files are
|
||||
// an error.
|
||||
//
|
||||
// Missing entries, that are listed in want but not seen, are an
|
||||
// error.
|
||||
func CheckDir(path string, want map[string]FileInfoCheck) error {
|
||||
problems := &checkDirError{
|
||||
missing: make(map[string]struct{}, len(want)),
|
||||
extra: make(map[string]os.FileMode),
|
||||
}
|
||||
for k := range want {
|
||||
if k == "" {
|
||||
continue
|
||||
}
|
||||
problems.missing[k] = struct{}{}
|
||||
}
|
||||
|
||||
fis, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read directory: %v", err)
|
||||
}
|
||||
|
||||
for _, fi := range fis {
|
||||
check, ok := want[fi.Name()]
|
||||
if !ok {
|
||||
check, ok = want[""]
|
||||
}
|
||||
if !ok {
|
||||
problems.extra[fi.Name()] = fi.Mode()
|
||||
continue
|
||||
}
|
||||
delete(problems.missing, fi.Name())
|
||||
if check != nil {
|
||||
if err := check(fi); err != nil {
|
||||
return fmt.Errorf("check failed: %v: %v", fi.Name(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(problems.missing) > 0 || len(problems.extra) > 0 {
|
||||
return problems
|
||||
}
|
||||
return nil
|
||||
}
|
65
vendor/bazil.org/fuse/fs/fstestutil/debug.go
generated
vendored
Normal file
65
vendor/bazil.org/fuse/fs/fstestutil/debug.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
package fstestutil
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"bazil.org/fuse"
|
||||
)
|
||||
|
||||
type flagDebug bool
|
||||
|
||||
var debug flagDebug
|
||||
|
||||
var _ = flag.Value(&debug)
|
||||
|
||||
func (f *flagDebug) IsBoolFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func nop(msg interface{}) {}
|
||||
|
||||
func (f *flagDebug) Set(s string) error {
|
||||
v, err := strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*f = flagDebug(v)
|
||||
if v {
|
||||
fuse.Debug = logMsg
|
||||
} else {
|
||||
fuse.Debug = nop
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *flagDebug) String() string {
|
||||
return strconv.FormatBool(bool(*f))
|
||||
}
|
||||
|
||||
func logMsg(msg interface{}) {
|
||||
log.Printf("FUSE: %s\n", msg)
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.Var(&debug, "fuse.debug", "log FUSE processing details")
|
||||
}
|
||||
|
||||
// DebugByDefault changes the default of the `-fuse.debug` flag to
|
||||
// true.
|
||||
//
|
||||
// This package registers a command line flag `-fuse.debug` and when
|
||||
// run with that flag (and activated inside the tests), logs FUSE
|
||||
// debug messages.
|
||||
//
|
||||
// This is disabled by default, as most callers probably won't care
|
||||
// about FUSE details. Use DebugByDefault for tests where you'd
|
||||
// normally be passing `-fuse.debug` all the time anyway.
|
||||
//
|
||||
// Call from an init function.
|
||||
func DebugByDefault() {
|
||||
f := flag.Lookup("fuse.debug")
|
||||
f.DefValue = "true"
|
||||
f.Value.Set(f.DefValue)
|
||||
}
|
1
vendor/bazil.org/fuse/fs/fstestutil/doc.go
generated
vendored
Normal file
1
vendor/bazil.org/fuse/fs/fstestutil/doc.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package fstestutil // import "bazil.org/fuse/fs/fstestutil"
|
141
vendor/bazil.org/fuse/fs/fstestutil/mounted.go
generated
vendored
Normal file
141
vendor/bazil.org/fuse/fs/fstestutil/mounted.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
|||
package fstestutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
)
|
||||
|
||||
// Mount contains information about the mount for the test to use.
|
||||
type Mount struct {
|
||||
// Dir is the temporary directory where the filesystem is mounted.
|
||||
Dir string
|
||||
|
||||
Conn *fuse.Conn
|
||||
Server *fs.Server
|
||||
|
||||
// Error will receive the return value of Serve.
|
||||
Error <-chan error
|
||||
|
||||
done <-chan struct{}
|
||||
closed bool
|
||||
}
|
||||
|
||||
// Close unmounts the filesystem and waits for fs.Serve to return. Any
|
||||
// returned error will be stored in Err. It is safe to call Close
|
||||
// multiple times.
|
||||
func (mnt *Mount) Close() {
|
||||
if mnt.closed {
|
||||
return
|
||||
}
|
||||
mnt.closed = true
|
||||
for tries := 0; tries < 1000; tries++ {
|
||||
err := fuse.Unmount(mnt.Dir)
|
||||
if err != nil {
|
||||
// TODO do more than log?
|
||||
log.Printf("unmount error: %v", err)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
<-mnt.done
|
||||
mnt.Conn.Close()
|
||||
os.Remove(mnt.Dir)
|
||||
}
|
||||
|
||||
// MountedFunc mounts a filesystem at a temporary directory. The
|
||||
// filesystem used is constructed by calling a function, to allow
|
||||
// storing fuse.Conn and fs.Server in the FS.
|
||||
//
|
||||
// It also waits until the filesystem is known to be visible (OS X
|
||||
// workaround).
|
||||
//
|
||||
// After successful return, caller must clean up by calling Close.
|
||||
func MountedFunc(fn func(*Mount) fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
||||
dir, err := ioutil.TempDir("", "fusetest")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := fuse.Mount(dir, options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := fs.New(c, conf)
|
||||
done := make(chan struct{})
|
||||
serveErr := make(chan error, 1)
|
||||
mnt := &Mount{
|
||||
Dir: dir,
|
||||
Conn: c,
|
||||
Server: server,
|
||||
Error: serveErr,
|
||||
done: done,
|
||||
}
|
||||
filesys := fn(mnt)
|
||||
go func() {
|
||||
defer close(done)
|
||||
serveErr <- server.Serve(filesys)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-mnt.Conn.Ready:
|
||||
if err := mnt.Conn.MountError; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mnt, nil
|
||||
case err = <-mnt.Error:
|
||||
// Serve quit early
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errors.New("Serve exited early")
|
||||
}
|
||||
}
|
||||
|
||||
// Mounted mounts the fuse.Server at a temporary directory.
|
||||
//
|
||||
// It also waits until the filesystem is known to be visible (OS X
|
||||
// workaround).
|
||||
//
|
||||
// After successful return, caller must clean up by calling Close.
|
||||
func Mounted(filesys fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
||||
fn := func(*Mount) fs.FS { return filesys }
|
||||
return MountedFunc(fn, conf, options...)
|
||||
}
|
||||
|
||||
// MountedFuncT mounts a filesystem at a temporary directory,
|
||||
// directing it's debug log to the testing logger.
|
||||
//
|
||||
// See MountedFunc for usage.
|
||||
//
|
||||
// The debug log is not enabled by default. Use `-fuse.debug` or call
|
||||
// DebugByDefault to enable.
|
||||
func MountedFuncT(t testing.TB, fn func(*Mount) fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
||||
if conf == nil {
|
||||
conf = &fs.Config{}
|
||||
}
|
||||
if debug && conf.Debug == nil {
|
||||
conf.Debug = func(msg interface{}) {
|
||||
t.Logf("FUSE: %s", msg)
|
||||
}
|
||||
}
|
||||
return MountedFunc(fn, conf, options...)
|
||||
}
|
||||
|
||||
// MountedT mounts the filesystem at a temporary directory,
|
||||
// directing it's debug log to the testing logger.
|
||||
//
|
||||
// See Mounted for usage.
|
||||
//
|
||||
// The debug log is not enabled by default. Use `-fuse.debug` or call
|
||||
// DebugByDefault to enable.
|
||||
func MountedT(t testing.TB, filesys fs.FS, conf *fs.Config, options ...fuse.MountOption) (*Mount, error) {
|
||||
fn := func(*Mount) fs.FS { return filesys }
|
||||
return MountedFuncT(t, fn, conf, options...)
|
||||
}
|
26
vendor/bazil.org/fuse/fs/fstestutil/mountinfo.go
generated
vendored
Normal file
26
vendor/bazil.org/fuse/fs/fstestutil/mountinfo.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
package fstestutil
|
||||
|
||||
// MountInfo describes a mounted file system.
|
||||
type MountInfo struct {
|
||||
FSName string
|
||||
Type string
|
||||
}
|
||||
|
||||
// GetMountInfo finds information about the mount at mnt. It is
|
||||
// intended for use by tests only, and only fetches information
|
||||
// relevant to the current tests.
|
||||
func GetMountInfo(mnt string) (*MountInfo, error) {
|
||||
return getMountInfo(mnt)
|
||||
}
|
||||
|
||||
// cstr converts a nil-terminated C string into a Go string
|
||||
func cstr(ca []int8) string {
|
||||
s := make([]byte, 0, len(ca))
|
||||
for _, c := range ca {
|
||||
if c == 0x00 {
|
||||
break
|
||||
}
|
||||
s = append(s, byte(c))
|
||||
}
|
||||
return string(s)
|
||||
}
|
29
vendor/bazil.org/fuse/fs/fstestutil/mountinfo_darwin.go
generated
vendored
Normal file
29
vendor/bazil.org/fuse/fs/fstestutil/mountinfo_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
package fstestutil
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var re = regexp.MustCompile(`\\(.)`)
|
||||
|
||||
// unescape removes backslash-escaping. The escaped characters are not
|
||||
// mapped in any way; that is, unescape(`\n` ) == `n`.
|
||||
func unescape(s string) string {
|
||||
return re.ReplaceAllString(s, `$1`)
|
||||
}
|
||||
|
||||
func getMountInfo(mnt string) (*MountInfo, error) {
|
||||
var st syscall.Statfs_t
|
||||
err := syscall.Statfs(mnt, &st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i := &MountInfo{
|
||||
// osx getmntent(3) fails to un-escape the data, so we do it..
|
||||
// this might lead to double-unescaping in the future. fun.
|
||||
// TestMountOptionFSNameEvilBackslashDouble checks for that.
|
||||
FSName: unescape(cstr(st.Mntfromname[:])),
|
||||
}
|
||||
return i, nil
|
||||
}
|
7
vendor/bazil.org/fuse/fs/fstestutil/mountinfo_freebsd.go
generated
vendored
Normal file
7
vendor/bazil.org/fuse/fs/fstestutil/mountinfo_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package fstestutil
|
||||
|
||||
import "errors"
|
||||
|
||||
func getMountInfo(mnt string) (*MountInfo, error) {
|
||||
return nil, errors.New("FreeBSD has no useful mount information")
|
||||
}
|
51
vendor/bazil.org/fuse/fs/fstestutil/mountinfo_linux.go
generated
vendored
Normal file
51
vendor/bazil.org/fuse/fs/fstestutil/mountinfo_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
package fstestutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Linux /proc/mounts shows current mounts.
|
||||
// Same format as /etc/fstab. Quoting getmntent(3):
|
||||
//
|
||||
// Since fields in the mtab and fstab files are separated by whitespace,
|
||||
// octal escapes are used to represent the four characters space (\040),
|
||||
// tab (\011), newline (\012) and backslash (\134) in those files when
|
||||
// they occur in one of the four strings in a mntent structure.
|
||||
//
|
||||
// http://linux.die.net/man/3/getmntent
|
||||
|
||||
var fstabUnescape = strings.NewReplacer(
|
||||
`\040`, "\040",
|
||||
`\011`, "\011",
|
||||
`\012`, "\012",
|
||||
`\134`, "\134",
|
||||
)
|
||||
|
||||
var errNotFound = errors.New("mount not found")
|
||||
|
||||
func getMountInfo(mnt string) (*MountInfo, error) {
|
||||
data, err := ioutil.ReadFile("/proc/mounts")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 3 {
|
||||
continue
|
||||
}
|
||||
// Fields are: fsname dir type opts freq passno
|
||||
fsname := fstabUnescape.Replace(fields[0])
|
||||
dir := fstabUnescape.Replace(fields[1])
|
||||
fstype := fstabUnescape.Replace(fields[2])
|
||||
if mnt == dir {
|
||||
info := &MountInfo{
|
||||
FSName: fsname,
|
||||
Type: fstype,
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
}
|
||||
return nil, errNotFound
|
||||
}
|
28
vendor/bazil.org/fuse/fs/fstestutil/record/buffer.go
generated
vendored
Normal file
28
vendor/bazil.org/fuse/fs/fstestutil/record/buffer.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
package record
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Buffer is like bytes.Buffer but safe to access from multiple
|
||||
// goroutines.
|
||||
type Buffer struct {
|
||||
mu sync.Mutex
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
var _ = io.Writer(&Buffer{})
|
||||
|
||||
func (b *Buffer) Write(p []byte) (n int, err error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.buf.Write(p)
|
||||
}
|
||||
|
||||
func (b *Buffer) Bytes() []byte {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.buf.Bytes()
|
||||
}
|
409
vendor/bazil.org/fuse/fs/fstestutil/record/record.go
generated
vendored
Normal file
409
vendor/bazil.org/fuse/fs/fstestutil/record/record.go
generated
vendored
Normal file
|
@ -0,0 +1,409 @@
|
|||
package record // import "bazil.org/fuse/fs/fstestutil/record"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Writes gathers data from FUSE Write calls.
|
||||
type Writes struct {
|
||||
buf Buffer
|
||||
}
|
||||
|
||||
var _ = fs.HandleWriter(&Writes{})
|
||||
|
||||
func (w *Writes) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
|
||||
n, err := w.buf.Write(req.Data)
|
||||
resp.Size = n
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Writes) RecordedWriteData() []byte {
|
||||
return w.buf.Bytes()
|
||||
}
|
||||
|
||||
// Counter records number of times a thing has occurred.
|
||||
type Counter struct {
|
||||
count uint32
|
||||
}
|
||||
|
||||
func (r *Counter) Inc() {
|
||||
atomic.AddUint32(&r.count, 1)
|
||||
}
|
||||
|
||||
func (r *Counter) Count() uint32 {
|
||||
return atomic.LoadUint32(&r.count)
|
||||
}
|
||||
|
||||
// MarkRecorder records whether a thing has occurred.
|
||||
type MarkRecorder struct {
|
||||
count Counter
|
||||
}
|
||||
|
||||
func (r *MarkRecorder) Mark() {
|
||||
r.count.Inc()
|
||||
}
|
||||
|
||||
func (r *MarkRecorder) Recorded() bool {
|
||||
return r.count.Count() > 0
|
||||
}
|
||||
|
||||
// Flushes notes whether a FUSE Flush call has been seen.
|
||||
type Flushes struct {
|
||||
rec MarkRecorder
|
||||
}
|
||||
|
||||
var _ = fs.HandleFlusher(&Flushes{})
|
||||
|
||||
func (r *Flushes) Flush(ctx context.Context, req *fuse.FlushRequest) error {
|
||||
r.rec.Mark()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Flushes) RecordedFlush() bool {
|
||||
return r.rec.Recorded()
|
||||
}
|
||||
|
||||
type Recorder struct {
|
||||
mu sync.Mutex
|
||||
val interface{}
|
||||
}
|
||||
|
||||
// Record that we've seen value. A nil value is indistinguishable from
|
||||
// no value recorded.
|
||||
func (r *Recorder) Record(value interface{}) {
|
||||
r.mu.Lock()
|
||||
r.val = value
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
func (r *Recorder) Recorded() interface{} {
|
||||
r.mu.Lock()
|
||||
val := r.val
|
||||
r.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
type RequestRecorder struct {
|
||||
rec Recorder
|
||||
}
|
||||
|
||||
// Record a fuse.Request, after zeroing header fields that are hard to
|
||||
// reproduce.
|
||||
//
|
||||
// Make sure to record a copy, not the original request.
|
||||
func (r *RequestRecorder) RecordRequest(req fuse.Request) {
|
||||
hdr := req.Hdr()
|
||||
*hdr = fuse.Header{}
|
||||
r.rec.Record(req)
|
||||
}
|
||||
|
||||
func (r *RequestRecorder) Recorded() fuse.Request {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(fuse.Request)
|
||||
}
|
||||
|
||||
// Setattrs records a Setattr request and its fields.
|
||||
type Setattrs struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeSetattrer(&Setattrs{})
|
||||
|
||||
func (r *Setattrs) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Setattrs) RecordedSetattr() fuse.SetattrRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.SetattrRequest{}
|
||||
}
|
||||
return *(val.(*fuse.SetattrRequest))
|
||||
}
|
||||
|
||||
// Fsyncs records an Fsync request and its fields.
|
||||
type Fsyncs struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeFsyncer(&Fsyncs{})
|
||||
|
||||
func (r *Fsyncs) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Fsyncs) RecordedFsync() fuse.FsyncRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.FsyncRequest{}
|
||||
}
|
||||
return *(val.(*fuse.FsyncRequest))
|
||||
}
|
||||
|
||||
// Mkdirs records a Mkdir request and its fields.
|
||||
type Mkdirs struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeMkdirer(&Mkdirs{})
|
||||
|
||||
// Mkdir records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Mkdirs) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil, fuse.EIO
|
||||
}
|
||||
|
||||
// RecordedMkdir returns information about the Mkdir request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Mkdirs) RecordedMkdir() fuse.MkdirRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.MkdirRequest{}
|
||||
}
|
||||
return *(val.(*fuse.MkdirRequest))
|
||||
}
|
||||
|
||||
// Symlinks records a Symlink request and its fields.
|
||||
type Symlinks struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeSymlinker(&Symlinks{})
|
||||
|
||||
// Symlink records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Symlinks) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, error) {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil, fuse.EIO
|
||||
}
|
||||
|
||||
// RecordedSymlink returns information about the Symlink request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Symlinks) RecordedSymlink() fuse.SymlinkRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.SymlinkRequest{}
|
||||
}
|
||||
return *(val.(*fuse.SymlinkRequest))
|
||||
}
|
||||
|
||||
// Links records a Link request and its fields.
|
||||
type Links struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeLinker(&Links{})
|
||||
|
||||
// Link records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Links) Link(ctx context.Context, req *fuse.LinkRequest, old fs.Node) (fs.Node, error) {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil, fuse.EIO
|
||||
}
|
||||
|
||||
// RecordedLink returns information about the Link request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Links) RecordedLink() fuse.LinkRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.LinkRequest{}
|
||||
}
|
||||
return *(val.(*fuse.LinkRequest))
|
||||
}
|
||||
|
||||
// Mknods records a Mknod request and its fields.
|
||||
type Mknods struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeMknoder(&Mknods{})
|
||||
|
||||
// Mknod records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Mknods) Mknod(ctx context.Context, req *fuse.MknodRequest) (fs.Node, error) {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil, fuse.EIO
|
||||
}
|
||||
|
||||
// RecordedMknod returns information about the Mknod request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Mknods) RecordedMknod() fuse.MknodRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.MknodRequest{}
|
||||
}
|
||||
return *(val.(*fuse.MknodRequest))
|
||||
}
|
||||
|
||||
// Opens records a Open request and its fields.
|
||||
type Opens struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeOpener(&Opens{})
|
||||
|
||||
// Open records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Opens) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil, fuse.EIO
|
||||
}
|
||||
|
||||
// RecordedOpen returns information about the Open request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Opens) RecordedOpen() fuse.OpenRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.OpenRequest{}
|
||||
}
|
||||
return *(val.(*fuse.OpenRequest))
|
||||
}
|
||||
|
||||
// Getxattrs records a Getxattr request and its fields.
|
||||
type Getxattrs struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeGetxattrer(&Getxattrs{})
|
||||
|
||||
// Getxattr records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Getxattrs) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return fuse.ErrNoXattr
|
||||
}
|
||||
|
||||
// RecordedGetxattr returns information about the Getxattr request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Getxattrs) RecordedGetxattr() fuse.GetxattrRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.GetxattrRequest{}
|
||||
}
|
||||
return *(val.(*fuse.GetxattrRequest))
|
||||
}
|
||||
|
||||
// Listxattrs records a Listxattr request and its fields.
|
||||
type Listxattrs struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeListxattrer(&Listxattrs{})
|
||||
|
||||
// Listxattr records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Listxattrs) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return fuse.ErrNoXattr
|
||||
}
|
||||
|
||||
// RecordedListxattr returns information about the Listxattr request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Listxattrs) RecordedListxattr() fuse.ListxattrRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.ListxattrRequest{}
|
||||
}
|
||||
return *(val.(*fuse.ListxattrRequest))
|
||||
}
|
||||
|
||||
// Setxattrs records a Setxattr request and its fields.
|
||||
type Setxattrs struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeSetxattrer(&Setxattrs{})
|
||||
|
||||
// Setxattr records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Setxattrs) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
|
||||
tmp := *req
|
||||
// The byte slice points to memory that will be reused, so make a
|
||||
// deep copy.
|
||||
tmp.Xattr = append([]byte(nil), req.Xattr...)
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecordedSetxattr returns information about the Setxattr request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Setxattrs) RecordedSetxattr() fuse.SetxattrRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.SetxattrRequest{}
|
||||
}
|
||||
return *(val.(*fuse.SetxattrRequest))
|
||||
}
|
||||
|
||||
// Removexattrs records a Removexattr request and its fields.
|
||||
type Removexattrs struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeRemovexattrer(&Removexattrs{})
|
||||
|
||||
// Removexattr records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Removexattrs) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecordedRemovexattr returns information about the Removexattr request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Removexattrs) RecordedRemovexattr() fuse.RemovexattrRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.RemovexattrRequest{}
|
||||
}
|
||||
return *(val.(*fuse.RemovexattrRequest))
|
||||
}
|
||||
|
||||
// Creates records a Create request and its fields.
|
||||
type Creates struct {
|
||||
rec RequestRecorder
|
||||
}
|
||||
|
||||
var _ = fs.NodeCreater(&Creates{})
|
||||
|
||||
// Create records the request and returns an error. Most callers should
|
||||
// wrap this call in a function that returns a more useful result.
|
||||
func (r *Creates) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
|
||||
tmp := *req
|
||||
r.rec.RecordRequest(&tmp)
|
||||
return nil, nil, fuse.EIO
|
||||
}
|
||||
|
||||
// RecordedCreate returns information about the Create request.
|
||||
// If no request was seen, returns a zero value.
|
||||
func (r *Creates) RecordedCreate() fuse.CreateRequest {
|
||||
val := r.rec.Recorded()
|
||||
if val == nil {
|
||||
return fuse.CreateRequest{}
|
||||
}
|
||||
return *(val.(*fuse.CreateRequest))
|
||||
}
|
55
vendor/bazil.org/fuse/fs/fstestutil/record/wait.go
generated
vendored
Normal file
55
vendor/bazil.org/fuse/fs/fstestutil/record/wait.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package record
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type nothing struct{}
|
||||
|
||||
// ReleaseWaiter notes whether a FUSE Release call has been seen.
|
||||
//
|
||||
// Releases are not guaranteed to happen synchronously with any client
|
||||
// call, so they must be waited for.
|
||||
type ReleaseWaiter struct {
|
||||
once sync.Once
|
||||
seen chan nothing
|
||||
}
|
||||
|
||||
var _ = fs.HandleReleaser(&ReleaseWaiter{})
|
||||
|
||||
func (r *ReleaseWaiter) init() {
|
||||
r.once.Do(func() {
|
||||
r.seen = make(chan nothing, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *ReleaseWaiter) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
|
||||
r.init()
|
||||
close(r.seen)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForRelease waits for Release to be called.
|
||||
//
|
||||
// With zero duration, wait forever. Otherwise, timeout early
|
||||
// in a more controller way than `-test.timeout`.
|
||||
//
|
||||
// Returns whether a Release was seen. Always true if dur==0.
|
||||
func (r *ReleaseWaiter) WaitForRelease(dur time.Duration) bool {
|
||||
r.init()
|
||||
var timeout <-chan time.Time
|
||||
if dur > 0 {
|
||||
timeout = time.After(dur)
|
||||
}
|
||||
select {
|
||||
case <-r.seen:
|
||||
return true
|
||||
case <-timeout:
|
||||
return false
|
||||
}
|
||||
}
|
55
vendor/bazil.org/fuse/fs/fstestutil/testfs.go
generated
vendored
Normal file
55
vendor/bazil.org/fuse/fs/fstestutil/testfs.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package fstestutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// SimpleFS is a trivial FS that just implements the Root method.
|
||||
type SimpleFS struct {
|
||||
Node fs.Node
|
||||
}
|
||||
|
||||
var _ = fs.FS(SimpleFS{})
|
||||
|
||||
func (f SimpleFS) Root() (fs.Node, error) {
|
||||
return f.Node, nil
|
||||
}
|
||||
|
||||
// File can be embedded in a struct to make it look like a file.
|
||||
type File struct{}
|
||||
|
||||
func (f File) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Mode = 0666
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dir can be embedded in a struct to make it look like a directory.
|
||||
type Dir struct{}
|
||||
|
||||
func (f Dir) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Mode = os.ModeDir | 0777
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChildMap is a directory with child nodes looked up from a map.
|
||||
type ChildMap map[string]fs.Node
|
||||
|
||||
var _ = fs.Node(&ChildMap{})
|
||||
var _ = fs.NodeStringLookuper(&ChildMap{})
|
||||
|
||||
func (f *ChildMap) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Mode = os.ModeDir | 0777
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *ChildMap) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||
child, ok := (*f)[name]
|
||||
if !ok {
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
return child, nil
|
||||
}
|
67
vendor/bazil.org/fuse/fs/helpers_test.go
generated
vendored
Normal file
67
vendor/bazil.org/fuse/fs/helpers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
package fs_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var childHelpers = map[string]func(){}
|
||||
|
||||
type childProcess struct {
|
||||
name string
|
||||
fn func()
|
||||
}
|
||||
|
||||
var _ flag.Value = (*childProcess)(nil)
|
||||
|
||||
func (c *childProcess) String() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *childProcess) Set(s string) error {
|
||||
fn, ok := childHelpers[s]
|
||||
if !ok {
|
||||
return errors.New("helper not found")
|
||||
}
|
||||
c.name = s
|
||||
c.fn = fn
|
||||
return nil
|
||||
}
|
||||
|
||||
var childMode childProcess
|
||||
|
||||
func init() {
|
||||
flag.Var(&childMode, "fuse.internal.child", "internal use only")
|
||||
}
|
||||
|
||||
// childCmd prepares a test function to be run in a subprocess, with
|
||||
// childMode set to true. Caller must still call Run or Start.
|
||||
//
|
||||
// Re-using the test executable as the subprocess is useful because
|
||||
// now test executables can e.g. be cross-compiled, transferred
|
||||
// between hosts, and run in settings where the whole Go development
|
||||
// environment is not installed.
|
||||
func childCmd(childName string) (*exec.Cmd, error) {
|
||||
// caller may set cwd, so we can't rely on relative paths
|
||||
executable, err := filepath.Abs(os.Args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := exec.Command(executable, "-fuse.internal.child="+childName)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
if childMode.fn != nil {
|
||||
childMode.fn()
|
||||
os.Exit(0)
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
1568
vendor/bazil.org/fuse/fs/serve.go
generated
vendored
Normal file
1568
vendor/bazil.org/fuse/fs/serve.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
30
vendor/bazil.org/fuse/fs/serve_darwin_test.go
generated
vendored
Normal file
30
vendor/bazil.org/fuse/fs/serve_darwin_test.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
package fs_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"bazil.org/fuse/fs/fstestutil"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type exchangeData struct {
|
||||
fstestutil.File
|
||||
// this struct cannot be zero size or multiple instances may look identical
|
||||
_ int
|
||||
}
|
||||
|
||||
func TestExchangeDataNotSupported(t *testing.T) {
|
||||
t.Parallel()
|
||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{&fstestutil.ChildMap{
|
||||
"one": &exchangeData{},
|
||||
"two": &exchangeData{},
|
||||
}}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
if err := unix.Exchangedata(mnt.Dir+"/one", mnt.Dir+"/two", 0); err != unix.ENOTSUP {
|
||||
t.Fatalf("expected ENOTSUP from exchangedata: %v", err)
|
||||
}
|
||||
}
|
2843
vendor/bazil.org/fuse/fs/serve_test.go
generated
vendored
Normal file
2843
vendor/bazil.org/fuse/fs/serve_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
99
vendor/bazil.org/fuse/fs/tree.go
generated
vendored
Normal file
99
vendor/bazil.org/fuse/fs/tree.go
generated
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
// FUSE directory tree, for servers that wish to use it with the service loop.
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
pathpkg "path"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
import (
|
||||
"bazil.org/fuse"
|
||||
)
|
||||
|
||||
// A Tree implements a basic read-only directory tree for FUSE.
|
||||
// The Nodes contained in it may still be writable.
|
||||
type Tree struct {
|
||||
tree
|
||||
}
|
||||
|
||||
func (t *Tree) Root() (Node, error) {
|
||||
return &t.tree, nil
|
||||
}
|
||||
|
||||
// Add adds the path to the tree, resolving to the given node.
|
||||
// If path or a prefix of path has already been added to the tree,
|
||||
// Add panics.
|
||||
//
|
||||
// Add is only safe to call before starting to serve requests.
|
||||
func (t *Tree) Add(path string, node Node) {
|
||||
path = pathpkg.Clean("/" + path)[1:]
|
||||
elems := strings.Split(path, "/")
|
||||
dir := Node(&t.tree)
|
||||
for i, elem := range elems {
|
||||
dt, ok := dir.(*tree)
|
||||
if !ok {
|
||||
panic("fuse: Tree.Add for " + strings.Join(elems[:i], "/") + " and " + path)
|
||||
}
|
||||
n := dt.lookup(elem)
|
||||
if n != nil {
|
||||
if i+1 == len(elems) {
|
||||
panic("fuse: Tree.Add for " + path + " conflicts with " + elem)
|
||||
}
|
||||
dir = n
|
||||
} else {
|
||||
if i+1 == len(elems) {
|
||||
dt.add(elem, node)
|
||||
} else {
|
||||
dir = &tree{}
|
||||
dt.add(elem, dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type treeDir struct {
|
||||
name string
|
||||
node Node
|
||||
}
|
||||
|
||||
type tree struct {
|
||||
dir []treeDir
|
||||
}
|
||||
|
||||
func (t *tree) lookup(name string) Node {
|
||||
for _, d := range t.dir {
|
||||
if d.name == name {
|
||||
return d.node
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tree) add(name string, n Node) {
|
||||
t.dir = append(t.dir, treeDir{name, n})
|
||||
}
|
||||
|
||||
func (t *tree) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Mode = os.ModeDir | 0555
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tree) Lookup(ctx context.Context, name string) (Node, error) {
|
||||
n := t.lookup(name)
|
||||
if n != nil {
|
||||
return n, nil
|
||||
}
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
|
||||
func (t *tree) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
||||
var out []fuse.Dirent
|
||||
for _, d := range t.dir {
|
||||
out = append(out, fuse.Dirent{Name: d.name})
|
||||
}
|
||||
return out, nil
|
||||
}
|
2303
vendor/bazil.org/fuse/fuse.go
generated
vendored
Normal file
2303
vendor/bazil.org/fuse/fuse.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
9
vendor/bazil.org/fuse/fuse_darwin.go
generated
vendored
Normal file
9
vendor/bazil.org/fuse/fuse_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
package fuse
|
||||
|
||||
// Maximum file write size we are prepared to receive from the kernel.
|
||||
//
|
||||
// This value has to be >=16MB or OSXFUSE (3.4.0 observed) will
|
||||
// forcibly close the /dev/fuse file descriptor on a Setxattr with a
|
||||
// 16MB value. See TestSetxattr16MB and
|
||||
// https://github.com/bazil/fuse/issues/42
|
||||
const maxWrite = 16 * 1024 * 1024
|
6
vendor/bazil.org/fuse/fuse_freebsd.go
generated
vendored
Normal file
6
vendor/bazil.org/fuse/fuse_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
package fuse
|
||||
|
||||
// Maximum file write size we are prepared to receive from the kernel.
|
||||
//
|
||||
// This number is just a guess.
|
||||
const maxWrite = 128 * 1024
|
774
vendor/bazil.org/fuse/fuse_kernel.go
generated
vendored
Normal file
774
vendor/bazil.org/fuse/fuse_kernel.go
generated
vendored
Normal file
|
@ -0,0 +1,774 @@
|
|||
// See the file LICENSE for copyright and licensing information.
|
||||
|
||||
// Derived from FUSE's fuse_kernel.h, which carries this notice:
|
||||
/*
|
||||
This file defines the kernel interface of FUSE
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
|
||||
This -- and only this -- header file may also be distributed under
|
||||
the terms of the BSD Licence as follows:
|
||||
|
||||
Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. 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.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
|
||||
*/
|
||||
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// The FUSE version implemented by the package.
|
||||
const (
|
||||
protoVersionMinMajor = 7
|
||||
protoVersionMinMinor = 8
|
||||
protoVersionMaxMajor = 7
|
||||
protoVersionMaxMinor = 12
|
||||
)
|
||||
|
||||
const (
|
||||
rootID = 1
|
||||
)
|
||||
|
||||
type kstatfs struct {
|
||||
Blocks uint64
|
||||
Bfree uint64
|
||||
Bavail uint64
|
||||
Files uint64
|
||||
Ffree uint64
|
||||
Bsize uint32
|
||||
Namelen uint32
|
||||
Frsize uint32
|
||||
_ uint32
|
||||
Spare [6]uint32
|
||||
}
|
||||
|
||||
type fileLock struct {
|
||||
Start uint64
|
||||
End uint64
|
||||
Type uint32
|
||||
Pid uint32
|
||||
}
|
||||
|
||||
// GetattrFlags are bit flags that can be seen in GetattrRequest.
|
||||
type GetattrFlags uint32
|
||||
|
||||
const (
|
||||
// Indicates the handle is valid.
|
||||
GetattrFh GetattrFlags = 1 << 0
|
||||
)
|
||||
|
||||
var getattrFlagsNames = []flagName{
|
||||
{uint32(GetattrFh), "GetattrFh"},
|
||||
}
|
||||
|
||||
func (fl GetattrFlags) String() string {
|
||||
return flagString(uint32(fl), getattrFlagsNames)
|
||||
}
|
||||
|
||||
// The SetattrValid are bit flags describing which fields in the SetattrRequest
|
||||
// are included in the change.
|
||||
type SetattrValid uint32
|
||||
|
||||
const (
|
||||
SetattrMode SetattrValid = 1 << 0
|
||||
SetattrUid SetattrValid = 1 << 1
|
||||
SetattrGid SetattrValid = 1 << 2
|
||||
SetattrSize SetattrValid = 1 << 3
|
||||
SetattrAtime SetattrValid = 1 << 4
|
||||
SetattrMtime SetattrValid = 1 << 5
|
||||
SetattrHandle SetattrValid = 1 << 6
|
||||
|
||||
// Linux only(?)
|
||||
SetattrAtimeNow SetattrValid = 1 << 7
|
||||
SetattrMtimeNow SetattrValid = 1 << 8
|
||||
SetattrLockOwner SetattrValid = 1 << 9 // http://www.mail-archive.com/git-commits-head@vger.kernel.org/msg27852.html
|
||||
|
||||
// OS X only
|
||||
SetattrCrtime SetattrValid = 1 << 28
|
||||
SetattrChgtime SetattrValid = 1 << 29
|
||||
SetattrBkuptime SetattrValid = 1 << 30
|
||||
SetattrFlags SetattrValid = 1 << 31
|
||||
)
|
||||
|
||||
func (fl SetattrValid) Mode() bool { return fl&SetattrMode != 0 }
|
||||
func (fl SetattrValid) Uid() bool { return fl&SetattrUid != 0 }
|
||||
func (fl SetattrValid) Gid() bool { return fl&SetattrGid != 0 }
|
||||
func (fl SetattrValid) Size() bool { return fl&SetattrSize != 0 }
|
||||
func (fl SetattrValid) Atime() bool { return fl&SetattrAtime != 0 }
|
||||
func (fl SetattrValid) Mtime() bool { return fl&SetattrMtime != 0 }
|
||||
func (fl SetattrValid) Handle() bool { return fl&SetattrHandle != 0 }
|
||||
func (fl SetattrValid) AtimeNow() bool { return fl&SetattrAtimeNow != 0 }
|
||||
func (fl SetattrValid) MtimeNow() bool { return fl&SetattrMtimeNow != 0 }
|
||||
func (fl SetattrValid) LockOwner() bool { return fl&SetattrLockOwner != 0 }
|
||||
func (fl SetattrValid) Crtime() bool { return fl&SetattrCrtime != 0 }
|
||||
func (fl SetattrValid) Chgtime() bool { return fl&SetattrChgtime != 0 }
|
||||
func (fl SetattrValid) Bkuptime() bool { return fl&SetattrBkuptime != 0 }
|
||||
func (fl SetattrValid) Flags() bool { return fl&SetattrFlags != 0 }
|
||||
|
||||
func (fl SetattrValid) String() string {
|
||||
return flagString(uint32(fl), setattrValidNames)
|
||||
}
|
||||
|
||||
var setattrValidNames = []flagName{
|
||||
{uint32(SetattrMode), "SetattrMode"},
|
||||
{uint32(SetattrUid), "SetattrUid"},
|
||||
{uint32(SetattrGid), "SetattrGid"},
|
||||
{uint32(SetattrSize), "SetattrSize"},
|
||||
{uint32(SetattrAtime), "SetattrAtime"},
|
||||
{uint32(SetattrMtime), "SetattrMtime"},
|
||||
{uint32(SetattrHandle), "SetattrHandle"},
|
||||
{uint32(SetattrAtimeNow), "SetattrAtimeNow"},
|
||||
{uint32(SetattrMtimeNow), "SetattrMtimeNow"},
|
||||
{uint32(SetattrLockOwner), "SetattrLockOwner"},
|
||||
{uint32(SetattrCrtime), "SetattrCrtime"},
|
||||
{uint32(SetattrChgtime), "SetattrChgtime"},
|
||||
{uint32(SetattrBkuptime), "SetattrBkuptime"},
|
||||
{uint32(SetattrFlags), "SetattrFlags"},
|
||||
}
|
||||
|
||||
// Flags that can be seen in OpenRequest.Flags.
|
||||
const (
|
||||
// Access modes. These are not 1-bit flags, but alternatives where
|
||||
// only one can be chosen. See the IsReadOnly etc convenience
|
||||
// methods.
|
||||
OpenReadOnly OpenFlags = syscall.O_RDONLY
|
||||
OpenWriteOnly OpenFlags = syscall.O_WRONLY
|
||||
OpenReadWrite OpenFlags = syscall.O_RDWR
|
||||
|
||||
// File was opened in append-only mode, all writes will go to end
|
||||
// of file. OS X does not provide this information.
|
||||
OpenAppend OpenFlags = syscall.O_APPEND
|
||||
OpenCreate OpenFlags = syscall.O_CREAT
|
||||
OpenDirectory OpenFlags = syscall.O_DIRECTORY
|
||||
OpenExclusive OpenFlags = syscall.O_EXCL
|
||||
OpenNonblock OpenFlags = syscall.O_NONBLOCK
|
||||
OpenSync OpenFlags = syscall.O_SYNC
|
||||
OpenTruncate OpenFlags = syscall.O_TRUNC
|
||||
)
|
||||
|
||||
// OpenAccessModeMask is a bitmask that separates the access mode
|
||||
// from the other flags in OpenFlags.
|
||||
const OpenAccessModeMask OpenFlags = syscall.O_ACCMODE
|
||||
|
||||
// OpenFlags are the O_FOO flags passed to open/create/etc calls. For
|
||||
// example, os.O_WRONLY | os.O_APPEND.
|
||||
type OpenFlags uint32
|
||||
|
||||
func (fl OpenFlags) String() string {
|
||||
// O_RDONLY, O_RWONLY, O_RDWR are not flags
|
||||
s := accModeName(fl & OpenAccessModeMask)
|
||||
flags := uint32(fl &^ OpenAccessModeMask)
|
||||
if flags != 0 {
|
||||
s = s + "+" + flagString(flags, openFlagNames)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Return true if OpenReadOnly is set.
|
||||
func (fl OpenFlags) IsReadOnly() bool {
|
||||
return fl&OpenAccessModeMask == OpenReadOnly
|
||||
}
|
||||
|
||||
// Return true if OpenWriteOnly is set.
|
||||
func (fl OpenFlags) IsWriteOnly() bool {
|
||||
return fl&OpenAccessModeMask == OpenWriteOnly
|
||||
}
|
||||
|
||||
// Return true if OpenReadWrite is set.
|
||||
func (fl OpenFlags) IsReadWrite() bool {
|
||||
return fl&OpenAccessModeMask == OpenReadWrite
|
||||
}
|
||||
|
||||
func accModeName(flags OpenFlags) string {
|
||||
switch flags {
|
||||
case OpenReadOnly:
|
||||
return "OpenReadOnly"
|
||||
case OpenWriteOnly:
|
||||
return "OpenWriteOnly"
|
||||
case OpenReadWrite:
|
||||
return "OpenReadWrite"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
var openFlagNames = []flagName{
|
||||
{uint32(OpenAppend), "OpenAppend"},
|
||||
{uint32(OpenCreate), "OpenCreate"},
|
||||
{uint32(OpenDirectory), "OpenDirectory"},
|
||||
{uint32(OpenExclusive), "OpenExclusive"},
|
||||
{uint32(OpenNonblock), "OpenNonblock"},
|
||||
{uint32(OpenSync), "OpenSync"},
|
||||
{uint32(OpenTruncate), "OpenTruncate"},
|
||||
}
|
||||
|
||||
// The OpenResponseFlags are returned in the OpenResponse.
|
||||
type OpenResponseFlags uint32
|
||||
|
||||
const (
|
||||
OpenDirectIO OpenResponseFlags = 1 << 0 // bypass page cache for this open file
|
||||
OpenKeepCache OpenResponseFlags = 1 << 1 // don't invalidate the data cache on open
|
||||
OpenNonSeekable OpenResponseFlags = 1 << 2 // mark the file as non-seekable (not supported on OS X)
|
||||
|
||||
OpenPurgeAttr OpenResponseFlags = 1 << 30 // OS X
|
||||
OpenPurgeUBC OpenResponseFlags = 1 << 31 // OS X
|
||||
)
|
||||
|
||||
func (fl OpenResponseFlags) String() string {
|
||||
return flagString(uint32(fl), openResponseFlagNames)
|
||||
}
|
||||
|
||||
var openResponseFlagNames = []flagName{
|
||||
{uint32(OpenDirectIO), "OpenDirectIO"},
|
||||
{uint32(OpenKeepCache), "OpenKeepCache"},
|
||||
{uint32(OpenNonSeekable), "OpenNonSeekable"},
|
||||
{uint32(OpenPurgeAttr), "OpenPurgeAttr"},
|
||||
{uint32(OpenPurgeUBC), "OpenPurgeUBC"},
|
||||
}
|
||||
|
||||
// The InitFlags are used in the Init exchange.
|
||||
type InitFlags uint32
|
||||
|
||||
const (
|
||||
InitAsyncRead InitFlags = 1 << 0
|
||||
InitPosixLocks InitFlags = 1 << 1
|
||||
InitFileOps InitFlags = 1 << 2
|
||||
InitAtomicTrunc InitFlags = 1 << 3
|
||||
InitExportSupport InitFlags = 1 << 4
|
||||
InitBigWrites InitFlags = 1 << 5
|
||||
// Do not mask file access modes with umask. Not supported on OS X.
|
||||
InitDontMask InitFlags = 1 << 6
|
||||
InitSpliceWrite InitFlags = 1 << 7
|
||||
InitSpliceMove InitFlags = 1 << 8
|
||||
InitSpliceRead InitFlags = 1 << 9
|
||||
InitFlockLocks InitFlags = 1 << 10
|
||||
InitHasIoctlDir InitFlags = 1 << 11
|
||||
InitAutoInvalData InitFlags = 1 << 12
|
||||
InitDoReaddirplus InitFlags = 1 << 13
|
||||
InitReaddirplusAuto InitFlags = 1 << 14
|
||||
InitAsyncDIO InitFlags = 1 << 15
|
||||
InitWritebackCache InitFlags = 1 << 16
|
||||
InitNoOpenSupport InitFlags = 1 << 17
|
||||
|
||||
InitCaseSensitive InitFlags = 1 << 29 // OS X only
|
||||
InitVolRename InitFlags = 1 << 30 // OS X only
|
||||
InitXtimes InitFlags = 1 << 31 // OS X only
|
||||
)
|
||||
|
||||
type flagName struct {
|
||||
bit uint32
|
||||
name string
|
||||
}
|
||||
|
||||
var initFlagNames = []flagName{
|
||||
{uint32(InitAsyncRead), "InitAsyncRead"},
|
||||
{uint32(InitPosixLocks), "InitPosixLocks"},
|
||||
{uint32(InitFileOps), "InitFileOps"},
|
||||
{uint32(InitAtomicTrunc), "InitAtomicTrunc"},
|
||||
{uint32(InitExportSupport), "InitExportSupport"},
|
||||
{uint32(InitBigWrites), "InitBigWrites"},
|
||||
{uint32(InitDontMask), "InitDontMask"},
|
||||
{uint32(InitSpliceWrite), "InitSpliceWrite"},
|
||||
{uint32(InitSpliceMove), "InitSpliceMove"},
|
||||
{uint32(InitSpliceRead), "InitSpliceRead"},
|
||||
{uint32(InitFlockLocks), "InitFlockLocks"},
|
||||
{uint32(InitHasIoctlDir), "InitHasIoctlDir"},
|
||||
{uint32(InitAutoInvalData), "InitAutoInvalData"},
|
||||
{uint32(InitDoReaddirplus), "InitDoReaddirplus"},
|
||||
{uint32(InitReaddirplusAuto), "InitReaddirplusAuto"},
|
||||
{uint32(InitAsyncDIO), "InitAsyncDIO"},
|
||||
{uint32(InitWritebackCache), "InitWritebackCache"},
|
||||
{uint32(InitNoOpenSupport), "InitNoOpenSupport"},
|
||||
|
||||
{uint32(InitCaseSensitive), "InitCaseSensitive"},
|
||||
{uint32(InitVolRename), "InitVolRename"},
|
||||
{uint32(InitXtimes), "InitXtimes"},
|
||||
}
|
||||
|
||||
func (fl InitFlags) String() string {
|
||||
return flagString(uint32(fl), initFlagNames)
|
||||
}
|
||||
|
||||
func flagString(f uint32, names []flagName) string {
|
||||
var s string
|
||||
|
||||
if f == 0 {
|
||||
return "0"
|
||||
}
|
||||
|
||||
for _, n := range names {
|
||||
if f&n.bit != 0 {
|
||||
s += "+" + n.name
|
||||
f &^= n.bit
|
||||
}
|
||||
}
|
||||
if f != 0 {
|
||||
s += fmt.Sprintf("%+#x", f)
|
||||
}
|
||||
return s[1:]
|
||||
}
|
||||
|
||||
// The ReleaseFlags are used in the Release exchange.
|
||||
type ReleaseFlags uint32
|
||||
|
||||
const (
|
||||
ReleaseFlush ReleaseFlags = 1 << 0
|
||||
)
|
||||
|
||||
func (fl ReleaseFlags) String() string {
|
||||
return flagString(uint32(fl), releaseFlagNames)
|
||||
}
|
||||
|
||||
var releaseFlagNames = []flagName{
|
||||
{uint32(ReleaseFlush), "ReleaseFlush"},
|
||||
}
|
||||
|
||||
// Opcodes
|
||||
const (
|
||||
opLookup = 1
|
||||
opForget = 2 // no reply
|
||||
opGetattr = 3
|
||||
opSetattr = 4
|
||||
opReadlink = 5
|
||||
opSymlink = 6
|
||||
opMknod = 8
|
||||
opMkdir = 9
|
||||
opUnlink = 10
|
||||
opRmdir = 11
|
||||
opRename = 12
|
||||
opLink = 13
|
||||
opOpen = 14
|
||||
opRead = 15
|
||||
opWrite = 16
|
||||
opStatfs = 17
|
||||
opRelease = 18
|
||||
opFsync = 20
|
||||
opSetxattr = 21
|
||||
opGetxattr = 22
|
||||
opListxattr = 23
|
||||
opRemovexattr = 24
|
||||
opFlush = 25
|
||||
opInit = 26
|
||||
opOpendir = 27
|
||||
opReaddir = 28
|
||||
opReleasedir = 29
|
||||
opFsyncdir = 30
|
||||
opGetlk = 31
|
||||
opSetlk = 32
|
||||
opSetlkw = 33
|
||||
opAccess = 34
|
||||
opCreate = 35
|
||||
opInterrupt = 36
|
||||
opBmap = 37
|
||||
opDestroy = 38
|
||||
opIoctl = 39 // Linux?
|
||||
opPoll = 40 // Linux?
|
||||
|
||||
// OS X
|
||||
opSetvolname = 61
|
||||
opGetxtimes = 62
|
||||
opExchange = 63
|
||||
)
|
||||
|
||||
type entryOut struct {
|
||||
Nodeid uint64 // Inode ID
|
||||
Generation uint64 // Inode generation
|
||||
EntryValid uint64 // Cache timeout for the name
|
||||
AttrValid uint64 // Cache timeout for the attributes
|
||||
EntryValidNsec uint32
|
||||
AttrValidNsec uint32
|
||||
Attr attr
|
||||
}
|
||||
|
||||
func entryOutSize(p Protocol) uintptr {
|
||||
switch {
|
||||
case p.LT(Protocol{7, 9}):
|
||||
return unsafe.Offsetof(entryOut{}.Attr) + unsafe.Offsetof(entryOut{}.Attr.Blksize)
|
||||
default:
|
||||
return unsafe.Sizeof(entryOut{})
|
||||
}
|
||||
}
|
||||
|
||||
type forgetIn struct {
|
||||
Nlookup uint64
|
||||
}
|
||||
|
||||
type getattrIn struct {
|
||||
GetattrFlags uint32
|
||||
_ uint32
|
||||
Fh uint64
|
||||
}
|
||||
|
||||
type attrOut struct {
|
||||
AttrValid uint64 // Cache timeout for the attributes
|
||||
AttrValidNsec uint32
|
||||
_ uint32
|
||||
Attr attr
|
||||
}
|
||||
|
||||
func attrOutSize(p Protocol) uintptr {
|
||||
switch {
|
||||
case p.LT(Protocol{7, 9}):
|
||||
return unsafe.Offsetof(attrOut{}.Attr) + unsafe.Offsetof(attrOut{}.Attr.Blksize)
|
||||
default:
|
||||
return unsafe.Sizeof(attrOut{})
|
||||
}
|
||||
}
|
||||
|
||||
// OS X
|
||||
type getxtimesOut struct {
|
||||
Bkuptime uint64
|
||||
Crtime uint64
|
||||
BkuptimeNsec uint32
|
||||
CrtimeNsec uint32
|
||||
}
|
||||
|
||||
type mknodIn struct {
|
||||
Mode uint32
|
||||
Rdev uint32
|
||||
Umask uint32
|
||||
_ uint32
|
||||
// "filename\x00" follows.
|
||||
}
|
||||
|
||||
func mknodInSize(p Protocol) uintptr {
|
||||
switch {
|
||||
case p.LT(Protocol{7, 12}):
|
||||
return unsafe.Offsetof(mknodIn{}.Umask)
|
||||
default:
|
||||
return unsafe.Sizeof(mknodIn{})
|
||||
}
|
||||
}
|
||||
|
||||
type mkdirIn struct {
|
||||
Mode uint32
|
||||
Umask uint32
|
||||
// filename follows
|
||||
}
|
||||
|
||||
func mkdirInSize(p Protocol) uintptr {
|
||||
switch {
|
||||
case p.LT(Protocol{7, 12}):
|
||||
return unsafe.Offsetof(mkdirIn{}.Umask) + 4
|
||||
default:
|
||||
return unsafe.Sizeof(mkdirIn{})
|
||||
}
|
||||
}
|
||||
|
||||
type renameIn struct {
|
||||
Newdir uint64
|
||||
// "oldname\x00newname\x00" follows
|
||||
}
|
||||
|
||||
// OS X
|
||||
type exchangeIn struct {
|
||||
Olddir uint64
|
||||
Newdir uint64
|
||||
Options uint64
|
||||
// "oldname\x00newname\x00" follows
|
||||
}
|
||||
|
||||
type linkIn struct {
|
||||
Oldnodeid uint64
|
||||
}
|
||||
|
||||
type setattrInCommon struct {
|
||||
Valid uint32
|
||||
_ uint32
|
||||
Fh uint64
|
||||
Size uint64
|
||||
LockOwner uint64 // unused on OS X?
|
||||
Atime uint64
|
||||
Mtime uint64
|
||||
Unused2 uint64
|
||||
AtimeNsec uint32
|
||||
MtimeNsec uint32
|
||||
Unused3 uint32
|
||||
Mode uint32
|
||||
Unused4 uint32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
Unused5 uint32
|
||||
}
|
||||
|
||||
type openIn struct {
|
||||
Flags uint32
|
||||
Unused uint32
|
||||
}
|
||||
|
||||
type openOut struct {
|
||||
Fh uint64
|
||||
OpenFlags uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type createIn struct {
|
||||
Flags uint32
|
||||
Mode uint32
|
||||
Umask uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func createInSize(p Protocol) uintptr {
|
||||
switch {
|
||||
case p.LT(Protocol{7, 12}):
|
||||
return unsafe.Offsetof(createIn{}.Umask)
|
||||
default:
|
||||
return unsafe.Sizeof(createIn{})
|
||||
}
|
||||
}
|
||||
|
||||
type releaseIn struct {
|
||||
Fh uint64
|
||||
Flags uint32
|
||||
ReleaseFlags uint32
|
||||
LockOwner uint32
|
||||
}
|
||||
|
||||
type flushIn struct {
|
||||
Fh uint64
|
||||
FlushFlags uint32
|
||||
_ uint32
|
||||
LockOwner uint64
|
||||
}
|
||||
|
||||
type readIn struct {
|
||||
Fh uint64
|
||||
Offset uint64
|
||||
Size uint32
|
||||
ReadFlags uint32
|
||||
LockOwner uint64
|
||||
Flags uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func readInSize(p Protocol) uintptr {
|
||||
switch {
|
||||
case p.LT(Protocol{7, 9}):
|
||||
return unsafe.Offsetof(readIn{}.ReadFlags) + 4
|
||||
default:
|
||||
return unsafe.Sizeof(readIn{})
|
||||
}
|
||||
}
|
||||
|
||||
// The ReadFlags are passed in ReadRequest.
|
||||
type ReadFlags uint32
|
||||
|
||||
const (
|
||||
// LockOwner field is valid.
|
||||
ReadLockOwner ReadFlags = 1 << 1
|
||||
)
|
||||
|
||||
var readFlagNames = []flagName{
|
||||
{uint32(ReadLockOwner), "ReadLockOwner"},
|
||||
}
|
||||
|
||||
func (fl ReadFlags) String() string {
|
||||
return flagString(uint32(fl), readFlagNames)
|
||||
}
|
||||
|
||||
type writeIn struct {
|
||||
Fh uint64
|
||||
Offset uint64
|
||||
Size uint32
|
||||
WriteFlags uint32
|
||||
LockOwner uint64
|
||||
Flags uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func writeInSize(p Protocol) uintptr {
|
||||
switch {
|
||||
case p.LT(Protocol{7, 9}):
|
||||
return unsafe.Offsetof(writeIn{}.LockOwner)
|
||||
default:
|
||||
return unsafe.Sizeof(writeIn{})
|
||||
}
|
||||
}
|
||||
|
||||
type writeOut struct {
|
||||
Size uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
// The WriteFlags are passed in WriteRequest.
|
||||
type WriteFlags uint32
|
||||
|
||||
const (
|
||||
WriteCache WriteFlags = 1 << 0
|
||||
// LockOwner field is valid.
|
||||
WriteLockOwner WriteFlags = 1 << 1
|
||||
)
|
||||
|
||||
var writeFlagNames = []flagName{
|
||||
{uint32(WriteCache), "WriteCache"},
|
||||
{uint32(WriteLockOwner), "WriteLockOwner"},
|
||||
}
|
||||
|
||||
func (fl WriteFlags) String() string {
|
||||
return flagString(uint32(fl), writeFlagNames)
|
||||
}
|
||||
|
||||
const compatStatfsSize = 48
|
||||
|
||||
type statfsOut struct {
|
||||
St kstatfs
|
||||
}
|
||||
|
||||
type fsyncIn struct {
|
||||
Fh uint64
|
||||
FsyncFlags uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type setxattrInCommon struct {
|
||||
Size uint32
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
func (setxattrInCommon) position() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type getxattrInCommon struct {
|
||||
Size uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func (getxattrInCommon) position() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type getxattrOut struct {
|
||||
Size uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type lkIn struct {
|
||||
Fh uint64
|
||||
Owner uint64
|
||||
Lk fileLock
|
||||
LkFlags uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
func lkInSize(p Protocol) uintptr {
|
||||
switch {
|
||||
case p.LT(Protocol{7, 9}):
|
||||
return unsafe.Offsetof(lkIn{}.LkFlags)
|
||||
default:
|
||||
return unsafe.Sizeof(lkIn{})
|
||||
}
|
||||
}
|
||||
|
||||
type lkOut struct {
|
||||
Lk fileLock
|
||||
}
|
||||
|
||||
type accessIn struct {
|
||||
Mask uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type initIn struct {
|
||||
Major uint32
|
||||
Minor uint32
|
||||
MaxReadahead uint32
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
const initInSize = int(unsafe.Sizeof(initIn{}))
|
||||
|
||||
type initOut struct {
|
||||
Major uint32
|
||||
Minor uint32
|
||||
MaxReadahead uint32
|
||||
Flags uint32
|
||||
Unused uint32
|
||||
MaxWrite uint32
|
||||
}
|
||||
|
||||
type interruptIn struct {
|
||||
Unique uint64
|
||||
}
|
||||
|
||||
type bmapIn struct {
|
||||
Block uint64
|
||||
BlockSize uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type bmapOut struct {
|
||||
Block uint64
|
||||
}
|
||||
|
||||
type inHeader struct {
|
||||
Len uint32
|
||||
Opcode uint32
|
||||
Unique uint64
|
||||
Nodeid uint64
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
Pid uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
const inHeaderSize = int(unsafe.Sizeof(inHeader{}))
|
||||
|
||||
type outHeader struct {
|
||||
Len uint32
|
||||
Error int32
|
||||
Unique uint64
|
||||
}
|
||||
|
||||
type dirent struct {
|
||||
Ino uint64
|
||||
Off uint64
|
||||
Namelen uint32
|
||||
Type uint32
|
||||
Name [0]byte
|
||||
}
|
||||
|
||||
const direntSize = 8 + 8 + 4 + 4
|
||||
|
||||
const (
|
||||
notifyCodePoll int32 = 1
|
||||
notifyCodeInvalInode int32 = 2
|
||||
notifyCodeInvalEntry int32 = 3
|
||||
)
|
||||
|
||||
type notifyInvalInodeOut struct {
|
||||
Ino uint64
|
||||
Off int64
|
||||
Len int64
|
||||
}
|
||||
|
||||
type notifyInvalEntryOut struct {
|
||||
Parent uint64
|
||||
Namelen uint32
|
||||
_ uint32
|
||||
}
|
88
vendor/bazil.org/fuse/fuse_kernel_darwin.go
generated
vendored
Normal file
88
vendor/bazil.org/fuse/fuse_kernel_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type attr struct {
|
||||
Ino uint64
|
||||
Size uint64
|
||||
Blocks uint64
|
||||
Atime uint64
|
||||
Mtime uint64
|
||||
Ctime uint64
|
||||
Crtime_ uint64 // OS X only
|
||||
AtimeNsec uint32
|
||||
MtimeNsec uint32
|
||||
CtimeNsec uint32
|
||||
CrtimeNsec uint32 // OS X only
|
||||
Mode uint32
|
||||
Nlink uint32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
Rdev uint32
|
||||
Flags_ uint32 // OS X only; see chflags(2)
|
||||
Blksize uint32
|
||||
padding uint32
|
||||
}
|
||||
|
||||
func (a *attr) SetCrtime(s uint64, ns uint32) {
|
||||
a.Crtime_, a.CrtimeNsec = s, ns
|
||||
}
|
||||
|
||||
func (a *attr) SetFlags(f uint32) {
|
||||
a.Flags_ = f
|
||||
}
|
||||
|
||||
type setattrIn struct {
|
||||
setattrInCommon
|
||||
|
||||
// OS X only
|
||||
Bkuptime_ uint64
|
||||
Chgtime_ uint64
|
||||
Crtime uint64
|
||||
BkuptimeNsec uint32
|
||||
ChgtimeNsec uint32
|
||||
CrtimeNsec uint32
|
||||
Flags_ uint32 // see chflags(2)
|
||||
}
|
||||
|
||||
func (in *setattrIn) BkupTime() time.Time {
|
||||
return time.Unix(int64(in.Bkuptime_), int64(in.BkuptimeNsec))
|
||||
}
|
||||
|
||||
func (in *setattrIn) Chgtime() time.Time {
|
||||
return time.Unix(int64(in.Chgtime_), int64(in.ChgtimeNsec))
|
||||
}
|
||||
|
||||
func (in *setattrIn) Flags() uint32 {
|
||||
return in.Flags_
|
||||
}
|
||||
|
||||
func openFlags(flags uint32) OpenFlags {
|
||||
return OpenFlags(flags)
|
||||
}
|
||||
|
||||
type getxattrIn struct {
|
||||
getxattrInCommon
|
||||
|
||||
// OS X only
|
||||
Position uint32
|
||||
Padding uint32
|
||||
}
|
||||
|
||||
func (g *getxattrIn) position() uint32 {
|
||||
return g.Position
|
||||
}
|
||||
|
||||
type setxattrIn struct {
|
||||
setxattrInCommon
|
||||
|
||||
// OS X only
|
||||
Position uint32
|
||||
Padding uint32
|
||||
}
|
||||
|
||||
func (s *setxattrIn) position() uint32 {
|
||||
return s.Position
|
||||
}
|
62
vendor/bazil.org/fuse/fuse_kernel_freebsd.go
generated
vendored
Normal file
62
vendor/bazil.org/fuse/fuse_kernel_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
package fuse
|
||||
|
||||
import "time"
|
||||
|
||||
type attr struct {
|
||||
Ino uint64
|
||||
Size uint64
|
||||
Blocks uint64
|
||||
Atime uint64
|
||||
Mtime uint64
|
||||
Ctime uint64
|
||||
AtimeNsec uint32
|
||||
MtimeNsec uint32
|
||||
CtimeNsec uint32
|
||||
Mode uint32
|
||||
Nlink uint32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
Rdev uint32
|
||||
Blksize uint32
|
||||
padding uint32
|
||||
}
|
||||
|
||||
func (a *attr) Crtime() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (a *attr) SetCrtime(s uint64, ns uint32) {
|
||||
// ignored on freebsd
|
||||
}
|
||||
|
||||
func (a *attr) SetFlags(f uint32) {
|
||||
// ignored on freebsd
|
||||
}
|
||||
|
||||
type setattrIn struct {
|
||||
setattrInCommon
|
||||
}
|
||||
|
||||
func (in *setattrIn) BkupTime() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (in *setattrIn) Chgtime() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (in *setattrIn) Flags() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func openFlags(flags uint32) OpenFlags {
|
||||
return OpenFlags(flags)
|
||||
}
|
||||
|
||||
type getxattrIn struct {
|
||||
getxattrInCommon
|
||||
}
|
||||
|
||||
type setxattrIn struct {
|
||||
setxattrInCommon
|
||||
}
|
70
vendor/bazil.org/fuse/fuse_kernel_linux.go
generated
vendored
Normal file
70
vendor/bazil.org/fuse/fuse_kernel_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
package fuse
|
||||
|
||||
import "time"
|
||||
|
||||
type attr struct {
|
||||
Ino uint64
|
||||
Size uint64
|
||||
Blocks uint64
|
||||
Atime uint64
|
||||
Mtime uint64
|
||||
Ctime uint64
|
||||
AtimeNsec uint32
|
||||
MtimeNsec uint32
|
||||
CtimeNsec uint32
|
||||
Mode uint32
|
||||
Nlink uint32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
Rdev uint32
|
||||
Blksize uint32
|
||||
padding uint32
|
||||
}
|
||||
|
||||
func (a *attr) Crtime() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (a *attr) SetCrtime(s uint64, ns uint32) {
|
||||
// Ignored on Linux.
|
||||
}
|
||||
|
||||
func (a *attr) SetFlags(f uint32) {
|
||||
// Ignored on Linux.
|
||||
}
|
||||
|
||||
type setattrIn struct {
|
||||
setattrInCommon
|
||||
}
|
||||
|
||||
func (in *setattrIn) BkupTime() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (in *setattrIn) Chgtime() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (in *setattrIn) Flags() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func openFlags(flags uint32) OpenFlags {
|
||||
// on amd64, the 32-bit O_LARGEFILE flag is always seen;
|
||||
// on i386, the flag probably depends on the app
|
||||
// requesting, but in any case should be utterly
|
||||
// uninteresting to us here; our kernel protocol messages
|
||||
// are not directly related to the client app's kernel
|
||||
// API/ABI
|
||||
flags &^= 0x8000
|
||||
|
||||
return OpenFlags(flags)
|
||||
}
|
||||
|
||||
type getxattrIn struct {
|
||||
getxattrInCommon
|
||||
}
|
||||
|
||||
type setxattrIn struct {
|
||||
setxattrInCommon
|
||||
}
|
1
vendor/bazil.org/fuse/fuse_kernel_std.go
generated
vendored
Normal file
1
vendor/bazil.org/fuse/fuse_kernel_std.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package fuse
|
63
vendor/bazil.org/fuse/fuse_kernel_test.go
generated
vendored
Normal file
63
vendor/bazil.org/fuse/fuse_kernel_test.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
package fuse_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"bazil.org/fuse"
|
||||
)
|
||||
|
||||
func TestOpenFlagsAccmodeMaskReadWrite(t *testing.T) {
|
||||
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC)
|
||||
if g, e := f&fuse.OpenAccessModeMask, fuse.OpenReadWrite; g != e {
|
||||
t.Fatalf("OpenAccessModeMask behaves wrong: %v: %o != %o", f, g, e)
|
||||
}
|
||||
if f.IsReadOnly() {
|
||||
t.Fatalf("IsReadOnly is wrong: %v", f)
|
||||
}
|
||||
if f.IsWriteOnly() {
|
||||
t.Fatalf("IsWriteOnly is wrong: %v", f)
|
||||
}
|
||||
if !f.IsReadWrite() {
|
||||
t.Fatalf("IsReadWrite is wrong: %v", f)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenFlagsAccmodeMaskReadOnly(t *testing.T) {
|
||||
var f = fuse.OpenFlags(os.O_RDONLY | os.O_SYNC)
|
||||
if g, e := f&fuse.OpenAccessModeMask, fuse.OpenReadOnly; g != e {
|
||||
t.Fatalf("OpenAccessModeMask behaves wrong: %v: %o != %o", f, g, e)
|
||||
}
|
||||
if !f.IsReadOnly() {
|
||||
t.Fatalf("IsReadOnly is wrong: %v", f)
|
||||
}
|
||||
if f.IsWriteOnly() {
|
||||
t.Fatalf("IsWriteOnly is wrong: %v", f)
|
||||
}
|
||||
if f.IsReadWrite() {
|
||||
t.Fatalf("IsReadWrite is wrong: %v", f)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenFlagsAccmodeMaskWriteOnly(t *testing.T) {
|
||||
var f = fuse.OpenFlags(os.O_WRONLY | os.O_SYNC)
|
||||
if g, e := f&fuse.OpenAccessModeMask, fuse.OpenWriteOnly; g != e {
|
||||
t.Fatalf("OpenAccessModeMask behaves wrong: %v: %o != %o", f, g, e)
|
||||
}
|
||||
if f.IsReadOnly() {
|
||||
t.Fatalf("IsReadOnly is wrong: %v", f)
|
||||
}
|
||||
if !f.IsWriteOnly() {
|
||||
t.Fatalf("IsWriteOnly is wrong: %v", f)
|
||||
}
|
||||
if f.IsReadWrite() {
|
||||
t.Fatalf("IsReadWrite is wrong: %v", f)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenFlagsString(t *testing.T) {
|
||||
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC | os.O_APPEND)
|
||||
if g, e := f.String(), "OpenReadWrite+OpenAppend+OpenSync"; g != e {
|
||||
t.Fatalf("OpenFlags.String: %q != %q", g, e)
|
||||
}
|
||||
}
|
7
vendor/bazil.org/fuse/fuse_linux.go
generated
vendored
Normal file
7
vendor/bazil.org/fuse/fuse_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package fuse
|
||||
|
||||
// Maximum file write size we are prepared to receive from the kernel.
|
||||
//
|
||||
// Linux 4.2.0 has been observed to cap this value at 128kB
|
||||
// (FUSE_MAX_PAGES_PER_REQ=32, 4kB pages).
|
||||
const maxWrite = 128 * 1024
|
20
vendor/bazil.org/fuse/fuseutil/fuseutil.go
generated
vendored
Normal file
20
vendor/bazil.org/fuse/fuseutil/fuseutil.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
package fuseutil // import "bazil.org/fuse/fuseutil"
|
||||
|
||||
import (
|
||||
"bazil.org/fuse"
|
||||
)
|
||||
|
||||
// HandleRead handles a read request assuming that data is the entire file content.
|
||||
// It adjusts the amount returned in resp according to req.Offset and req.Size.
|
||||
func HandleRead(req *fuse.ReadRequest, resp *fuse.ReadResponse, data []byte) {
|
||||
if req.Offset >= int64(len(data)) {
|
||||
data = nil
|
||||
} else {
|
||||
data = data[req.Offset:]
|
||||
}
|
||||
if len(data) > req.Size {
|
||||
data = data[:req.Size]
|
||||
}
|
||||
n := copy(resp.Data[:req.Size], data)
|
||||
resp.Data = resp.Data[:n]
|
||||
}
|
38
vendor/bazil.org/fuse/mount.go
generated
vendored
Normal file
38
vendor/bazil.org/fuse/mount.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrOSXFUSENotFound is returned from Mount when the OSXFUSE
|
||||
// installation is not detected.
|
||||
//
|
||||
// Only happens on OS X. Make sure OSXFUSE is installed, or see
|
||||
// OSXFUSELocations for customization.
|
||||
ErrOSXFUSENotFound = errors.New("cannot locate OSXFUSE")
|
||||
)
|
||||
|
||||
func neverIgnoreLine(line string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func lineLogger(wg *sync.WaitGroup, prefix string, ignore func(line string) bool, r io.ReadCloser) {
|
||||
defer wg.Done()
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if ignore(line) {
|
||||
continue
|
||||
}
|
||||
log.Printf("%s: %s", prefix, line)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Printf("%s, error reading: %v", prefix, err)
|
||||
}
|
||||
}
|
208
vendor/bazil.org/fuse/mount_darwin.go
generated
vendored
Normal file
208
vendor/bazil.org/fuse/mount_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,208 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoAvail = errors.New("no available fuse devices")
|
||||
errNotLoaded = errors.New("osxfuse is not loaded")
|
||||
)
|
||||
|
||||
func loadOSXFUSE(bin string) error {
|
||||
cmd := exec.Command(bin)
|
||||
cmd.Dir = "/"
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
return err
|
||||
}
|
||||
|
||||
func openOSXFUSEDev(devPrefix string) (*os.File, error) {
|
||||
var f *os.File
|
||||
var err error
|
||||
for i := uint64(0); ; i++ {
|
||||
path := devPrefix + strconv.FormatUint(i, 10)
|
||||
f, err = os.OpenFile(path, os.O_RDWR, 0000)
|
||||
if os.IsNotExist(err) {
|
||||
if i == 0 {
|
||||
// not even the first device was found -> fuse is not loaded
|
||||
return nil, errNotLoaded
|
||||
}
|
||||
|
||||
// we've run out of kernel-provided devices
|
||||
return nil, errNoAvail
|
||||
}
|
||||
|
||||
if err2, ok := err.(*os.PathError); ok && err2.Err == syscall.EBUSY {
|
||||
// try the next one
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
|
||||
func handleMountOSXFUSE(helperName string, errCh chan<- error) func(line string) (ignore bool) {
|
||||
var noMountpointPrefix = helperName + `: `
|
||||
const noMountpointSuffix = `: No such file or directory`
|
||||
return func(line string) (ignore bool) {
|
||||
if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) {
|
||||
// re-extract it from the error message in case some layer
|
||||
// changed the path
|
||||
mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)]
|
||||
err := &MountpointDoesNotExistError{
|
||||
Path: mountpoint,
|
||||
}
|
||||
select {
|
||||
case errCh <- err:
|
||||
return true
|
||||
default:
|
||||
// not the first error; fall back to logging it
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// isBoringMountOSXFUSEError returns whether the Wait error is
|
||||
// uninteresting; exit status 64 is.
|
||||
func isBoringMountOSXFUSEError(err error) bool {
|
||||
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
||||
if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 64 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func callMount(bin string, daemonVar string, dir string, conf *mountConfig, f *os.File, ready chan<- struct{}, errp *error) error {
|
||||
for k, v := range conf.options {
|
||||
if strings.Contains(k, ",") || strings.Contains(v, ",") {
|
||||
// Silly limitation but the mount helper does not
|
||||
// understand any escaping. See TestMountOptionCommaError.
|
||||
return fmt.Errorf("mount options cannot contain commas on darwin: %q=%q", k, v)
|
||||
}
|
||||
}
|
||||
cmd := exec.Command(
|
||||
bin,
|
||||
"-o", conf.getOptions(),
|
||||
// Tell osxfuse-kext how large our buffer is. It must split
|
||||
// writes larger than this into multiple writes.
|
||||
//
|
||||
// OSXFUSE seems to ignore InitResponse.MaxWrite, and uses
|
||||
// this instead.
|
||||
"-o", "iosize="+strconv.FormatUint(maxWrite, 10),
|
||||
// refers to fd passed in cmd.ExtraFiles
|
||||
"3",
|
||||
dir,
|
||||
)
|
||||
cmd.ExtraFiles = []*os.File{f}
|
||||
cmd.Env = os.Environ()
|
||||
// OSXFUSE <3.3.0
|
||||
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
|
||||
// OSXFUSE >=3.3.0
|
||||
cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=")
|
||||
|
||||
daemon := os.Args[0]
|
||||
if daemonVar != "" {
|
||||
cmd.Env = append(cmd.Env, daemonVar+"="+daemon)
|
||||
}
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up mount_osxfusefs stderr: %v", err)
|
||||
}
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up mount_osxfusefs stderr: %v", err)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("mount_osxfusefs: %v", err)
|
||||
}
|
||||
helperErrCh := make(chan error, 1)
|
||||
go func() {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
|
||||
helperName := path.Base(bin)
|
||||
go lineLogger(&wg, "mount helper error", handleMountOSXFUSE(helperName, helperErrCh), stderr)
|
||||
wg.Wait()
|
||||
if err := cmd.Wait(); err != nil {
|
||||
// see if we have a better error to report
|
||||
select {
|
||||
case helperErr := <-helperErrCh:
|
||||
// log the Wait error if it's not what we expected
|
||||
if !isBoringMountOSXFUSEError(err) {
|
||||
log.Printf("mount helper failed: %v", err)
|
||||
}
|
||||
// and now return what we grabbed from stderr as the real
|
||||
// error
|
||||
*errp = helperErr
|
||||
close(ready)
|
||||
return
|
||||
default:
|
||||
// nope, fall back to generic message
|
||||
}
|
||||
|
||||
*errp = fmt.Errorf("mount_osxfusefs: %v", err)
|
||||
close(ready)
|
||||
return
|
||||
}
|
||||
|
||||
*errp = nil
|
||||
close(ready)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
|
||||
locations := conf.osxfuseLocations
|
||||
if locations == nil {
|
||||
locations = []OSXFUSEPaths{
|
||||
OSXFUSELocationV3,
|
||||
OSXFUSELocationV2,
|
||||
}
|
||||
}
|
||||
for _, loc := range locations {
|
||||
if _, err := os.Stat(loc.Mount); os.IsNotExist(err) {
|
||||
// try the other locations
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := openOSXFUSEDev(loc.DevicePrefix)
|
||||
if err == errNotLoaded {
|
||||
err = loadOSXFUSE(loc.Load)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// try again
|
||||
f, err = openOSXFUSEDev(loc.DevicePrefix)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = callMount(loc.Mount, loc.DaemonVar, dir, conf, f, ready, errp)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
return nil, ErrOSXFUSENotFound
|
||||
}
|
111
vendor/bazil.org/fuse/mount_freebsd.go
generated
vendored
Normal file
111
vendor/bazil.org/fuse/mount_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func handleMountFusefsStderr(errCh chan<- error) func(line string) (ignore bool) {
|
||||
return func(line string) (ignore bool) {
|
||||
const (
|
||||
noMountpointPrefix = `mount_fusefs: `
|
||||
noMountpointSuffix = `: No such file or directory`
|
||||
)
|
||||
if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) {
|
||||
// re-extract it from the error message in case some layer
|
||||
// changed the path
|
||||
mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)]
|
||||
err := &MountpointDoesNotExistError{
|
||||
Path: mountpoint,
|
||||
}
|
||||
select {
|
||||
case errCh <- err:
|
||||
return true
|
||||
default:
|
||||
// not the first error; fall back to logging it
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// isBoringMountFusefsError returns whether the Wait error is
|
||||
// uninteresting; exit status 1 is.
|
||||
func isBoringMountFusefsError(err error) bool {
|
||||
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
||||
if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
|
||||
for k, v := range conf.options {
|
||||
if strings.Contains(k, ",") || strings.Contains(v, ",") {
|
||||
// Silly limitation but the mount helper does not
|
||||
// understand any escaping. See TestMountOptionCommaError.
|
||||
return nil, fmt.Errorf("mount options cannot contain commas on FreeBSD: %q=%q", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0000)
|
||||
if err != nil {
|
||||
*errp = err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := exec.Command(
|
||||
"/sbin/mount_fusefs",
|
||||
"--safe",
|
||||
"-o", conf.getOptions(),
|
||||
"3",
|
||||
dir,
|
||||
)
|
||||
cmd.ExtraFiles = []*os.File{f}
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err)
|
||||
}
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("mount_fusefs: %v", err)
|
||||
}
|
||||
helperErrCh := make(chan error, 1)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
|
||||
go lineLogger(&wg, "mount helper error", handleMountFusefsStderr(helperErrCh), stderr)
|
||||
wg.Wait()
|
||||
if err := cmd.Wait(); err != nil {
|
||||
// see if we have a better error to report
|
||||
select {
|
||||
case helperErr := <-helperErrCh:
|
||||
// log the Wait error if it's not what we expected
|
||||
if !isBoringMountFusefsError(err) {
|
||||
log.Printf("mount helper failed: %v", err)
|
||||
}
|
||||
// and now return what we grabbed from stderr as the real
|
||||
// error
|
||||
return nil, helperErr
|
||||
default:
|
||||
// nope, fall back to generic message
|
||||
}
|
||||
return nil, fmt.Errorf("mount_fusefs: %v", err)
|
||||
}
|
||||
|
||||
close(ready)
|
||||
return f, nil
|
||||
}
|
150
vendor/bazil.org/fuse/mount_linux.go
generated
vendored
Normal file
150
vendor/bazil.org/fuse/mount_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func handleFusermountStderr(errCh chan<- error) func(line string) (ignore bool) {
|
||||
return func(line string) (ignore bool) {
|
||||
if line == `fusermount: failed to open /etc/fuse.conf: Permission denied` {
|
||||
// Silence this particular message, it occurs way too
|
||||
// commonly and isn't very relevant to whether the mount
|
||||
// succeeds or not.
|
||||
return true
|
||||
}
|
||||
|
||||
const (
|
||||
noMountpointPrefix = `fusermount: failed to access mountpoint `
|
||||
noMountpointSuffix = `: No such file or directory`
|
||||
)
|
||||
if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) {
|
||||
// re-extract it from the error message in case some layer
|
||||
// changed the path
|
||||
mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)]
|
||||
err := &MountpointDoesNotExistError{
|
||||
Path: mountpoint,
|
||||
}
|
||||
select {
|
||||
case errCh <- err:
|
||||
return true
|
||||
default:
|
||||
// not the first error; fall back to logging it
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// isBoringFusermountError returns whether the Wait error is
|
||||
// uninteresting; exit status 1 is.
|
||||
func isBoringFusermountError(err error) bool {
|
||||
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
||||
if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
|
||||
// linux mount is never delayed
|
||||
close(ready)
|
||||
|
||||
fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("socketpair error: %v", err)
|
||||
}
|
||||
|
||||
writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes")
|
||||
defer writeFile.Close()
|
||||
|
||||
readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads")
|
||||
defer readFile.Close()
|
||||
|
||||
cmd := exec.Command(
|
||||
"fusermount",
|
||||
"-o", conf.getOptions(),
|
||||
"--",
|
||||
dir,
|
||||
)
|
||||
cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3")
|
||||
|
||||
cmd.ExtraFiles = []*os.File{writeFile}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up fusermount stderr: %v", err)
|
||||
}
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up fusermount stderr: %v", err)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("fusermount: %v", err)
|
||||
}
|
||||
helperErrCh := make(chan error, 1)
|
||||
wg.Add(2)
|
||||
go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
|
||||
go lineLogger(&wg, "mount helper error", handleFusermountStderr(helperErrCh), stderr)
|
||||
wg.Wait()
|
||||
if err := cmd.Wait(); err != nil {
|
||||
// see if we have a better error to report
|
||||
select {
|
||||
case helperErr := <-helperErrCh:
|
||||
// log the Wait error if it's not what we expected
|
||||
if !isBoringFusermountError(err) {
|
||||
log.Printf("mount helper failed: %v", err)
|
||||
}
|
||||
// and now return what we grabbed from stderr as the real
|
||||
// error
|
||||
return nil, helperErr
|
||||
default:
|
||||
// nope, fall back to generic message
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("fusermount: %v", err)
|
||||
}
|
||||
|
||||
c, err := net.FileConn(readFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FileConn from fusermount socket: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
uc, ok := c.(*net.UnixConn)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected FileConn type; expected UnixConn, got %T", c)
|
||||
}
|
||||
|
||||
buf := make([]byte, 32) // expect 1 byte
|
||||
oob := make([]byte, 32) // expect 24 bytes
|
||||
_, oobn, _, _, err := uc.ReadMsgUnix(buf, oob)
|
||||
scms, err := syscall.ParseSocketControlMessage(oob[:oobn])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ParseSocketControlMessage: %v", err)
|
||||
}
|
||||
if len(scms) != 1 {
|
||||
return nil, fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms)
|
||||
}
|
||||
scm := scms[0]
|
||||
gotFds, err := syscall.ParseUnixRights(&scm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("syscall.ParseUnixRights: %v", err)
|
||||
}
|
||||
if len(gotFds) != 1 {
|
||||
return nil, fmt.Errorf("wanted 1 fd; got %#v", gotFds)
|
||||
}
|
||||
f := os.NewFile(uintptr(gotFds[0]), "/dev/fuse")
|
||||
return f, nil
|
||||
}
|
310
vendor/bazil.org/fuse/options.go
generated
vendored
Normal file
310
vendor/bazil.org/fuse/options.go
generated
vendored
Normal file
|
@ -0,0 +1,310 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func dummyOption(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// mountConfig holds the configuration for a mount operation.
|
||||
// Use it by passing MountOption values to Mount.
|
||||
type mountConfig struct {
|
||||
options map[string]string
|
||||
maxReadahead uint32
|
||||
initFlags InitFlags
|
||||
osxfuseLocations []OSXFUSEPaths
|
||||
}
|
||||
|
||||
func escapeComma(s string) string {
|
||||
s = strings.Replace(s, `\`, `\\`, -1)
|
||||
s = strings.Replace(s, `,`, `\,`, -1)
|
||||
return s
|
||||
}
|
||||
|
||||
// getOptions makes a string of options suitable for passing to FUSE
|
||||
// mount flag `-o`. Returns an empty string if no options were set.
|
||||
// Any platform specific adjustments should happen before the call.
|
||||
func (m *mountConfig) getOptions() string {
|
||||
var opts []string
|
||||
for k, v := range m.options {
|
||||
k = escapeComma(k)
|
||||
if v != "" {
|
||||
k += "=" + escapeComma(v)
|
||||
}
|
||||
opts = append(opts, k)
|
||||
}
|
||||
return strings.Join(opts, ",")
|
||||
}
|
||||
|
||||
type mountOption func(*mountConfig) error
|
||||
|
||||
// MountOption is passed to Mount to change the behavior of the mount.
|
||||
type MountOption mountOption
|
||||
|
||||
// FSName sets the file system name (also called source) that is
|
||||
// visible in the list of mounted file systems.
|
||||
//
|
||||
// FreeBSD ignores this option.
|
||||
func FSName(name string) MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["fsname"] = name
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Subtype sets the subtype of the mount. The main type is always
|
||||
// `fuse`. The type in a list of mounted file systems will look like
|
||||
// `fuse.foo`.
|
||||
//
|
||||
// OS X ignores this option.
|
||||
// FreeBSD ignores this option.
|
||||
func Subtype(fstype string) MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["subtype"] = fstype
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// LocalVolume sets the volume to be local (instead of network),
|
||||
// changing the behavior of Finder, Spotlight, and such.
|
||||
//
|
||||
// OS X only. Others ignore this option.
|
||||
func LocalVolume() MountOption {
|
||||
return localVolume
|
||||
}
|
||||
|
||||
// VolumeName sets the volume name shown in Finder.
|
||||
//
|
||||
// OS X only. Others ignore this option.
|
||||
func VolumeName(name string) MountOption {
|
||||
return volumeName(name)
|
||||
}
|
||||
|
||||
// NoAppleDouble makes OSXFUSE disallow files with names used by OS X
|
||||
// to store extended attributes on file systems that do not support
|
||||
// them natively.
|
||||
//
|
||||
// Such file names are:
|
||||
//
|
||||
// ._*
|
||||
// .DS_Store
|
||||
//
|
||||
// OS X only. Others ignore this option.
|
||||
func NoAppleDouble() MountOption {
|
||||
return noAppleDouble
|
||||
}
|
||||
|
||||
// NoAppleXattr makes OSXFUSE disallow extended attributes with the
|
||||
// prefix "com.apple.". This disables persistent Finder state and
|
||||
// other such information.
|
||||
//
|
||||
// OS X only. Others ignore this option.
|
||||
func NoAppleXattr() MountOption {
|
||||
return noAppleXattr
|
||||
}
|
||||
|
||||
// ExclCreate causes O_EXCL flag to be set for only "truly" exclusive creates,
|
||||
// i.e. create calls for which the initiator explicitly set the O_EXCL flag.
|
||||
//
|
||||
// OSXFUSE expects all create calls to return EEXIST in case the file
|
||||
// already exists, regardless of whether O_EXCL was specified or not.
|
||||
// To ensure this behavior, it normally sets OpenExclusive for all
|
||||
// Create calls, regardless of whether the original call had it set.
|
||||
// For distributed filesystems, that may force every file create to be
|
||||
// a distributed consensus action, causing undesirable delays.
|
||||
//
|
||||
// This option makes the FUSE filesystem see the original flag value,
|
||||
// and better decide when to ensure global consensus.
|
||||
//
|
||||
// Note that returning EEXIST on existing file create is still
|
||||
// expected with OSXFUSE, regardless of the presence of the
|
||||
// OpenExclusive flag.
|
||||
//
|
||||
// For more information, see
|
||||
// https://github.com/osxfuse/osxfuse/issues/209
|
||||
//
|
||||
// OS X only. Others ignore this options.
|
||||
// Requires OSXFUSE 3.4.1 or newer.
|
||||
func ExclCreate() MountOption {
|
||||
return exclCreate
|
||||
}
|
||||
|
||||
// DaemonTimeout sets the time in seconds between a request and a reply before
|
||||
// the FUSE mount is declared dead.
|
||||
//
|
||||
// OS X and FreeBSD only. Others ignore this option.
|
||||
func DaemonTimeout(name string) MountOption {
|
||||
return daemonTimeout(name)
|
||||
}
|
||||
|
||||
var ErrCannotCombineAllowOtherAndAllowRoot = errors.New("cannot combine AllowOther and AllowRoot")
|
||||
|
||||
// AllowOther allows other users to access the file system.
|
||||
//
|
||||
// Only one of AllowOther or AllowRoot can be used.
|
||||
func AllowOther() MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
if _, ok := conf.options["allow_root"]; ok {
|
||||
return ErrCannotCombineAllowOtherAndAllowRoot
|
||||
}
|
||||
conf.options["allow_other"] = ""
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AllowRoot allows other users to access the file system.
|
||||
//
|
||||
// Only one of AllowOther or AllowRoot can be used.
|
||||
//
|
||||
// FreeBSD ignores this option.
|
||||
func AllowRoot() MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
if _, ok := conf.options["allow_other"]; ok {
|
||||
return ErrCannotCombineAllowOtherAndAllowRoot
|
||||
}
|
||||
conf.options["allow_root"] = ""
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AllowDev enables interpreting character or block special devices on the
|
||||
// filesystem.
|
||||
func AllowDev() MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["dev"] = ""
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AllowSUID allows set-user-identifier or set-group-identifier bits to take
|
||||
// effect.
|
||||
func AllowSUID() MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["suid"] = ""
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultPermissions makes the kernel enforce access control based on
|
||||
// the file mode (as in chmod).
|
||||
//
|
||||
// Without this option, the Node itself decides what is and is not
|
||||
// allowed. This is normally ok because FUSE file systems cannot be
|
||||
// accessed by other users without AllowOther/AllowRoot.
|
||||
//
|
||||
// FreeBSD ignores this option.
|
||||
func DefaultPermissions() MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["default_permissions"] = ""
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ReadOnly makes the mount read-only.
|
||||
func ReadOnly() MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["ro"] = ""
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxReadahead sets the number of bytes that can be prefetched for
|
||||
// sequential reads. The kernel can enforce a maximum value lower than
|
||||
// this.
|
||||
//
|
||||
// This setting makes the kernel perform speculative reads that do not
|
||||
// originate from any client process. This usually tremendously
|
||||
// improves read performance.
|
||||
func MaxReadahead(n uint32) MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.maxReadahead = n
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AsyncRead enables multiple outstanding read requests for the same
|
||||
// handle. Without this, there is at most one request in flight at a
|
||||
// time.
|
||||
func AsyncRead() MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.initFlags |= InitAsyncRead
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WritebackCache enables the kernel to buffer writes before sending
|
||||
// them to the FUSE server. Without this, writethrough caching is
|
||||
// used.
|
||||
func WritebackCache() MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.initFlags |= InitWritebackCache
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OSXFUSEPaths describes the paths used by an installed OSXFUSE
|
||||
// version. See OSXFUSELocationV3 for typical values.
|
||||
type OSXFUSEPaths struct {
|
||||
// Prefix for the device file. At mount time, an incrementing
|
||||
// number is suffixed until a free FUSE device is found.
|
||||
DevicePrefix string
|
||||
// Path of the load helper, used to load the kernel extension if
|
||||
// no device files are found.
|
||||
Load string
|
||||
// Path of the mount helper, used for the actual mount operation.
|
||||
Mount string
|
||||
// Environment variable used to pass the path to the executable
|
||||
// calling the mount helper.
|
||||
DaemonVar string
|
||||
}
|
||||
|
||||
// Default paths for OSXFUSE. See OSXFUSELocations.
|
||||
var (
|
||||
OSXFUSELocationV3 = OSXFUSEPaths{
|
||||
DevicePrefix: "/dev/osxfuse",
|
||||
Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse",
|
||||
Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse",
|
||||
DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH",
|
||||
}
|
||||
OSXFUSELocationV2 = OSXFUSEPaths{
|
||||
DevicePrefix: "/dev/osxfuse",
|
||||
Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs",
|
||||
Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs",
|
||||
DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH",
|
||||
}
|
||||
)
|
||||
|
||||
// OSXFUSELocations sets where to look for OSXFUSE files. The
|
||||
// arguments are all the possible locations. The previous locations
|
||||
// are replaced.
|
||||
//
|
||||
// Without this option, OSXFUSELocationV3 and OSXFUSELocationV2 are
|
||||
// used.
|
||||
//
|
||||
// OS X only. Others ignore this option.
|
||||
func OSXFUSELocations(paths ...OSXFUSEPaths) MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
if len(paths) == 0 {
|
||||
return errors.New("must specify at least one location for OSXFUSELocations")
|
||||
}
|
||||
// replace previous values, but make a copy so there's no
|
||||
// worries about caller mutating their slice
|
||||
conf.osxfuseLocations = append(conf.osxfuseLocations[:0], paths...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AllowNonEmptyMount allows the mounting over a non-empty directory.
|
||||
//
|
||||
// The files in it will be shadowed by the freshly created mount. By
|
||||
// default these mounts are rejected to prevent accidental covering up
|
||||
// of data, which could for example prevent automatic backup.
|
||||
func AllowNonEmptyMount() MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["nonempty"] = ""
|
||||
return nil
|
||||
}
|
||||
}
|
64
vendor/bazil.org/fuse/options_daemon_timeout_test.go
generated
vendored
Normal file
64
vendor/bazil.org/fuse/options_daemon_timeout_test.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Test for adjustable timeout between a FUSE request and the daemon's response.
|
||||
//
|
||||
// +build darwin freebsd
|
||||
|
||||
package fuse_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"bazil.org/fuse/fs/fstestutil"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type slowCreaterDir struct {
|
||||
fstestutil.Dir
|
||||
}
|
||||
|
||||
var _ fs.NodeCreater = slowCreaterDir{}
|
||||
|
||||
func (c slowCreaterDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
|
||||
time.Sleep(10 * time.Second)
|
||||
// pick a really distinct error, to identify it later
|
||||
return nil, nil, fuse.Errno(syscall.ENAMETOOLONG)
|
||||
}
|
||||
|
||||
func TestMountOptionDaemonTimeout(t *testing.T) {
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "freebsd" {
|
||||
return
|
||||
}
|
||||
if testing.Short() {
|
||||
t.Skip("skipping time-based test in short mode")
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
mnt, err := fstestutil.MountedT(t,
|
||||
fstestutil.SimpleFS{slowCreaterDir{}},
|
||||
nil,
|
||||
fuse.DaemonTimeout("2"),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
// This should fail by the kernel timing out the request.
|
||||
f, err := os.Create(mnt.Dir + "/child")
|
||||
if err == nil {
|
||||
f.Close()
|
||||
t.Fatal("expected an error")
|
||||
}
|
||||
perr, ok := err.(*os.PathError)
|
||||
if !ok {
|
||||
t.Fatalf("expected PathError, got %T: %v", err, err)
|
||||
}
|
||||
if perr.Err == syscall.ENAMETOOLONG {
|
||||
t.Fatalf("expected other than ENAMETOOLONG, got %T: %v", err, err)
|
||||
}
|
||||
}
|
35
vendor/bazil.org/fuse/options_darwin.go
generated
vendored
Normal file
35
vendor/bazil.org/fuse/options_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
package fuse
|
||||
|
||||
func localVolume(conf *mountConfig) error {
|
||||
conf.options["local"] = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func volumeName(name string) MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["volname"] = name
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func daemonTimeout(name string) MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["daemon_timeout"] = name
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func noAppleXattr(conf *mountConfig) error {
|
||||
conf.options["noapplexattr"] = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func noAppleDouble(conf *mountConfig) error {
|
||||
conf.options["noappledouble"] = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func exclCreate(conf *mountConfig) error {
|
||||
conf.options["excl_create"] = ""
|
||||
return nil
|
||||
}
|
28
vendor/bazil.org/fuse/options_freebsd.go
generated
vendored
Normal file
28
vendor/bazil.org/fuse/options_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
package fuse
|
||||
|
||||
func localVolume(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func volumeName(name string) MountOption {
|
||||
return dummyOption
|
||||
}
|
||||
|
||||
func daemonTimeout(name string) MountOption {
|
||||
return func(conf *mountConfig) error {
|
||||
conf.options["timeout"] = name
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func noAppleXattr(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func noAppleDouble(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func exclCreate(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
10
vendor/bazil.org/fuse/options_helper_test.go
generated
vendored
Normal file
10
vendor/bazil.org/fuse/options_helper_test.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
package fuse
|
||||
|
||||
// for TestMountOptionCommaError
|
||||
func ForTestSetMountOption(k, v string) MountOption {
|
||||
fn := func(conf *mountConfig) error {
|
||||
conf.options[k] = v
|
||||
return nil
|
||||
}
|
||||
return fn
|
||||
}
|
25
vendor/bazil.org/fuse/options_linux.go
generated
vendored
Normal file
25
vendor/bazil.org/fuse/options_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
package fuse
|
||||
|
||||
func localVolume(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func volumeName(name string) MountOption {
|
||||
return dummyOption
|
||||
}
|
||||
|
||||
func daemonTimeout(name string) MountOption {
|
||||
return dummyOption
|
||||
}
|
||||
|
||||
func noAppleXattr(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func noAppleDouble(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func exclCreate(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
31
vendor/bazil.org/fuse/options_nocomma_test.go
generated
vendored
Normal file
31
vendor/bazil.org/fuse/options_nocomma_test.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
// This file contains tests for platforms that have no escape
|
||||
// mechanism for including commas in mount options.
|
||||
//
|
||||
// +build darwin
|
||||
|
||||
package fuse_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs/fstestutil"
|
||||
)
|
||||
|
||||
func TestMountOptionCommaError(t *testing.T) {
|
||||
t.Parallel()
|
||||
// this test is not tied to any specific option, it just needs
|
||||
// some string content
|
||||
var evil = "FuseTest,Marker"
|
||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
|
||||
fuse.ForTestSetMountOption("fusetest", evil),
|
||||
)
|
||||
if err == nil {
|
||||
mnt.Close()
|
||||
t.Fatal("expected an error about commas")
|
||||
}
|
||||
if g, e := err.Error(), `mount options cannot contain commas on `+runtime.GOOS+`: "fusetest"="FuseTest,Marker"`; g != e {
|
||||
t.Fatalf("wrong error: %q != %q", g, e)
|
||||
}
|
||||
}
|
231
vendor/bazil.org/fuse/options_test.go
generated
vendored
Normal file
231
vendor/bazil.org/fuse/options_test.go
generated
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
package fuse_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"bazil.org/fuse/fs/fstestutil"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fstestutil.DebugByDefault()
|
||||
}
|
||||
|
||||
func TestMountOptionFSName(t *testing.T) {
|
||||
if runtime.GOOS == "freebsd" {
|
||||
t.Skip("FreeBSD does not support FSName")
|
||||
}
|
||||
t.Parallel()
|
||||
const name = "FuseTestMarker"
|
||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
|
||||
fuse.FSName(name),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
info, err := fstestutil.GetMountInfo(mnt.Dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if g, e := info.FSName, name; g != e {
|
||||
t.Errorf("wrong FSName: %q != %q", g, e)
|
||||
}
|
||||
}
|
||||
|
||||
func testMountOptionFSNameEvil(t *testing.T, evil string) {
|
||||
if runtime.GOOS == "freebsd" {
|
||||
t.Skip("FreeBSD does not support FSName")
|
||||
}
|
||||
t.Parallel()
|
||||
var name = "FuseTest" + evil + "Marker"
|
||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
|
||||
fuse.FSName(name),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
info, err := fstestutil.GetMountInfo(mnt.Dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if g, e := info.FSName, name; g != e {
|
||||
t.Errorf("wrong FSName: %q != %q", g, e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountOptionFSNameEvilComma(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
// see TestMountOptionCommaError for a test that enforces we
|
||||
// at least give a nice error, instead of corrupting the mount
|
||||
// options
|
||||
t.Skip("TODO: OS X gets this wrong, commas in mount options cannot be escaped at all")
|
||||
}
|
||||
testMountOptionFSNameEvil(t, ",")
|
||||
}
|
||||
|
||||
func TestMountOptionFSNameEvilSpace(t *testing.T) {
|
||||
testMountOptionFSNameEvil(t, " ")
|
||||
}
|
||||
|
||||
func TestMountOptionFSNameEvilTab(t *testing.T) {
|
||||
testMountOptionFSNameEvil(t, "\t")
|
||||
}
|
||||
|
||||
func TestMountOptionFSNameEvilNewline(t *testing.T) {
|
||||
testMountOptionFSNameEvil(t, "\n")
|
||||
}
|
||||
|
||||
func TestMountOptionFSNameEvilBackslash(t *testing.T) {
|
||||
testMountOptionFSNameEvil(t, `\`)
|
||||
}
|
||||
|
||||
func TestMountOptionFSNameEvilBackslashDouble(t *testing.T) {
|
||||
// catch double-unescaping, if it were to happen
|
||||
testMountOptionFSNameEvil(t, `\\`)
|
||||
}
|
||||
|
||||
func TestMountOptionSubtype(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
t.Skip("OS X does not support Subtype")
|
||||
}
|
||||
if runtime.GOOS == "freebsd" {
|
||||
t.Skip("FreeBSD does not support Subtype")
|
||||
}
|
||||
t.Parallel()
|
||||
const name = "FuseTestMarker"
|
||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
|
||||
fuse.Subtype(name),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
info, err := fstestutil.GetMountInfo(mnt.Dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if g, e := info.Type, "fuse."+name; g != e {
|
||||
t.Errorf("wrong Subtype: %q != %q", g, e)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO test LocalVolume
|
||||
|
||||
// TODO test AllowOther; hard because needs system-level authorization
|
||||
|
||||
func TestMountOptionAllowOtherThenAllowRoot(t *testing.T) {
|
||||
t.Parallel()
|
||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
|
||||
fuse.AllowOther(),
|
||||
fuse.AllowRoot(),
|
||||
)
|
||||
if err == nil {
|
||||
mnt.Close()
|
||||
}
|
||||
if g, e := err, fuse.ErrCannotCombineAllowOtherAndAllowRoot; g != e {
|
||||
t.Fatalf("wrong error: %v != %v", g, e)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO test AllowRoot; hard because needs system-level authorization
|
||||
|
||||
func TestMountOptionAllowRootThenAllowOther(t *testing.T) {
|
||||
t.Parallel()
|
||||
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
|
||||
fuse.AllowRoot(),
|
||||
fuse.AllowOther(),
|
||||
)
|
||||
if err == nil {
|
||||
mnt.Close()
|
||||
}
|
||||
if g, e := err, fuse.ErrCannotCombineAllowOtherAndAllowRoot; g != e {
|
||||
t.Fatalf("wrong error: %v != %v", g, e)
|
||||
}
|
||||
}
|
||||
|
||||
type unwritableFile struct{}
|
||||
|
||||
func (f unwritableFile) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
a.Mode = 0000
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMountOptionDefaultPermissions(t *testing.T) {
|
||||
if runtime.GOOS == "freebsd" {
|
||||
t.Skip("FreeBSD does not support DefaultPermissions")
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
mnt, err := fstestutil.MountedT(t,
|
||||
fstestutil.SimpleFS{
|
||||
&fstestutil.ChildMap{"child": unwritableFile{}},
|
||||
},
|
||||
nil,
|
||||
fuse.DefaultPermissions(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
// This will be prevented by kernel-level access checking when
|
||||
// DefaultPermissions is used.
|
||||
f, err := os.OpenFile(mnt.Dir+"/child", os.O_WRONLY, 0000)
|
||||
if err == nil {
|
||||
f.Close()
|
||||
t.Fatal("expected an error")
|
||||
}
|
||||
if !os.IsPermission(err) {
|
||||
t.Fatalf("expected a permission error, got %T: %v", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
type createrDir struct {
|
||||
fstestutil.Dir
|
||||
}
|
||||
|
||||
var _ fs.NodeCreater = createrDir{}
|
||||
|
||||
func (createrDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
|
||||
// pick a really distinct error, to identify it later
|
||||
return nil, nil, fuse.Errno(syscall.ENAMETOOLONG)
|
||||
}
|
||||
|
||||
func TestMountOptionReadOnly(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mnt, err := fstestutil.MountedT(t,
|
||||
fstestutil.SimpleFS{createrDir{}},
|
||||
nil,
|
||||
fuse.ReadOnly(),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer mnt.Close()
|
||||
|
||||
// This will be prevented by kernel-level access checking when
|
||||
// ReadOnly is used.
|
||||
f, err := os.Create(mnt.Dir + "/child")
|
||||
if err == nil {
|
||||
f.Close()
|
||||
t.Fatal("expected an error")
|
||||
}
|
||||
perr, ok := err.(*os.PathError)
|
||||
if !ok {
|
||||
t.Fatalf("expected PathError, got %T: %v", err, err)
|
||||
}
|
||||
if perr.Err != syscall.EROFS {
|
||||
t.Fatalf("expected EROFS, got %T: %v", err, err)
|
||||
}
|
||||
}
|
75
vendor/bazil.org/fuse/protocol.go
generated
vendored
Normal file
75
vendor/bazil.org/fuse/protocol.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Protocol is a FUSE protocol version number.
|
||||
type Protocol struct {
|
||||
Major uint32
|
||||
Minor uint32
|
||||
}
|
||||
|
||||
func (p Protocol) String() string {
|
||||
return fmt.Sprintf("%d.%d", p.Major, p.Minor)
|
||||
}
|
||||
|
||||
// LT returns whether a is less than b.
|
||||
func (a Protocol) LT(b Protocol) bool {
|
||||
return a.Major < b.Major ||
|
||||
(a.Major == b.Major && a.Minor < b.Minor)
|
||||
}
|
||||
|
||||
// GE returns whether a is greater than or equal to b.
|
||||
func (a Protocol) GE(b Protocol) bool {
|
||||
return a.Major > b.Major ||
|
||||
(a.Major == b.Major && a.Minor >= b.Minor)
|
||||
}
|
||||
|
||||
func (a Protocol) is79() bool {
|
||||
return a.GE(Protocol{7, 9})
|
||||
}
|
||||
|
||||
// HasAttrBlockSize returns whether Attr.BlockSize is respected by the
|
||||
// kernel.
|
||||
func (a Protocol) HasAttrBlockSize() bool {
|
||||
return a.is79()
|
||||
}
|
||||
|
||||
// HasReadWriteFlags returns whether ReadRequest/WriteRequest
|
||||
// fields Flags and FileFlags are valid.
|
||||
func (a Protocol) HasReadWriteFlags() bool {
|
||||
return a.is79()
|
||||
}
|
||||
|
||||
// HasGetattrFlags returns whether GetattrRequest field Flags is
|
||||
// valid.
|
||||
func (a Protocol) HasGetattrFlags() bool {
|
||||
return a.is79()
|
||||
}
|
||||
|
||||
func (a Protocol) is710() bool {
|
||||
return a.GE(Protocol{7, 10})
|
||||
}
|
||||
|
||||
// HasOpenNonSeekable returns whether OpenResponse field Flags flag
|
||||
// OpenNonSeekable is supported.
|
||||
func (a Protocol) HasOpenNonSeekable() bool {
|
||||
return a.is710()
|
||||
}
|
||||
|
||||
func (a Protocol) is712() bool {
|
||||
return a.GE(Protocol{7, 12})
|
||||
}
|
||||
|
||||
// HasUmask returns whether CreateRequest/MkdirRequest/MknodRequest
|
||||
// field Umask is valid.
|
||||
func (a Protocol) HasUmask() bool {
|
||||
return a.is712()
|
||||
}
|
||||
|
||||
// HasInvalidate returns whether InvalidateNode/InvalidateEntry are
|
||||
// supported.
|
||||
func (a Protocol) HasInvalidate() bool {
|
||||
return a.is712()
|
||||
}
|
13
vendor/bazil.org/fuse/syscallx/doc.go
generated
vendored
Normal file
13
vendor/bazil.org/fuse/syscallx/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Package syscallx provides wrappers that make syscalls on various
|
||||
// platforms more interoperable.
|
||||
//
|
||||
// The API intentionally omits the OS X-specific position and option
|
||||
// arguments for extended attribute calls.
|
||||
//
|
||||
// Not having position means it might not be useful for accessing the
|
||||
// resource fork. If that's needed by code inside fuse, a function
|
||||
// with a different name may be added on the side.
|
||||
//
|
||||
// Options can be implemented with separate wrappers, in the style of
|
||||
// Linux getxattr/lgetxattr/fgetxattr.
|
||||
package syscallx // import "bazil.org/fuse/syscallx"
|
34
vendor/bazil.org/fuse/syscallx/generate
generated
vendored
Executable file
34
vendor/bazil.org/fuse/syscallx/generate
generated
vendored
Executable file
|
@ -0,0 +1,34 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
mksys="$(go env GOROOT)/src/pkg/syscall/mksyscall.pl"
|
||||
|
||||
fix() {
|
||||
sed 's,^package syscall$,&x\nimport "syscall",' \
|
||||
| gofmt -r='BytePtrFromString -> syscall.BytePtrFromString' \
|
||||
| gofmt -r='Syscall6 -> syscall.Syscall6' \
|
||||
| gofmt -r='Syscall -> syscall.Syscall' \
|
||||
| gofmt -r='SYS_GETXATTR -> syscall.SYS_GETXATTR' \
|
||||
| gofmt -r='SYS_LISTXATTR -> syscall.SYS_LISTXATTR' \
|
||||
| gofmt -r='SYS_SETXATTR -> syscall.SYS_SETXATTR' \
|
||||
| gofmt -r='SYS_REMOVEXATTR -> syscall.SYS_REMOVEXATTR' \
|
||||
| gofmt -r='SYS_MSYNC -> syscall.SYS_MSYNC'
|
||||
}
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
$mksys xattr_darwin.go \
|
||||
| fix \
|
||||
>xattr_darwin_amd64.go
|
||||
|
||||
$mksys -l32 xattr_darwin.go \
|
||||
| fix \
|
||||
>xattr_darwin_386.go
|
||||
|
||||
$mksys msync.go \
|
||||
| fix \
|
||||
>msync_amd64.go
|
||||
|
||||
$mksys -l32 msync.go \
|
||||
| fix \
|
||||
>msync_386.go
|
9
vendor/bazil.org/fuse/syscallx/msync.go
generated
vendored
Normal file
9
vendor/bazil.org/fuse/syscallx/msync.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
package syscallx
|
||||
|
||||
/* This is the source file for msync_*.go, to regenerate run
|
||||
|
||||
./generate
|
||||
|
||||
*/
|
||||
|
||||
//sys Msync(b []byte, flags int) (err error)
|
24
vendor/bazil.org/fuse/syscallx/msync_386.go
generated
vendored
Normal file
24
vendor/bazil.org/fuse/syscallx/msync_386.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
// mksyscall.pl -l32 msync.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package syscallx
|
||||
|
||||
import "syscall"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Msync(b []byte, flags int) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(b) > 0 {
|
||||
_p0 = unsafe.Pointer(&b[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_MSYNC, uintptr(_p0), uintptr(len(b)), uintptr(flags))
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
24
vendor/bazil.org/fuse/syscallx/msync_amd64.go
generated
vendored
Normal file
24
vendor/bazil.org/fuse/syscallx/msync_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
// mksyscall.pl msync.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package syscallx
|
||||
|
||||
import "syscall"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Msync(b []byte, flags int) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(b) > 0 {
|
||||
_p0 = unsafe.Pointer(&b[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_MSYNC, uintptr(_p0), uintptr(len(b)), uintptr(flags))
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
4
vendor/bazil.org/fuse/syscallx/syscallx.go
generated
vendored
Normal file
4
vendor/bazil.org/fuse/syscallx/syscallx.go
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
package syscallx
|
||||
|
||||
// make us look more like package syscall, so mksyscall.pl output works
|
||||
var _zero uintptr
|
26
vendor/bazil.org/fuse/syscallx/syscallx_std.go
generated
vendored
Normal file
26
vendor/bazil.org/fuse/syscallx/syscallx_std.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
// +build !darwin
|
||||
|
||||
package syscallx
|
||||
|
||||
// This file just contains wrappers for platforms that already have
|
||||
// the right stuff in golang.org/x/sys/unix.
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
|
||||
return unix.Getxattr(path, attr, dest)
|
||||
}
|
||||
|
||||
func Listxattr(path string, dest []byte) (sz int, err error) {
|
||||
return unix.Listxattr(path, dest)
|
||||
}
|
||||
|
||||
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||
return unix.Setxattr(path, attr, data, flags)
|
||||
}
|
||||
|
||||
func Removexattr(path string, attr string) (err error) {
|
||||
return unix.Removexattr(path, attr)
|
||||
}
|
38
vendor/bazil.org/fuse/syscallx/xattr_darwin.go
generated
vendored
Normal file
38
vendor/bazil.org/fuse/syscallx/xattr_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
package syscallx
|
||||
|
||||
/* This is the source file for syscallx_darwin_*.go, to regenerate run
|
||||
|
||||
./generate
|
||||
|
||||
*/
|
||||
|
||||
// cannot use dest []byte here because OS X getxattr really wants a
|
||||
// NULL to trigger size probing, size==0 is not enough
|
||||
//
|
||||
//sys getxattr(path string, attr string, dest *byte, size int, position uint32, options int) (sz int, err error)
|
||||
|
||||
func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
|
||||
var destp *byte
|
||||
if len(dest) > 0 {
|
||||
destp = &dest[0]
|
||||
}
|
||||
return getxattr(path, attr, destp, len(dest), 0, 0)
|
||||
}
|
||||
|
||||
//sys listxattr(path string, dest []byte, options int) (sz int, err error)
|
||||
|
||||
func Listxattr(path string, dest []byte) (sz int, err error) {
|
||||
return listxattr(path, dest, 0)
|
||||
}
|
||||
|
||||
//sys setxattr(path string, attr string, data []byte, position uint32, flags int) (err error)
|
||||
|
||||
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||
return setxattr(path, attr, data, 0, flags)
|
||||
}
|
||||
|
||||
//sys removexattr(path string, attr string, options int) (err error)
|
||||
|
||||
func Removexattr(path string, attr string) (err error) {
|
||||
return removexattr(path, attr, 0)
|
||||
}
|
97
vendor/bazil.org/fuse/syscallx/xattr_darwin_386.go
generated
vendored
Normal file
97
vendor/bazil.org/fuse/syscallx/xattr_darwin_386.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
// mksyscall.pl -l32 xattr_darwin.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package syscallx
|
||||
|
||||
import "syscall"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func getxattr(path string, attr string, dest *byte, size int, position uint32, options int) (sz int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = syscall.BytePtrFromString(attr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(dest)), uintptr(size), uintptr(position), uintptr(options))
|
||||
sz = int(r0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func listxattr(path string, dest []byte, options int) (sz int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(dest) > 0 {
|
||||
_p1 = unsafe.Pointer(&dest[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall6(syscall.SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)), uintptr(options), 0, 0)
|
||||
sz = int(r0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func setxattr(path string, attr string, data []byte, position uint32, flags int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = syscall.BytePtrFromString(attr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p2 unsafe.Pointer
|
||||
if len(data) > 0 {
|
||||
_p2 = unsafe.Pointer(&data[0])
|
||||
} else {
|
||||
_p2 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
_, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(position), uintptr(flags))
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func removexattr(path string, attr string, options int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = syscall.BytePtrFromString(attr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options))
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
97
vendor/bazil.org/fuse/syscallx/xattr_darwin_amd64.go
generated
vendored
Normal file
97
vendor/bazil.org/fuse/syscallx/xattr_darwin_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
// mksyscall.pl xattr_darwin.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package syscallx
|
||||
|
||||
import "syscall"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func getxattr(path string, attr string, dest *byte, size int, position uint32, options int) (sz int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = syscall.BytePtrFromString(attr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(dest)), uintptr(size), uintptr(position), uintptr(options))
|
||||
sz = int(r0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func listxattr(path string, dest []byte, options int) (sz int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(dest) > 0 {
|
||||
_p1 = unsafe.Pointer(&dest[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall6(syscall.SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)), uintptr(options), 0, 0)
|
||||
sz = int(r0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func setxattr(path string, attr string, data []byte, position uint32, flags int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = syscall.BytePtrFromString(attr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p2 unsafe.Pointer
|
||||
if len(data) > 0 {
|
||||
_p2 = unsafe.Pointer(&data[0])
|
||||
} else {
|
||||
_p2 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
_, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(position), uintptr(flags))
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func removexattr(path string, attr string, options int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = syscall.BytePtrFromString(attr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options))
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
6
vendor/bazil.org/fuse/unmount.go
generated
vendored
Normal file
6
vendor/bazil.org/fuse/unmount.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
package fuse
|
||||
|
||||
// Unmount tries to unmount the filesystem mounted at dir.
|
||||
func Unmount(dir string) error {
|
||||
return unmount(dir)
|
||||
}
|
21
vendor/bazil.org/fuse/unmount_linux.go
generated
vendored
Normal file
21
vendor/bazil.org/fuse/unmount_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func unmount(dir string) error {
|
||||
cmd := exec.Command("fusermount", "-u", dir)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if len(output) > 0 {
|
||||
output = bytes.TrimRight(output, "\n")
|
||||
msg := err.Error() + ": " + string(output)
|
||||
err = errors.New(msg)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
17
vendor/bazil.org/fuse/unmount_std.go
generated
vendored
Normal file
17
vendor/bazil.org/fuse/unmount_std.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
// +build !linux
|
||||
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func unmount(dir string) error {
|
||||
err := syscall.Unmount(dir, 0)
|
||||
if err != nil {
|
||||
err = &os.PathError{Op: "unmount", Path: dir, Err: err}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
26
vendor/github.com/elithrar/simple-scrypt/.gitignore
generated
vendored
Normal file
26
vendor/github.com/elithrar/simple-scrypt/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
*.DS_Store
|
15
vendor/github.com/elithrar/simple-scrypt/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/elithrar/simple-scrypt/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- tip
|
||||
install:
|
||||
- go get golang.org/x/tools/cmd/vet
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d -s .)
|
||||
- go tool vet .
|
||||
- go test -v ./...
|
22
vendor/github.com/elithrar/simple-scrypt/LICENSE
generated
vendored
Normal file
22
vendor/github.com/elithrar/simple-scrypt/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Matthew Silverlock (matt@eatsleeprepeat.net)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
110
vendor/github.com/elithrar/simple-scrypt/README.md
generated
vendored
Normal file
110
vendor/github.com/elithrar/simple-scrypt/README.md
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
# simple-scrypt
|
||||
[![GoDoc](https://godoc.org/github.com/elithrar/simple-scrypt?status.svg)](https://godoc.org/github.com/elithrar/simple-scrypt) [![Build Status](https://travis-ci.org/elithrar/simple-scrypt.svg?branch=master)](https://travis-ci.org/elithrar/simple-scrypt)
|
||||
|
||||
simple-scrypt provides a convenience wrapper around Go's existing
|
||||
[scrypt](http://golang.org/x/crypto/scrypt) package that makes it easier to
|
||||
securely derive strong keys ("hash user passwords"). This library allows you to:
|
||||
|
||||
* Generate a scrypt derived key with a crytographically secure salt and sane
|
||||
default parameters for N, r and p.
|
||||
* Upgrade the parameters used to generate keys as hardware improves by storing
|
||||
them with the derived key (the scrypt spec. doesn't allow for this by
|
||||
default).
|
||||
* Provide your own parameters (if you wish to).
|
||||
|
||||
The API closely mirrors Go's [bcrypt](https://golang.org/x/crypto/bcrypt)
|
||||
library in an effort to make it easy to migrate—and because it's an easy to grok
|
||||
API.
|
||||
|
||||
## Example
|
||||
|
||||
simple-scrypt doesn't try to re-invent the wheel or do anything "special". It
|
||||
wraps the `scrypt.Key` function as thinly as possible, generates a
|
||||
crytographically secure salt for you using Go's `crypto/rand` package, and
|
||||
returns the derived key with the parameters prepended:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import(
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/elithrar/simple-scrypt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// e.g. r.PostFormValue("password")
|
||||
passwordFromForm := "prew8fid9hick6c"
|
||||
|
||||
// Generates a derived key of the form "N$r$p$salt$dk" where N, r and p are defined as per
|
||||
// Colin Percival's scrypt paper: http://www.tarsnap.com/scrypt/scrypt.pdf
|
||||
// scrypt.Defaults (N=16384, r=8, p=1) makes it easy to provide these parameters, and
|
||||
// (should you wish) provide your own values via the scrypt.Params type.
|
||||
hash, err := scrypt.GenerateFromPassword([]byte(passwordFromForm), scrypt.DefaultParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print the derived key with its parameters prepended.
|
||||
fmt.Printf("%s\n", hash)
|
||||
|
||||
// Uses the parameters from the existing derived key. Return an error if they don't match.
|
||||
err := scrypt.CompareHashAndPassword(hash, []byte(passwordFromForm))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Upgrading Parameters
|
||||
|
||||
Upgrading derived keys from a set of parameters to a "stronger" set of parameters
|
||||
as hardware improves, or as you scale (and move your auth process to separate
|
||||
hardware), can be pretty useful. Here's how to do it with simple-scrypt:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// SCENE: We've successfully authenticated a user, compared their submitted
|
||||
// (cleartext) password against the derived key stored in our database, and
|
||||
// now want to upgrade the parameters (more rounds, more parallelism) to
|
||||
// reflect some shiny new hardware we just purchased. As the user is logging
|
||||
// in, we can retrieve the parameters used to generate their key, and if
|
||||
// they don't match our "new" parameters, we can re-generate the key while
|
||||
// we still have the cleartext password in memory
|
||||
// (e.g. before the HTTP request ends).
|
||||
current, err := scrypt.Cost(hash)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Now to check them against our own Params struct (e.g. using reflect.DeepEquals)
|
||||
// and determine whether we want to generate a new key with our "upgraded" parameters.
|
||||
slower := scrypt.Params{
|
||||
N: 32768,
|
||||
R: 8,
|
||||
P: 2,
|
||||
SaltLen: 16,
|
||||
DKLen: 32,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(current, slower) {
|
||||
// Re-generate the key with the slower parameters
|
||||
// here using scrypt.GenerateFromPassword
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## TO-DO:
|
||||
|
||||
The following features are planned. PRs are welcome.
|
||||
|
||||
- [x] Tag a release build.
|
||||
- [x] Automatically calculate "optimal" values for N, r, p similar [to the Ruby scrypt library](https://github.com/pbhogan/scrypt/blob/master/lib/scrypt.rb#L97-L146)
|
||||
e.g. `func Calibrate(duration int, mem int, fallback Params) (Params, error)`
|
||||
- contributed thanks to @tgulacsi.
|
||||
|
||||
## License
|
||||
|
||||
MIT Licensed. See LICENSE file for details.
|
||||
|
295
vendor/github.com/elithrar/simple-scrypt/scrypt.go
generated
vendored
Normal file
295
vendor/github.com/elithrar/simple-scrypt/scrypt.go
generated
vendored
Normal file
|
@ -0,0 +1,295 @@
|
|||
// Package scrypt provides a convenience wrapper around Go's existing scrypt package
|
||||
// that makes it easier to securely derive strong keys from weak
|
||||
// inputs (i.e. user passwords).
|
||||
// The package provides password generation, constant-time comparison and
|
||||
// parameter upgrading for scrypt derived keys.
|
||||
package scrypt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
// Constants
|
||||
const (
|
||||
maxInt = 1<<31 - 1
|
||||
minDKLen = 16 // the minimum derived key length in bytes.
|
||||
minSaltLen = 8 // the minimum allowed salt length in bytes.
|
||||
)
|
||||
|
||||
// Params describes the input parameters to the scrypt
|
||||
// key derivation function as per Colin Percival's scrypt
|
||||
// paper: http://www.tarsnap.com/scrypt/scrypt.pdf
|
||||
type Params struct {
|
||||
N int // CPU/memory cost parameter (logN)
|
||||
R int // block size parameter (octets)
|
||||
P int // parallelisation parameter (positive int)
|
||||
SaltLen int // bytes to use as salt (octets)
|
||||
DKLen int // length of the derived key (octets)
|
||||
}
|
||||
|
||||
// DefaultParams provides sensible default inputs into the scrypt function
|
||||
// for interactive use (i.e. web applications).
|
||||
// These defaults will consume approxmiately 16MB of memory (128 * r * N).
|
||||
// The default key length is 256 bits.
|
||||
var DefaultParams = Params{N: 16384, R: 8, P: 1, SaltLen: 16, DKLen: 32}
|
||||
|
||||
// ErrInvalidHash is returned when failing to parse a provided scrypt
|
||||
// hash and/or parameters.
|
||||
var ErrInvalidHash = errors.New("scrypt: the provided hash is not in the correct format")
|
||||
|
||||
// ErrInvalidParams is returned when the cost parameters (N, r, p), salt length
|
||||
// or derived key length are invalid.
|
||||
var ErrInvalidParams = errors.New("scrypt: the parameters provided are invalid")
|
||||
|
||||
// ErrMismatchedHashAndPassword is returned when a password (hashed) and
|
||||
// given hash do not match.
|
||||
var ErrMismatchedHashAndPassword = errors.New("scrypt: the hashed password does not match the hash of the given password")
|
||||
|
||||
// GenerateRandomBytes returns securely generated random bytes.
|
||||
// It will return an error if the system's secure random
|
||||
// number generator fails to function correctly, in which
|
||||
// case the caller should not continue.
|
||||
func GenerateRandomBytes(n int) ([]byte, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
// err == nil only if len(b) == n
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// GenerateFromPassword returns the derived key of the password using the
|
||||
// parameters provided. The parameters are prepended to the derived key and
|
||||
// separated by the "$" character (0x24).
|
||||
// If the parameters provided are less than the minimum acceptable values,
|
||||
// an error will be returned.
|
||||
func GenerateFromPassword(password []byte, params Params) ([]byte, error) {
|
||||
salt, err := GenerateRandomBytes(params.SaltLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := params.Check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// scrypt.Key returns the raw scrypt derived key.
|
||||
dk, err := scrypt.Key(password, salt, params.N, params.R, params.P, params.DKLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prepend the params and the salt to the derived key, each separated
|
||||
// by a "$" character. The salt and the derived key are hex encoded.
|
||||
return []byte(fmt.Sprintf("%d$%d$%d$%x$%x", params.N, params.R, params.P, salt, dk)), nil
|
||||
}
|
||||
|
||||
// CompareHashAndPassword compares a derived key with the possible cleartext
|
||||
// equivalent. The parameters used in the provided derived key are used.
|
||||
// The comparison performed by this function is constant-time. It returns nil
|
||||
// on success, and an error if the derived keys do not match.
|
||||
func CompareHashAndPassword(hash []byte, password []byte) error {
|
||||
// Decode existing hash, retrieve params and salt.
|
||||
params, salt, dk, err := decodeHash(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// scrypt the cleartext password with the same parameters and salt
|
||||
other, err := scrypt.Key(password, salt, params.N, params.R, params.P, params.DKLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Constant time comparison
|
||||
if subtle.ConstantTimeCompare(dk, other) == 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrMismatchedHashAndPassword
|
||||
}
|
||||
|
||||
// Check checks that the parameters are valid for input into the
|
||||
// scrypt key derivation function.
|
||||
func (p *Params) Check() error {
|
||||
// Validate N
|
||||
if p.N > maxInt || p.N <= 1 || p.N%2 != 0 {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate r
|
||||
if p.R < 1 || p.R > maxInt {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate p
|
||||
if p.P < 1 || p.P > maxInt {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate that r & p don't exceed 2^30 and that N, r, p values don't
|
||||
// exceed the limits defined by the scrypt algorithm.
|
||||
if uint64(p.R)*uint64(p.P) >= 1<<30 || p.R > maxInt/128/p.P || p.R > maxInt/256 || p.N > maxInt/128/p.R {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate the salt length
|
||||
if p.SaltLen < minSaltLen || p.SaltLen > maxInt {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate the derived key length
|
||||
if p.DKLen < minDKLen || p.DKLen > maxInt {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeHash extracts the parameters, salt and derived key from the
|
||||
// provided hash. It returns an error if the hash format is invalid and/or
|
||||
// the parameters are invalid.
|
||||
func decodeHash(hash []byte) (Params, []byte, []byte, error) {
|
||||
vals := strings.Split(string(hash), "$")
|
||||
|
||||
// P, N, R, salt, scrypt derived key
|
||||
if len(vals) != 5 {
|
||||
return Params{}, nil, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
var params Params
|
||||
var err error
|
||||
|
||||
params.N, err = strconv.Atoi(vals[0])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
params.R, err = strconv.Atoi(vals[1])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
params.P, err = strconv.Atoi(vals[2])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
salt, err := hex.DecodeString(vals[3])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
params.SaltLen = len(salt)
|
||||
|
||||
dk, err := hex.DecodeString(vals[4])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
params.DKLen = len(dk)
|
||||
|
||||
if err := params.Check(); err != nil {
|
||||
return params, nil, nil, err
|
||||
}
|
||||
|
||||
return params, salt, dk, nil
|
||||
}
|
||||
|
||||
// Cost returns the scrypt parameters used to generate the derived key. This
|
||||
// allows a package user to increase the cost (in time & resources) used as
|
||||
// computational performance increases over time.
|
||||
func Cost(hash []byte) (Params, error) {
|
||||
params, _, _, err := decodeHash(hash)
|
||||
|
||||
return params, err
|
||||
}
|
||||
|
||||
// Calibrate returns the hardest parameters (not weaker than the given params),
|
||||
// allowed by the given limits.
|
||||
// The returned params will not use more memory than the given (MiB);
|
||||
// will not take more time than the given timeout, but more than timeout/2.
|
||||
//
|
||||
//
|
||||
// The default timeout (when the timeout arg is zero) is 200ms.
|
||||
// The default memMiBytes (when memMiBytes is zero) is 16MiB.
|
||||
// The default parameters (when params == Params{}) is DefaultParams.
|
||||
func Calibrate(timeout time.Duration, memMiBytes int, params Params) (Params, error) {
|
||||
p := params
|
||||
if p.N == 0 || p.R == 0 || p.P == 0 || p.SaltLen == 0 || p.DKLen == 0 {
|
||||
p = DefaultParams
|
||||
} else if err := p.Check(); err != nil {
|
||||
return p, err
|
||||
}
|
||||
if timeout == 0 {
|
||||
timeout = 200 * time.Millisecond
|
||||
}
|
||||
if memMiBytes == 0 {
|
||||
memMiBytes = 16
|
||||
}
|
||||
salt, err := GenerateRandomBytes(p.SaltLen)
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
password := []byte("weakpassword")
|
||||
|
||||
// First, we calculate the minimal required time.
|
||||
start := time.Now()
|
||||
if _, err := scrypt.Key(password, salt, p.N, p.R, p.P, p.DKLen); err != nil {
|
||||
return p, err
|
||||
}
|
||||
dur := time.Since(start)
|
||||
|
||||
for dur < timeout && p.N < maxInt>>1 {
|
||||
p.N <<= 1
|
||||
}
|
||||
|
||||
// Memory usage is at least 128 * r * N, see
|
||||
// http://blog.ircmaxell.com/2014/03/why-i-dont-recommend-scrypt.html
|
||||
// or https://drupal.org/comment/4675994#comment-4675994
|
||||
|
||||
var again bool
|
||||
memBytes := memMiBytes << 20
|
||||
// If we'd use more memory then the allowed, we can tune the memory usage
|
||||
for 128*p.R*p.N > memBytes {
|
||||
if p.R > 1 {
|
||||
// by lowering r
|
||||
p.R--
|
||||
} else if p.N > 16 {
|
||||
again = true
|
||||
p.N >>= 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !again {
|
||||
return p, p.Check()
|
||||
}
|
||||
|
||||
// We have to compensate the lowering of N, by increasing p.
|
||||
for i := 0; i < 10 && p.P > 0; i++ {
|
||||
start := time.Now()
|
||||
if _, err := scrypt.Key(password, salt, p.N, p.R, p.P, p.DKLen); err != nil {
|
||||
return p, err
|
||||
}
|
||||
dur := time.Since(start)
|
||||
if dur < timeout/2 {
|
||||
p.P = int(float64(p.P)*float64(timeout/dur) + 1)
|
||||
} else if dur > timeout && p.P > 1 {
|
||||
p.P--
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return p, p.Check()
|
||||
}
|
156
vendor/github.com/elithrar/simple-scrypt/scrypt_test.go
generated
vendored
Normal file
156
vendor/github.com/elithrar/simple-scrypt/scrypt_test.go
generated
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
package scrypt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Test cases
|
||||
var (
|
||||
testLengths = []int{1, 8, 16, 32, 100, 500, 2500}
|
||||
password = "super-secret-password"
|
||||
)
|
||||
|
||||
var testParams = []struct {
|
||||
pass bool
|
||||
params Params
|
||||
}{
|
||||
{true, Params{16384, 8, 1, 32, 64}},
|
||||
{true, Params{16384, 8, 1, 16, 32}},
|
||||
{true, Params{65536, 8, 1, 16, 64}},
|
||||
{true, Params{1048576, 8, 2, 64, 128}},
|
||||
{false, Params{-1, 8, 1, 16, 32}}, // invalid N
|
||||
{false, Params{0, 8, 1, 16, 32}}, // invalid N
|
||||
{false, Params{1 << 31, 8, 1, 16, 32}}, // invalid N
|
||||
{false, Params{16384, 0, 12, 16, 32}}, // invalid R
|
||||
{false, Params{16384, 8, 0, 16, 32}}, // invalid R > maxInt/128/P
|
||||
{false, Params{16384, 1 << 24, 1, 16, 32}}, // invalid R > maxInt/256
|
||||
{false, Params{1 << 31, 8, 0, 16, 32}}, // invalid p < 0
|
||||
{false, Params{4096, 8, 1, 5, 32}}, // invalid SaltLen
|
||||
{false, Params{4096, 8, 1, 16, 2}}, // invalid DKLen
|
||||
}
|
||||
|
||||
var testHashes = []struct {
|
||||
pass bool
|
||||
hash string
|
||||
}{
|
||||
{false, "1$8$1$9003d0e8e69482843e6bd560c2c9cd94$1976f233124e0ee32bb2678eb1b0ed668eb66cff6fa43279d1e33f6e81af893b"}, // N too small
|
||||
{false, "$9003d0e8e69482843e6bd560c2c9cd94$1976f233124e0ee32bb2678eb1b0ed668eb66cff6fa43279d1e33f6e81af893b"}, // too short
|
||||
{false, "16384#8#1#18fbc325efa37402d27c3c2172900cbf$d4e5e1b9eedc1a6a14aad6624ab57b7b42ae75b9c9845fde32de765835f2aaf9"}, // incorrect separators
|
||||
{false, "16384$nogood$1$18fbc325efa37402d27c3c2172900cbf$d4e5e1b9eedc1a6a14aad6624ab57b7b42ae75b9c9845fde32de765835f2aaf9"}, // invalid R
|
||||
{false, "16384$8$abc1$18fbc325efa37402d27c3c2172900cbf$d4e5e1b9eedc1a6a14aad6624ab57b7b42ae75b9c9845fde32de765835f2aaf9"}, // invalid P
|
||||
{false, "16384$8$1$Tk9QRQ==$d4e5e1b9eedc1a6a14aad6624ab57b7b42ae75b9c9845fde32de765835f2aaf9"}, // invalid salt (not hex)
|
||||
{false, "16384$8$1$18fbc325efa37402d27c3c2172900cbf$42ae====/75b9c9845fde32de765835f2aaf9"}, // invalid dk (not hex)
|
||||
}
|
||||
|
||||
func TestGenerateRandomBytes(t *testing.T) {
|
||||
for _, v := range testLengths {
|
||||
_, err := GenerateRandomBytes(v)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate random bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateFromPassword(t *testing.T) {
|
||||
for _, v := range testParams {
|
||||
_, err := GenerateFromPassword([]byte(password), v.params)
|
||||
if err != nil && v.pass == true {
|
||||
t.Fatalf("no error was returned when expected for params: %+v", v.params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareHashAndPassword(t *testing.T) {
|
||||
hash, err := GenerateFromPassword([]byte(password), DefaultParams)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := CompareHashAndPassword(hash, []byte(password)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := CompareHashAndPassword(hash, []byte("invalid-password")); err == nil {
|
||||
t.Fatalf("mismatched passwords did not produce an error")
|
||||
}
|
||||
|
||||
invalidHash := []byte("$166$$11$a2ad56a415af5")
|
||||
if err := CompareHashAndPassword(invalidHash, []byte(password)); err == nil {
|
||||
t.Fatalf("did not identify an invalid hash")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCost(t *testing.T) {
|
||||
hash, err := GenerateFromPassword([]byte(password), DefaultParams)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
params, err := Cost(hash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(params, DefaultParams) {
|
||||
t.Fatal("cost mismatch: parameters used did not match those retrieved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeHash(t *testing.T) {
|
||||
for _, v := range testHashes {
|
||||
_, err := Cost([]byte(v.hash))
|
||||
if err == nil && v.pass == false {
|
||||
t.Fatal("invalid hash: did not correctly detect invalid password hash")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalibrate(t *testing.T) {
|
||||
timeout := 500 * time.Millisecond
|
||||
for testNum, tc := range []struct {
|
||||
MemMiB int
|
||||
}{
|
||||
{64},
|
||||
{32},
|
||||
{16},
|
||||
{8},
|
||||
{1},
|
||||
} {
|
||||
var (
|
||||
p Params
|
||||
err error
|
||||
)
|
||||
p, err = Calibrate(timeout, tc.MemMiB, p)
|
||||
if err != nil {
|
||||
t.Fatalf("%d. %#v: %v", testNum, p, err)
|
||||
}
|
||||
if (128*p.R*p.N)>>20 > tc.MemMiB {
|
||||
t.Errorf("%d. wanted memory limit %d, got %d.", testNum, tc.MemMiB, (128*p.R*p.N)>>20)
|
||||
}
|
||||
start := time.Now()
|
||||
_, err = GenerateFromPassword([]byte(password), p)
|
||||
dur := time.Since(start)
|
||||
t.Logf("GenerateFromPassword with %#v took %s (%v)", p, dur, err)
|
||||
if err != nil {
|
||||
t.Fatalf("%d. GenerateFromPassword with %#v: %v", testNum, p, err)
|
||||
}
|
||||
if dur < timeout/2 {
|
||||
t.Errorf("%d. GenerateFromPassword was too fast (wanted around %s, got %s) with %#v.", testNum, timeout, dur, p)
|
||||
} else if timeout*2 < dur {
|
||||
t.Errorf("%d. GenerateFromPassword took too long (wanted around %s, got %s) with %#v.", testNum, timeout, dur, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleCalibrate() {
|
||||
p, err := Calibrate(1*time.Second, 128, Params{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dk, err := GenerateFromPassword([]byte("super-secret-password"), p)
|
||||
fmt.Printf("generated password is %q (%v)", dk, err)
|
||||
}
|
5
vendor/github.com/go-ini/ini/.gitignore
generated
vendored
Normal file
5
vendor/github.com/go-ini/ini/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
testdata/conf_out.ini
|
||||
ini.sublime-project
|
||||
ini.sublime-workspace
|
||||
testdata/conf_reflect.ini
|
||||
.idea
|
14
vendor/github.com/go-ini/ini/.travis.yml
generated
vendored
Normal file
14
vendor/github.com/go-ini/ini/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.8
|
||||
- master
|
||||
|
||||
script:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/smartystreets/goconvey
|
||||
- go test -v -cover -race
|
191
vendor/github.com/go-ini/ini/LICENSE
generated
vendored
Normal file
191
vendor/github.com/go-ini/ini/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
12
vendor/github.com/go-ini/ini/Makefile
generated
vendored
Normal file
12
vendor/github.com/go-ini/ini/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
.PHONY: build test bench vet
|
||||
|
||||
build: vet bench
|
||||
|
||||
test:
|
||||
go test -v -cover -race
|
||||
|
||||
bench:
|
||||
go test -v -cover -race -test.bench=. -test.benchmem
|
||||
|
||||
vet:
|
||||
go vet
|
746
vendor/github.com/go-ini/ini/README.md
generated
vendored
Normal file
746
vendor/github.com/go-ini/ini/README.md
generated
vendored
Normal file
|
@ -0,0 +1,746 @@
|
|||
INI [![Build Status](https://travis-ci.org/go-ini/ini.svg?branch=master)](https://travis-ci.org/go-ini/ini) [![Sourcegraph](https://sourcegraph.com/github.com/go-ini/ini/-/badge.svg)](https://sourcegraph.com/github.com/go-ini/ini?badge)
|
||||
===
|
||||
|
||||
![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
|
||||
|
||||
Package ini provides INI file read and write functionality in Go.
|
||||
|
||||
[简体中文](README_ZH.md)
|
||||
|
||||
## Feature
|
||||
|
||||
- Load multiple data sources(`[]byte`, file and `io.ReadCloser`) with overwrites.
|
||||
- Read with recursion values.
|
||||
- Read with parent-child sections.
|
||||
- Read with auto-increment key names.
|
||||
- Read with multiple-line values.
|
||||
- Read with tons of helper methods.
|
||||
- Read and convert values to Go types.
|
||||
- Read and **WRITE** comments of sections and keys.
|
||||
- Manipulate sections, keys and comments with ease.
|
||||
- Keep sections and keys in order as you parse and save.
|
||||
|
||||
## Installation
|
||||
|
||||
To use a tagged revision:
|
||||
|
||||
go get gopkg.in/ini.v1
|
||||
|
||||
To use with latest changes:
|
||||
|
||||
go get github.com/go-ini/ini
|
||||
|
||||
Please add `-u` flag to update in the future.
|
||||
|
||||
### Testing
|
||||
|
||||
If you want to test on your machine, please apply `-t` flag:
|
||||
|
||||
go get -t gopkg.in/ini.v1
|
||||
|
||||
Please add `-u` flag to update in the future.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Loading from data sources
|
||||
|
||||
A **Data Source** is either raw data in type `[]byte`, a file name with type `string` or `io.ReadCloser`. You can load **as many data sources as you want**. Passing other types will simply return an error.
|
||||
|
||||
```go
|
||||
cfg, err := ini.Load([]byte("raw data"), "filename", ioutil.NopCloser(bytes.NewReader([]byte("some other data"))))
|
||||
```
|
||||
|
||||
Or start with an empty object:
|
||||
|
||||
```go
|
||||
cfg := ini.Empty()
|
||||
```
|
||||
|
||||
When you cannot decide how many data sources to load at the beginning, you will still be able to **Append()** them later.
|
||||
|
||||
```go
|
||||
err := cfg.Append("other file", []byte("other raw data"))
|
||||
```
|
||||
|
||||
If you have a list of files with possibilities that some of them may not available at the time, and you don't know exactly which ones, you can use `LooseLoad` to ignore nonexistent files without returning error.
|
||||
|
||||
```go
|
||||
cfg, err := ini.LooseLoad("filename", "filename_404")
|
||||
```
|
||||
|
||||
The cool thing is, whenever the file is available to load while you're calling `Reload` method, it will be counted as usual.
|
||||
|
||||
#### Ignore cases of key name
|
||||
|
||||
When you do not care about cases of section and key names, you can use `InsensitiveLoad` to force all names to be lowercased while parsing.
|
||||
|
||||
```go
|
||||
cfg, err := ini.InsensitiveLoad("filename")
|
||||
//...
|
||||
|
||||
// sec1 and sec2 are the exactly same section object
|
||||
sec1, err := cfg.GetSection("Section")
|
||||
sec2, err := cfg.GetSection("SecTIOn")
|
||||
|
||||
// key1 and key2 are the exactly same key object
|
||||
key1, err := sec1.GetKey("Key")
|
||||
key2, err := sec2.GetKey("KeY")
|
||||
```
|
||||
|
||||
#### MySQL-like boolean key
|
||||
|
||||
MySQL's configuration allows a key without value as follows:
|
||||
|
||||
```ini
|
||||
[mysqld]
|
||||
...
|
||||
skip-host-cache
|
||||
skip-name-resolve
|
||||
```
|
||||
|
||||
By default, this is considered as missing value. But if you know you're going to deal with those cases, you can assign advanced load options:
|
||||
|
||||
```go
|
||||
cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf"))
|
||||
```
|
||||
|
||||
The value of those keys are always `true`, and when you save to a file, it will keep in the same foramt as you read.
|
||||
|
||||
To generate such keys in your program, you could use `NewBooleanKey`:
|
||||
|
||||
```go
|
||||
key, err := sec.NewBooleanKey("skip-host-cache")
|
||||
```
|
||||
|
||||
#### Comment
|
||||
|
||||
Take care that following format will be treated as comment:
|
||||
|
||||
1. Line begins with `#` or `;`
|
||||
2. Words after `#` or `;`
|
||||
3. Words after section name (i.e words after `[some section name]`)
|
||||
|
||||
If you want to save a value with `#` or `;`, please quote them with ``` ` ``` or ``` """ ```.
|
||||
|
||||
Alternatively, you can use following `LoadOptions` to completely ignore inline comments:
|
||||
|
||||
```go
|
||||
cfg, err := LoadSources(LoadOptions{IgnoreInlineComment: true}, "app.ini"))
|
||||
```
|
||||
|
||||
### Working with sections
|
||||
|
||||
To get a section, you would need to:
|
||||
|
||||
```go
|
||||
section, err := cfg.GetSection("section name")
|
||||
```
|
||||
|
||||
For a shortcut for default section, just give an empty string as name:
|
||||
|
||||
```go
|
||||
section, err := cfg.GetSection("")
|
||||
```
|
||||
|
||||
When you're pretty sure the section exists, following code could make your life easier:
|
||||
|
||||
```go
|
||||
section := cfg.Section("section name")
|
||||
```
|
||||
|
||||
What happens when the section somehow does not exist? Don't panic, it automatically creates and returns a new section to you.
|
||||
|
||||
To create a new section:
|
||||
|
||||
```go
|
||||
err := cfg.NewSection("new section")
|
||||
```
|
||||
|
||||
To get a list of sections or section names:
|
||||
|
||||
```go
|
||||
sections := cfg.Sections()
|
||||
names := cfg.SectionStrings()
|
||||
```
|
||||
|
||||
### Working with keys
|
||||
|
||||
To get a key under a section:
|
||||
|
||||
```go
|
||||
key, err := cfg.Section("").GetKey("key name")
|
||||
```
|
||||
|
||||
Same rule applies to key operations:
|
||||
|
||||
```go
|
||||
key := cfg.Section("").Key("key name")
|
||||
```
|
||||
|
||||
To check if a key exists:
|
||||
|
||||
```go
|
||||
yes := cfg.Section("").HasKey("key name")
|
||||
```
|
||||
|
||||
To create a new key:
|
||||
|
||||
```go
|
||||
err := cfg.Section("").NewKey("name", "value")
|
||||
```
|
||||
|
||||
To get a list of keys or key names:
|
||||
|
||||
```go
|
||||
keys := cfg.Section("").Keys()
|
||||
names := cfg.Section("").KeyStrings()
|
||||
```
|
||||
|
||||
To get a clone hash of keys and corresponding values:
|
||||
|
||||
```go
|
||||
hash := cfg.Section("").KeysHash()
|
||||
```
|
||||
|
||||
### Working with values
|
||||
|
||||
To get a string value:
|
||||
|
||||
```go
|
||||
val := cfg.Section("").Key("key name").String()
|
||||
```
|
||||
|
||||
To validate key value on the fly:
|
||||
|
||||
```go
|
||||
val := cfg.Section("").Key("key name").Validate(func(in string) string {
|
||||
if len(in) == 0 {
|
||||
return "default"
|
||||
}
|
||||
return in
|
||||
})
|
||||
```
|
||||
|
||||
If you do not want any auto-transformation (such as recursive read) for the values, you can get raw value directly (this way you get much better performance):
|
||||
|
||||
```go
|
||||
val := cfg.Section("").Key("key name").Value()
|
||||
```
|
||||
|
||||
To check if raw value exists:
|
||||
|
||||
```go
|
||||
yes := cfg.Section("").HasValue("test value")
|
||||
```
|
||||
|
||||
To get value with types:
|
||||
|
||||
```go
|
||||
// For boolean values:
|
||||
// true when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On
|
||||
// false when value is: 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off
|
||||
v, err = cfg.Section("").Key("BOOL").Bool()
|
||||
v, err = cfg.Section("").Key("FLOAT64").Float64()
|
||||
v, err = cfg.Section("").Key("INT").Int()
|
||||
v, err = cfg.Section("").Key("INT64").Int64()
|
||||
v, err = cfg.Section("").Key("UINT").Uint()
|
||||
v, err = cfg.Section("").Key("UINT64").Uint64()
|
||||
v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
|
||||
v, err = cfg.Section("").Key("TIME").Time() // RFC3339
|
||||
|
||||
v = cfg.Section("").Key("BOOL").MustBool()
|
||||
v = cfg.Section("").Key("FLOAT64").MustFloat64()
|
||||
v = cfg.Section("").Key("INT").MustInt()
|
||||
v = cfg.Section("").Key("INT64").MustInt64()
|
||||
v = cfg.Section("").Key("UINT").MustUint()
|
||||
v = cfg.Section("").Key("UINT64").MustUint64()
|
||||
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
|
||||
v = cfg.Section("").Key("TIME").MustTime() // RFC3339
|
||||
|
||||
// Methods start with Must also accept one argument for default value
|
||||
// when key not found or fail to parse value to given type.
|
||||
// Except method MustString, which you have to pass a default value.
|
||||
|
||||
v = cfg.Section("").Key("String").MustString("default")
|
||||
v = cfg.Section("").Key("BOOL").MustBool(true)
|
||||
v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
|
||||
v = cfg.Section("").Key("INT").MustInt(10)
|
||||
v = cfg.Section("").Key("INT64").MustInt64(99)
|
||||
v = cfg.Section("").Key("UINT").MustUint(3)
|
||||
v = cfg.Section("").Key("UINT64").MustUint64(6)
|
||||
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
|
||||
v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339
|
||||
```
|
||||
|
||||
What if my value is three-line long?
|
||||
|
||||
```ini
|
||||
[advance]
|
||||
ADDRESS = """404 road,
|
||||
NotFound, State, 5000
|
||||
Earth"""
|
||||
```
|
||||
|
||||
Not a problem!
|
||||
|
||||
```go
|
||||
cfg.Section("advance").Key("ADDRESS").String()
|
||||
|
||||
/* --- start ---
|
||||
404 road,
|
||||
NotFound, State, 5000
|
||||
Earth
|
||||
------ end --- */
|
||||
```
|
||||
|
||||
That's cool, how about continuation lines?
|
||||
|
||||
```ini
|
||||
[advance]
|
||||
two_lines = how about \
|
||||
continuation lines?
|
||||
lots_of_lines = 1 \
|
||||
2 \
|
||||
3 \
|
||||
4
|
||||
```
|
||||
|
||||
Piece of cake!
|
||||
|
||||
```go
|
||||
cfg.Section("advance").Key("two_lines").String() // how about continuation lines?
|
||||
cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4
|
||||
```
|
||||
|
||||
Well, I hate continuation lines, how do I disable that?
|
||||
|
||||
```go
|
||||
cfg, err := ini.LoadSources(ini.LoadOptions{
|
||||
IgnoreContinuation: true,
|
||||
}, "filename")
|
||||
```
|
||||
|
||||
Holy crap!
|
||||
|
||||
Note that single quotes around values will be stripped:
|
||||
|
||||
```ini
|
||||
foo = "some value" // foo: some value
|
||||
bar = 'some value' // bar: some value
|
||||
```
|
||||
|
||||
That's all? Hmm, no.
|
||||
|
||||
#### Helper methods of working with values
|
||||
|
||||
To get value with given candidates:
|
||||
|
||||
```go
|
||||
v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
|
||||
v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
|
||||
v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
|
||||
v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
|
||||
v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9})
|
||||
v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9})
|
||||
v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
|
||||
v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339
|
||||
```
|
||||
|
||||
Default value will be presented if value of key is not in candidates you given, and default value does not need be one of candidates.
|
||||
|
||||
To validate value in a given range:
|
||||
|
||||
```go
|
||||
vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2)
|
||||
vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20)
|
||||
vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20)
|
||||
vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9)
|
||||
vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9)
|
||||
vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime)
|
||||
vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
|
||||
```
|
||||
|
||||
##### Auto-split values into a slice
|
||||
|
||||
To use zero value of type for invalid inputs:
|
||||
|
||||
```go
|
||||
// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
|
||||
// Input: how, 2.2, are, you -> [0.0 2.2 0.0 0.0]
|
||||
vals = cfg.Section("").Key("STRINGS").Strings(",")
|
||||
vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
|
||||
vals = cfg.Section("").Key("INTS").Ints(",")
|
||||
vals = cfg.Section("").Key("INT64S").Int64s(",")
|
||||
vals = cfg.Section("").Key("UINTS").Uints(",")
|
||||
vals = cfg.Section("").Key("UINT64S").Uint64s(",")
|
||||
vals = cfg.Section("").Key("TIMES").Times(",")
|
||||
```
|
||||
|
||||
To exclude invalid values out of result slice:
|
||||
|
||||
```go
|
||||
// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
|
||||
// Input: how, 2.2, are, you -> [2.2]
|
||||
vals = cfg.Section("").Key("FLOAT64S").ValidFloat64s(",")
|
||||
vals = cfg.Section("").Key("INTS").ValidInts(",")
|
||||
vals = cfg.Section("").Key("INT64S").ValidInt64s(",")
|
||||
vals = cfg.Section("").Key("UINTS").ValidUints(",")
|
||||
vals = cfg.Section("").Key("UINT64S").ValidUint64s(",")
|
||||
vals = cfg.Section("").Key("TIMES").ValidTimes(",")
|
||||
```
|
||||
|
||||
Or to return nothing but error when have invalid inputs:
|
||||
|
||||
```go
|
||||
// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
|
||||
// Input: how, 2.2, are, you -> error
|
||||
vals = cfg.Section("").Key("FLOAT64S").StrictFloat64s(",")
|
||||
vals = cfg.Section("").Key("INTS").StrictInts(",")
|
||||
vals = cfg.Section("").Key("INT64S").StrictInt64s(",")
|
||||
vals = cfg.Section("").Key("UINTS").StrictUints(",")
|
||||
vals = cfg.Section("").Key("UINT64S").StrictUint64s(",")
|
||||
vals = cfg.Section("").Key("TIMES").StrictTimes(",")
|
||||
```
|
||||
|
||||
### Save your configuration
|
||||
|
||||
Finally, it's time to save your configuration to somewhere.
|
||||
|
||||
A typical way to save configuration is writing it to a file:
|
||||
|
||||
```go
|
||||
// ...
|
||||
err = cfg.SaveTo("my.ini")
|
||||
err = cfg.SaveToIndent("my.ini", "\t")
|
||||
```
|
||||
|
||||
Another way to save is writing to a `io.Writer` interface:
|
||||
|
||||
```go
|
||||
// ...
|
||||
cfg.WriteTo(writer)
|
||||
cfg.WriteToIndent(writer, "\t")
|
||||
```
|
||||
|
||||
By default, spaces are used to align "=" sign between key and values, to disable that:
|
||||
|
||||
```go
|
||||
ini.PrettyFormat = false
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Recursive Values
|
||||
|
||||
For all value of keys, there is a special syntax `%(<name>)s`, where `<name>` is the key name in same section or default section, and `%(<name>)s` will be replaced by corresponding value(empty string if key not found). You can use this syntax at most 99 level of recursions.
|
||||
|
||||
```ini
|
||||
NAME = ini
|
||||
|
||||
[author]
|
||||
NAME = Unknwon
|
||||
GITHUB = https://github.com/%(NAME)s
|
||||
|
||||
[package]
|
||||
FULL_NAME = github.com/go-ini/%(NAME)s
|
||||
```
|
||||
|
||||
```go
|
||||
cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon
|
||||
cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini
|
||||
```
|
||||
|
||||
### Parent-child Sections
|
||||
|
||||
You can use `.` in section name to indicate parent-child relationship between two or more sections. If the key not found in the child section, library will try again on its parent section until there is no parent section.
|
||||
|
||||
```ini
|
||||
NAME = ini
|
||||
VERSION = v1
|
||||
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
|
||||
|
||||
[package]
|
||||
CLONE_URL = https://%(IMPORT_PATH)s
|
||||
|
||||
[package.sub]
|
||||
```
|
||||
|
||||
```go
|
||||
cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1
|
||||
```
|
||||
|
||||
#### Retrieve parent keys available to a child section
|
||||
|
||||
```go
|
||||
cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"]
|
||||
```
|
||||
|
||||
### Unparseable Sections
|
||||
|
||||
Sometimes, you have sections that do not contain key-value pairs but raw content, to handle such case, you can use `LoadOptions.UnparsableSections`:
|
||||
|
||||
```go
|
||||
cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS]
|
||||
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
|
||||
|
||||
body := cfg.Section("COMMENTS").Body()
|
||||
|
||||
/* --- start ---
|
||||
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
|
||||
------ end --- */
|
||||
```
|
||||
|
||||
### Auto-increment Key Names
|
||||
|
||||
If key name is `-` in data source, then it would be seen as special syntax for auto-increment key name start from 1, and every section is independent on counter.
|
||||
|
||||
```ini
|
||||
[features]
|
||||
-: Support read/write comments of keys and sections
|
||||
-: Support auto-increment of key names
|
||||
-: Support load multiple files to overwrite key values
|
||||
```
|
||||
|
||||
```go
|
||||
cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"}
|
||||
```
|
||||
|
||||
### Map To Struct
|
||||
|
||||
Want more objective way to play with INI? Cool.
|
||||
|
||||
```ini
|
||||
Name = Unknwon
|
||||
age = 21
|
||||
Male = true
|
||||
Born = 1993-01-01T20:17:05Z
|
||||
|
||||
[Note]
|
||||
Content = Hi is a good man!
|
||||
Cities = HangZhou, Boston
|
||||
```
|
||||
|
||||
```go
|
||||
type Note struct {
|
||||
Content string
|
||||
Cities []string
|
||||
}
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int `ini:"age"`
|
||||
Male bool
|
||||
Born time.Time
|
||||
Note
|
||||
Created time.Time `ini:"-"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg, err := ini.Load("path/to/ini")
|
||||
// ...
|
||||
p := new(Person)
|
||||
err = cfg.MapTo(p)
|
||||
// ...
|
||||
|
||||
// Things can be simpler.
|
||||
err = ini.MapTo(p, "path/to/ini")
|
||||
// ...
|
||||
|
||||
// Just map a section? Fine.
|
||||
n := new(Note)
|
||||
err = cfg.Section("Note").MapTo(n)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Can I have default value for field? Absolutely.
|
||||
|
||||
Assign it before you map to struct. It will keep the value as it is if the key is not presented or got wrong type.
|
||||
|
||||
```go
|
||||
// ...
|
||||
p := &Person{
|
||||
Name: "Joe",
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
It's really cool, but what's the point if you can't give me my file back from struct?
|
||||
|
||||
### Reflect From Struct
|
||||
|
||||
Why not?
|
||||
|
||||
```go
|
||||
type Embeded struct {
|
||||
Dates []time.Time `delim:"|"`
|
||||
Places []string `ini:"places,omitempty"`
|
||||
None []int `ini:",omitempty"`
|
||||
}
|
||||
|
||||
type Author struct {
|
||||
Name string `ini:"NAME"`
|
||||
Male bool
|
||||
Age int
|
||||
GPA float64
|
||||
NeverMind string `ini:"-"`
|
||||
*Embeded
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := &Author{"Unknwon", true, 21, 2.8, "",
|
||||
&Embeded{
|
||||
[]time.Time{time.Now(), time.Now()},
|
||||
[]string{"HangZhou", "Boston"},
|
||||
[]int{},
|
||||
}}
|
||||
cfg := ini.Empty()
|
||||
err = ini.ReflectFrom(cfg, a)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
So, what do I get?
|
||||
|
||||
```ini
|
||||
NAME = Unknwon
|
||||
Male = true
|
||||
Age = 21
|
||||
GPA = 2.8
|
||||
|
||||
[Embeded]
|
||||
Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00
|
||||
places = HangZhou,Boston
|
||||
```
|
||||
|
||||
#### Name Mapper
|
||||
|
||||
To save your time and make your code cleaner, this library supports [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) between struct field and actual section and key name.
|
||||
|
||||
There are 2 built-in name mappers:
|
||||
|
||||
- `AllCapsUnderscore`: it converts to format `ALL_CAPS_UNDERSCORE` then match section or key.
|
||||
- `TitleUnderscore`: it converts to format `title_underscore` then match section or key.
|
||||
|
||||
To use them:
|
||||
|
||||
```go
|
||||
type Info struct {
|
||||
PackageName string
|
||||
}
|
||||
|
||||
func main() {
|
||||
err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini"))
|
||||
// ...
|
||||
|
||||
cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
|
||||
// ...
|
||||
info := new(Info)
|
||||
cfg.NameMapper = ini.AllCapsUnderscore
|
||||
err = cfg.MapTo(info)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Same rules of name mapper apply to `ini.ReflectFromWithMapper` function.
|
||||
|
||||
#### Value Mapper
|
||||
|
||||
To expand values (e.g. from environment variables), you can use the `ValueMapper` to transform values:
|
||||
|
||||
```go
|
||||
type Env struct {
|
||||
Foo string `ini:"foo"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg, err := ini.Load([]byte("[env]\nfoo = ${MY_VAR}\n")
|
||||
cfg.ValueMapper = os.ExpandEnv
|
||||
// ...
|
||||
env := &Env{}
|
||||
err = cfg.Section("env").MapTo(env)
|
||||
}
|
||||
```
|
||||
|
||||
This would set the value of `env.Foo` to the value of the environment variable `MY_VAR`.
|
||||
|
||||
#### Other Notes On Map/Reflect
|
||||
|
||||
Any embedded struct is treated as a section by default, and there is no automatic parent-child relations in map/reflect feature:
|
||||
|
||||
```go
|
||||
type Child struct {
|
||||
Age string
|
||||
}
|
||||
|
||||
type Parent struct {
|
||||
Name string
|
||||
Child
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
City string
|
||||
Parent
|
||||
}
|
||||
```
|
||||
|
||||
Example configuration:
|
||||
|
||||
```ini
|
||||
City = Boston
|
||||
|
||||
[Parent]
|
||||
Name = Unknwon
|
||||
|
||||
[Child]
|
||||
Age = 21
|
||||
```
|
||||
|
||||
What if, yes, I'm paranoid, I want embedded struct to be in the same section. Well, all roads lead to Rome.
|
||||
|
||||
```go
|
||||
type Child struct {
|
||||
Age string
|
||||
}
|
||||
|
||||
type Parent struct {
|
||||
Name string
|
||||
Child `ini:"Parent"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
City string
|
||||
Parent
|
||||
}
|
||||
```
|
||||
|
||||
Example configuration:
|
||||
|
||||
```ini
|
||||
City = Boston
|
||||
|
||||
[Parent]
|
||||
Name = Unknwon
|
||||
Age = 21
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
- [API Documentation](https://gowalker.org/gopkg.in/ini.v1)
|
||||
- [File An Issue](https://github.com/go-ini/ini/issues/new)
|
||||
|
||||
## FAQs
|
||||
|
||||
### What does `BlockMode` field do?
|
||||
|
||||
By default, library lets you read and write values so we need a locker to make sure your data is safe. But in cases that you are very sure about only reading data through the library, you can set `cfg.BlockMode = false` to speed up read operations about **50-70%** faster.
|
||||
|
||||
### Why another INI library?
|
||||
|
||||
Many people are using my another INI library [goconfig](https://github.com/Unknwon/goconfig), so the reason for this one is I would like to make more Go style code. Also when you set `cfg.BlockMode = false`, this one is about **10-30%** faster.
|
||||
|
||||
To make those changes I have to confirm API broken, so it's safer to keep it in another place and start using `gopkg.in` to version my package at this time.(PS: shorter import path)
|
||||
|
||||
## License
|
||||
|
||||
This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.
|
733
vendor/github.com/go-ini/ini/README_ZH.md
generated
vendored
Normal file
733
vendor/github.com/go-ini/ini/README_ZH.md
generated
vendored
Normal file
|
@ -0,0 +1,733 @@
|
|||
本包提供了 Go 语言中读写 INI 文件的功能。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 支持覆盖加载多个数据源(`[]byte`、文件和 `io.ReadCloser`)
|
||||
- 支持递归读取键值
|
||||
- 支持读取父子分区
|
||||
- 支持读取自增键名
|
||||
- 支持读取多行的键值
|
||||
- 支持大量辅助方法
|
||||
- 支持在读取时直接转换为 Go 语言类型
|
||||
- 支持读取和 **写入** 分区和键的注释
|
||||
- 轻松操作分区、键值和注释
|
||||
- 在保存文件时分区和键值会保持原有的顺序
|
||||
|
||||
## 下载安装
|
||||
|
||||
使用一个特定版本:
|
||||
|
||||
go get gopkg.in/ini.v1
|
||||
|
||||
使用最新版:
|
||||
|
||||
go get github.com/go-ini/ini
|
||||
|
||||
如需更新请添加 `-u` 选项。
|
||||
|
||||
### 测试安装
|
||||
|
||||
如果您想要在自己的机器上运行测试,请使用 `-t` 标记:
|
||||
|
||||
go get -t gopkg.in/ini.v1
|
||||
|
||||
如需更新请添加 `-u` 选项。
|
||||
|
||||
## 开始使用
|
||||
|
||||
### 从数据源加载
|
||||
|
||||
一个 **数据源** 可以是 `[]byte` 类型的原始数据,`string` 类型的文件路径或 `io.ReadCloser`。您可以加载 **任意多个** 数据源。如果您传递其它类型的数据源,则会直接返回错误。
|
||||
|
||||
```go
|
||||
cfg, err := ini.Load([]byte("raw data"), "filename", ioutil.NopCloser(bytes.NewReader([]byte("some other data"))))
|
||||
```
|
||||
|
||||
或者从一个空白的文件开始:
|
||||
|
||||
```go
|
||||
cfg := ini.Empty()
|
||||
```
|
||||
|
||||
当您在一开始无法决定需要加载哪些数据源时,仍可以使用 **Append()** 在需要的时候加载它们。
|
||||
|
||||
```go
|
||||
err := cfg.Append("other file", []byte("other raw data"))
|
||||
```
|
||||
|
||||
当您想要加载一系列文件,但是不能够确定其中哪些文件是不存在的,可以通过调用函数 `LooseLoad` 来忽略它们(`Load` 会因为文件不存在而返回错误):
|
||||
|
||||
```go
|
||||
cfg, err := ini.LooseLoad("filename", "filename_404")
|
||||
```
|
||||
|
||||
更牛逼的是,当那些之前不存在的文件在重新调用 `Reload` 方法的时候突然出现了,那么它们会被正常加载。
|
||||
|
||||
#### 忽略键名的大小写
|
||||
|
||||
有时候分区和键的名称大小写混合非常烦人,这个时候就可以通过 `InsensitiveLoad` 将所有分区和键名在读取里强制转换为小写:
|
||||
|
||||
```go
|
||||
cfg, err := ini.InsensitiveLoad("filename")
|
||||
//...
|
||||
|
||||
// sec1 和 sec2 指向同一个分区对象
|
||||
sec1, err := cfg.GetSection("Section")
|
||||
sec2, err := cfg.GetSection("SecTIOn")
|
||||
|
||||
// key1 和 key2 指向同一个键对象
|
||||
key1, err := sec1.GetKey("Key")
|
||||
key2, err := sec2.GetKey("KeY")
|
||||
```
|
||||
|
||||
#### 类似 MySQL 配置中的布尔值键
|
||||
|
||||
MySQL 的配置文件中会出现没有具体值的布尔类型的键:
|
||||
|
||||
```ini
|
||||
[mysqld]
|
||||
...
|
||||
skip-host-cache
|
||||
skip-name-resolve
|
||||
```
|
||||
|
||||
默认情况下这被认为是缺失值而无法完成解析,但可以通过高级的加载选项对它们进行处理:
|
||||
|
||||
```go
|
||||
cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf"))
|
||||
```
|
||||
|
||||
这些键的值永远为 `true`,且在保存到文件时也只会输出键名。
|
||||
|
||||
如果您想要通过程序来生成此类键,则可以使用 `NewBooleanKey`:
|
||||
|
||||
```go
|
||||
key, err := sec.NewBooleanKey("skip-host-cache")
|
||||
```
|
||||
|
||||
#### 关于注释
|
||||
|
||||
下述几种情况的内容将被视为注释:
|
||||
|
||||
1. 所有以 `#` 或 `;` 开头的行
|
||||
2. 所有在 `#` 或 `;` 之后的内容
|
||||
3. 分区标签后的文字 (即 `[分区名]` 之后的内容)
|
||||
|
||||
如果你希望使用包含 `#` 或 `;` 的值,请使用 ``` ` ``` 或 ``` """ ``` 进行包覆。
|
||||
|
||||
除此之外,您还可以通过 `LoadOptions` 完全忽略行内注释:
|
||||
|
||||
```go
|
||||
cfg, err := LoadSources(LoadOptions{IgnoreInlineComment: true}, "app.ini"))
|
||||
```
|
||||
|
||||
### 操作分区(Section)
|
||||
|
||||
获取指定分区:
|
||||
|
||||
```go
|
||||
section, err := cfg.GetSection("section name")
|
||||
```
|
||||
|
||||
如果您想要获取默认分区,则可以用空字符串代替分区名:
|
||||
|
||||
```go
|
||||
section, err := cfg.GetSection("")
|
||||
```
|
||||
|
||||
当您非常确定某个分区是存在的,可以使用以下简便方法:
|
||||
|
||||
```go
|
||||
section := cfg.Section("section name")
|
||||
```
|
||||
|
||||
如果不小心判断错了,要获取的分区其实是不存在的,那会发生什么呢?没事的,它会自动创建并返回一个对应的分区对象给您。
|
||||
|
||||
创建一个分区:
|
||||
|
||||
```go
|
||||
err := cfg.NewSection("new section")
|
||||
```
|
||||
|
||||
获取所有分区对象或名称:
|
||||
|
||||
```go
|
||||
sections := cfg.Sections()
|
||||
names := cfg.SectionStrings()
|
||||
```
|
||||
|
||||
### 操作键(Key)
|
||||
|
||||
获取某个分区下的键:
|
||||
|
||||
```go
|
||||
key, err := cfg.Section("").GetKey("key name")
|
||||
```
|
||||
|
||||
和分区一样,您也可以直接获取键而忽略错误处理:
|
||||
|
||||
```go
|
||||
key := cfg.Section("").Key("key name")
|
||||
```
|
||||
|
||||
判断某个键是否存在:
|
||||
|
||||
```go
|
||||
yes := cfg.Section("").HasKey("key name")
|
||||
```
|
||||
|
||||
创建一个新的键:
|
||||
|
||||
```go
|
||||
err := cfg.Section("").NewKey("name", "value")
|
||||
```
|
||||
|
||||
获取分区下的所有键或键名:
|
||||
|
||||
```go
|
||||
keys := cfg.Section("").Keys()
|
||||
names := cfg.Section("").KeyStrings()
|
||||
```
|
||||
|
||||
获取分区下的所有键值对的克隆:
|
||||
|
||||
```go
|
||||
hash := cfg.Section("").KeysHash()
|
||||
```
|
||||
|
||||
### 操作键值(Value)
|
||||
|
||||
获取一个类型为字符串(string)的值:
|
||||
|
||||
```go
|
||||
val := cfg.Section("").Key("key name").String()
|
||||
```
|
||||
|
||||
获取值的同时通过自定义函数进行处理验证:
|
||||
|
||||
```go
|
||||
val := cfg.Section("").Key("key name").Validate(func(in string) string {
|
||||
if len(in) == 0 {
|
||||
return "default"
|
||||
}
|
||||
return in
|
||||
})
|
||||
```
|
||||
|
||||
如果您不需要任何对值的自动转变功能(例如递归读取),可以直接获取原值(这种方式性能最佳):
|
||||
|
||||
```go
|
||||
val := cfg.Section("").Key("key name").Value()
|
||||
```
|
||||
|
||||
判断某个原值是否存在:
|
||||
|
||||
```go
|
||||
yes := cfg.Section("").HasValue("test value")
|
||||
```
|
||||
|
||||
获取其它类型的值:
|
||||
|
||||
```go
|
||||
// 布尔值的规则:
|
||||
// true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On
|
||||
// false 当值为:0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off
|
||||
v, err = cfg.Section("").Key("BOOL").Bool()
|
||||
v, err = cfg.Section("").Key("FLOAT64").Float64()
|
||||
v, err = cfg.Section("").Key("INT").Int()
|
||||
v, err = cfg.Section("").Key("INT64").Int64()
|
||||
v, err = cfg.Section("").Key("UINT").Uint()
|
||||
v, err = cfg.Section("").Key("UINT64").Uint64()
|
||||
v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
|
||||
v, err = cfg.Section("").Key("TIME").Time() // RFC3339
|
||||
|
||||
v = cfg.Section("").Key("BOOL").MustBool()
|
||||
v = cfg.Section("").Key("FLOAT64").MustFloat64()
|
||||
v = cfg.Section("").Key("INT").MustInt()
|
||||
v = cfg.Section("").Key("INT64").MustInt64()
|
||||
v = cfg.Section("").Key("UINT").MustUint()
|
||||
v = cfg.Section("").Key("UINT64").MustUint64()
|
||||
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
|
||||
v = cfg.Section("").Key("TIME").MustTime() // RFC3339
|
||||
|
||||
// 由 Must 开头的方法名允许接收一个相同类型的参数来作为默认值,
|
||||
// 当键不存在或者转换失败时,则会直接返回该默认值。
|
||||
// 但是,MustString 方法必须传递一个默认值。
|
||||
|
||||
v = cfg.Seciont("").Key("String").MustString("default")
|
||||
v = cfg.Section("").Key("BOOL").MustBool(true)
|
||||
v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
|
||||
v = cfg.Section("").Key("INT").MustInt(10)
|
||||
v = cfg.Section("").Key("INT64").MustInt64(99)
|
||||
v = cfg.Section("").Key("UINT").MustUint(3)
|
||||
v = cfg.Section("").Key("UINT64").MustUint64(6)
|
||||
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
|
||||
v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339
|
||||
```
|
||||
|
||||
如果我的值有好多行怎么办?
|
||||
|
||||
```ini
|
||||
[advance]
|
||||
ADDRESS = """404 road,
|
||||
NotFound, State, 5000
|
||||
Earth"""
|
||||
```
|
||||
|
||||
嗯哼?小 case!
|
||||
|
||||
```go
|
||||
cfg.Section("advance").Key("ADDRESS").String()
|
||||
|
||||
/* --- start ---
|
||||
404 road,
|
||||
NotFound, State, 5000
|
||||
Earth
|
||||
------ end --- */
|
||||
```
|
||||
|
||||
赞爆了!那要是我属于一行的内容写不下想要写到第二行怎么办?
|
||||
|
||||
```ini
|
||||
[advance]
|
||||
two_lines = how about \
|
||||
continuation lines?
|
||||
lots_of_lines = 1 \
|
||||
2 \
|
||||
3 \
|
||||
4
|
||||
```
|
||||
|
||||
简直是小菜一碟!
|
||||
|
||||
```go
|
||||
cfg.Section("advance").Key("two_lines").String() // how about continuation lines?
|
||||
cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4
|
||||
```
|
||||
|
||||
可是我有时候觉得两行连在一起特别没劲,怎么才能不自动连接两行呢?
|
||||
|
||||
```go
|
||||
cfg, err := ini.LoadSources(ini.LoadOptions{
|
||||
IgnoreContinuation: true,
|
||||
}, "filename")
|
||||
```
|
||||
|
||||
哇靠给力啊!
|
||||
|
||||
需要注意的是,值两侧的单引号会被自动剔除:
|
||||
|
||||
```ini
|
||||
foo = "some value" // foo: some value
|
||||
bar = 'some value' // bar: some value
|
||||
```
|
||||
|
||||
这就是全部了?哈哈,当然不是。
|
||||
|
||||
#### 操作键值的辅助方法
|
||||
|
||||
获取键值时设定候选值:
|
||||
|
||||
```go
|
||||
v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
|
||||
v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
|
||||
v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
|
||||
v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
|
||||
v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9})
|
||||
v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9})
|
||||
v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
|
||||
v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339
|
||||
```
|
||||
|
||||
如果获取到的值不是候选值的任意一个,则会返回默认值,而默认值不需要是候选值中的一员。
|
||||
|
||||
验证获取的值是否在指定范围内:
|
||||
|
||||
```go
|
||||
vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2)
|
||||
vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20)
|
||||
vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20)
|
||||
vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9)
|
||||
vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9)
|
||||
vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime)
|
||||
vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
|
||||
```
|
||||
|
||||
##### 自动分割键值到切片(slice)
|
||||
|
||||
当存在无效输入时,使用零值代替:
|
||||
|
||||
```go
|
||||
// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
|
||||
// Input: how, 2.2, are, you -> [0.0 2.2 0.0 0.0]
|
||||
vals = cfg.Section("").Key("STRINGS").Strings(",")
|
||||
vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
|
||||
vals = cfg.Section("").Key("INTS").Ints(",")
|
||||
vals = cfg.Section("").Key("INT64S").Int64s(",")
|
||||
vals = cfg.Section("").Key("UINTS").Uints(",")
|
||||
vals = cfg.Section("").Key("UINT64S").Uint64s(",")
|
||||
vals = cfg.Section("").Key("TIMES").Times(",")
|
||||
```
|
||||
|
||||
从结果切片中剔除无效输入:
|
||||
|
||||
```go
|
||||
// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
|
||||
// Input: how, 2.2, are, you -> [2.2]
|
||||
vals = cfg.Section("").Key("FLOAT64S").ValidFloat64s(",")
|
||||
vals = cfg.Section("").Key("INTS").ValidInts(",")
|
||||
vals = cfg.Section("").Key("INT64S").ValidInt64s(",")
|
||||
vals = cfg.Section("").Key("UINTS").ValidUints(",")
|
||||
vals = cfg.Section("").Key("UINT64S").ValidUint64s(",")
|
||||
vals = cfg.Section("").Key("TIMES").ValidTimes(",")
|
||||
```
|
||||
|
||||
当存在无效输入时,直接返回错误:
|
||||
|
||||
```go
|
||||
// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
|
||||
// Input: how, 2.2, are, you -> error
|
||||
vals = cfg.Section("").Key("FLOAT64S").StrictFloat64s(",")
|
||||
vals = cfg.Section("").Key("INTS").StrictInts(",")
|
||||
vals = cfg.Section("").Key("INT64S").StrictInt64s(",")
|
||||
vals = cfg.Section("").Key("UINTS").StrictUints(",")
|
||||
vals = cfg.Section("").Key("UINT64S").StrictUint64s(",")
|
||||
vals = cfg.Section("").Key("TIMES").StrictTimes(",")
|
||||
```
|
||||
|
||||
### 保存配置
|
||||
|
||||
终于到了这个时刻,是时候保存一下配置了。
|
||||
|
||||
比较原始的做法是输出配置到某个文件:
|
||||
|
||||
```go
|
||||
// ...
|
||||
err = cfg.SaveTo("my.ini")
|
||||
err = cfg.SaveToIndent("my.ini", "\t")
|
||||
```
|
||||
|
||||
另一个比较高级的做法是写入到任何实现 `io.Writer` 接口的对象中:
|
||||
|
||||
```go
|
||||
// ...
|
||||
cfg.WriteTo(writer)
|
||||
cfg.WriteToIndent(writer, "\t")
|
||||
```
|
||||
|
||||
默认情况下,空格将被用于对齐键值之间的等号以美化输出结果,以下代码可以禁用该功能:
|
||||
|
||||
```go
|
||||
ini.PrettyFormat = false
|
||||
```
|
||||
|
||||
## 高级用法
|
||||
|
||||
### 递归读取键值
|
||||
|
||||
在获取所有键值的过程中,特殊语法 `%(<name>)s` 会被应用,其中 `<name>` 可以是相同分区或者默认分区下的键名。字符串 `%(<name>)s` 会被相应的键值所替代,如果指定的键不存在,则会用空字符串替代。您可以最多使用 99 层的递归嵌套。
|
||||
|
||||
```ini
|
||||
NAME = ini
|
||||
|
||||
[author]
|
||||
NAME = Unknwon
|
||||
GITHUB = https://github.com/%(NAME)s
|
||||
|
||||
[package]
|
||||
FULL_NAME = github.com/go-ini/%(NAME)s
|
||||
```
|
||||
|
||||
```go
|
||||
cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon
|
||||
cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini
|
||||
```
|
||||
|
||||
### 读取父子分区
|
||||
|
||||
您可以在分区名称中使用 `.` 来表示两个或多个分区之间的父子关系。如果某个键在子分区中不存在,则会去它的父分区中再次寻找,直到没有父分区为止。
|
||||
|
||||
```ini
|
||||
NAME = ini
|
||||
VERSION = v1
|
||||
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
|
||||
|
||||
[package]
|
||||
CLONE_URL = https://%(IMPORT_PATH)s
|
||||
|
||||
[package.sub]
|
||||
```
|
||||
|
||||
```go
|
||||
cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1
|
||||
```
|
||||
|
||||
#### 获取上级父分区下的所有键名
|
||||
|
||||
```go
|
||||
cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"]
|
||||
```
|
||||
|
||||
### 无法解析的分区
|
||||
|
||||
如果遇到一些比较特殊的分区,它们不包含常见的键值对,而是没有固定格式的纯文本,则可以使用 `LoadOptions.UnparsableSections` 进行处理:
|
||||
|
||||
```go
|
||||
cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS]
|
||||
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
|
||||
|
||||
body := cfg.Section("COMMENTS").Body()
|
||||
|
||||
/* --- start ---
|
||||
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
|
||||
------ end --- */
|
||||
```
|
||||
|
||||
### 读取自增键名
|
||||
|
||||
如果数据源中的键名为 `-`,则认为该键使用了自增键名的特殊语法。计数器从 1 开始,并且分区之间是相互独立的。
|
||||
|
||||
```ini
|
||||
[features]
|
||||
-: Support read/write comments of keys and sections
|
||||
-: Support auto-increment of key names
|
||||
-: Support load multiple files to overwrite key values
|
||||
```
|
||||
|
||||
```go
|
||||
cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"}
|
||||
```
|
||||
|
||||
### 映射到结构
|
||||
|
||||
想要使用更加面向对象的方式玩转 INI 吗?好主意。
|
||||
|
||||
```ini
|
||||
Name = Unknwon
|
||||
age = 21
|
||||
Male = true
|
||||
Born = 1993-01-01T20:17:05Z
|
||||
|
||||
[Note]
|
||||
Content = Hi is a good man!
|
||||
Cities = HangZhou, Boston
|
||||
```
|
||||
|
||||
```go
|
||||
type Note struct {
|
||||
Content string
|
||||
Cities []string
|
||||
}
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int `ini:"age"`
|
||||
Male bool
|
||||
Born time.Time
|
||||
Note
|
||||
Created time.Time `ini:"-"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg, err := ini.Load("path/to/ini")
|
||||
// ...
|
||||
p := new(Person)
|
||||
err = cfg.MapTo(p)
|
||||
// ...
|
||||
|
||||
// 一切竟可以如此的简单。
|
||||
err = ini.MapTo(p, "path/to/ini")
|
||||
// ...
|
||||
|
||||
// 嗯哼?只需要映射一个分区吗?
|
||||
n := new(Note)
|
||||
err = cfg.Section("Note").MapTo(n)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
结构的字段怎么设置默认值呢?很简单,只要在映射之前对指定字段进行赋值就可以了。如果键未找到或者类型错误,该值不会发生改变。
|
||||
|
||||
```go
|
||||
// ...
|
||||
p := &Person{
|
||||
Name: "Joe",
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
这样玩 INI 真的好酷啊!然而,如果不能还给我原来的配置文件,有什么卵用?
|
||||
|
||||
### 从结构反射
|
||||
|
||||
可是,我有说不能吗?
|
||||
|
||||
```go
|
||||
type Embeded struct {
|
||||
Dates []time.Time `delim:"|"`
|
||||
Places []string `ini:"places,omitempty"`
|
||||
None []int `ini:",omitempty"`
|
||||
}
|
||||
|
||||
type Author struct {
|
||||
Name string `ini:"NAME"`
|
||||
Male bool
|
||||
Age int
|
||||
GPA float64
|
||||
NeverMind string `ini:"-"`
|
||||
*Embeded
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := &Author{"Unknwon", true, 21, 2.8, "",
|
||||
&Embeded{
|
||||
[]time.Time{time.Now(), time.Now()},
|
||||
[]string{"HangZhou", "Boston"},
|
||||
[]int{},
|
||||
}}
|
||||
cfg := ini.Empty()
|
||||
err = ini.ReflectFrom(cfg, a)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
瞧瞧,奇迹发生了。
|
||||
|
||||
```ini
|
||||
NAME = Unknwon
|
||||
Male = true
|
||||
Age = 21
|
||||
GPA = 2.8
|
||||
|
||||
[Embeded]
|
||||
Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00
|
||||
places = HangZhou,Boston
|
||||
```
|
||||
|
||||
#### 名称映射器(Name Mapper)
|
||||
|
||||
为了节省您的时间并简化代码,本库支持类型为 [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) 的名称映射器,该映射器负责结构字段名与分区名和键名之间的映射。
|
||||
|
||||
目前有 2 款内置的映射器:
|
||||
|
||||
- `AllCapsUnderscore`:该映射器将字段名转换至格式 `ALL_CAPS_UNDERSCORE` 后再去匹配分区名和键名。
|
||||
- `TitleUnderscore`:该映射器将字段名转换至格式 `title_underscore` 后再去匹配分区名和键名。
|
||||
|
||||
使用方法:
|
||||
|
||||
```go
|
||||
type Info struct{
|
||||
PackageName string
|
||||
}
|
||||
|
||||
func main() {
|
||||
err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini"))
|
||||
// ...
|
||||
|
||||
cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
|
||||
// ...
|
||||
info := new(Info)
|
||||
cfg.NameMapper = ini.AllCapsUnderscore
|
||||
err = cfg.MapTo(info)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
使用函数 `ini.ReflectFromWithMapper` 时也可应用相同的规则。
|
||||
|
||||
#### 值映射器(Value Mapper)
|
||||
|
||||
值映射器允许使用一个自定义函数自动展开值的具体内容,例如:运行时获取环境变量:
|
||||
|
||||
```go
|
||||
type Env struct {
|
||||
Foo string `ini:"foo"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg, err := ini.Load([]byte("[env]\nfoo = ${MY_VAR}\n")
|
||||
cfg.ValueMapper = os.ExpandEnv
|
||||
// ...
|
||||
env := &Env{}
|
||||
err = cfg.Section("env").MapTo(env)
|
||||
}
|
||||
```
|
||||
|
||||
本例中,`env.Foo` 将会是运行时所获取到环境变量 `MY_VAR` 的值。
|
||||
|
||||
#### 映射/反射的其它说明
|
||||
|
||||
任何嵌入的结构都会被默认认作一个不同的分区,并且不会自动产生所谓的父子分区关联:
|
||||
|
||||
```go
|
||||
type Child struct {
|
||||
Age string
|
||||
}
|
||||
|
||||
type Parent struct {
|
||||
Name string
|
||||
Child
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
City string
|
||||
Parent
|
||||
}
|
||||
```
|
||||
|
||||
示例配置文件:
|
||||
|
||||
```ini
|
||||
City = Boston
|
||||
|
||||
[Parent]
|
||||
Name = Unknwon
|
||||
|
||||
[Child]
|
||||
Age = 21
|
||||
```
|
||||
|
||||
很好,但是,我就是要嵌入结构也在同一个分区。好吧,你爹是李刚!
|
||||
|
||||
```go
|
||||
type Child struct {
|
||||
Age string
|
||||
}
|
||||
|
||||
type Parent struct {
|
||||
Name string
|
||||
Child `ini:"Parent"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
City string
|
||||
Parent
|
||||
}
|
||||
```
|
||||
|
||||
示例配置文件:
|
||||
|
||||
```ini
|
||||
City = Boston
|
||||
|
||||
[Parent]
|
||||
Name = Unknwon
|
||||
Age = 21
|
||||
```
|
||||
|
||||
## 获取帮助
|
||||
|
||||
- [API 文档](https://gowalker.org/gopkg.in/ini.v1)
|
||||
- [创建工单](https://github.com/go-ini/ini/issues/new)
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 字段 `BlockMode` 是什么?
|
||||
|
||||
默认情况下,本库会在您进行读写操作时采用锁机制来确保数据时间。但在某些情况下,您非常确定只进行读操作。此时,您可以通过设置 `cfg.BlockMode = false` 来将读操作提升大约 **50-70%** 的性能。
|
||||
|
||||
### 为什么要写另一个 INI 解析库?
|
||||
|
||||
许多人都在使用我的 [goconfig](https://github.com/Unknwon/goconfig) 来完成对 INI 文件的操作,但我希望使用更加 Go 风格的代码。并且当您设置 `cfg.BlockMode = false` 时,会有大约 **10-30%** 的性能提升。
|
||||
|
||||
为了做出这些改变,我必须对 API 进行破坏,所以新开一个仓库是最安全的做法。除此之外,本库直接使用 `gopkg.in` 来进行版本化发布。(其实真相是导入路径更短了)
|
32
vendor/github.com/go-ini/ini/error.go
generated
vendored
Normal file
32
vendor/github.com/go-ini/ini/error.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2016 Unknwon
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package ini
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ErrDelimiterNotFound struct {
|
||||
Line string
|
||||
}
|
||||
|
||||
func IsErrDelimiterNotFound(err error) bool {
|
||||
_, ok := err.(ErrDelimiterNotFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDelimiterNotFound) Error() string {
|
||||
return fmt.Sprintf("key-value delimiter not found: %s", err.Line)
|
||||
}
|
561
vendor/github.com/go-ini/ini/ini.go
generated
vendored
Normal file
561
vendor/github.com/go-ini/ini/ini.go
generated
vendored
Normal file
|
@ -0,0 +1,561 @@
|
|||
// Copyright 2014 Unknwon
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
// Package ini provides INI file read and write functionality in Go.
|
||||
package ini
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Name for default section. You can use this constant or the string literal.
|
||||
// In most of cases, an empty string is all you need to access the section.
|
||||
DEFAULT_SECTION = "DEFAULT"
|
||||
|
||||
// Maximum allowed depth when recursively substituing variable names.
|
||||
_DEPTH_VALUES = 99
|
||||
_VERSION = "1.28.0"
|
||||
)
|
||||
|
||||
// Version returns current package version literal.
|
||||
func Version() string {
|
||||
return _VERSION
|
||||
}
|
||||
|
||||
var (
|
||||
// Delimiter to determine or compose a new line.
|
||||
// This variable will be changed to "\r\n" automatically on Windows
|
||||
// at package init time.
|
||||
LineBreak = "\n"
|
||||
|
||||
// Variable regexp pattern: %(variable)s
|
||||
varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
|
||||
|
||||
// Indicate whether to align "=" sign with spaces to produce pretty output
|
||||
// or reduce all possible spaces for compact format.
|
||||
PrettyFormat = true
|
||||
|
||||
// Explicitly write DEFAULT section header
|
||||
DefaultHeader = false
|
||||
|
||||
// Indicate whether to put a line between sections
|
||||
PrettySection = true
|
||||
)
|
||||
|
||||
func init() {
|
||||
if runtime.GOOS == "windows" {
|
||||
LineBreak = "\r\n"
|
||||
}
|
||||
}
|
||||
|
||||
func inSlice(str string, s []string) bool {
|
||||
for _, v := range s {
|
||||
if str == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// dataSource is an interface that returns object which can be read and closed.
|
||||
type dataSource interface {
|
||||
ReadCloser() (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// sourceFile represents an object that contains content on the local file system.
|
||||
type sourceFile struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
|
||||
return os.Open(s.name)
|
||||
}
|
||||
|
||||
type bytesReadCloser struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (rc *bytesReadCloser) Read(p []byte) (n int, err error) {
|
||||
return rc.reader.Read(p)
|
||||
}
|
||||
|
||||
func (rc *bytesReadCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// sourceData represents an object that contains content in memory.
|
||||
type sourceData struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(bytes.NewReader(s.data)), nil
|
||||
}
|
||||
|
||||
// sourceReadCloser represents an input stream with Close method.
|
||||
type sourceReadCloser struct {
|
||||
reader io.ReadCloser
|
||||
}
|
||||
|
||||
func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
|
||||
return s.reader, nil
|
||||
}
|
||||
|
||||
// File represents a combination of a or more INI file(s) in memory.
|
||||
type File struct {
|
||||
// Should make things safe, but sometimes doesn't matter.
|
||||
BlockMode bool
|
||||
// Make sure data is safe in multiple goroutines.
|
||||
lock sync.RWMutex
|
||||
|
||||
// Allow combination of multiple data sources.
|
||||
dataSources []dataSource
|
||||
// Actual data is stored here.
|
||||
sections map[string]*Section
|
||||
|
||||
// To keep data in order.
|
||||
sectionList []string
|
||||
|
||||
options LoadOptions
|
||||
|
||||
NameMapper
|
||||
ValueMapper
|
||||
}
|
||||
|
||||
// newFile initializes File object with given data sources.
|
||||
func newFile(dataSources []dataSource, opts LoadOptions) *File {
|
||||
return &File{
|
||||
BlockMode: true,
|
||||
dataSources: dataSources,
|
||||
sections: make(map[string]*Section),
|
||||
sectionList: make([]string, 0, 10),
|
||||
options: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func parseDataSource(source interface{}) (dataSource, error) {
|
||||
switch s := source.(type) {
|
||||
case string:
|
||||
return sourceFile{s}, nil
|
||||
case []byte:
|
||||
return &sourceData{s}, nil
|
||||
case io.ReadCloser:
|
||||
return &sourceReadCloser{s}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s)
|
||||
}
|
||||
}
|
||||
|
||||
type LoadOptions struct {
|
||||
// Loose indicates whether the parser should ignore nonexistent files or return error.
|
||||
Loose bool
|
||||
// Insensitive indicates whether the parser forces all section and key names to lowercase.
|
||||
Insensitive bool
|
||||
// IgnoreContinuation indicates whether to ignore continuation lines while parsing.
|
||||
IgnoreContinuation bool
|
||||
// IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
|
||||
IgnoreInlineComment bool
|
||||
// AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
|
||||
// This type of keys are mostly used in my.cnf.
|
||||
AllowBooleanKeys bool
|
||||
// AllowShadows indicates whether to keep track of keys with same name under same section.
|
||||
AllowShadows bool
|
||||
// Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
|
||||
// conform to key/value pairs. Specify the names of those blocks here.
|
||||
UnparseableSections []string
|
||||
}
|
||||
|
||||
func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
|
||||
sources := make([]dataSource, len(others)+1)
|
||||
sources[0], err = parseDataSource(source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range others {
|
||||
sources[i+1], err = parseDataSource(others[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
f := newFile(sources, opts)
|
||||
if err = f.Reload(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Load loads and parses from INI data sources.
|
||||
// Arguments can be mixed of file name with string type, or raw data in []byte.
|
||||
// It will return error if list contains nonexistent files.
|
||||
func Load(source interface{}, others ...interface{}) (*File, error) {
|
||||
return LoadSources(LoadOptions{}, source, others...)
|
||||
}
|
||||
|
||||
// LooseLoad has exactly same functionality as Load function
|
||||
// except it ignores nonexistent files instead of returning error.
|
||||
func LooseLoad(source interface{}, others ...interface{}) (*File, error) {
|
||||
return LoadSources(LoadOptions{Loose: true}, source, others...)
|
||||
}
|
||||
|
||||
// InsensitiveLoad has exactly same functionality as Load function
|
||||
// except it forces all section and key names to be lowercased.
|
||||
func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
|
||||
return LoadSources(LoadOptions{Insensitive: true}, source, others...)
|
||||
}
|
||||
|
||||
// InsensitiveLoad has exactly same functionality as Load function
|
||||
// except it allows have shadow keys.
|
||||
func ShadowLoad(source interface{}, others ...interface{}) (*File, error) {
|
||||
return LoadSources(LoadOptions{AllowShadows: true}, source, others...)
|
||||
}
|
||||
|
||||
// Empty returns an empty file object.
|
||||
func Empty() *File {
|
||||
// Ignore error here, we sure our data is good.
|
||||
f, _ := Load([]byte(""))
|
||||
return f
|
||||
}
|
||||
|
||||
// NewSection creates a new section.
|
||||
func (f *File) NewSection(name string) (*Section, error) {
|
||||
if len(name) == 0 {
|
||||
return nil, errors.New("error creating new section: empty section name")
|
||||
} else if f.options.Insensitive && name != DEFAULT_SECTION {
|
||||
name = strings.ToLower(name)
|
||||
}
|
||||
|
||||
if f.BlockMode {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
}
|
||||
|
||||
if inSlice(name, f.sectionList) {
|
||||
return f.sections[name], nil
|
||||
}
|
||||
|
||||
f.sectionList = append(f.sectionList, name)
|
||||
f.sections[name] = newSection(f, name)
|
||||
return f.sections[name], nil
|
||||
}
|
||||
|
||||
// NewRawSection creates a new section with an unparseable body.
|
||||
func (f *File) NewRawSection(name, body string) (*Section, error) {
|
||||
section, err := f.NewSection(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
section.isRawSection = true
|
||||
section.rawBody = body
|
||||
return section, nil
|
||||
}
|
||||
|
||||
// NewSections creates a list of sections.
|
||||
func (f *File) NewSections(names ...string) (err error) {
|
||||
for _, name := range names {
|
||||
if _, err = f.NewSection(name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSection returns section by given name.
|
||||
func (f *File) GetSection(name string) (*Section, error) {
|
||||
if len(name) == 0 {
|
||||
name = DEFAULT_SECTION
|
||||
} else if f.options.Insensitive {
|
||||
name = strings.ToLower(name)
|
||||
}
|
||||
|
||||
if f.BlockMode {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
}
|
||||
|
||||
sec := f.sections[name]
|
||||
if sec == nil {
|
||||
return nil, fmt.Errorf("section '%s' does not exist", name)
|
||||
}
|
||||
return sec, nil
|
||||
}
|
||||
|
||||
// Section assumes named section exists and returns a zero-value when not.
|
||||
func (f *File) Section(name string) *Section {
|
||||
sec, err := f.GetSection(name)
|
||||
if err != nil {
|
||||
// Note: It's OK here because the only possible error is empty section name,
|
||||
// but if it's empty, this piece of code won't be executed.
|
||||
sec, _ = f.NewSection(name)
|
||||
return sec
|
||||
}
|
||||
return sec
|
||||
}
|
||||
|
||||
// Section returns list of Section.
|
||||
func (f *File) Sections() []*Section {
|
||||
sections := make([]*Section, len(f.sectionList))
|
||||
for i := range f.sectionList {
|
||||
sections[i] = f.Section(f.sectionList[i])
|
||||
}
|
||||
return sections
|
||||
}
|
||||
|
||||
// ChildSections returns a list of child sections of given section name.
|
||||
func (f *File) ChildSections(name string) []*Section {
|
||||
return f.Section(name).ChildSections()
|
||||
}
|
||||
|
||||
// SectionStrings returns list of section names.
|
||||
func (f *File) SectionStrings() []string {
|
||||
list := make([]string, len(f.sectionList))
|
||||
copy(list, f.sectionList)
|
||||
return list
|
||||
}
|
||||
|
||||
// DeleteSection deletes a section.
|
||||
func (f *File) DeleteSection(name string) {
|
||||
if f.BlockMode {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
}
|
||||
|
||||
if len(name) == 0 {
|
||||
name = DEFAULT_SECTION
|
||||
}
|
||||
|
||||
for i, s := range f.sectionList {
|
||||
if s == name {
|
||||
f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
|
||||
delete(f.sections, name)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *File) reload(s dataSource) error {
|
||||
r, err := s.ReadCloser()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
return f.parse(r)
|
||||
}
|
||||
|
||||
// Reload reloads and parses all data sources.
|
||||
func (f *File) Reload() (err error) {
|
||||
for _, s := range f.dataSources {
|
||||
if err = f.reload(s); err != nil {
|
||||
// In loose mode, we create an empty default section for nonexistent files.
|
||||
if os.IsNotExist(err) && f.options.Loose {
|
||||
f.parse(bytes.NewBuffer(nil))
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Append appends one or more data sources and reloads automatically.
|
||||
func (f *File) Append(source interface{}, others ...interface{}) error {
|
||||
ds, err := parseDataSource(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.dataSources = append(f.dataSources, ds)
|
||||
for _, s := range others {
|
||||
ds, err = parseDataSource(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.dataSources = append(f.dataSources, ds)
|
||||
}
|
||||
return f.Reload()
|
||||
}
|
||||
|
||||
// WriteToIndent writes content into io.Writer with given indention.
|
||||
// If PrettyFormat has been set to be true,
|
||||
// it will align "=" sign with spaces under each section.
|
||||
func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
|
||||
equalSign := "="
|
||||
if PrettyFormat {
|
||||
equalSign = " = "
|
||||
}
|
||||
|
||||
// Use buffer to make sure target is safe until finish encoding.
|
||||
buf := bytes.NewBuffer(nil)
|
||||
for i, sname := range f.sectionList {
|
||||
sec := f.Section(sname)
|
||||
if len(sec.Comment) > 0 {
|
||||
if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
|
||||
sec.Comment = "; " + sec.Comment
|
||||
}
|
||||
if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if i > 0 || DefaultHeader {
|
||||
if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
// Write nothing if default section is empty
|
||||
if len(sec.keyList) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if sec.isRawSection {
|
||||
if _, err = buf.WriteString(sec.rawBody); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Count and generate alignment length and buffer spaces using the
|
||||
// longest key. Keys may be modifed if they contain certain characters so
|
||||
// we need to take that into account in our calculation.
|
||||
alignLength := 0
|
||||
if PrettyFormat {
|
||||
for _, kname := range sec.keyList {
|
||||
keyLength := len(kname)
|
||||
// First case will surround key by ` and second by """
|
||||
if strings.ContainsAny(kname, "\"=:") {
|
||||
keyLength += 2
|
||||
} else if strings.Contains(kname, "`") {
|
||||
keyLength += 6
|
||||
}
|
||||
|
||||
if keyLength > alignLength {
|
||||
alignLength = keyLength
|
||||
}
|
||||
}
|
||||
}
|
||||
alignSpaces := bytes.Repeat([]byte(" "), alignLength)
|
||||
|
||||
KEY_LIST:
|
||||
for _, kname := range sec.keyList {
|
||||
key := sec.Key(kname)
|
||||
if len(key.Comment) > 0 {
|
||||
if len(indent) > 0 && sname != DEFAULT_SECTION {
|
||||
buf.WriteString(indent)
|
||||
}
|
||||
if key.Comment[0] != '#' && key.Comment[0] != ';' {
|
||||
key.Comment = "; " + key.Comment
|
||||
}
|
||||
if _, err = buf.WriteString(key.Comment + LineBreak); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(indent) > 0 && sname != DEFAULT_SECTION {
|
||||
buf.WriteString(indent)
|
||||
}
|
||||
|
||||
switch {
|
||||
case key.isAutoIncrement:
|
||||
kname = "-"
|
||||
case strings.ContainsAny(kname, "\"=:"):
|
||||
kname = "`" + kname + "`"
|
||||
case strings.Contains(kname, "`"):
|
||||
kname = `"""` + kname + `"""`
|
||||
}
|
||||
|
||||
for _, val := range key.ValueWithShadows() {
|
||||
if _, err = buf.WriteString(kname); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if key.isBooleanType {
|
||||
if kname != sec.keyList[len(sec.keyList)-1] {
|
||||
buf.WriteString(LineBreak)
|
||||
}
|
||||
continue KEY_LIST
|
||||
}
|
||||
|
||||
// Write out alignment spaces before "=" sign
|
||||
if PrettyFormat {
|
||||
buf.Write(alignSpaces[:alignLength-len(kname)])
|
||||
}
|
||||
|
||||
// In case key value contains "\n", "`", "\"", "#" or ";"
|
||||
if strings.ContainsAny(val, "\n`") {
|
||||
val = `"""` + val + `"""`
|
||||
} else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
|
||||
val = "`" + val + "`"
|
||||
}
|
||||
if _, err = buf.WriteString(equalSign + val + LineBreak); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PrettySection {
|
||||
// Put a line between sections
|
||||
if _, err = buf.WriteString(LineBreak); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf.WriteTo(w)
|
||||
}
|
||||
|
||||
// WriteTo writes file content into io.Writer.
|
||||
func (f *File) WriteTo(w io.Writer) (int64, error) {
|
||||
return f.WriteToIndent(w, "")
|
||||
}
|
||||
|
||||
// SaveToIndent writes content to file system with given value indention.
|
||||
func (f *File) SaveToIndent(filename, indent string) error {
|
||||
// Note: Because we are truncating with os.Create,
|
||||
// so it's safer to save to a temporary file location and rename afte done.
|
||||
tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp"
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
fw, err := os.Create(tmpPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = f.WriteToIndent(fw, indent); err != nil {
|
||||
fw.Close()
|
||||
return err
|
||||
}
|
||||
fw.Close()
|
||||
|
||||
// Remove old file and rename the new one.
|
||||
os.Remove(filename)
|
||||
return os.Rename(tmpPath, filename)
|
||||
}
|
||||
|
||||
// SaveTo writes content to file system.
|
||||
func (f *File) SaveTo(filename string) error {
|
||||
return f.SaveToIndent(filename, "")
|
||||
}
|
491
vendor/github.com/go-ini/ini/ini_test.go
generated
vendored
Normal file
491
vendor/github.com/go-ini/ini/ini_test.go
generated
vendored
Normal file
|
@ -0,0 +1,491 @@
|
|||
// Copyright 2014 Unknwon
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package ini
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func Test_Version(t *testing.T) {
|
||||
Convey("Get version", t, func() {
|
||||
So(Version(), ShouldEqual, _VERSION)
|
||||
})
|
||||
}
|
||||
|
||||
const _CONF_DATA = `
|
||||
; Package name
|
||||
NAME = ini
|
||||
; Package version
|
||||
VERSION = v1
|
||||
; Package import path
|
||||
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
|
||||
|
||||
# Information about package author
|
||||
# Bio can be written in multiple lines.
|
||||
[author]
|
||||
NAME = Unknwon ; Succeeding comment
|
||||
E-MAIL = fake@localhost
|
||||
GITHUB = https://github.com/%(NAME)s
|
||||
BIO = """Gopher.
|
||||
Coding addict.
|
||||
Good man.
|
||||
""" # Succeeding comment
|
||||
|
||||
[package]
|
||||
CLONE_URL = https://%(IMPORT_PATH)s
|
||||
|
||||
[package.sub]
|
||||
UNUSED_KEY = should be deleted
|
||||
|
||||
[features]
|
||||
-: Support read/write comments of keys and sections
|
||||
-: Support auto-increment of key names
|
||||
-: Support load multiple files to overwrite key values
|
||||
|
||||
[types]
|
||||
STRING = str
|
||||
BOOL = true
|
||||
BOOL_FALSE = false
|
||||
FLOAT64 = 1.25
|
||||
INT = 10
|
||||
TIME = 2015-01-01T20:17:05Z
|
||||
DURATION = 2h45m
|
||||
UINT = 3
|
||||
|
||||
[array]
|
||||
STRINGS = en, zh, de
|
||||
FLOAT64S = 1.1, 2.2, 3.3
|
||||
INTS = 1, 2, 3
|
||||
UINTS = 1, 2, 3
|
||||
TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z
|
||||
|
||||
[note]
|
||||
empty_lines = next line is empty\
|
||||
|
||||
; Comment before the section
|
||||
[comments] ; This is a comment for the section too
|
||||
; Comment before key
|
||||
key = "value"
|
||||
key2 = "value2" ; This is a comment for key2
|
||||
key3 = "one", "two", "three"
|
||||
|
||||
[advance]
|
||||
value with quotes = "some value"
|
||||
value quote2 again = 'some value'
|
||||
includes comment sign = ` + "`" + "my#password" + "`" + `
|
||||
includes comment sign2 = ` + "`" + "my;password" + "`" + `
|
||||
true = 2+3=5
|
||||
"1+1=2" = true
|
||||
"""6+1=7""" = true
|
||||
"""` + "`" + `5+5` + "`" + `""" = 10
|
||||
` + "`" + `"6+6"` + "`" + ` = 12
|
||||
` + "`" + `7-2=4` + "`" + ` = false
|
||||
ADDRESS = ` + "`" + `404 road,
|
||||
NotFound, State, 50000` + "`" + `
|
||||
|
||||
two_lines = how about \
|
||||
continuation lines?
|
||||
lots_of_lines = 1 \
|
||||
2 \
|
||||
3 \
|
||||
4 \
|
||||
`
|
||||
|
||||
func Test_Load(t *testing.T) {
|
||||
Convey("Load from data sources", t, func() {
|
||||
|
||||
Convey("Load with empty data", func() {
|
||||
So(Empty(), ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Load with multiple data sources", func() {
|
||||
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini", ioutil.NopCloser(bytes.NewReader([]byte(_CONF_DATA))))
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
|
||||
f, err := Load([]byte(_CONF_DATA), "testdata/404.ini")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(f, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Load with io.ReadCloser", func() {
|
||||
cfg, err := Load(ioutil.NopCloser(bytes.NewReader([]byte(_CONF_DATA))))
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
|
||||
So(cfg.Section("").Key("NAME").String(), ShouldEqual, "ini")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Bad load process", t, func() {
|
||||
|
||||
Convey("Load from invalid data sources", func() {
|
||||
_, err := Load(_CONF_DATA)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
f, err := Load("testdata/404.ini")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(f, ShouldBeNil)
|
||||
|
||||
_, err = Load(1)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = Load([]byte(""), 1)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Load with bad section name", func() {
|
||||
_, err := Load([]byte("[]"))
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = Load([]byte("["))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Load with bad keys", func() {
|
||||
_, err := Load([]byte(`"""name`))
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = Load([]byte(`"""name"""`))
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = Load([]byte(`""=1`))
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = Load([]byte(`=`))
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = Load([]byte(`name`))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Load with bad values", func() {
|
||||
_, err := Load([]byte(`name="""Unknwon`))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Get section and key insensitively", t, func() {
|
||||
cfg, err := InsensitiveLoad([]byte(_CONF_DATA), "testdata/conf.ini")
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
|
||||
sec, err := cfg.GetSection("Author")
|
||||
So(err, ShouldBeNil)
|
||||
So(sec, ShouldNotBeNil)
|
||||
|
||||
key, err := sec.GetKey("E-mail")
|
||||
So(err, ShouldBeNil)
|
||||
So(key, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Load with ignoring continuation lines", t, func() {
|
||||
cfg, err := LoadSources(LoadOptions{IgnoreContinuation: true}, []byte(`key1=a\b\
|
||||
key2=c\d\`))
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
|
||||
So(cfg.Section("").Key("key1").String(), ShouldEqual, `a\b\`)
|
||||
So(cfg.Section("").Key("key2").String(), ShouldEqual, `c\d\`)
|
||||
})
|
||||
|
||||
Convey("Load with ignoring inline comments", t, func() {
|
||||
cfg, err := LoadSources(LoadOptions{IgnoreInlineComment: true}, []byte(`key1=value ;comment
|
||||
key2=value #comment2`))
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
|
||||
So(cfg.Section("").Key("key1").String(), ShouldEqual, `value ;comment`)
|
||||
So(cfg.Section("").Key("key2").String(), ShouldEqual, `value #comment2`)
|
||||
|
||||
var buf bytes.Buffer
|
||||
cfg.WriteTo(&buf)
|
||||
So(buf.String(), ShouldEqual, `key1 = value ;comment
|
||||
key2 = value #comment2
|
||||
|
||||
`)
|
||||
})
|
||||
|
||||
Convey("Load with boolean type keys", t, func() {
|
||||
cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, []byte(`key1=hello
|
||||
key2
|
||||
#key3
|
||||
key4
|
||||
key5`))
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
|
||||
So(strings.Join(cfg.Section("").KeyStrings(), ","), ShouldEqual, "key1,key2,key4,key5")
|
||||
So(cfg.Section("").Key("key2").MustBool(false), ShouldBeTrue)
|
||||
|
||||
var buf bytes.Buffer
|
||||
cfg.WriteTo(&buf)
|
||||
// there is always a trailing \n at the end of the section
|
||||
So(buf.String(), ShouldEqual, `key1 = hello
|
||||
key2
|
||||
#key3
|
||||
key4
|
||||
key5
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_File_ChildSections(t *testing.T) {
|
||||
Convey("Find child sections by parent name", t, func() {
|
||||
cfg, err := Load([]byte(`
|
||||
[node]
|
||||
|
||||
[node.biz1]
|
||||
|
||||
[node.biz2]
|
||||
|
||||
[node.biz3]
|
||||
|
||||
[node.bizN]
|
||||
`))
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
|
||||
children := cfg.ChildSections("node")
|
||||
names := make([]string, len(children))
|
||||
for i := range children {
|
||||
names[i] = children[i].name
|
||||
}
|
||||
So(strings.Join(names, ","), ShouldEqual, "node.biz1,node.biz2,node.biz3,node.bizN")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_LooseLoad(t *testing.T) {
|
||||
Convey("Loose load from data sources", t, func() {
|
||||
Convey("Loose load mixed with nonexistent file", func() {
|
||||
cfg, err := LooseLoad("testdata/404.ini")
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
var fake struct {
|
||||
Name string `ini:"name"`
|
||||
}
|
||||
So(cfg.MapTo(&fake), ShouldBeNil)
|
||||
|
||||
cfg, err = LooseLoad([]byte("name=Unknwon"), "testdata/404.ini")
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg.Section("").Key("name").String(), ShouldEqual, "Unknwon")
|
||||
So(cfg.MapTo(&fake), ShouldBeNil)
|
||||
So(fake.Name, ShouldEqual, "Unknwon")
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_File_Append(t *testing.T) {
|
||||
Convey("Append data sources", t, func() {
|
||||
cfg, err := Load([]byte(""))
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
|
||||
So(cfg.Append([]byte(""), []byte("")), ShouldBeNil)
|
||||
|
||||
Convey("Append bad data sources", func() {
|
||||
So(cfg.Append(1), ShouldNotBeNil)
|
||||
So(cfg.Append([]byte(""), 1), ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_File_WriteTo(t *testing.T) {
|
||||
Convey("Write to somewhere", t, func() {
|
||||
var buf bytes.Buffer
|
||||
cfg := Empty()
|
||||
cfg.WriteTo(&buf)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_File_SaveTo_WriteTo(t *testing.T) {
|
||||
Convey("Save file", t, func() {
|
||||
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
|
||||
cfg.Section("").Key("NAME").Comment = "Package name"
|
||||
cfg.Section("author").Comment = `Information about package author
|
||||
# Bio can be written in multiple lines.`
|
||||
cfg.Section("advanced").Key("val w/ pound").SetValue("my#password")
|
||||
cfg.Section("advanced").Key("longest key has a colon : yes/no").SetValue("yes")
|
||||
So(cfg.SaveTo("testdata/conf_out.ini"), ShouldBeNil)
|
||||
|
||||
cfg.Section("author").Key("NAME").Comment = "This is author name"
|
||||
|
||||
So(cfg.SaveToIndent("testdata/conf_out.ini", "\t"), ShouldBeNil)
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = cfg.WriteToIndent(&buf, "\t")
|
||||
So(err, ShouldBeNil)
|
||||
So(buf.String(), ShouldEqual, `; Package name
|
||||
NAME = ini
|
||||
; Package version
|
||||
VERSION = v1
|
||||
; Package import path
|
||||
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
|
||||
|
||||
; Information about package author
|
||||
# Bio can be written in multiple lines.
|
||||
[author]
|
||||
; This is author name
|
||||
NAME = Unknwon
|
||||
E-MAIL = u@gogs.io
|
||||
GITHUB = https://github.com/%(NAME)s
|
||||
# Succeeding comment
|
||||
BIO = """Gopher.
|
||||
Coding addict.
|
||||
Good man.
|
||||
"""
|
||||
|
||||
[package]
|
||||
CLONE_URL = https://%(IMPORT_PATH)s
|
||||
|
||||
[package.sub]
|
||||
UNUSED_KEY = should be deleted
|
||||
|
||||
[features]
|
||||
- = Support read/write comments of keys and sections
|
||||
- = Support auto-increment of key names
|
||||
- = Support load multiple files to overwrite key values
|
||||
|
||||
[types]
|
||||
STRING = str
|
||||
BOOL = true
|
||||
BOOL_FALSE = false
|
||||
FLOAT64 = 1.25
|
||||
INT = 10
|
||||
TIME = 2015-01-01T20:17:05Z
|
||||
DURATION = 2h45m
|
||||
UINT = 3
|
||||
|
||||
[array]
|
||||
STRINGS = en, zh, de
|
||||
FLOAT64S = 1.1, 2.2, 3.3
|
||||
INTS = 1, 2, 3
|
||||
UINTS = 1, 2, 3
|
||||
TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z
|
||||
|
||||
[note]
|
||||
empty_lines = next line is empty
|
||||
|
||||
; Comment before the section
|
||||
; This is a comment for the section too
|
||||
[comments]
|
||||
; Comment before key
|
||||
key = value
|
||||
; This is a comment for key2
|
||||
key2 = value2
|
||||
key3 = "one", "two", "three"
|
||||
|
||||
[advance]
|
||||
value with quotes = some value
|
||||
value quote2 again = some value
|
||||
includes comment sign = `+"`"+"my#password"+"`"+`
|
||||
includes comment sign2 = `+"`"+"my;password"+"`"+`
|
||||
true = 2+3=5
|
||||
`+"`"+`1+1=2`+"`"+` = true
|
||||
`+"`"+`6+1=7`+"`"+` = true
|
||||
"""`+"`"+`5+5`+"`"+`""" = 10
|
||||
`+"`"+`"6+6"`+"`"+` = 12
|
||||
`+"`"+`7-2=4`+"`"+` = false
|
||||
ADDRESS = """404 road,
|
||||
NotFound, State, 50000"""
|
||||
two_lines = how about continuation lines?
|
||||
lots_of_lines = 1 2 3 4
|
||||
|
||||
[advanced]
|
||||
val w/ pound = `+"`"+`my#password`+"`"+`
|
||||
`+"`"+`longest key has a colon : yes/no`+"`"+` = yes
|
||||
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_File_WriteTo_SectionRaw(t *testing.T) {
|
||||
Convey("Write a INI with a raw section", t, func() {
|
||||
var buf bytes.Buffer
|
||||
cfg, err := LoadSources(
|
||||
LoadOptions{
|
||||
UnparseableSections: []string{"CORE_LESSON", "COMMENTS"},
|
||||
},
|
||||
"testdata/aicc.ini")
|
||||
So(err, ShouldBeNil)
|
||||
So(cfg, ShouldNotBeNil)
|
||||
cfg.WriteToIndent(&buf, "\t")
|
||||
So(buf.String(), ShouldEqual, `[Core]
|
||||
Lesson_Location = 87
|
||||
Lesson_Status = C
|
||||
Score = 3
|
||||
Time = 00:02:30
|
||||
|
||||
[CORE_LESSON]
|
||||
my lesson state data – 1111111111111111111000000000000000001110000
|
||||
111111111111111111100000000000111000000000 – end my lesson state data
|
||||
[COMMENTS]
|
||||
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
// Helpers for slice tests.
|
||||
func float64sEqual(values []float64, expected ...float64) {
|
||||
So(values, ShouldHaveLength, len(expected))
|
||||
for i, v := range expected {
|
||||
So(values[i], ShouldEqual, v)
|
||||
}
|
||||
}
|
||||
|
||||
func intsEqual(values []int, expected ...int) {
|
||||
So(values, ShouldHaveLength, len(expected))
|
||||
for i, v := range expected {
|
||||
So(values[i], ShouldEqual, v)
|
||||
}
|
||||
}
|
||||
|
||||
func int64sEqual(values []int64, expected ...int64) {
|
||||
So(values, ShouldHaveLength, len(expected))
|
||||
for i, v := range expected {
|
||||
So(values[i], ShouldEqual, v)
|
||||
}
|
||||
}
|
||||
|
||||
func uintsEqual(values []uint, expected ...uint) {
|
||||
So(values, ShouldHaveLength, len(expected))
|
||||
for i, v := range expected {
|
||||
So(values[i], ShouldEqual, v)
|
||||
}
|
||||
}
|
||||
|
||||
func uint64sEqual(values []uint64, expected ...uint64) {
|
||||
So(values, ShouldHaveLength, len(expected))
|
||||
for i, v := range expected {
|
||||
So(values[i], ShouldEqual, v)
|
||||
}
|
||||
}
|
||||
|
||||
func timesEqual(values []time.Time, expected ...time.Time) {
|
||||
So(values, ShouldHaveLength, len(expected))
|
||||
for i, v := range expected {
|
||||
So(values[i].String(), ShouldEqual, v.String())
|
||||
}
|
||||
}
|
699
vendor/github.com/go-ini/ini/key.go
generated
vendored
Normal file
699
vendor/github.com/go-ini/ini/key.go
generated
vendored
Normal file
|
@ -0,0 +1,699 @@
|
|||
// Copyright 2014 Unknwon
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package ini
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Key represents a key under a section.
|
||||
type Key struct {
|
||||
s *Section
|
||||
name string
|
||||
value string
|
||||
isAutoIncrement bool
|
||||
isBooleanType bool
|
||||
|
||||
isShadow bool
|
||||
shadows []*Key
|
||||
|
||||
Comment string
|
||||
}
|
||||
|
||||
// newKey simply return a key object with given values.
|
||||
func newKey(s *Section, name, val string) *Key {
|
||||
return &Key{
|
||||
s: s,
|
||||
name: name,
|
||||
value: val,
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Key) addShadow(val string) error {
|
||||
if k.isShadow {
|
||||
return errors.New("cannot add shadow to another shadow key")
|
||||
} else if k.isAutoIncrement || k.isBooleanType {
|
||||
return errors.New("cannot add shadow to auto-increment or boolean key")
|
||||
}
|
||||
|
||||
shadow := newKey(k.s, k.name, val)
|
||||
shadow.isShadow = true
|
||||
k.shadows = append(k.shadows, shadow)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddShadow adds a new shadow key to itself.
|
||||
func (k *Key) AddShadow(val string) error {
|
||||
if !k.s.f.options.AllowShadows {
|
||||
return errors.New("shadow key is not allowed")
|
||||
}
|
||||
return k.addShadow(val)
|
||||
}
|
||||
|
||||
// ValueMapper represents a mapping function for values, e.g. os.ExpandEnv
|
||||
type ValueMapper func(string) string
|
||||
|
||||
// Name returns name of key.
|
||||
func (k *Key) Name() string {
|
||||
return k.name
|
||||
}
|
||||
|
||||
// Value returns raw value of key for performance purpose.
|
||||
func (k *Key) Value() string {
|
||||
return k.value
|
||||
}
|
||||
|
||||
// ValueWithShadows returns raw values of key and its shadows if any.
|
||||
func (k *Key) ValueWithShadows() []string {
|
||||
if len(k.shadows) == 0 {
|
||||
return []string{k.value}
|
||||
}
|
||||
vals := make([]string, len(k.shadows)+1)
|
||||
vals[0] = k.value
|
||||
for i := range k.shadows {
|
||||
vals[i+1] = k.shadows[i].value
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// transformValue takes a raw value and transforms to its final string.
|
||||
func (k *Key) transformValue(val string) string {
|
||||
if k.s.f.ValueMapper != nil {
|
||||
val = k.s.f.ValueMapper(val)
|
||||
}
|
||||
|
||||
// Fail-fast if no indicate char found for recursive value
|
||||
if !strings.Contains(val, "%") {
|
||||
return val
|
||||
}
|
||||
for i := 0; i < _DEPTH_VALUES; i++ {
|
||||
vr := varPattern.FindString(val)
|
||||
if len(vr) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Take off leading '%(' and trailing ')s'.
|
||||
noption := strings.TrimLeft(vr, "%(")
|
||||
noption = strings.TrimRight(noption, ")s")
|
||||
|
||||
// Search in the same section.
|
||||
nk, err := k.s.GetKey(noption)
|
||||
if err != nil {
|
||||
// Search again in default section.
|
||||
nk, _ = k.s.f.Section("").GetKey(noption)
|
||||
}
|
||||
|
||||
// Substitute by new value and take off leading '%(' and trailing ')s'.
|
||||
val = strings.Replace(val, vr, nk.value, -1)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// String returns string representation of value.
|
||||
func (k *Key) String() string {
|
||||
return k.transformValue(k.value)
|
||||
}
|
||||
|
||||
// Validate accepts a validate function which can
|
||||
// return modifed result as key value.
|
||||
func (k *Key) Validate(fn func(string) string) string {
|
||||
return fn(k.String())
|
||||
}
|
||||
|
||||
// parseBool returns the boolean value represented by the string.
|
||||
//
|
||||
// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On,
|
||||
// 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off.
|
||||
// Any other value returns an error.
|
||||
func parseBool(str string) (value bool, err error) {
|
||||
switch str {
|
||||
case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On":
|
||||
return true, nil
|
||||
case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off":
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("parsing \"%s\": invalid syntax", str)
|
||||
}
|
||||
|
||||
// Bool returns bool type value.
|
||||
func (k *Key) Bool() (bool, error) {
|
||||
return parseBool(k.String())
|
||||
}
|
||||
|
||||
// Float64 returns float64 type value.
|
||||
func (k *Key) Float64() (float64, error) {
|
||||
return strconv.ParseFloat(k.String(), 64)
|
||||
}
|
||||
|
||||
// Int returns int type value.
|
||||
func (k *Key) Int() (int, error) {
|
||||
return strconv.Atoi(k.String())
|
||||
}
|
||||
|
||||
// Int64 returns int64 type value.
|
||||
func (k *Key) Int64() (int64, error) {
|
||||
return strconv.ParseInt(k.String(), 10, 64)
|
||||
}
|
||||
|
||||
// Uint returns uint type valued.
|
||||
func (k *Key) Uint() (uint, error) {
|
||||
u, e := strconv.ParseUint(k.String(), 10, 64)
|
||||
return uint(u), e
|
||||
}
|
||||
|
||||
// Uint64 returns uint64 type value.
|
||||
func (k *Key) Uint64() (uint64, error) {
|
||||
return strconv.ParseUint(k.String(), 10, 64)
|
||||
}
|
||||
|
||||
// Duration returns time.Duration type value.
|
||||
func (k *Key) Duration() (time.Duration, error) {
|
||||
return time.ParseDuration(k.String())
|
||||
}
|
||||
|
||||
// TimeFormat parses with given format and returns time.Time type value.
|
||||
func (k *Key) TimeFormat(format string) (time.Time, error) {
|
||||
return time.Parse(format, k.String())
|
||||
}
|
||||
|
||||
// Time parses with RFC3339 format and returns time.Time type value.
|
||||
func (k *Key) Time() (time.Time, error) {
|
||||
return k.TimeFormat(time.RFC3339)
|
||||
}
|
||||
|
||||
// MustString returns default value if key value is empty.
|
||||
func (k *Key) MustString(defaultVal string) string {
|
||||
val := k.String()
|
||||
if len(val) == 0 {
|
||||
k.value = defaultVal
|
||||
return defaultVal
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustBool always returns value without error,
|
||||
// it returns false if error occurs.
|
||||
func (k *Key) MustBool(defaultVal ...bool) bool {
|
||||
val, err := k.Bool()
|
||||
if len(defaultVal) > 0 && err != nil {
|
||||
k.value = strconv.FormatBool(defaultVal[0])
|
||||
return defaultVal[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustFloat64 always returns value without error,
|
||||
// it returns 0.0 if error occurs.
|
||||
func (k *Key) MustFloat64(defaultVal ...float64) float64 {
|
||||
val, err := k.Float64()
|
||||
if len(defaultVal) > 0 && err != nil {
|
||||
k.value = strconv.FormatFloat(defaultVal[0], 'f', -1, 64)
|
||||
return defaultVal[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustInt always returns value without error,
|
||||
// it returns 0 if error occurs.
|
||||
func (k *Key) MustInt(defaultVal ...int) int {
|
||||
val, err := k.Int()
|
||||
if len(defaultVal) > 0 && err != nil {
|
||||
k.value = strconv.FormatInt(int64(defaultVal[0]), 10)
|
||||
return defaultVal[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustInt64 always returns value without error,
|
||||
// it returns 0 if error occurs.
|
||||
func (k *Key) MustInt64(defaultVal ...int64) int64 {
|
||||
val, err := k.Int64()
|
||||
if len(defaultVal) > 0 && err != nil {
|
||||
k.value = strconv.FormatInt(defaultVal[0], 10)
|
||||
return defaultVal[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustUint always returns value without error,
|
||||
// it returns 0 if error occurs.
|
||||
func (k *Key) MustUint(defaultVal ...uint) uint {
|
||||
val, err := k.Uint()
|
||||
if len(defaultVal) > 0 && err != nil {
|
||||
k.value = strconv.FormatUint(uint64(defaultVal[0]), 10)
|
||||
return defaultVal[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustUint64 always returns value without error,
|
||||
// it returns 0 if error occurs.
|
||||
func (k *Key) MustUint64(defaultVal ...uint64) uint64 {
|
||||
val, err := k.Uint64()
|
||||
if len(defaultVal) > 0 && err != nil {
|
||||
k.value = strconv.FormatUint(defaultVal[0], 10)
|
||||
return defaultVal[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustDuration always returns value without error,
|
||||
// it returns zero value if error occurs.
|
||||
func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration {
|
||||
val, err := k.Duration()
|
||||
if len(defaultVal) > 0 && err != nil {
|
||||
k.value = defaultVal[0].String()
|
||||
return defaultVal[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustTimeFormat always parses with given format and returns value without error,
|
||||
// it returns zero value if error occurs.
|
||||
func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time {
|
||||
val, err := k.TimeFormat(format)
|
||||
if len(defaultVal) > 0 && err != nil {
|
||||
k.value = defaultVal[0].Format(format)
|
||||
return defaultVal[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustTime always parses with RFC3339 format and returns value without error,
|
||||
// it returns zero value if error occurs.
|
||||
func (k *Key) MustTime(defaultVal ...time.Time) time.Time {
|
||||
return k.MustTimeFormat(time.RFC3339, defaultVal...)
|
||||
}
|
||||
|
||||
// In always returns value without error,
|
||||
// it returns default value if error occurs or doesn't fit into candidates.
|
||||
func (k *Key) In(defaultVal string, candidates []string) string {
|
||||
val := k.String()
|
||||
for _, cand := range candidates {
|
||||
if val == cand {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// InFloat64 always returns value without error,
|
||||
// it returns default value if error occurs or doesn't fit into candidates.
|
||||
func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 {
|
||||
val := k.MustFloat64()
|
||||
for _, cand := range candidates {
|
||||
if val == cand {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// InInt always returns value without error,
|
||||
// it returns default value if error occurs or doesn't fit into candidates.
|
||||
func (k *Key) InInt(defaultVal int, candidates []int) int {
|
||||
val := k.MustInt()
|
||||
for _, cand := range candidates {
|
||||
if val == cand {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// InInt64 always returns value without error,
|
||||
// it returns default value if error occurs or doesn't fit into candidates.
|
||||
func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 {
|
||||
val := k.MustInt64()
|
||||
for _, cand := range candidates {
|
||||
if val == cand {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// InUint always returns value without error,
|
||||
// it returns default value if error occurs or doesn't fit into candidates.
|
||||
func (k *Key) InUint(defaultVal uint, candidates []uint) uint {
|
||||
val := k.MustUint()
|
||||
for _, cand := range candidates {
|
||||
if val == cand {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// InUint64 always returns value without error,
|
||||
// it returns default value if error occurs or doesn't fit into candidates.
|
||||
func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64 {
|
||||
val := k.MustUint64()
|
||||
for _, cand := range candidates {
|
||||
if val == cand {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// InTimeFormat always parses with given format and returns value without error,
|
||||
// it returns default value if error occurs or doesn't fit into candidates.
|
||||
func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time {
|
||||
val := k.MustTimeFormat(format)
|
||||
for _, cand := range candidates {
|
||||
if val == cand {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// InTime always parses with RFC3339 format and returns value without error,
|
||||
// it returns default value if error occurs or doesn't fit into candidates.
|
||||
func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time {
|
||||
return k.InTimeFormat(time.RFC3339, defaultVal, candidates)
|
||||
}
|
||||
|
||||
// RangeFloat64 checks if value is in given range inclusively,
|
||||
// and returns default value if it's not.
|
||||
func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 {
|
||||
val := k.MustFloat64()
|
||||
if val < min || val > max {
|
||||
return defaultVal
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// RangeInt checks if value is in given range inclusively,
|
||||
// and returns default value if it's not.
|
||||
func (k *Key) RangeInt(defaultVal, min, max int) int {
|
||||
val := k.MustInt()
|
||||
if val < min || val > max {
|
||||
return defaultVal
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// RangeInt64 checks if value is in given range inclusively,
|
||||
// and returns default value if it's not.
|
||||
func (k *Key) RangeInt64(defaultVal, min, max int64) int64 {
|
||||
val := k.MustInt64()
|
||||
if val < min || val > max {
|
||||
return defaultVal
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// RangeTimeFormat checks if value with given format is in given range inclusively,
|
||||
// and returns default value if it's not.
|
||||
func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time {
|
||||
val := k.MustTimeFormat(format)
|
||||
if val.Unix() < min.Unix() || val.Unix() > max.Unix() {
|
||||
return defaultVal
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// RangeTime checks if value with RFC3339 format is in given range inclusively,
|
||||
// and returns default value if it's not.
|
||||
func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time {
|
||||
return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max)
|
||||
}
|
||||
|
||||
// Strings returns list of string divided by given delimiter.
|
||||
func (k *Key) Strings(delim string) []string {
|
||||
str := k.String()
|
||||
if len(str) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
vals := strings.Split(str, delim)
|
||||
for i := range vals {
|
||||
// vals[i] = k.transformValue(strings.TrimSpace(vals[i]))
|
||||
vals[i] = strings.TrimSpace(vals[i])
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// StringsWithShadows returns list of string divided by given delimiter.
|
||||
// Shadows will also be appended if any.
|
||||
func (k *Key) StringsWithShadows(delim string) []string {
|
||||
vals := k.ValueWithShadows()
|
||||
results := make([]string, 0, len(vals)*2)
|
||||
for i := range vals {
|
||||
if len(vals) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, strings.Split(vals[i], delim)...)
|
||||
}
|
||||
|
||||
for i := range results {
|
||||
results[i] = k.transformValue(strings.TrimSpace(results[i]))
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// Float64s returns list of float64 divided by given delimiter. Any invalid input will be treated as zero value.
|
||||
func (k *Key) Float64s(delim string) []float64 {
|
||||
vals, _ := k.parseFloat64s(k.Strings(delim), true, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// Ints returns list of int divided by given delimiter. Any invalid input will be treated as zero value.
|
||||
func (k *Key) Ints(delim string) []int {
|
||||
vals, _ := k.parseInts(k.Strings(delim), true, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// Int64s returns list of int64 divided by given delimiter. Any invalid input will be treated as zero value.
|
||||
func (k *Key) Int64s(delim string) []int64 {
|
||||
vals, _ := k.parseInt64s(k.Strings(delim), true, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// Uints returns list of uint divided by given delimiter. Any invalid input will be treated as zero value.
|
||||
func (k *Key) Uints(delim string) []uint {
|
||||
vals, _ := k.parseUints(k.Strings(delim), true, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// Uint64s returns list of uint64 divided by given delimiter. Any invalid input will be treated as zero value.
|
||||
func (k *Key) Uint64s(delim string) []uint64 {
|
||||
vals, _ := k.parseUint64s(k.Strings(delim), true, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// TimesFormat parses with given format and returns list of time.Time divided by given delimiter.
|
||||
// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
|
||||
func (k *Key) TimesFormat(format, delim string) []time.Time {
|
||||
vals, _ := k.parseTimesFormat(format, k.Strings(delim), true, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// Times parses with RFC3339 format and returns list of time.Time divided by given delimiter.
|
||||
// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
|
||||
func (k *Key) Times(delim string) []time.Time {
|
||||
return k.TimesFormat(time.RFC3339, delim)
|
||||
}
|
||||
|
||||
// ValidFloat64s returns list of float64 divided by given delimiter. If some value is not float, then
|
||||
// it will not be included to result list.
|
||||
func (k *Key) ValidFloat64s(delim string) []float64 {
|
||||
vals, _ := k.parseFloat64s(k.Strings(delim), false, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// ValidInts returns list of int divided by given delimiter. If some value is not integer, then it will
|
||||
// not be included to result list.
|
||||
func (k *Key) ValidInts(delim string) []int {
|
||||
vals, _ := k.parseInts(k.Strings(delim), false, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// ValidInt64s returns list of int64 divided by given delimiter. If some value is not 64-bit integer,
|
||||
// then it will not be included to result list.
|
||||
func (k *Key) ValidInt64s(delim string) []int64 {
|
||||
vals, _ := k.parseInt64s(k.Strings(delim), false, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// ValidUints returns list of uint divided by given delimiter. If some value is not unsigned integer,
|
||||
// then it will not be included to result list.
|
||||
func (k *Key) ValidUints(delim string) []uint {
|
||||
vals, _ := k.parseUints(k.Strings(delim), false, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// ValidUint64s returns list of uint64 divided by given delimiter. If some value is not 64-bit unsigned
|
||||
// integer, then it will not be included to result list.
|
||||
func (k *Key) ValidUint64s(delim string) []uint64 {
|
||||
vals, _ := k.parseUint64s(k.Strings(delim), false, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter.
|
||||
func (k *Key) ValidTimesFormat(format, delim string) []time.Time {
|
||||
vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false)
|
||||
return vals
|
||||
}
|
||||
|
||||
// ValidTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter.
|
||||
func (k *Key) ValidTimes(delim string) []time.Time {
|
||||
return k.ValidTimesFormat(time.RFC3339, delim)
|
||||
}
|
||||
|
||||
// StrictFloat64s returns list of float64 divided by given delimiter or error on first invalid input.
|
||||
func (k *Key) StrictFloat64s(delim string) ([]float64, error) {
|
||||
return k.parseFloat64s(k.Strings(delim), false, true)
|
||||
}
|
||||
|
||||
// StrictInts returns list of int divided by given delimiter or error on first invalid input.
|
||||
func (k *Key) StrictInts(delim string) ([]int, error) {
|
||||
return k.parseInts(k.Strings(delim), false, true)
|
||||
}
|
||||
|
||||
// StrictInt64s returns list of int64 divided by given delimiter or error on first invalid input.
|
||||
func (k *Key) StrictInt64s(delim string) ([]int64, error) {
|
||||
return k.parseInt64s(k.Strings(delim), false, true)
|
||||
}
|
||||
|
||||
// StrictUints returns list of uint divided by given delimiter or error on first invalid input.
|
||||
func (k *Key) StrictUints(delim string) ([]uint, error) {
|
||||
return k.parseUints(k.Strings(delim), false, true)
|
||||
}
|
||||
|
||||
// StrictUint64s returns list of uint64 divided by given delimiter or error on first invalid input.
|
||||
func (k *Key) StrictUint64s(delim string) ([]uint64, error) {
|
||||
return k.parseUint64s(k.Strings(delim), false, true)
|
||||
}
|
||||
|
||||
// StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter
|
||||
// or error on first invalid input.
|
||||
func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) {
|
||||
return k.parseTimesFormat(format, k.Strings(delim), false, true)
|
||||
}
|
||||
|
||||
// StrictTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter
|
||||
// or error on first invalid input.
|
||||
func (k *Key) StrictTimes(delim string) ([]time.Time, error) {
|
||||
return k.StrictTimesFormat(time.RFC3339, delim)
|
||||
}
|
||||
|
||||
// parseFloat64s transforms strings to float64s.
|
||||
func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) {
|
||||
vals := make([]float64, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil || addInvalid {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// parseInts transforms strings to ints.
|
||||
func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) {
|
||||
vals := make([]int, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := strconv.Atoi(str)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil || addInvalid {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// parseInt64s transforms strings to int64s.
|
||||
func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) {
|
||||
vals := make([]int64, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := strconv.ParseInt(str, 10, 64)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil || addInvalid {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// parseUints transforms strings to uints.
|
||||
func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) {
|
||||
vals := make([]uint, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := strconv.ParseUint(str, 10, 0)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil || addInvalid {
|
||||
vals = append(vals, uint(val))
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// parseUint64s transforms strings to uint64s.
|
||||
func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) {
|
||||
vals := make([]uint64, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := strconv.ParseUint(str, 10, 64)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil || addInvalid {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// parseTimesFormat transforms strings to times in given format.
|
||||
func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) {
|
||||
vals := make([]time.Time, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
val, err := time.Parse(format, str)
|
||||
if err != nil && returnOnInvalid {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil || addInvalid {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// SetValue changes key value.
|
||||
func (k *Key) SetValue(v string) {
|
||||
if k.s.f.BlockMode {
|
||||
k.s.f.lock.Lock()
|
||||
defer k.s.f.lock.Unlock()
|
||||
}
|
||||
|
||||
k.value = v
|
||||
k.s.keysHash[k.name] = v
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue