Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Neumann
5c076245d4 add version file 2015-08-19 17:19:09 +02:00
1227 changed files with 67228 additions and 96000 deletions

View File

@@ -1,12 +0,0 @@
# Folders
.git/
.github/
changelog/
doc/
docker/
helpers/
# Files
.gitignore
.golangci.yml
*.md

View File

@@ -1,93 +0,0 @@
---
name: Bug report
about: Report a problem with restic to help us resolve it and improve
---
<!--
Welcome! - We kindly ask that you:
1. Fill out the issue template below - not doing so needs a good reason.
2. Use the forum if you have a question rather than a bug or feature request.
The forum is at: https://forum.restic.net
NOTE: Not filling out the issue template needs a good reason, as otherwise it
may take a lot longer to find the problem, not to mention it can take up a lot
more time which can otherwise be spent on development. Please also take the
time to help us debug the issue by collecting relevant information, even if
it doesn't seem to be relevant to you. Thanks!
The forum is a better place for questions about restic or general suggestions
and topics, e.g. usage or documentation questions! This issue tracker is mainly
for tracking bugs and feature requests directly relating to the development of
the software itself, rather than the project.
Thanks for understanding, and for contributing to the project!
-->
Output of `restic version`
--------------------------
How did you run restic exactly?
-------------------------------
<!--
This section should include at least:
* The complete command line and any environment variables you used to
configure restic's backend access. Make sure to replace sensitive values!
* The output of the commands, what restic prints gives may give us much
information to diagnose the problem!
-->
What backend/server/service did you use to store the repository?
----------------------------------------------------------------
Expected behavior
-----------------
<!--
Describe what you'd like restic to do differently.
-->
Actual behavior
---------------
<!--
In this section, please try to concentrate on observations, so only describe
what you observed directly.
-->
Steps to reproduce the behavior
-------------------------------
<!--
The more time you spend describing an easy way to reproduce the behavior (if
this is possible), the easier it is for the project developers to fix it!
-->
Do you have any idea what may have caused this?
-----------------------------------------------
Do you have an idea how to solve the issue?
-------------------------------------------
Did restic help you today? Did it make you happy in any way?
------------------------------------------------------------
<!--
Answering this question is not required, but if you have anything positive to share, please do so here!
Sometimes we get tired of reading bug reports all day and a little positive end note does wonders.
Idea by Joey Hess, https://joeyh.name/blog/entry/two_holiday_stories/
-->

View File

@@ -1,57 +0,0 @@
---
name: Feature request
about: Suggest a new feature or enhancement for restic
---
<!--
Welcome! - We kindly ask that you:
1. Fill out the issue template below - not doing so needs a good reason.
2. Use the forum if you have a question rather than a bug or feature request.
The forum is at: https://forum.restic.net
The forum is a better place for questions about restic or general suggestions
and topics, e.g. usage or documentation questions! This issue tracker is mainly
for tracking bugs and feature requests directly relating to the development of
the software itself, rather than the project.
Thanks for understanding, and for contributing to the project!
-->
Output of `restic version`
--------------------------
<!--
Please add the version of restic you're currently using here, this helps us
later to see what has changed in restic when we revisit this issue after some
time.
-->
What should restic do differently? Which functionality do you think we should add?
----------------------------------------------------------------------------------
<!--
Please describe the feature you'd like us to add here.
-->
What are you trying to do? What problem would this solve?
---------------------------------------------------------
<!--
This section should contain a brief description what you're trying to do, which
would be possible after implementing the new feature.
-->
Did restic help you today? Did it make you happy in any way?
------------------------------------------------------------
<!--
Answering this question is not required, but if you have anything positive to share, please do so here!
Sometimes we get tired of reading bug reports all day and a little positive end note does wonders.
Idea by Joey Hess, https://joeyh.name/blog/entry/two_holiday_stories/
-->

View File

@@ -1,4 +0,0 @@
contact_links:
- name: restic forum
url: https://forum.restic.net
about: Please ask questions about using restic here, do not open an issue for questions.

View File

@@ -1,40 +0,0 @@
<!--
Thank you very much for contributing code or documentation to restic! Please
fill out the following questions to make it easier for us to review your
changes.
-->
What does this PR change? What problem does it solve?
-----------------------------------------------------
<!--
Describe the changes and their purpose here, as detailed as needed.
-->
Was the change previously discussed in an issue or on the forum?
----------------------------------------------------------------
<!--
Link issues and relevant forum posts here.
If this PR resolves an issue on GitHub, use "Closes #1234" so that the issue
is closed automatically when this PR is merged.
-->
Checklist
---------
<!--
You do not need to check all the boxes below all at once. Feel free to take
your time and add more commits. If you're done and ready for review, please
check the last box. Enable a checkbox by replacing [ ] with [x].
-->
- [ ] I have read the [contribution guidelines](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#providing-patches).
- [ ] I have [enabled maintainer edits](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork).
- [ ] I have added tests for all code changes.
- [ ] I have added documentation for relevant changes (in the manual).
- [ ] There's a new file in `changelog/unreleased/` that describes the changes for our users (see [template](https://github.com/restic/restic/blob/master/changelog/TEMPLATE)).
- [ ] I have run `gofmt` on the code in all commits.
- [ ] All commit messages are formatted in the same style as [the other commits in the repo](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#git-commits).
- [ ] I'm done! This pull request is ready for review.

View File

@@ -1,325 +0,0 @@
name: test
on:
# run tests on push to master, but not when other branches are pushed to
push:
branches:
- master
# run tests for all pull requests
pull_request:
env:
latest_go: "1.18.x"
GO111MODULE: on
jobs:
test:
strategy:
matrix:
# list of jobs to run:
include:
- job_name: Windows
go: 1.18.x
os: windows-latest
install_verb: install
- job_name: macOS
go: 1.18.x
os: macOS-latest
test_fuse: false
install_verb: install
- job_name: Linux
go: 1.18.x
os: ubuntu-latest
test_cloud_backends: true
test_fuse: true
check_changelog: true
install_verb: install
- job_name: Linux
go: 1.17.x
os: ubuntu-latest
test_fuse: true
install_verb: install
- job_name: Linux
go: 1.16.x
os: ubuntu-latest
test_fuse: true
install_verb: get
- job_name: Linux
go: 1.15.x
os: ubuntu-latest
test_fuse: true
install_verb: get
- job_name: Linux
go: 1.14.x
os: ubuntu-latest
test_fuse: true
install_verb: get
name: ${{ matrix.job_name }} Go ${{ matrix.go }}
runs-on: ${{ matrix.os }}
env:
GOPROXY: https://proxy.golang.org
steps:
- name: Set up Go ${{ matrix.go }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}
- name: Get programs (Linux/macOS)
run: |
echo "build Go tools"
go ${{ matrix.install_verb }} github.com/restic/rest-server/cmd/rest-server@latest
echo "install minio server"
mkdir $HOME/bin
if [ "$RUNNER_OS" == "macOS" ]; then
wget --no-verbose -O $HOME/bin/minio https://dl.minio.io/server/minio/release/darwin-amd64/minio
else
wget --no-verbose -O $HOME/bin/minio https://dl.minio.io/server/minio/release/linux-amd64/minio
fi
chmod 755 $HOME/bin/minio
echo "install rclone"
if [ "$RUNNER_OS" == "macOS" ]; then
wget --no-verbose -O rclone.zip https://downloads.rclone.org/rclone-current-osx-amd64.zip
else
wget --no-verbose -O rclone.zip https://downloads.rclone.org/rclone-current-linux-amd64.zip
fi
unzip rclone.zip
cp rclone*/rclone $HOME/bin
chmod 755 $HOME/bin/rclone
rm -rf rclone*
# add $HOME/bin to path ($GOBIN was already added to the path by setup-go@v2)
echo $HOME/bin >> $GITHUB_PATH
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest'
- name: Get programs (Windows)
shell: powershell
run: |
$ProgressPreference = 'SilentlyContinue'
echo "build Go tools"
go ${{ matrix.install_verb }} github.com/restic/rest-server/...
echo "install minio server"
mkdir $Env:USERPROFILE/bin
Invoke-WebRequest https://dl.minio.io/server/minio/release/windows-amd64/minio.exe -OutFile $Env:USERPROFILE/bin/minio.exe
echo "install rclone"
Invoke-WebRequest https://downloads.rclone.org/rclone-current-windows-amd64.zip -OutFile rclone.zip
unzip rclone.zip
copy rclone*/rclone.exe $Env:USERPROFILE/bin
# add $USERPROFILE/bin to path ($GOBIN was already added to the path by setup-go@v2)
echo $Env:USERPROFILE\bin >> $Env:GITHUB_PATH
echo "install tar"
cd $env:USERPROFILE
mkdir tar
cd tar
# install exactly these versions of tar and the libraries, other combinations might not work!
Invoke-WebRequest https://github.com/restic/test-assets/raw/master/tar-1.13-1-bin.zip -OutFile tar.zip
unzip tar.zip
Invoke-WebRequest https://github.com/restic/test-assets/raw/master/libintl-0.11.5-2-bin.zip -OutFile libintl.zip
unzip libintl.zip
Invoke-WebRequest https://github.com/restic/test-assets/raw/master/libiconv-1.8-1-bin.zip -OutFile libiconv.zip
unzip libiconv.zip
# add $USERPROFILE/tar/bin to path
echo $Env:USERPROFILE\tar\bin >> $Env:GITHUB_PATH
if: matrix.os == 'windows-latest'
- name: Check out code
uses: actions/checkout@v2
- name: Build with build.go
run: |
go run build.go
- name: Run local Tests
env:
RESTIC_TEST_FUSE: ${{ matrix.test_fuse }}
run: |
go test -cover ./...
- name: Test cloud backends
env:
RESTIC_TEST_S3_KEY: ${{ secrets.RESTIC_TEST_S3_KEY }}
RESTIC_TEST_S3_SECRET: ${{ secrets.RESTIC_TEST_S3_SECRET }}
RESTIC_TEST_S3_REPOSITORY: ${{ secrets.RESTIC_TEST_S3_REPOSITORY }}
RESTIC_TEST_AZURE_ACCOUNT_NAME: ${{ secrets.RESTIC_TEST_AZURE_ACCOUNT_NAME }}
RESTIC_TEST_AZURE_ACCOUNT_KEY: ${{ secrets.RESTIC_TEST_AZURE_ACCOUNT_KEY }}
RESTIC_TEST_AZURE_REPOSITORY: ${{ secrets.RESTIC_TEST_AZURE_REPOSITORY }}
RESTIC_TEST_B2_ACCOUNT_ID: ${{ secrets.RESTIC_TEST_B2_ACCOUNT_ID }}
RESTIC_TEST_B2_ACCOUNT_KEY: ${{ secrets.RESTIC_TEST_B2_ACCOUNT_KEY }}
RESTIC_TEST_B2_REPOSITORY: ${{ secrets.RESTIC_TEST_B2_REPOSITORY }}
RESTIC_TEST_GS_REPOSITORY: ${{ secrets.RESTIC_TEST_GS_REPOSITORY }}
RESTIC_TEST_GS_PROJECT_ID: ${{ secrets.RESTIC_TEST_GS_PROJECT_ID }}
GOOGLE_PROJECT_ID: ${{ secrets.RESTIC_TEST_GS_PROJECT_ID }}
RESTIC_TEST_GS_APPLICATION_CREDENTIALS_B64: ${{ secrets.RESTIC_TEST_GS_APPLICATION_CREDENTIALS_B64 }}
RESTIC_TEST_OS_AUTH_URL: ${{ secrets.RESTIC_TEST_OS_AUTH_URL }}
RESTIC_TEST_OS_TENANT_NAME: ${{ secrets.RESTIC_TEST_OS_TENANT_NAME }}
RESTIC_TEST_OS_USERNAME: ${{ secrets.RESTIC_TEST_OS_USERNAME }}
RESTIC_TEST_OS_PASSWORD: ${{ secrets.RESTIC_TEST_OS_PASSWORD }}
RESTIC_TEST_OS_REGION_NAME: ${{ secrets.RESTIC_TEST_OS_REGION_NAME }}
RESTIC_TEST_SWIFT: ${{ secrets.RESTIC_TEST_SWIFT }}
# fail if any of the following tests cannot be run
RESTIC_TEST_DISALLOW_SKIP: "restic/backend/rest.TestBackendREST,\
restic/backend/sftp.TestBackendSFTP,\
restic/backend/s3.TestBackendMinio,\
restic/backend/rclone.TestBackendRclone,\
restic/backend/s3.TestBackendS3,\
restic/backend/swift.TestBackendSwift,\
restic/backend/b2.TestBackendB2,\
restic/backend/gs.TestBackendGS,\
restic/backend/azure.TestBackendAzure"
run: |
# prepare credentials for Google Cloud Storage tests in a temp file
export GOOGLE_APPLICATION_CREDENTIALS=$(mktemp --tmpdir restic-gcs-auth-XXXXXXX)
echo $RESTIC_TEST_GS_APPLICATION_CREDENTIALS_B64 | base64 -d > $GOOGLE_APPLICATION_CREDENTIALS
go test -cover -parallel 4 ./internal/backend/...
# only run cloud backend tests for pull requests from and pushes to our
# own repo, otherwise the secrets are not available
if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && matrix.test_cloud_backends
- name: Check changelog files with calens
run: |
echo "install calens"
go install github.com/restic/calens@latest
echo "check changelog files"
calens
if: matrix.check_changelog
cross_compile:
strategy:
# ATTENTION: the list of architectures must be in sync with helpers/build-release-binaries/main.go!
matrix:
# run cross-compile in two batches parallel so the overall tests run faster
targets:
- "linux/386 linux/amd64 linux/arm linux/arm64 linux/ppc64le linux/mips linux/mipsle linux/mips64 linux/mips64le linux/s390x \
openbsd/386 openbsd/amd64"
- "freebsd/386 freebsd/amd64 freebsd/arm \
aix/ppc64 \
darwin/amd64 darwin/arm64 \
netbsd/386 netbsd/amd64 \
windows/386 windows/amd64 \
solaris/amd64"
env:
GOPROXY: https://proxy.golang.org
runs-on: ubuntu-latest
name: Cross Compile for ${{ matrix.targets }}
steps:
- name: Set up Go ${{ env.latest_go }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.latest_go }}
- name: Install gox
run: |
go install github.com/mitchellh/gox@latest
- name: Check out code
uses: actions/checkout@v2
- name: Cross-compile with gox for ${{ matrix.targets }}
env:
GOFLAGS: "-trimpath"
GOX_ARCHS: "${{ matrix.targets }}"
run: |
mkdir build-output
gox -parallel 2 -verbose -osarch "$GOX_ARCHS" -output "build-output/{{.Dir}}_{{.OS}}_{{.Arch}}" ./cmd/restic
gox -parallel 2 -verbose -osarch "$GOX_ARCHS" -tags debug -output "build-output/{{.Dir}}_{{.OS}}_{{.Arch}}_debug" ./cmd/restic
lint:
name: lint
runs-on: ubuntu-latest
steps:
- name: Set up Go ${{ env.latest_go }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.latest_go }}
- name: Check out code
uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.45
# Optional: show only new issues if it's a pull request. The default value is `false`.
only-new-issues: true
args: --verbose --timeout 5m
skip-go-installation: true
# only run golangci-lint for pull requests, otherwise ALL hints get
# reported. We need to slowly address all issues until we can enable
# linting the master branch :)
if: github.event_name == 'pull_request'
- name: Check go.mod/go.sum
run: |
echo "check if go.mod and go.sum are up to date"
go mod tidy
git diff --exit-code go.mod go.sum
docker:
name: docker
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
# list of Docker images to use as base name for tags
images: |
restic/restic
# generate Docker tags based on the following events/attributes
tags: |
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
push: false
context: .
file: docker/Dockerfile
pull: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

7
.gitignore vendored
View File

@@ -1,3 +1,8 @@
/.gopath
/restic
/restic.debug
/dirdiff
cmd/dirdiff/dirdiff
cmd/gentestdata/gentestdata
cmd/restic/restic
/.vagrant
/.vscode

View File

@@ -1,57 +0,0 @@
# This is the configuration for golangci-lint for the restic project.
#
# A sample config with all settings is here:
# https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml
linters:
# only enable the linters listed below
disable-all: true
enable:
# make sure all errors returned by functions are handled
- errcheck
# find unused code
- deadcode
# show how code can be simplified
- gosimple
# # make sure code is formatted
- gofmt
# examine code and report suspicious constructs, such as Printf calls whose
# arguments do not align with the format string
- govet
# make sure names and comments are used according to the conventions
- golint
# detect when assignments to existing variables are not used
- ineffassign
# run static analysis and find errors
- staticcheck
# find unused variables, functions, structs, types, etc.
- unused
# find unused struct fields
- structcheck
# find unused global variables
- varcheck
# parse and typecheck code
- typecheck
issues:
# don't use the default exclude rules, this hides (among others) ignored
# errors from Close() calls
exclude-use-default: false
# list of things to not warn about
exclude:
# golint: do not warn about missing comments for exported stuff
- exported (function|method|var|type|const) `.*` should have comment or be unexported
# golint: ignore constants in all caps
- don't use ALL_CAPS in Go names; use CamelCase

46
.travis.yml Normal file
View File

@@ -0,0 +1,46 @@
language: go
sudo: false
go:
- 1.3.3
- 1.4.2
os:
- linux
- osx
env: GOX_OS="linux darwin openbsd freebsd" GOX_ARCH="386 amd64 arm"
notifications:
irc:
channels:
- "chat.freenode.net#restic"
on_success: change
on_failure: change
skip_join: true
install:
- go version
- export GOBIN="$GOPATH/bin"
- export PATH="$PATH:$GOBIN"
- export GOPATH="$GOPATH:${TRAVIS_BUILD_DIR}/Godeps/_workspace"
- go env
- go get github.com/mattn/goveralls
- go get github.com/mitchellh/gox
- go version | grep -q "go1\.3" && export GOX_ARCH="386 amd64" || true
- go version | grep -q "darwin" && export GOX_OS="darwin" || true
- uname -s | grep -qi darwin && brew install caskroom/cask/brew-cask || true
- uname -s | grep -qi darwin && brew cask install osxfuse || true
- uname -s | grep -vqi darwin && export RESTIC_TEST_FUSE="0" || true
- echo "cross-compile for \"$GOX_OS\" on \"$GOX_ARCH\""
- gox -build-toolchain -os "$GOX_OS" -arch "$GOX_ARCH"
script:
- gox -verbose -os "$GOX_OS" -arch "$GOX_ARCH" -tags "release" ./cmd/restic
- gox -verbose -os "$GOX_OS" -arch "$GOX_ARCH" -tags "debug" ./cmd/restic
- go run build.go
- go run run_tests.go all.cov
- GOARCH=386 RESTIC_TEST_INTEGRATION=0 go test ./...
- goveralls -coverprofile=all.cov -service=travis-ci -repotoken "$COVERALLS_TOKEN" || true
- gofmt -l *.go */*.go */*/*.go
- test -z "$(gofmt -l *.go */*.go */*/*.go)"

File diff suppressed because it is too large Load Diff

View File

@@ -3,20 +3,17 @@ This document describes the way you can contribute to the restic project.
Ways to Help Out
================
Thank you for your contribution! Please **open an issue first** (or add a
comment to an existing issue) if you plan to work on any code or add a new
feature. This way, duplicate work is prevented and we can discuss your ideas
and design first.
Thank you for your contribution!
There are several ways you can help us out. First of all code contributions and
bug fixes are most welcome. However even "minor" details as fixing spelling
errors, improving documentation or pointing out usability issues are a great
help also.
The restic project uses the GitHub infrastructure (see the
[project page](https://github.com/restic/restic)) for all related discussions
as well as the [forum](https://forum.restic.net/) and the `#restic` channel
on [irc.libera.chat](https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:6697/#restic).
as well as the `#restic` channel on `irc.freenode.net`.
If you want to find an area that currently needs improving have a look at the
open issues listed at the
@@ -25,63 +22,53 @@ for discussing enhancement to the restic tools.
If you are unsure what to do, please have a look at the issues, especially
those tagged
[minor complexity](https://github.com/restic/restic/labels/help%3A%20minor%20complexity)
or [good first issue](https://github.com/restic/restic/labels/help%3A%20good%20first%20issue).
If you are already a bit experienced with the restic internals, take a look
at the issues tagged as [help wanted](https://github.com/restic/restic/labels/help%3A%20wanted).
Reporting Bugs
==============
You've found a bug? Thanks for letting us know so we can fix it! It is a good
idea to describe in detail how to reproduce the bug (when you know how), what
environment was used and so on. Please tell us at least the following things:
* What's the version of restic you used? Please include the output of
`restic version` in your bug report.
* What commands did you execute to get to where the bug occurred?
* What did you expect?
* What happened instead?
* Are you aware of a way to reproduce the bug?
Remember, the easier it is for us to reproduce the bug, the earlier it will be
corrected!
In addition, you can compile restic with debug support by running
`go run build.go -tags debug` and instructing it to create a debug
log by setting the environment variable `DEBUG_LOG` to a file, e.g. like this:
$ export DEBUG_LOG=/tmp/restic-debug.log
$ restic backup ~/work
Please be aware that the debug log file will contain potentially sensitive
things like file and directory names, so please either redact it before
uploading it somewhere or post only the parts that are really relevant.
[minor complexity](https://github.com/restic/restic/labels/minor%20complexity).
Development Environment
=======================
The repository contains the code written for restic in the directories
`cmd/` and `internal/`.
For development, it is recommended to check out the restic repository within a
`GOPATH`, an introductory text is
["How to Write Go Code"](https://golang.org/doc/code.html). It is recommended
to have a working directory, we're using `~/work/restic` in the following. This
directory mainly contains the directory `src`, where the source code is stored.
Restic requires Go version 1.14 or later for compiling. Clone the repo (without
having `$GOPATH` set) and `cd` into the directory:
First, create the necessary directory structure and clone the restic repository
to the correct location:
$ unset GOPATH
$ mkdir --parents ~/work/restic/src/github.com/restic
$ cd ~/work/restic/src/github.com/restic
$ git clone https://github.com/restic/restic
$ cd restic
Then use the `go` tool to build restic:
Now we're in the main directory of the restic repository. The last step is to
set the environment variable `$GOPATH` to the correct value:
$ go build ./cmd/restic
$ ./restic version
restic 0.10.0-dev (compiled manually) compiled with go1.15.2 on linux/amd64
$ export GOPATH=~/work/restic:~/work/restic/src/github.com/restic/restic/Godeps/_workspace
You can run all tests with the following command:
The following commands can be used to run all the tests:
$ go test ./...
ok github.com/restic/restic 8.174s
[...]
The restic binary can be built from the directory `cmd/restic` this way:
$ cd cmd/restic
$ go build
$ ./restic version
restic compiled manually on go1.4.2
if you want to run your tests on Linux, OpenBSD or FreeBSD, you can use
[vagrant](https://www.vagrantup.com/) with the proveded `Vagrantfile` to
quickly set up VMs and run the tests, e.g.:
$ vagrant up freebsd
[...]
$ vagrant ssh freebsd -c 'cd restic/restic; go test -v ./...'
[...]
Providing Patches
=================
@@ -91,85 +78,48 @@ get it into the project! The workflow we're using is also described on the
[GitHub Flow](https://guides.github.com/introduction/flow/) website, it boils
down to the following steps:
0. If you want to work on something, please add a comment to the issue on
GitHub. For a new feature, please add an issue before starting to work on
it, so that duplicate work is prevented.
1. Next, fork our project on GitHub if you haven't done so already.
2. Clone your fork of the repository locally and **create a new branch** for
your changes. If you are working on the code itself, please set up the
development environment as described in the previous section.
3. Commit your changes to the new branch as fine grained as possible, as
smaller patches, for individual changes, are easier to discuss and merge.
1. First we would kindly ask you to fork our project on GitHub if you haven't
done so already.
2. Clone the repository locally and create a new branch. If you are working on
the code itself, please set up the development environment as described in
the previous section and instead of cloning add your fork on GitHub as a
remote to the clone of the restic repository.
3. Then commit your changes as fine grained as possible, as smaller patches,
that handle one and only one issue are easier to discuss and merge.
4. Push the new branch with your changes to your fork of the repository.
5. Create a pull request by visiting the GitHub website, it will guide you
through the process. Please [allow edits from maintainers](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork).
through the process.
6. You will receive comments on your code and the feature or bug that they
address. Maybe you need to rework some minor things, in this case push new
commits to the branch you created for the pull request (or amend the
existing commit, use common sense to decide which is better), they will be
commits to the branch you created for the pull request, they will be
automatically added to the pull request.
7. If your pull request changes anything that users should be aware of
(a bugfix, a new feature, ...) please add an entry as a new file in
`changelog/unreleased` including the issue number in the filename (e.g.
`issue-8756`). Use the template in `changelog/TEMPLATE` for the content.
It will be used in the announcement of the next stable release. While
writing, ask yourself: If I were the user, what would I need to be aware
of with this change?
8. Do not edit the man pages under `doc/man` or `doc/manual_rest.rst` -
these are autogenerated before new releases.
9. Once your code looks good and passes all the tests, we'll merge it. Thanks
a lot for your contribution!
7. Once your code looks good, we'll merge it. Thanks a low for your
contribution!
Please provide the patches for each bug or feature in a separate branch and
open up a pull request for each, as this simplifies discussion and merging.
open up a pull request for each.
The restic project uses the `gofmt` tool for Go source indentation, so please
run
gofmt -w **/*.go
in the project root directory before committing. For each Pull Request, the
formatting is tested with `gofmt` for the latest stable version of Go.
Installing the script `fmt-check` from https://github.com/edsrzf/gofmt-git-hook
locally as a pre-commit hook checks formatting before committing automatically,
just copy this script to `.git/hooks/pre-commit`.
The project is using the program
[`golangci-lint`](https://github.com/golangci/golangci-lint) to run a list of
linters and checkers. It will be run on the code when you submit a PR. In order
to check your code beforehand, you can run `golangci-lint run` manually.
Eventually, we will enable `golangci-lint` for the whole code base. For now,
you can ignore warnings printed for lines you did not modify, those will be
ignored by the CI.
For each pull request, several different systems run the integration tests on
Linux, macOS and Windows. We won't merge any code that does not pass all tests
for all systems, so when a tests fails, try to find out what's wrong and fix
it. If you need help on this, please leave a comment in the pull request, and
we'll be glad to assist. Having a PR with failing integration tests is nothing
to be ashamed of. In contrast, that happens regularly for all of us. That's
what the tests are there for.
in the project root directory before committing. Installing the script
`fmt-check` from https://github.com/edsrzf/gofmt-git-hook locally as a
pre-commit hook checks formatting before committing automatically, just copy
this script to `.git/hooks/pre-commit`.
Git Commits
-----------
It would be good if you could follow the same general style regarding Git
I would be good if you could follow the same general style regarding Git
commits as the rest of the project, this makes reviewing code, browsing the
history and triaging bugs much easier.
Git commit messages have a very terse summary in the first line of the commit
message, followed by an empty line, followed by a more verbose description or a
List of changed things. For examples, please refer to the excellent [How to
Write a Git Commit Message](https://chris.beams.io/posts/git-commit/).
Write a Git Commit Message](http://chris.beams.io/posts/git-commit/).
If you change/add multiple different things that aren't related at all, try to
make several smaller commits. This is much easier to review. Using `git add -p`

View File

@@ -1,27 +0,0 @@
# restic project governance
## Overview
The restic project uses a governance model commonly described as Benevolent
Dictator For Life (BDFL). This document outlines our understanding of what this
means. It is derived from the [i3 window manager project
governance](https://raw.githubusercontent.com/i3/i3/next/.github/GOVERNANCE.md).
## Roles
* user: anyone who interacts with the restic project
* core contributor: a handful of people who have contributed significantly to
the project by any means (issue triage, support, documentation, code, etc.).
Core contributors are recognizable via GitHubs "Member" badge.
* Benevolent Dictator For Life (BDFL): a single individual who makes decisions
when consensus cannot be reached. restic's current BDFL is [@fd0](https://github.com/fd0).
## Decision making process
In general, we try to reach consensus in discussions. In case consensus cannot
be reached, the BDFL makes a decision.
## Contribution process
The contribution process is described in a separate document called
[CONTRIBUTING](CONTRIBUTING.md).

62
Godeps/Godeps.json generated Normal file
View File

@@ -0,0 +1,62 @@
{
"ImportPath": "github.com/restic/restic",
"GoVersion": "go1.4.2",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "bazil.org/fuse",
"Rev": "18419ee53958df28fcfc9490fe6123bd59e237bb"
},
{
"ImportPath": "github.com/jessevdk/go-flags",
"Comment": "v1-297-g1b89bf7",
"Rev": "1b89bf73cd2c3a911d7b2a279ab085c4a18cf539"
},
{
"ImportPath": "github.com/juju/errors",
"Rev": "4567a5e69fd3130ca0d89f69478e7ac025b67452"
},
{
"ImportPath": "github.com/kr/fs",
"Rev": "2788f0dbd16903de03cb8186e5c7d97b69ad387b"
},
{
"ImportPath": "github.com/mitchellh/goamz/aws",
"Rev": "caaaea8b30ee15616494ee68abd5d8ebbbef05cf"
},
{
"ImportPath": "github.com/mitchellh/goamz/s3",
"Rev": "caaaea8b30ee15616494ee68abd5d8ebbbef05cf"
},
{
"ImportPath": "github.com/pkg/sftp",
"Rev": "518aed2757a65cfa64d4b1b2baf08410f8b7a6bc"
},
{
"ImportPath": "github.com/restic/chunker",
"Rev": "e795b80f4c927ebcf2687ce18bcf1a39fee740b1"
},
{
"ImportPath": "github.com/vaughan0/go-ini",
"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
},
{
"ImportPath": "golang.org/x/crypto/pbkdf2",
"Rev": "cc04154d65fb9296747569b107cfd05380b1ea3e"
},
{
"ImportPath": "golang.org/x/crypto/poly1305",
"Rev": "cc04154d65fb9296747569b107cfd05380b1ea3e"
},
{
"ImportPath": "golang.org/x/crypto/scrypt",
"Rev": "cc04154d65fb9296747569b107cfd05380b1ea3e"
},
{
"ImportPath": "golang.org/x/crypto/ssh",
"Rev": "cc04154d65fb9296747569b107cfd05380b1ea3e"
}
]
}

5
Godeps/Readme generated Normal file
View File

@@ -0,0 +1,5 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

2
Godeps/_workspace/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
/pkg
/bin

2
Godeps/_workspace/src/bazil.org/fuse/.gitattributes generated vendored Normal file
View File

@@ -0,0 +1,2 @@
*.go filter=gofmt
*.cgo filter=gofmt

11
Godeps/_workspace/src/bazil.org/fuse/.gitignore generated vendored Normal file
View 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
Godeps/_workspace/src/bazil.org/fuse/LICENSE generated vendored Normal file
View 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
Godeps/_workspace/src/bazil.org/fuse/README.md generated vendored Normal file
View 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.
Heres 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
Godeps/_workspace/src/bazil.org/fuse/buffer.go generated vendored Normal file
View 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
Godeps/_workspace/src/bazil.org/fuse/debug.go generated vendored Normal file
View 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
Godeps/_workspace/src/bazil.org/fuse/doc/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,4 @@
/*.seq.svg
# not ignoring *.seq.png; we want those committed to the repo
# for embedding on Github

6
Godeps/_workspace/src/bazil.org/fuse/doc/README.md generated vendored Normal file
View 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)

View 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 ...
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View 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 ...
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

45
Godeps/_workspace/src/bazil.org/fuse/doc/mount-osx.seq generated vendored Normal file
View 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View 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)

View 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
Godeps/_workspace/src/bazil.org/fuse/error_darwin.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
package fuse
import (
"syscall"
)
const (
ENOATTR = Errno(syscall.ENOATTR)
)
const (
errNoXattr = ENOATTR
)
func init() {
errnoNames[errNoXattr] = "ENOATTR"
}

15
Godeps/_workspace/src/bazil.org/fuse/error_freebsd.go generated vendored Normal file
View File

@@ -0,0 +1,15 @@
package fuse
import "syscall"
const (
ENOATTR = Errno(syscall.ENOATTR)
)
const (
errNoXattr = ENOATTR
)
func init() {
errnoNames[errNoXattr] = "ENOATTR"
}

17
Godeps/_workspace/src/bazil.org/fuse/error_linux.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
package fuse
import (
"syscall"
)
const (
ENODATA = Errno(syscall.ENODATA)
)
const (
errNoXattr = ENODATA
)
func init() {
errnoNames[errNoXattr] = "ENODATA"
}

31
Godeps/_workspace/src/bazil.org/fuse/error_std.go generated vendored Normal file
View 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

View File

@@ -0,0 +1,173 @@
// 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 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("clock"),
fuse.Subtype("clockfsfs"),
fuse.LocalVolume(),
fuse.VolumeName("Clock filesystem"),
)
if err != nil {
log.Fatal(err)
}
defer c.Close()
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 {
log.Fatal(err)
}
// Check if the mount process has an error to report.
<-c.Ready
if err := c.MountError; 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()
}
}

View 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"
)
var Usage = func() {
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
}

View 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
Godeps/_workspace/src/bazil.org/fuse/fs/bench/doc.go generated vendored Normal file
View 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

View 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)
}

View File

@@ -0,0 +1 @@
package fstestutil

View File

@@ -0,0 +1,115 @@
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)
}
// 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) {
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,
}
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")
}
}
// 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) {
if conf == nil {
conf = &fs.Config{}
}
if debug && conf.Debug == nil {
conf.Debug = func(msg interface{}) {
t.Logf("FUSE: %s", msg)
}
}
return Mounted(filesys, conf, options...)
}

View 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)
}

View 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
}

View 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")
}

View 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
}

View 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()
}

View File

@@ -0,0 +1,384 @@
package 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))
}

View 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
}
}

View 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
}

View 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())
}

1567
Godeps/_workspace/src/bazil.org/fuse/fs/serve.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

2403
Godeps/_workspace/src/bazil.org/fuse/fs/serve_test.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

99
Godeps/_workspace/src/bazil.org/fuse/fs/tree.go generated vendored Normal file
View 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
}

2193
Godeps/_workspace/src/bazil.org/fuse/fuse.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

766
Godeps/_workspace/src/bazil.org/fuse/fuse_kernel.go generated vendored Normal file
View File

@@ -0,0 +1,766 @@
// 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
Padding 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
OpenAppend OpenFlags = syscall.O_APPEND
OpenCreate OpenFlags = syscall.O_CREAT
OpenExclusive OpenFlags = syscall.O_EXCL
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(OpenCreate), "OpenCreate"},
{uint32(OpenExclusive), "OpenExclusive"},
{uint32(OpenTruncate), "OpenTruncate"},
{uint32(OpenAppend), "OpenAppend"},
{uint32(OpenSync), "OpenSync"},
}
// 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
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
dummy uint32
Fh uint64
}
type attrOut struct {
AttrValid uint64 // Cache timeout for the attributes
AttrValidNsec uint32
Dummy 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
padding 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
}
type linkIn struct {
Oldnodeid uint64
}
type setattrInCommon struct {
Valid uint32
Padding 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
Padding uint32
}
type createIn struct {
Flags uint32
Mode uint32
Umask uint32
padding 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
Padding uint32
LockOwner uint64
}
type readIn struct {
Fh uint64
Offset uint64
Size uint32
ReadFlags uint32
LockOwner uint64
Flags uint32
padding 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
padding 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
Padding 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
Padding uint32
}
type setxattrInCommon struct {
Size uint32
Flags uint32
}
func (setxattrInCommon) position() uint32 {
return 0
}
type getxattrInCommon struct {
Size uint32
Padding uint32
}
func (getxattrInCommon) position() uint32 {
return 0
}
type getxattrOut struct {
Size uint32
Padding uint32
}
type lkIn struct {
Fh uint64
Owner uint64
Lk fileLock
LkFlags uint32
padding 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
Padding 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
Padding uint32
}
type bmapOut struct {
Block uint64
}
type inHeader struct {
Len uint32
Opcode uint32
Unique uint64
Nodeid uint64
Uid uint32
Gid uint32
Pid uint32
Padding 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
padding uint32
}

View 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
}

View 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
}

View 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
}

View File

@@ -0,0 +1 @@
package fuse

View File

@@ -0,0 +1,31 @@
package fuse_test
import (
"os"
"testing"
"bazil.org/fuse"
)
func TestOpenFlagsAccmodeMask(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 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)
}
}

View File

@@ -0,0 +1,20 @@
package 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]
}

126
Godeps/_workspace/src/bazil.org/fuse/mount_darwin.go generated vendored Normal file
View File

@@ -0,0 +1,126 @@
package fuse
import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
)
var errNoAvail = errors.New("no available fuse devices")
var errNotLoaded = errors.New("osxfusefs is not loaded")
func loadOSXFUSE() error {
cmd := exec.Command("/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs")
cmd.Dir = "/"
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
return err
}
func openOSXFUSEDev() (*os.File, error) {
var f *os.File
var err error
for i := uint64(0); ; i++ {
path := "/dev/osxfuse" + 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 callMount(dir string, conf *mountConfig, f *os.File, ready chan<- struct{}, errp *error) error {
bin := "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
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()
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
// TODO this is used for fs typenames etc, let app influence it
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_DAEMON_PATH="+bin)
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
err := cmd.Start()
if err != nil {
return err
}
go func() {
err := cmd.Wait()
if err != nil {
if buf.Len() > 0 {
output := buf.Bytes()
output = bytes.TrimRight(output, "\n")
msg := err.Error() + ": " + string(output)
err = errors.New(msg)
}
}
*errp = err
close(ready)
}()
return err
}
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
f, err := openOSXFUSEDev()
if err == errNotLoaded {
err = loadOSXFUSE()
if err != nil {
return nil, err
}
// try again
f, err = openOSXFUSEDev()
}
if err != nil {
return nil, err
}
err = callMount(dir, conf, f, ready, errp)
if err != nil {
f.Close()
return nil, err
}
return f, nil
}

41
Godeps/_workspace/src/bazil.org/fuse/mount_freebsd.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
package fuse
import (
"fmt"
"os"
"os/exec"
"strings"
)
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}
out, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("mount_fusefs: %q, %v", out, err)
}
close(ready)
return f, nil
}

112
Godeps/_workspace/src/bazil.org/fuse/mount_linux.go generated vendored Normal file
View File

@@ -0,0 +1,112 @@
package fuse
import (
"bufio"
"fmt"
"io"
"log"
"net"
"os"
"os/exec"
"sync"
"syscall"
)
func lineLogger(wg *sync.WaitGroup, prefix string, r io.ReadCloser) {
defer wg.Done()
scanner := bufio.NewScanner(r)
for scanner.Scan() {
switch line := scanner.Text(); line {
case `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.
continue
default:
log.Printf("%s: %s", prefix, line)
}
}
if err := scanner.Err(); err != nil {
log.Printf("%s, error reading: %v", prefix, err)
}
}
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)
}
wg.Add(2)
go lineLogger(&wg, "mount helper output", stdout)
go lineLogger(&wg, "mount helper error", stderr)
wg.Wait()
if err := cmd.Wait(); err != nil {
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
}

170
Godeps/_workspace/src/bazil.org/fuse/options.go generated vendored Normal file
View File

@@ -0,0 +1,170 @@
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
}
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)
}
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
}
}
// 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
}
}

13
Godeps/_workspace/src/bazil.org/fuse/options_darwin.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
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
}
}

View File

@@ -0,0 +1,9 @@
package fuse
func localVolume(conf *mountConfig) error {
return nil
}
func volumeName(name string) MountOption {
return dummyOption
}

View 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
}

View File

@@ -0,0 +1,9 @@
package fuse
func localVolume(conf *mountConfig) error {
return nil
}
func volumeName(name string) MountOption {
return dummyOption
}

View 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
Godeps/_workspace/src/bazil.org/fuse/options_test.go generated vendored Normal file
View 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
Godeps/_workspace/src/bazil.org/fuse/protocol.go generated vendored Normal file
View 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
Godeps/_workspace/src/bazil.org/fuse/syscallx/doc.go generated vendored Normal file
View 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

34
Godeps/_workspace/src/bazil.org/fuse/syscallx/generate generated vendored Normal file
View 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

View 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)

View 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
}

View 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
}

View File

@@ -0,0 +1,4 @@
package syscallx
// make us look more like package syscall, so mksyscall.pl output works
var _zero uintptr

View 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)
}

View 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)
}

View 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
}

View 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
Godeps/_workspace/src/bazil.org/fuse/unmount.go generated vendored Normal file
View 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
Godeps/_workspace/src/bazil.org/fuse/unmount_linux.go generated vendored Normal file
View 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
Godeps/_workspace/src/bazil.org/fuse/unmount_std.go generated vendored Normal file
View 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
}

View File

@@ -0,0 +1,35 @@
language: go
install:
# go-flags
- go get -d -v ./...
- go build -v ./...
# linting
- go get golang.org/x/tools/cmd/vet
- go get github.com/golang/lint
- go install github.com/golang/lint/golint
# code coverage
- go get golang.org/x/tools/cmd/cover
- go get github.com/onsi/ginkgo/ginkgo
- go get github.com/modocache/gover
- if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then go get github.com/mattn/goveralls; fi
script:
# go-flags
- $(exit $(gofmt -l . | wc -l))
- go test -v ./...
# linting
- go tool vet -all=true -v=true . || true
- $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/golint ./...
# code coverage
- $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/ginkgo -r -cover
- $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/gover
- if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/goveralls -coverprofile=gover.coverprofile -service=travis-ci -repotoken $COVERALLS_TOKEN; fi
env:
# coveralls.io
secure: "RCYbiB4P0RjQRIoUx/vG/AjP3mmYCbzOmr86DCww1Z88yNcy3hYr3Cq8rpPtYU5v0g7wTpu4adaKIcqRE9xknYGbqj3YWZiCoBP1/n4Z+9sHW3Dsd9D/GRGeHUus0laJUGARjWoCTvoEtOgTdGQDoX7mH+pUUY0FBltNYUdOiiU="

View File

@@ -0,0 +1,26 @@
Copyright (c) 2012 Jesse van den Kieboom. 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.

View File

@@ -0,0 +1,131 @@
go-flags: a go library for parsing command line arguments
=========================================================
[![GoDoc](https://godoc.org/github.com/jessevdk/go-flags?status.png)](https://godoc.org/github.com/jessevdk/go-flags) [![Build Status](https://travis-ci.org/jessevdk/go-flags.svg?branch=master)](https://travis-ci.org/jessevdk/go-flags) [![Coverage Status](https://img.shields.io/coveralls/jessevdk/go-flags.svg)](https://coveralls.io/r/jessevdk/go-flags?branch=master)
This library provides similar functionality to the builtin flag library of
go, but provides much more functionality and nicer formatting. From the
documentation:
Package flags provides an extensive command line option parser.
The flags package is similar in functionality to the go builtin flag package
but provides more options and uses reflection to provide a convenient and
succinct way of specifying command line options.
Supported features:
* Options with short names (-v)
* Options with long names (--verbose)
* Options with and without arguments (bool v.s. other type)
* Options with optional arguments and default values
* Multiple option groups each containing a set of options
* Generate and print well-formatted help message
* Passing remaining command line arguments after -- (optional)
* Ignoring unknown command line options (optional)
* Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification
* Supports multiple short options -aux
* Supports all primitive go types (string, int{8..64}, uint{8..64}, float)
* Supports same option multiple times (can store in slice or last option counts)
* Supports maps
* Supports function callbacks
* Supports namespaces for (nested) option groups
The flags package uses structs, reflection and struct field tags
to allow users to specify command line options. This results in very simple
and concise specification of your application options. For example:
type Options struct {
Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"`
}
This specifies one option with a short name -v and a long name --verbose.
When either -v or --verbose is found on the command line, a 'true' value
will be appended to the Verbose field. e.g. when specifying -vvv, the
resulting value of Verbose will be {[true, true, true]}.
Example:
--------
var opts struct {
// Slice of bool will append 'true' each time the option
// is encountered (can be set multiple times, like -vvv)
Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"`
// Example of automatic marshalling to desired type (uint)
Offset uint `long:"offset" description:"Offset"`
// Example of a callback, called each time the option is found.
Call func(string) `short:"c" description:"Call phone number"`
// Example of a required flag
Name string `short:"n" long:"name" description:"A name" required:"true"`
// Example of a value name
File string `short:"f" long:"file" description:"A file" value-name:"FILE"`
// Example of a pointer
Ptr *int `short:"p" description:"A pointer to an integer"`
// Example of a slice of strings
StringSlice []string `short:"s" description:"A slice of strings"`
// Example of a slice of pointers
PtrSlice []*string `long:"ptrslice" description:"A slice of pointers to string"`
// Example of a map
IntMap map[string]int `long:"intmap" description:"A map from string to int"`
}
// Callback which will invoke callto:<argument> to call a number.
// Note that this works just on OS X (and probably only with
// Skype) but it shows the idea.
opts.Call = func(num string) {
cmd := exec.Command("open", "callto:"+num)
cmd.Start()
cmd.Process.Release()
}
// Make some fake arguments to parse.
args := []string{
"-vv",
"--offset=5",
"-n", "Me",
"-p", "3",
"-s", "hello",
"-s", "world",
"--ptrslice", "hello",
"--ptrslice", "world",
"--intmap", "a:1",
"--intmap", "b:5",
"arg1",
"arg2",
"arg3",
}
// Parse flags from `args'. Note that here we use flags.ParseArgs for
// the sake of making a working example. Normally, you would simply use
// flags.Parse(&opts) which uses os.Args
args, err := flags.ParseArgs(&opts, args)
if err != nil {
panic(err)
os.Exit(1)
}
fmt.Printf("Verbosity: %v\n", opts.Verbose)
fmt.Printf("Offset: %d\n", opts.Offset)
fmt.Printf("Name: %s\n", opts.Name)
fmt.Printf("Ptr: %d\n", *opts.Ptr)
fmt.Printf("StringSlice: %v\n", opts.StringSlice)
fmt.Printf("PtrSlice: [%v %v]\n", *opts.PtrSlice[0], *opts.PtrSlice[1])
fmt.Printf("IntMap: [a:%v b:%v]\n", opts.IntMap["a"], opts.IntMap["b"])
fmt.Printf("Remaining args: %s\n", strings.Join(args, " "))
// Output: Verbosity: [true true]
// Offset: 5
// Name: Me
// Ptr: 3
// StringSlice: [hello world]
// PtrSlice: [hello world]
// IntMap: [a:1 b:5]
// Remaining args: arg1 arg2 arg3
More information can be found in the godocs: <http://godoc.org/github.com/jessevdk/go-flags>

View File

@@ -0,0 +1,21 @@
package flags
import (
"reflect"
)
// Arg represents a positional argument on the command line.
type Arg struct {
// The name of the positional argument (used in the help)
Name string
// A description of the positional argument (used in the help)
Description string
value reflect.Value
tag multiTag
}
func (a *Arg) isRemaining() bool {
return a.value.Type().Kind() == reflect.Slice
}

View File

@@ -0,0 +1,53 @@
package flags
import (
"testing"
)
func TestPositional(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Positional struct {
Command int
Filename string
Rest []string
} `positional-args:"yes" required:"yes"`
}{}
p := NewParser(&opts, Default)
ret, err := p.ParseArgs([]string{"10", "arg_test.go", "a", "b"})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
return
}
if opts.Positional.Command != 10 {
t.Fatalf("Expected opts.Positional.Command to be 10, but got %v", opts.Positional.Command)
}
if opts.Positional.Filename != "arg_test.go" {
t.Fatalf("Expected opts.Positional.Filename to be \"arg_test.go\", but got %v", opts.Positional.Filename)
}
assertStringArray(t, opts.Positional.Rest, []string{"a", "b"})
assertStringArray(t, ret, []string{})
}
func TestPositionalRequired(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Positional struct {
Command int
Filename string
Rest []string
} `positional-args:"yes" required:"yes"`
}{}
p := NewParser(&opts, None)
_, err := p.ParseArgs([]string{"10"})
assertError(t, err, ErrRequired, "the required argument `Filename` was not provided")
}

View File

@@ -0,0 +1,177 @@
package flags
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
"runtime"
"testing"
)
func assertCallerInfo() (string, int) {
ptr := make([]uintptr, 15)
n := runtime.Callers(1, ptr)
if n == 0 {
return "", 0
}
mef := runtime.FuncForPC(ptr[0])
mefile, meline := mef.FileLine(ptr[0])
for i := 2; i < n; i++ {
f := runtime.FuncForPC(ptr[i])
file, line := f.FileLine(ptr[i])
if file != mefile {
return file, line
}
}
return mefile, meline
}
func assertErrorf(t *testing.T, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
file, line := assertCallerInfo()
t.Errorf("%s:%d: %s", path.Base(file), line, msg)
}
func assertFatalf(t *testing.T, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
file, line := assertCallerInfo()
t.Fatalf("%s:%d: %s", path.Base(file), line, msg)
}
func assertString(t *testing.T, a string, b string) {
if a != b {
assertErrorf(t, "Expected %#v, but got %#v", b, a)
}
}
func assertStringArray(t *testing.T, a []string, b []string) {
if len(a) != len(b) {
assertErrorf(t, "Expected %#v, but got %#v", b, a)
return
}
for i, v := range a {
if b[i] != v {
assertErrorf(t, "Expected %#v, but got %#v", b, a)
return
}
}
}
func assertBoolArray(t *testing.T, a []bool, b []bool) {
if len(a) != len(b) {
assertErrorf(t, "Expected %#v, but got %#v", b, a)
return
}
for i, v := range a {
if b[i] != v {
assertErrorf(t, "Expected %#v, but got %#v", b, a)
return
}
}
}
func assertParserSuccess(t *testing.T, data interface{}, args ...string) (*Parser, []string) {
parser := NewParser(data, Default&^PrintErrors)
ret, err := parser.ParseArgs(args)
if err != nil {
t.Fatalf("Unexpected parse error: %s", err)
return nil, nil
}
return parser, ret
}
func assertParseSuccess(t *testing.T, data interface{}, args ...string) []string {
_, ret := assertParserSuccess(t, data, args...)
return ret
}
func assertError(t *testing.T, err error, typ ErrorType, msg string) {
if err == nil {
assertFatalf(t, "Expected error: %s", msg)
return
}
if e, ok := err.(*Error); !ok {
assertFatalf(t, "Expected Error type, but got %#v", err)
} else {
if e.Type != typ {
assertErrorf(t, "Expected error type {%s}, but got {%s}", typ, e.Type)
}
if e.Message != msg {
assertErrorf(t, "Expected error message %#v, but got %#v", msg, e.Message)
}
}
}
func assertParseFail(t *testing.T, typ ErrorType, msg string, data interface{}, args ...string) []string {
parser := NewParser(data, Default&^PrintErrors)
ret, err := parser.ParseArgs(args)
assertError(t, err, typ, msg)
return ret
}
func diff(a, b string) (string, error) {
atmp, err := ioutil.TempFile("", "help-diff")
if err != nil {
return "", err
}
btmp, err := ioutil.TempFile("", "help-diff")
if err != nil {
return "", err
}
if _, err := io.WriteString(atmp, a); err != nil {
return "", err
}
if _, err := io.WriteString(btmp, b); err != nil {
return "", err
}
ret, err := exec.Command("diff", "-u", "-d", "--label", "got", atmp.Name(), "--label", "expected", btmp.Name()).Output()
os.Remove(atmp.Name())
os.Remove(btmp.Name())
if err.Error() == "exit status 1" {
return string(ret), nil
}
return string(ret), err
}
func assertDiff(t *testing.T, actual, expected, msg string) {
if actual == expected {
return
}
ret, err := diff(actual, expected)
if err != nil {
assertErrorf(t, "Unexpected diff error: %s", err)
assertErrorf(t, "Unexpected %s, expected:\n\n%s\n\nbut got\n\n%s", msg, expected, actual)
} else {
assertErrorf(t, "Unexpected %s:\n\n%s", msg, ret)
}
}

View File

@@ -0,0 +1,16 @@
#!/bin/bash
set -e
echo '# linux arm7'
GOARM=7 GOARCH=arm GOOS=linux go build
echo '# linux arm5'
GOARM=5 GOARCH=arm GOOS=linux go build
echo '# windows 386'
GOARCH=386 GOOS=windows go build
echo '# windows amd64'
GOARCH=amd64 GOOS=windows go build
echo '# darwin'
GOARCH=amd64 GOOS=darwin go build
echo '# freebsd'
GOARCH=amd64 GOOS=freebsd go build

View File

@@ -0,0 +1,59 @@
package flags
func levenshtein(s string, t string) int {
if len(s) == 0 {
return len(t)
}
if len(t) == 0 {
return len(s)
}
dists := make([][]int, len(s)+1)
for i := range dists {
dists[i] = make([]int, len(t)+1)
dists[i][0] = i
}
for j := range t {
dists[0][j] = j
}
for i, sc := range s {
for j, tc := range t {
if sc == tc {
dists[i+1][j+1] = dists[i][j]
} else {
dists[i+1][j+1] = dists[i][j] + 1
if dists[i+1][j] < dists[i+1][j+1] {
dists[i+1][j+1] = dists[i+1][j] + 1
}
if dists[i][j+1] < dists[i+1][j+1] {
dists[i+1][j+1] = dists[i][j+1] + 1
}
}
}
}
return dists[len(s)][len(t)]
}
func closestChoice(cmd string, choices []string) (string, int) {
if len(choices) == 0 {
return "", 0
}
mincmd := -1
mindist := -1
for i, c := range choices {
l := levenshtein(cmd, c)
if mincmd < 0 || l < mindist {
mindist = l
mincmd = i
}
}
return choices[mincmd], mindist
}

View File

@@ -0,0 +1,106 @@
package flags
// Command represents an application command. Commands can be added to the
// parser (which itself is a command) and are selected/executed when its name
// is specified on the command line. The Command type embeds a Group and
// therefore also carries a set of command specific options.
type Command struct {
// Embedded, see Group for more information
*Group
// The name by which the command can be invoked
Name string
// The active sub command (set by parsing) or nil
Active *Command
// Whether subcommands are optional
SubcommandsOptional bool
// Aliases for the command
Aliases []string
// Whether positional arguments are required
ArgsRequired bool
commands []*Command
hasBuiltinHelpGroup bool
args []*Arg
}
// Commander is an interface which can be implemented by any command added in
// the options. When implemented, the Execute method will be called for the last
// specified (sub)command providing the remaining command line arguments.
type Commander interface {
// Execute will be called for the last active (sub)command. The
// args argument contains the remaining command line arguments. The
// error that Execute returns will be eventually passed out of the
// Parse method of the Parser.
Execute(args []string) error
}
// Usage is an interface which can be implemented to show a custom usage string
// in the help message shown for a command.
type Usage interface {
// Usage is called for commands to allow customized printing of command
// usage in the generated help message.
Usage() string
}
// AddCommand adds a new command to the parser with the given name and data. The
// data needs to be a pointer to a struct from which the fields indicate which
// options are in the command. The provided data can implement the Command and
// Usage interfaces.
func (c *Command) AddCommand(command string, shortDescription string, longDescription string, data interface{}) (*Command, error) {
cmd := newCommand(command, shortDescription, longDescription, data)
cmd.parent = c
if err := cmd.scan(); err != nil {
return nil, err
}
c.commands = append(c.commands, cmd)
return cmd, nil
}
// AddGroup adds a new group to the command with the given name and data. The
// data needs to be a pointer to a struct from which the fields indicate which
// options are in the group.
func (c *Command) AddGroup(shortDescription string, longDescription string, data interface{}) (*Group, error) {
group := newGroup(shortDescription, longDescription, data)
group.parent = c
if err := group.scanType(c.scanSubcommandHandler(group)); err != nil {
return nil, err
}
c.groups = append(c.groups, group)
return group, nil
}
// Commands returns a list of subcommands of this command.
func (c *Command) Commands() []*Command {
return c.commands
}
// Find locates the subcommand with the given name and returns it. If no such
// command can be found Find will return nil.
func (c *Command) Find(name string) *Command {
for _, cc := range c.commands {
if cc.match(name) {
return cc
}
}
return nil
}
// Args returns a list of positional arguments associated with this command.
func (c *Command) Args() []*Arg {
ret := make([]*Arg, len(c.args))
copy(ret, c.args)
return ret
}

View File

@@ -0,0 +1,271 @@
package flags
import (
"reflect"
"sort"
"strings"
"unsafe"
)
type lookup struct {
shortNames map[string]*Option
longNames map[string]*Option
commands map[string]*Command
}
func newCommand(name string, shortDescription string, longDescription string, data interface{}) *Command {
return &Command{
Group: newGroup(shortDescription, longDescription, data),
Name: name,
}
}
func (c *Command) scanSubcommandHandler(parentg *Group) scanHandler {
f := func(realval reflect.Value, sfield *reflect.StructField) (bool, error) {
mtag := newMultiTag(string(sfield.Tag))
if err := mtag.Parse(); err != nil {
return true, err
}
positional := mtag.Get("positional-args")
if len(positional) != 0 {
stype := realval.Type()
for i := 0; i < stype.NumField(); i++ {
field := stype.Field(i)
m := newMultiTag((string(field.Tag)))
if err := m.Parse(); err != nil {
return true, err
}
name := m.Get("positional-arg-name")
if len(name) == 0 {
name = field.Name
}
arg := &Arg{
Name: name,
Description: m.Get("description"),
value: realval.Field(i),
tag: m,
}
c.args = append(c.args, arg)
if len(mtag.Get("required")) != 0 {
c.ArgsRequired = true
}
}
return true, nil
}
subcommand := mtag.Get("command")
if len(subcommand) != 0 {
ptrval := reflect.NewAt(realval.Type(), unsafe.Pointer(realval.UnsafeAddr()))
shortDescription := mtag.Get("description")
longDescription := mtag.Get("long-description")
subcommandsOptional := mtag.Get("subcommands-optional")
aliases := mtag.GetMany("alias")
subc, err := c.AddCommand(subcommand, shortDescription, longDescription, ptrval.Interface())
if err != nil {
return true, err
}
if len(subcommandsOptional) > 0 {
subc.SubcommandsOptional = true
}
if len(aliases) > 0 {
subc.Aliases = aliases
}
return true, nil
}
return parentg.scanSubGroupHandler(realval, sfield)
}
return f
}
func (c *Command) scan() error {
return c.scanType(c.scanSubcommandHandler(c.Group))
}
func (c *Command) eachCommand(f func(*Command), recurse bool) {
f(c)
for _, cc := range c.commands {
if recurse {
cc.eachCommand(f, true)
} else {
f(cc)
}
}
}
func (c *Command) eachActiveGroup(f func(cc *Command, g *Group)) {
c.eachGroup(func(g *Group) {
f(c, g)
})
if c.Active != nil {
c.Active.eachActiveGroup(f)
}
}
func (c *Command) addHelpGroups(showHelp func() error) {
if !c.hasBuiltinHelpGroup {
c.addHelpGroup(showHelp)
c.hasBuiltinHelpGroup = true
}
for _, cc := range c.commands {
cc.addHelpGroups(showHelp)
}
}
func (c *Command) makeLookup() lookup {
ret := lookup{
shortNames: make(map[string]*Option),
longNames: make(map[string]*Option),
commands: make(map[string]*Command),
}
parent := c.parent
for parent != nil {
if cmd, ok := parent.(*Command); ok {
cmd.fillLookup(&ret, true)
}
if grp, ok := parent.(*Group); ok {
parent = grp
} else {
parent = nil
}
}
c.fillLookup(&ret, false)
return ret
}
func (c *Command) fillLookup(ret *lookup, onlyOptions bool) {
c.eachGroup(func(g *Group) {
for _, option := range g.options {
if option.ShortName != 0 {
ret.shortNames[string(option.ShortName)] = option
}
if len(option.LongName) > 0 {
ret.longNames[option.LongNameWithNamespace()] = option
}
}
})
if onlyOptions {
return
}
for _, subcommand := range c.commands {
ret.commands[subcommand.Name] = subcommand
for _, a := range subcommand.Aliases {
ret.commands[a] = subcommand
}
}
}
func (c *Command) groupByName(name string) *Group {
if grp := c.Group.groupByName(name); grp != nil {
return grp
}
for _, subc := range c.commands {
prefix := subc.Name + "."
if strings.HasPrefix(name, prefix) {
if grp := subc.groupByName(name[len(prefix):]); grp != nil {
return grp
}
} else if name == subc.Name {
return subc.Group
}
}
return nil
}
type commandList []*Command
func (c commandList) Less(i, j int) bool {
return c[i].Name < c[j].Name
}
func (c commandList) Len() int {
return len(c)
}
func (c commandList) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
func (c *Command) sortedCommands() []*Command {
ret := make(commandList, len(c.commands))
copy(ret, c.commands)
sort.Sort(ret)
return []*Command(ret)
}
func (c *Command) match(name string) bool {
if c.Name == name {
return true
}
for _, v := range c.Aliases {
if v == name {
return true
}
}
return false
}
func (c *Command) hasCliOptions() bool {
ret := false
c.eachGroup(func(g *Group) {
if g.isBuiltinHelp {
return
}
for _, opt := range g.options {
if opt.canCli() {
ret = true
}
}
})
return ret
}
func (c *Command) fillParseState(s *parseState) {
s.positional = make([]*Arg, len(c.args))
copy(s.positional, c.args)
s.lookup = c.makeLookup()
s.command = c
}

View File

@@ -0,0 +1,402 @@
package flags
import (
"fmt"
"testing"
)
func TestCommandInline(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Command struct {
G bool `short:"g"`
} `command:"cmd"`
}{}
p, ret := assertParserSuccess(t, &opts, "-v", "cmd", "-g")
assertStringArray(t, ret, []string{})
if p.Active == nil {
t.Errorf("Expected active command")
}
if !opts.Value {
t.Errorf("Expected Value to be true")
}
if !opts.Command.G {
t.Errorf("Expected Command.G to be true")
}
if p.Command.Find("cmd") != p.Active {
t.Errorf("Expected to find command `cmd' to be active")
}
}
func TestCommandInlineMulti(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
C1 struct {
} `command:"c1"`
C2 struct {
G bool `short:"g"`
} `command:"c2"`
}{}
p, ret := assertParserSuccess(t, &opts, "-v", "c2", "-g")
assertStringArray(t, ret, []string{})
if p.Active == nil {
t.Errorf("Expected active command")
}
if !opts.Value {
t.Errorf("Expected Value to be true")
}
if !opts.C2.G {
t.Errorf("Expected C2.G to be true")
}
if p.Command.Find("c1") == nil {
t.Errorf("Expected to find command `c1'")
}
if c2 := p.Command.Find("c2"); c2 == nil {
t.Errorf("Expected to find command `c2'")
} else if c2 != p.Active {
t.Errorf("Expected to find command `c2' to be active")
}
}
func TestCommandFlagOrder1(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Command struct {
G bool `short:"g"`
} `command:"cmd"`
}{}
assertParseFail(t, ErrUnknownFlag, "unknown flag `g'", &opts, "-v", "-g", "cmd")
}
func TestCommandFlagOrder2(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Command struct {
G bool `short:"g"`
} `command:"cmd"`
}{}
assertParseSuccess(t, &opts, "cmd", "-v", "-g")
if !opts.Value {
t.Errorf("Expected Value to be true")
}
if !opts.Command.G {
t.Errorf("Expected Command.G to be true")
}
}
func TestCommandFlagOverride1(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Command struct {
Value bool `short:"v"`
} `command:"cmd"`
}{}
assertParseSuccess(t, &opts, "-v", "cmd")
if !opts.Value {
t.Errorf("Expected Value to be true")
}
if opts.Command.Value {
t.Errorf("Expected Command.Value to be false")
}
}
func TestCommandFlagOverride2(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Command struct {
Value bool `short:"v"`
} `command:"cmd"`
}{}
assertParseSuccess(t, &opts, "cmd", "-v")
if opts.Value {
t.Errorf("Expected Value to be false")
}
if !opts.Command.Value {
t.Errorf("Expected Command.Value to be true")
}
}
func TestCommandEstimate(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Cmd1 struct {
} `command:"remove"`
Cmd2 struct {
} `command:"add"`
}{}
p := NewParser(&opts, None)
_, err := p.ParseArgs([]string{})
assertError(t, err, ErrCommandRequired, "Please specify one command of: add or remove")
}
func TestCommandEstimate2(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Cmd1 struct {
} `command:"remove"`
Cmd2 struct {
} `command:"add"`
}{}
p := NewParser(&opts, None)
_, err := p.ParseArgs([]string{"rmive"})
assertError(t, err, ErrUnknownCommand, "Unknown command `rmive', did you mean `remove'?")
}
type testCommand struct {
G bool `short:"g"`
Executed bool
EArgs []string
}
func (c *testCommand) Execute(args []string) error {
c.Executed = true
c.EArgs = args
return nil
}
func TestCommandExecute(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Command testCommand `command:"cmd"`
}{}
assertParseSuccess(t, &opts, "-v", "cmd", "-g", "a", "b")
if !opts.Value {
t.Errorf("Expected Value to be true")
}
if !opts.Command.Executed {
t.Errorf("Did not execute command")
}
if !opts.Command.G {
t.Errorf("Expected Command.C to be true")
}
assertStringArray(t, opts.Command.EArgs, []string{"a", "b"})
}
func TestCommandClosest(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Cmd1 struct {
} `command:"remove"`
Cmd2 struct {
} `command:"add"`
}{}
args := assertParseFail(t, ErrUnknownCommand, "Unknown command `addd', did you mean `add'?", &opts, "-v", "addd")
assertStringArray(t, args, []string{"addd"})
}
func TestCommandAdd(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
}{}
var cmd = struct {
G bool `short:"g"`
}{}
p := NewParser(&opts, Default)
c, err := p.AddCommand("cmd", "", "", &cmd)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
return
}
ret, err := p.ParseArgs([]string{"-v", "cmd", "-g", "rest"})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
return
}
assertStringArray(t, ret, []string{"rest"})
if !opts.Value {
t.Errorf("Expected Value to be true")
}
if !cmd.G {
t.Errorf("Expected Command.G to be true")
}
if p.Command.Find("cmd") != c {
t.Errorf("Expected to find command `cmd'")
}
if p.Commands()[0] != c {
t.Errorf("Expected command %#v, but got %#v", c, p.Commands()[0])
}
if c.Options()[0].ShortName != 'g' {
t.Errorf("Expected short name `g' but got %v", c.Options()[0].ShortName)
}
}
func TestCommandNestedInline(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Command struct {
G bool `short:"g"`
Nested struct {
N string `long:"n"`
} `command:"nested"`
} `command:"cmd"`
}{}
p, ret := assertParserSuccess(t, &opts, "-v", "cmd", "-g", "nested", "--n", "n", "rest")
assertStringArray(t, ret, []string{"rest"})
if !opts.Value {
t.Errorf("Expected Value to be true")
}
if !opts.Command.G {
t.Errorf("Expected Command.G to be true")
}
assertString(t, opts.Command.Nested.N, "n")
if c := p.Command.Find("cmd"); c == nil {
t.Errorf("Expected to find command `cmd'")
} else {
if c != p.Active {
t.Errorf("Expected `cmd' to be the active parser command")
}
if nested := c.Find("nested"); nested == nil {
t.Errorf("Expected to find command `nested'")
} else if nested != c.Active {
t.Errorf("Expected to find command `nested' to be the active `cmd' command")
}
}
}
func TestRequiredOnCommand(t *testing.T) {
var opts = struct {
Value bool `short:"v" required:"true"`
Command struct {
G bool `short:"g"`
} `command:"cmd"`
}{}
assertParseFail(t, ErrRequired, fmt.Sprintf("the required flag `%cv' was not specified", defaultShortOptDelimiter), &opts, "cmd")
}
func TestRequiredAllOnCommand(t *testing.T) {
var opts = struct {
Value bool `short:"v" required:"true"`
Missing bool `long:"missing" required:"true"`
Command struct {
G bool `short:"g"`
} `command:"cmd"`
}{}
assertParseFail(t, ErrRequired, fmt.Sprintf("the required flags `%smissing' and `%cv' were not specified", defaultLongOptDelimiter, defaultShortOptDelimiter), &opts, "cmd")
}
func TestDefaultOnCommand(t *testing.T) {
var opts = struct {
Command struct {
G bool `short:"g" default:"true"`
} `command:"cmd"`
}{}
assertParseSuccess(t, &opts, "cmd")
if !opts.Command.G {
t.Errorf("Expected G to be true")
}
}
func TestSubcommandsOptional(t *testing.T) {
var opts = struct {
Value bool `short:"v"`
Cmd1 struct {
} `command:"remove"`
Cmd2 struct {
} `command:"add"`
}{}
p := NewParser(&opts, None)
p.SubcommandsOptional = true
_, err := p.ParseArgs([]string{"-v"})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
return
}
if !opts.Value {
t.Errorf("Expected Value to be true")
}
}
func TestCommandAlias(t *testing.T) {
var opts = struct {
Command struct {
G bool `short:"g" default:"true"`
} `command:"cmd" alias:"cm"`
}{}
assertParseSuccess(t, &opts, "cm")
if !opts.Command.G {
t.Errorf("Expected G to be true")
}
}

View File

@@ -0,0 +1,304 @@
package flags
import (
"fmt"
"path/filepath"
"reflect"
"sort"
"strings"
"unicode/utf8"
)
// Completion is a type containing information of a completion.
type Completion struct {
// The completed item
Item string
// A description of the completed item (optional)
Description string
}
type completions []Completion
func (c completions) Len() int {
return len(c)
}
func (c completions) Less(i, j int) bool {
return c[i].Item < c[j].Item
}
func (c completions) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
// Completer is an interface which can be implemented by types
// to provide custom command line argument completion.
type Completer interface {
// Complete receives a prefix representing a (partial) value
// for its type and should provide a list of possible valid
// completions.
Complete(match string) []Completion
}
type completion struct {
parser *Parser
ShowDescriptions bool
}
// Filename is a string alias which provides filename completion.
type Filename string
func completionsWithoutDescriptions(items []string) []Completion {
ret := make([]Completion, len(items))
for i, v := range items {
ret[i].Item = v
}
return ret
}
// Complete returns a list of existing files with the given
// prefix.
func (f *Filename) Complete(match string) []Completion {
ret, _ := filepath.Glob(match + "*")
return completionsWithoutDescriptions(ret)
}
func (c *completion) skipPositional(s *parseState, n int) {
if n >= len(s.positional) {
s.positional = nil
} else {
s.positional = s.positional[n:]
}
}
func (c *completion) completeOptionNames(names map[string]*Option, prefix string, match string) []Completion {
n := make([]Completion, 0, len(names))
for k, opt := range names {
if strings.HasPrefix(k, match) {
n = append(n, Completion{
Item: prefix + k,
Description: opt.Description,
})
}
}
return n
}
func (c *completion) completeLongNames(s *parseState, prefix string, match string) []Completion {
return c.completeOptionNames(s.lookup.longNames, prefix, match)
}
func (c *completion) completeShortNames(s *parseState, prefix string, match string) []Completion {
if len(match) != 0 {
return []Completion{
Completion{
Item: prefix + match,
},
}
}
return c.completeOptionNames(s.lookup.shortNames, prefix, match)
}
func (c *completion) completeCommands(s *parseState, match string) []Completion {
n := make([]Completion, 0, len(s.command.commands))
for _, cmd := range s.command.commands {
if cmd.data != c && strings.HasPrefix(cmd.Name, match) {
n = append(n, Completion{
Item: cmd.Name,
Description: cmd.ShortDescription,
})
}
}
return n
}
func (c *completion) completeValue(value reflect.Value, prefix string, match string) []Completion {
i := value.Interface()
var ret []Completion
if cmp, ok := i.(Completer); ok {
ret = cmp.Complete(match)
} else if value.CanAddr() {
if cmp, ok = value.Addr().Interface().(Completer); ok {
ret = cmp.Complete(match)
}
}
for i, v := range ret {
ret[i].Item = prefix + v.Item
}
return ret
}
func (c *completion) completeArg(arg *Arg, prefix string, match string) []Completion {
if arg.isRemaining() {
// For remaining positional args (that are parsed into a slice), complete
// based on the element type.
return c.completeValue(reflect.New(arg.value.Type().Elem()), prefix, match)
}
return c.completeValue(arg.value, prefix, match)
}
func (c *completion) complete(args []string) []Completion {
if len(args) == 0 {
args = []string{""}
}
s := &parseState{
args: args,
}
c.parser.fillParseState(s)
var opt *Option
for len(s.args) > 1 {
arg := s.pop()
if (c.parser.Options&PassDoubleDash) != None && arg == "--" {
opt = nil
c.skipPositional(s, len(s.args)-1)
break
}
if argumentIsOption(arg) {
prefix, optname, islong := stripOptionPrefix(arg)
optname, _, argument := splitOption(prefix, optname, islong)
if argument == nil {
var o *Option
canarg := true
if islong {
o = s.lookup.longNames[optname]
} else {
for i, r := range optname {
sname := string(r)
o = s.lookup.shortNames[sname]
if o == nil {
break
}
if i == 0 && o.canArgument() && len(optname) != len(sname) {
canarg = false
break
}
}
}
if o == nil && (c.parser.Options&PassAfterNonOption) != None {
opt = nil
c.skipPositional(s, len(s.args)-1)
break
} else if o != nil && o.canArgument() && !o.OptionalArgument && canarg {
if len(s.args) > 1 {
s.pop()
} else {
opt = o
}
}
}
} else {
if len(s.positional) > 0 {
if !s.positional[0].isRemaining() {
// Don't advance beyond a remaining positional arg (because
// it consumes all subsequent args).
s.positional = s.positional[1:]
}
} else if cmd, ok := s.lookup.commands[arg]; ok {
cmd.fillParseState(s)
}
opt = nil
}
}
lastarg := s.args[len(s.args)-1]
var ret []Completion
if opt != nil {
// Completion for the argument of 'opt'
ret = c.completeValue(opt.value, "", lastarg)
} else if argumentStartsOption(lastarg) {
// Complete the option
prefix, optname, islong := stripOptionPrefix(lastarg)
optname, split, argument := splitOption(prefix, optname, islong)
if argument == nil && !islong {
rname, n := utf8.DecodeRuneInString(optname)
sname := string(rname)
if opt := s.lookup.shortNames[sname]; opt != nil && opt.canArgument() {
ret = c.completeValue(opt.value, prefix+sname, optname[n:])
} else {
ret = c.completeShortNames(s, prefix, optname)
}
} else if argument != nil {
if islong {
opt = s.lookup.longNames[optname]
} else {
opt = s.lookup.shortNames[optname]
}
if opt != nil {
ret = c.completeValue(opt.value, prefix+optname+split, *argument)
}
} else if islong {
ret = c.completeLongNames(s, prefix, optname)
} else {
ret = c.completeShortNames(s, prefix, optname)
}
} else if len(s.positional) > 0 {
// Complete for positional argument
ret = c.completeArg(s.positional[0], "", lastarg)
} else if len(s.command.commands) > 0 {
// Complete for command
ret = c.completeCommands(s, lastarg)
}
sort.Sort(completions(ret))
return ret
}
func (c *completion) execute(args []string) {
ret := c.complete(args)
if c.ShowDescriptions && len(ret) > 1 {
maxl := 0
for _, v := range ret {
if len(v.Item) > maxl {
maxl = len(v.Item)
}
}
for _, v := range ret {
fmt.Printf("%s", v.Item)
if len(v.Description) > 0 {
fmt.Printf("%s # %s", strings.Repeat(" ", maxl-len(v.Item)), v.Description)
}
fmt.Printf("\n")
}
} else {
for _, v := range ret {
fmt.Println(v.Item)
}
}
}

Some files were not shown because too many files have changed in this diff Show More