Browse Source

Added jacobsa/fuse to vendor folder

removed deps
luis 7 years ago
parent
commit
7f5835fe3a
92 changed files with 16852 additions and 25 deletions
  1. 1 0
      .gitignore
  2. 3 3
      Makefile
  3. 0 1
      deps/src/cloud.google.com/go
  4. 0 1
      deps/src/dev.hexasoftware.com/hxs/prettylog
  5. 0 1
      deps/src/github.com/icattlecoder/godaemon
  6. 0 1
      deps/src/golang.org/x/crypto
  7. 0 1
      deps/src/golang.org/x/net
  8. 0 1
      deps/src/golang.org/x/oauth2
  9. 0 1
      deps/src/google.golang.org/api
  10. 1 1
      env.sh
  11. 1 13
      src/gdrivemount/fileentry.go
  12. 2 0
      src/gdrivemount/gdrive-fuse.go
  13. 27 0
      src/vendor/github.com/jacobsa/fuse/.gitignore
  14. 28 0
      src/vendor/github.com/jacobsa/fuse/.travis.yml
  15. 300 0
      src/vendor/github.com/jacobsa/fuse/LICENSE
  16. 30 0
      src/vendor/github.com/jacobsa/fuse/README.md
  17. 509 0
      src/vendor/github.com/jacobsa/fuse/connection.go
  18. 890 0
      src/vendor/github.com/jacobsa/fuse/conversions.go
  19. 129 0
      src/vendor/github.com/jacobsa/fuse/debug.go
  20. 36 0
      src/vendor/github.com/jacobsa/fuse/doc.go
  21. 30 0
      src/vendor/github.com/jacobsa/fuse/errors.go
  22. 70 0
      src/vendor/github.com/jacobsa/fuse/freelists.go
  23. 23 0
      src/vendor/github.com/jacobsa/fuse/fsutil/fdatasync_darwin.go
  24. 26 0
      src/vendor/github.com/jacobsa/fuse/fsutil/fdatasync_linux.go
  25. 57 0
      src/vendor/github.com/jacobsa/fuse/fsutil/fsutil.go
  26. 17 0
      src/vendor/github.com/jacobsa/fuse/fuseops/doc.go
  27. 847 0
      src/vendor/github.com/jacobsa/fuse/fuseops/ops.go
  28. 222 0
      src/vendor/github.com/jacobsa/fuse/fuseops/simple_types.go
  29. 367 0
      src/vendor/github.com/jacobsa/fuse/fusetesting/parallel.go
  30. 76 0
      src/vendor/github.com/jacobsa/fuse/fusetesting/readdir.go
  31. 125 0
      src/vendor/github.com/jacobsa/fuse/fusetesting/stat.go
  32. 45 0
      src/vendor/github.com/jacobsa/fuse/fusetesting/stat_darwin.go
  33. 43 0
      src/vendor/github.com/jacobsa/fuse/fusetesting/stat_linux.go
  34. 105 0
      src/vendor/github.com/jacobsa/fuse/fuseutil/dirent.go
  35. 16 0
      src/vendor/github.com/jacobsa/fuse/fuseutil/doc.go
  36. 208 0
      src/vendor/github.com/jacobsa/fuse/fuseutil/file_system.go
  37. 215 0
      src/vendor/github.com/jacobsa/fuse/fuseutil/not_implemented_file_system.go
  38. 115 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/in_message.go
  39. 21 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/in_message_darwin.go
  40. 21 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/in_message_linux.go
  41. 167 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/out_message.go
  42. 21 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/out_message_darwin.go
  43. 21 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/out_message_linux.go
  44. 357 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/out_message_test.go
  45. 29 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/runtime.go
  46. 40 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/runtime_go1.8.s
  47. 40 0
      src/vendor/github.com/jacobsa/fuse/internal/buffer/runtime_other.s
  48. 40 0
      src/vendor/github.com/jacobsa/fuse/internal/freelist/freelist.go
  49. 771 0
      src/vendor/github.com/jacobsa/fuse/internal/fusekernel/fuse_kernel.go
  50. 88 0
      src/vendor/github.com/jacobsa/fuse/internal/fusekernel/fuse_kernel_darwin.go
  51. 70 0
      src/vendor/github.com/jacobsa/fuse/internal/fusekernel/fuse_kernel_linux.go
  52. 1 0
      src/vendor/github.com/jacobsa/fuse/internal/fusekernel/fuse_kernel_std.go
  53. 75 0
      src/vendor/github.com/jacobsa/fuse/internal/fusekernel/protocol.go
  54. 102 0
      src/vendor/github.com/jacobsa/fuse/mount.go
  55. 232 0
      src/vendor/github.com/jacobsa/fuse/mount_config.go
  56. 217 0
      src/vendor/github.com/jacobsa/fuse/mount_darwin.go
  57. 113 0
      src/vendor/github.com/jacobsa/fuse/mount_linux.go
  58. 142 0
      src/vendor/github.com/jacobsa/fuse/mount_test.go
  59. 49 0
      src/vendor/github.com/jacobsa/fuse/mounted_file_system.go
  60. 46 0
      src/vendor/github.com/jacobsa/fuse/ops.go
  61. 378 0
      src/vendor/github.com/jacobsa/fuse/samples/cachingfs/caching_fs.go
  62. 725 0
      src/vendor/github.com/jacobsa/fuse/samples/cachingfs/caching_fs_test.go
  63. 284 0
      src/vendor/github.com/jacobsa/fuse/samples/dynamicfs/dynamic_fs.go
  64. 181 0
      src/vendor/github.com/jacobsa/fuse/samples/dynamicfs/dynamic_fs_test.go
  65. 221 0
      src/vendor/github.com/jacobsa/fuse/samples/errorfs/error_fs.go
  66. 106 0
      src/vendor/github.com/jacobsa/fuse/samples/errorfs/error_fs_test.go
  67. 338 0
      src/vendor/github.com/jacobsa/fuse/samples/flushfs/flush_fs.go
  68. 1051 0
      src/vendor/github.com/jacobsa/fuse/samples/flushfs/flush_fs_test.go
  69. 389 0
      src/vendor/github.com/jacobsa/fuse/samples/forgetfs/forget_fs.go
  70. 167 0
      src/vendor/github.com/jacobsa/fuse/samples/forgetfs/forget_fs_test.go
  71. 269 0
      src/vendor/github.com/jacobsa/fuse/samples/hellofs/hello_fs.go
  72. 226 0
      src/vendor/github.com/jacobsa/fuse/samples/hellofs/hello_fs_test.go
  73. 153 0
      src/vendor/github.com/jacobsa/fuse/samples/in_process.go
  74. 209 0
      src/vendor/github.com/jacobsa/fuse/samples/interruptfs/interrupt_fs.go
  75. 156 0
      src/vendor/github.com/jacobsa/fuse/samples/interruptfs/interrupt_fs_test.go
  76. 384 0
      src/vendor/github.com/jacobsa/fuse/samples/memfs/inode.go
  77. 716 0
      src/vendor/github.com/jacobsa/fuse/samples/memfs/memfs.go
  78. 5 0
      src/vendor/github.com/jacobsa/fuse/samples/memfs/memfs_go18_test.go
  79. 5 0
      src/vendor/github.com/jacobsa/fuse/samples/memfs/memfs_others_test.go
  80. 1781 0
      src/vendor/github.com/jacobsa/fuse/samples/memfs/memfs_test.go
  81. 447 0
      src/vendor/github.com/jacobsa/fuse/samples/memfs/posix_test.go
  82. 163 0
      src/vendor/github.com/jacobsa/fuse/samples/mount_sample/mount.go
  83. 184 0
      src/vendor/github.com/jacobsa/fuse/samples/statfs/statfs.go
  84. 222 0
      src/vendor/github.com/jacobsa/fuse/samples/statfs/statfs_darwin_test.go
  85. 161 0
      src/vendor/github.com/jacobsa/fuse/samples/statfs/statfs_linux_test.go
  86. 228 0
      src/vendor/github.com/jacobsa/fuse/samples/statfs/statfs_test.go
  87. 366 0
      src/vendor/github.com/jacobsa/fuse/samples/subprocess.go
  88. 48 0
      src/vendor/github.com/jacobsa/fuse/samples/unmount.go
  89. 21 0
      src/vendor/github.com/jacobsa/fuse/unmount.go
  90. 23 0
      src/vendor/github.com/jacobsa/fuse/unmount_linux.go
  91. 18 0
      src/vendor/github.com/jacobsa/fuse/unmount_std.go
  92. 0 1
      vendor/src/github.com/jacobsa/fuse

+ 1 - 0
.gitignore

@@ -1,4 +1,5 @@
 deps/pkg
+vendor/pkg
 pkg
 bin
 DIST

+ 3 - 3
Makefile

@@ -1,7 +1,7 @@
-GOPATH=$(CURDIR)/deps:$(CURDIR)/vendor:$(CURDIR)
+GOPATH=$(CURDIR)/deps:$(CURDIR)
 DIST=./DIST
 
-all:
+all: deps
 	GOPATH=$(GOPATH) go build -o DIST/gdrivemount gdrivemount/cmd/gdrivemount
 
 deps:
@@ -11,5 +11,5 @@ test:
 	cd test; ../DIST/gdrivemount mount
 
 clean:
-	rm -rf DIST deps
+	rm -rf DIST deps pkg bin
 

+ 0 - 1
deps/src/cloud.google.com/go

@@ -1 +0,0 @@
-Subproject commit 558b56dfa3c56acc26fef35cb07f97df0bb18b39

+ 0 - 1
deps/src/dev.hexasoftware.com/hxs/prettylog

@@ -1 +0,0 @@
-Subproject commit ede02c9e65b0e987e83398ec549d0088cde1bb23

+ 0 - 1
deps/src/github.com/icattlecoder/godaemon

@@ -1 +0,0 @@
-Subproject commit f0fff2a3c017c9d9d8520e3c5366946e96e9e081

+ 0 - 1
deps/src/golang.org/x/crypto

@@ -1 +0,0 @@
-Subproject commit 51714a8c4ac1764f07ab4127d7f739351ced4759

+ 0 - 1
deps/src/golang.org/x/net

@@ -1 +0,0 @@
-Subproject commit 570fa1c91359c1869590e9cedf3b53162a51a167

+ 0 - 1
deps/src/golang.org/x/oauth2

@@ -1 +0,0 @@
-Subproject commit cce311a261e6fcf29de72ca96827bdb0b7d9c9e6

+ 0 - 1
deps/src/google.golang.org/api

@@ -1 +0,0 @@
-Subproject commit e6586c9293b9d514c7f5d5076731ec977cff1be6

+ 1 - 1
env.sh

@@ -1 +1 @@
-export GOPATH=$(pwd)/deps:$(pwd)/vendor:$(pwd)
+export GOPATH=$(pwd)/deps:$(pwd)

+ 1 - 13
src/gdrivemount/fileentry.go

@@ -151,6 +151,7 @@ func (fe *FileEntry) appendGFile(f *drive.File) *FileEntry {
 	attr.Crtime, _ = time.Parse(time.RFC3339, f.CreatedTime)
 	attr.Mtime, _ = time.Parse(time.RFC3339, f.ModifiedTime)
 	attr.Mode = os.FileMode(0644) // default
+
 	if f.MimeType == "application/vnd.google-apps.folder" {
 		attr.Mode = os.FileMode(0755) | os.ModeDir
 	}
@@ -167,19 +168,6 @@ func (fe *FileEntry) appendGFile(f *drive.File) *FileEntry {
 		//fileMap: map[string]*FileEntry{},
 		fileList: []*FileEntry{},
 	}
-	if entry.Inode == 4 {
-		log.Println("Created entry for:", name, " with inode:", entry.Inode)
-		log.Printf("It will be added to Parent: '%s' (%d)", fe.Name, fe.Inode)
-		tparent := fe.parent
-		for {
-			if tparent == nil {
-				break
-			}
-			log.Printf("which have the parent '%s' (%d)", tparent.Name, tparent.Inode)
-			tparent = tparent.parent
-
-		}
-	}
 
 	fe.fileList = append(fe.fileList, entry)
 	//fe.fileMap[f.Name] = entry

+ 2 - 0
src/gdrivemount/gdrive-fuse.go

@@ -612,6 +612,7 @@ func (fs *GDriveFS) MkDir(ctx context.Context, op *fuseops.MkDirOp) (err error)
 	return
 }
 
+// RmDir fuse implementation
 func (fs *GDriveFS) RmDir(ctx context.Context, op *fuseops.RmDirOp) (err error) {
 
 	parentFile := fs.root.findByInode(op.Parent, true)
@@ -636,6 +637,7 @@ func (fs *GDriveFS) RmDir(ctx context.Context, op *fuseops.RmDirOp) (err error)
 	return
 }
 
+// Rename fuse implementation
 func (fs *GDriveFS) Rename(ctx context.Context, op *fuseops.RenameOp) (err error) {
 	oldParentFile := fs.root.findByInode(op.OldParent, true)
 	if oldParentFile == nil {

+ 27 - 0
src/vendor/github.com/jacobsa/fuse/.gitignore

@@ -0,0 +1,27 @@
+# Vim detritus
+.*.swp
+
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof

+ 28 - 0
src/vendor/github.com/jacobsa/fuse/.travis.yml

@@ -0,0 +1,28 @@
+# Cf. http://docs.travis-ci.com/user/getting-started/
+# Cf. http://docs.travis-ci.com/user/languages/go/
+
+matrix:
+  include:
+    - os: linux
+      language: go
+      go: 1.8.1
+      # Use the virtualized Trusty beta Travis is running in order to get
+      # support for installing fuse.
+      #
+      # Cf. Personal communication from support@travis-ci.com.
+      dist: trusty
+      sudo: required
+    - os: osx
+      language: go
+      go: 1.8.1
+
+# Install fuse before installing our code.
+before_install:
+  # For linux: install fuse.
+  - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+      sudo apt-get install -qq fuse;
+    fi
+
+  # For macOS: update homebrew and then install osxfuse.
+  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
+  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew cask install osxfuse; fi

+ 300 - 0
src/vendor/github.com/jacobsa/fuse/LICENSE

@@ -0,0 +1,300 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+==========================================================================
+Portions of this package were adopted from bazil.org/fuse, which contains the
+following license notice.
+
+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.
+*/

+ 30 - 0
src/vendor/github.com/jacobsa/fuse/README.md

@@ -0,0 +1,30 @@
+[![GoDoc](https://godoc.org/github.com/jacobsa/ogletest?status.svg)](https://godoc.org/github.com/jacobsa/fuse)
+
+This package allows for writing and mounting user-space file systems from Go.
+Install it as follows:
+
+    go get -u github.com/jacobsa/fuse
+
+Afterward, see the documentation for the following three packages:
+
+ *  Package [fuse][] provides support for mounting a new file system and
+    reading requests from the kernel.
+
+ *  Package [fuseops][] enumerates the supported requests from the kernel, and
+    provides documentation on their semantics.
+
+ *  Package [fuseutil][], in particular the `FileSystem` interface, provides a
+    convenient way to create a file system type and export it to the kernel via
+    `fuse.Mount`.
+
+Make sure to also see the sub-packages of the [samples][] package for examples
+and tests.
+
+This package owes its inspiration and most of its kernel-related code to
+[bazil.org/fuse][bazil].
+
+[fuse]: http://godoc.org/github.com/jacobsa/fuse
+[fuseops]: http://godoc.org/github.com/jacobsa/fuse/fuseops
+[fuseutil]: http://godoc.org/github.com/jacobsa/fuse/fuseutil
+[samples]: http://godoc.org/github.com/jacobsa/fuse/samples
+[bazil]: http://godoc.org/bazil.org/fuse

+ 509 - 0
src/vendor/github.com/jacobsa/fuse/connection.go

@@ -0,0 +1,509 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"path"
+	"runtime"
+	"sync"
+	"syscall"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/internal/buffer"
+	"github.com/jacobsa/fuse/internal/freelist"
+	"github.com/jacobsa/fuse/internal/fusekernel"
+)
+
+type contextKeyType uint64
+
+var contextKey interface{} = contextKeyType(0)
+
+// Ask the Linux kernel for larger read requests.
+//
+// As of 2015-03-26, the behavior in the kernel is:
+//
+//  *  (http://goo.gl/bQ1f1i, http://goo.gl/HwBrR6) Set the local variable
+//     ra_pages to be init_response->max_readahead divided by the page size.
+//
+//  *  (http://goo.gl/gcIsSh, http://goo.gl/LKV2vA) Set
+//     backing_dev_info::ra_pages to the min of that value and what was sent
+//     in the request's max_readahead field.
+//
+//  *  (http://goo.gl/u2SqzH) Use backing_dev_info::ra_pages when deciding
+//     how much to read ahead.
+//
+//  *  (http://goo.gl/JnhbdL) Don't read ahead at all if that field is zero.
+//
+// Reading a page at a time is a drag. Ask for a larger size.
+const maxReadahead = 1 << 20
+
+// Connection represents a connection to the fuse kernel process. It is used to
+// receive and reply to requests from the kernel.
+type Connection struct {
+	cfg         MountConfig
+	debugLogger *log.Logger
+	errorLogger *log.Logger
+
+	// The device through which we're talking to the kernel, and the protocol
+	// version that we're using to talk to it.
+	dev      *os.File
+	protocol fusekernel.Protocol
+
+	mu sync.Mutex
+
+	// A map from fuse "unique" request ID (*not* the op ID for logging used
+	// above) to a function that cancel's its associated context.
+	//
+	// GUARDED_BY(mu)
+	cancelFuncs map[uint64]func()
+
+	// Freelists, serviced by freelists.go.
+	inMessages  freelist.Freelist // GUARDED_BY(mu)
+	outMessages freelist.Freelist // GUARDED_BY(mu)
+}
+
+// State that is maintained for each in-flight op. This is stuffed into the
+// context that the user uses to reply to the op.
+type opState struct {
+	inMsg  *buffer.InMessage
+	outMsg *buffer.OutMessage
+	op     interface{}
+}
+
+// Create a connection wrapping the supplied file descriptor connected to the
+// kernel. You must eventually call c.close().
+//
+// The loggers may be nil.
+func newConnection(
+	cfg MountConfig,
+	debugLogger *log.Logger,
+	errorLogger *log.Logger,
+	dev *os.File) (c *Connection, err error) {
+	c = &Connection{
+		cfg:         cfg,
+		debugLogger: debugLogger,
+		errorLogger: errorLogger,
+		dev:         dev,
+		cancelFuncs: make(map[uint64]func()),
+	}
+
+	// Initialize.
+	err = c.Init()
+	if err != nil {
+		c.close()
+		err = fmt.Errorf("Init: %v", err)
+		return
+	}
+
+	return
+}
+
+// Init performs the work necessary to cause the mount process to complete.
+func (c *Connection) Init() (err error) {
+	// Read the init op.
+	ctx, op, err := c.ReadOp()
+	if err != nil {
+		err = fmt.Errorf("Reading init op: %v", err)
+		return
+	}
+
+	initOp, ok := op.(*initOp)
+	if !ok {
+		c.Reply(ctx, syscall.EPROTO)
+		err = fmt.Errorf("Expected *initOp, got %T", op)
+		return
+	}
+
+	// Make sure the protocol version spoken by the kernel is new enough.
+	min := fusekernel.Protocol{
+		fusekernel.ProtoVersionMinMajor,
+		fusekernel.ProtoVersionMinMinor,
+	}
+
+	if initOp.Kernel.LT(min) {
+		c.Reply(ctx, syscall.EPROTO)
+		err = fmt.Errorf("Version too old: %v", initOp.Kernel)
+		return
+	}
+
+	// Downgrade our protocol if necessary.
+	c.protocol = fusekernel.Protocol{
+		fusekernel.ProtoVersionMaxMajor,
+		fusekernel.ProtoVersionMaxMinor,
+	}
+
+	if initOp.Kernel.LT(c.protocol) {
+		c.protocol = initOp.Kernel
+	}
+
+	// Respond to the init op.
+	initOp.Library = c.protocol
+	initOp.MaxReadahead = maxReadahead
+	initOp.MaxWrite = buffer.MaxWriteSize
+
+	initOp.Flags = 0
+
+	// Tell the kernel not to use pitifully small 4 KiB writes.
+	initOp.Flags |= fusekernel.InitBigWrites
+
+	// Enable writeback caching if the user hasn't asked us not to.
+	if !c.cfg.DisableWritebackCaching {
+		initOp.Flags |= fusekernel.InitWritebackCache
+	}
+
+	c.Reply(ctx, nil)
+	return
+}
+
+// Log information for an operation with the given ID. calldepth is the depth
+// to use when recovering file:line information with runtime.Caller.
+func (c *Connection) debugLog(
+	fuseID uint64,
+	calldepth int,
+	format string,
+	v ...interface{}) {
+	if c.debugLogger == nil {
+		return
+	}
+
+	// Get file:line info.
+	var file string
+	var line int
+	var ok bool
+
+	_, file, line, ok = runtime.Caller(calldepth)
+	if !ok {
+		file = "???"
+	}
+
+	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)
+
+	// Format the actual message to be printed.
+	msg := fmt.Sprintf(
+		"Op 0x%08x %24s] %v",
+		fuseID,
+		fileLine,
+		fmt.Sprintf(format, v...))
+
+	// Print it.
+	c.debugLogger.Println(msg)
+}
+
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) recordCancelFunc(
+	fuseID uint64,
+	f func()) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	if _, ok := c.cancelFuncs[fuseID]; ok {
+		panic(fmt.Sprintf("Already have cancel func for request %v", fuseID))
+	}
+
+	c.cancelFuncs[fuseID] = f
+}
+
+// Set up state for an op that is about to be returned to the user, given its
+// underlying fuse opcode and request ID.
+//
+// Return a context that should be used for the op.
+//
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) beginOp(
+	opCode uint32,
+	fuseID uint64) (ctx context.Context) {
+	// Start with the parent context.
+	ctx = c.cfg.OpContext
+
+	// Set up a cancellation function.
+	//
+	// Special case: On Darwin, osxfuse aggressively reuses "unique" request IDs.
+	// This matters for Forget requests, which have no reply associated and
+	// therefore have IDs that are immediately eligible for reuse. For these, we
+	// should not record any state keyed on their ID.
+	//
+	// Cf. https://github.com/osxfuse/osxfuse/issues/208
+	if opCode != fusekernel.OpForget {
+		var cancel func()
+		ctx, cancel = context.WithCancel(ctx)
+		c.recordCancelFunc(fuseID, cancel)
+	}
+
+	return
+}
+
+// Clean up all state associated with an op to which the user has responded,
+// given its underlying fuse opcode and request ID. This must be called before
+// a response is sent to the kernel, to avoid a race where the request's ID
+// might be reused by osxfuse.
+//
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) finishOp(
+	opCode uint32,
+	fuseID uint64) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	// Even though the op is finished, context.WithCancel requires us to arrange
+	// for the cancellation function to be invoked. We also must remove it from
+	// our map.
+	//
+	// Special case: we don't do this for Forget requests. See the note in
+	// beginOp above.
+	if opCode != fusekernel.OpForget {
+		cancel, ok := c.cancelFuncs[fuseID]
+		if !ok {
+			panic(fmt.Sprintf("Unknown request ID in finishOp: %v", fuseID))
+		}
+
+		cancel()
+		delete(c.cancelFuncs, fuseID)
+	}
+}
+
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) handleInterrupt(fuseID uint64) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	// NOTE(jacobsa): fuse.txt in the Linux kernel documentation
+	// (https://goo.gl/H55Dnr) defines the kernel <-> userspace protocol for
+	// interrupts.
+	//
+	// In particular, my reading of it is that an interrupt request cannot be
+	// delivered to userspace before the original request. The part about the
+	// race and EAGAIN appears to be aimed at userspace programs that
+	// concurrently process requests (cf. http://goo.gl/BES2rs).
+	//
+	// So in this method if we can't find the ID to be interrupted, it means that
+	// the request has already been replied to.
+	//
+	// Cf. https://github.com/osxfuse/osxfuse/issues/208
+	// Cf. http://comments.gmane.org/gmane.comp.file-systems.fuse.devel/14675
+	cancel, ok := c.cancelFuncs[fuseID]
+	if !ok {
+		return
+	}
+
+	cancel()
+}
+
+// Read the next message from the kernel. The message must later be destroyed
+// using destroyInMessage.
+func (c *Connection) readMessage() (m *buffer.InMessage, err error) {
+	// Allocate a message.
+	m = c.getInMessage()
+
+	// Loop past transient errors.
+	for {
+		// Attempt a reaed.
+		err = m.Init(c.dev)
+
+		// Special cases:
+		//
+		//  *  ENODEV means fuse has hung up.
+		//
+		//  *  EINTR means we should try again. (This seems to happen often on
+		//     OS X, cf. http://golang.org/issue/11180)
+		//
+		if pe, ok := err.(*os.PathError); ok {
+			switch pe.Err {
+			case syscall.ENODEV:
+				err = io.EOF
+
+			case syscall.EINTR:
+				err = nil
+				continue
+			}
+		}
+
+		if err != nil {
+			c.putInMessage(m)
+			m = nil
+			return
+		}
+
+		return
+	}
+}
+
+// Write the supplied message to the kernel.
+func (c *Connection) writeMessage(msg []byte) (err error) {
+	// Avoid the retry loop in os.File.Write.
+	n, err := syscall.Write(int(c.dev.Fd()), msg)
+	if err != nil {
+		return
+	}
+
+	if n != len(msg) {
+		err = fmt.Errorf("Wrote %d bytes; expected %d", n, len(msg))
+		return
+	}
+
+	return
+}
+
+// ReadOp consumes the next op from the kernel process, returning the op and a
+// context that should be used for work related to the op. It returns io.EOF if
+// the kernel has closed the connection.
+//
+// If err != nil, the user is responsible for later calling c.Reply with the
+// returned context.
+//
+// This function delivers ops in exactly the order they are received from
+// /dev/fuse. It must not be called multiple times concurrently.
+//
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) ReadOp() (ctx context.Context, op interface{}, err error) {
+	// Keep going until we find a request we know how to convert.
+	for {
+		// Read the next message from the kernel.
+		var inMsg *buffer.InMessage
+		inMsg, err = c.readMessage()
+		if err != nil {
+			return
+		}
+
+		// Convert the message to an op.
+		outMsg := c.getOutMessage()
+		op, err = convertInMessage(inMsg, outMsg, c.protocol)
+		if err != nil {
+			c.putOutMessage(outMsg)
+			err = fmt.Errorf("convertInMessage: %v", err)
+			return
+		}
+
+		// Choose an ID for this operation for the purposes of logging, and log it.
+		if c.debugLogger != nil {
+			c.debugLog(inMsg.Header().Unique, 1, "<- %s", describeRequest(op))
+		}
+
+		// Special case: handle interrupt requests inline.
+		if interruptOp, ok := op.(*interruptOp); ok {
+			c.handleInterrupt(interruptOp.FuseID)
+			continue
+		}
+
+		// Set up a context that remembers information about this op.
+		ctx = c.beginOp(inMsg.Header().Opcode, inMsg.Header().Unique)
+		ctx = context.WithValue(ctx, contextKey, opState{inMsg, outMsg, op})
+
+		// Return the op to the user.
+		return
+	}
+}
+
+// Skip errors that happen as a matter of course, since they spook users.
+func (c *Connection) shouldLogError(
+	op interface{},
+	err error) bool {
+	// We don't log non-errors.
+	if err == nil {
+		return false
+	}
+
+	// We can't log if there's nothing to log to.
+	if c.errorLogger == nil {
+		return false
+	}
+
+	switch op.(type) {
+	case *fuseops.LookUpInodeOp:
+		// It is totally normal for the kernel to ask to look up an inode by name
+		// and find the name doesn't exist. For example, this happens when linking
+		// a new file.
+		if err == syscall.ENOENT {
+			return false
+		}
+
+	case *fuseops.GetXattrOp:
+		if err == syscall.ENODATA || err == syscall.ERANGE {
+			return false
+		}
+	case *unknownOp:
+		// Don't bother the user with methods we intentionally don't support.
+		if err == syscall.ENOSYS {
+			return false
+		}
+	}
+
+	return true
+}
+
+// Reply replies to an op previously read using ReadOp, with the supplied error
+// (or nil if successful). The context must be the context returned by ReadOp.
+//
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) Reply(ctx context.Context, opErr error) {
+	// Extract the state we stuffed in earlier.
+	var key interface{} = contextKey
+	foo := ctx.Value(key)
+	state, ok := foo.(opState)
+	if !ok {
+		panic(fmt.Sprintf("Reply called with invalid context: %#v", ctx))
+	}
+
+	op := state.op
+	inMsg := state.inMsg
+	outMsg := state.outMsg
+	fuseID := inMsg.Header().Unique
+
+	// Make sure we destroy the messages when we're done.
+	defer c.putInMessage(inMsg)
+	defer c.putOutMessage(outMsg)
+
+	// Clean up state for this op.
+	c.finishOp(inMsg.Header().Opcode, inMsg.Header().Unique)
+
+	// Debug logging
+	if c.debugLogger != nil {
+		if opErr == nil {
+			c.debugLog(fuseID, 1, "-> OK (%s)", describeResponse(op))
+		} else {
+			c.debugLog(fuseID, 1, "-> Error: %q", opErr.Error())
+		}
+	}
+
+	// Error logging
+	if c.shouldLogError(op, opErr) {
+		c.errorLogger.Printf("%T error: %v", op, opErr)
+	}
+
+	// Send the reply to the kernel, if one is required.
+	noResponse := c.kernelResponse(outMsg, inMsg.Header().Unique, op, opErr)
+
+	if !noResponse {
+		err := c.writeMessage(outMsg.Bytes())
+		if err != nil && c.errorLogger != nil {
+			c.errorLogger.Printf("writeMessage: %v %v", err, outMsg.Bytes())
+		}
+	}
+}
+
+// Close the connection. Must not be called until operations that were read
+// from the connection have been responded to.
+func (c *Connection) close() (err error) {
+	// Posix doesn't say that close can be called concurrently with read or
+	// write, but luckily we exclude the possibility of a race by requiring the
+	// user to respond to all ops first.
+	err = c.dev.Close()
+	return
+}

+ 890 - 0
src/vendor/github.com/jacobsa/fuse/conversions.go

@@ -0,0 +1,890 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"os"
+	"reflect"
+	"syscall"
+	"time"
+	"unsafe"
+
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/internal/buffer"
+	"github.com/jacobsa/fuse/internal/fusekernel"
+)
+
+////////////////////////////////////////////////////////////////////////
+// Incoming messages
+////////////////////////////////////////////////////////////////////////
+
+// Convert a kernel message to an appropriate op. If the op is unknown, a
+// special unexported type will be used.
+//
+// The caller is responsible for arranging for the message to be destroyed.
+func convertInMessage(
+	inMsg *buffer.InMessage,
+	outMsg *buffer.OutMessage,
+	protocol fusekernel.Protocol) (o interface{}, err error) {
+	switch inMsg.Header().Opcode {
+	case fusekernel.OpLookup:
+		buf := inMsg.ConsumeBytes(inMsg.Len())
+		n := len(buf)
+		if n == 0 || buf[n-1] != '\x00' {
+			err = errors.New("Corrupt OpLookup")
+			return
+		}
+
+		o = &fuseops.LookUpInodeOp{
+			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:   string(buf[:n-1]),
+		}
+
+	case fusekernel.OpGetattr:
+		o = &fuseops.GetInodeAttributesOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+		}
+
+	case fusekernel.OpSetattr:
+		type input fusekernel.SetattrIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpSetattr")
+			return
+		}
+
+		to := &fuseops.SetInodeAttributesOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+		}
+		o = to
+
+		valid := fusekernel.SetattrValid(in.Valid)
+		if valid&fusekernel.SetattrSize != 0 {
+			to.Size = &in.Size
+		}
+
+		if valid&fusekernel.SetattrMode != 0 {
+			mode := convertFileMode(in.Mode)
+			to.Mode = &mode
+		}
+
+		if valid&fusekernel.SetattrAtime != 0 {
+			t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
+			to.Atime = &t
+		}
+
+		if valid&fusekernel.SetattrMtime != 0 {
+			t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
+			to.Mtime = &t
+		}
+
+	case fusekernel.OpForget:
+		type input fusekernel.ForgetIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpForget")
+			return
+		}
+
+		o = &fuseops.ForgetInodeOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+			N:     in.Nlookup,
+		}
+
+	case fusekernel.OpMkdir:
+		in := (*fusekernel.MkdirIn)(inMsg.Consume(fusekernel.MkdirInSize(protocol)))
+		if in == nil {
+			err = errors.New("Corrupt OpMkdir")
+			return
+		}
+
+		name := inMsg.ConsumeBytes(inMsg.Len())
+		i := bytes.IndexByte(name, '\x00')
+		if i < 0 {
+			err = errors.New("Corrupt OpMkdir")
+			return
+		}
+		name = name[:i]
+
+		o = &fuseops.MkDirOp{
+			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:   string(name),
+
+			// On Linux, vfs_mkdir calls through to the inode with at most
+			// permissions and sticky bits set (cf. https://goo.gl/WxgQXk), and fuse
+			// passes that on directly (cf. https://goo.gl/f31aMo). In other words,
+			// the fact that this is a directory is implicit in the fact that the
+			// opcode is mkdir. But we want the correct mode to go through, so ensure
+			// that os.ModeDir is set.
+			Mode: convertFileMode(in.Mode) | os.ModeDir,
+		}
+
+	case fusekernel.OpMknod:
+		in := (*fusekernel.MknodIn)(inMsg.Consume(fusekernel.MknodInSize(protocol)))
+		if in == nil {
+			err = errors.New("Corrupt OpMknod")
+			return
+		}
+
+		name := inMsg.ConsumeBytes(inMsg.Len())
+		i := bytes.IndexByte(name, '\x00')
+		if i < 0 {
+			err = errors.New("Corrupt OpMknod")
+			return
+		}
+		name = name[:i]
+
+		o = &fuseops.MkNodeOp{
+			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:   string(name),
+			Mode:   convertFileMode(in.Mode),
+		}
+
+	case fusekernel.OpCreate:
+		in := (*fusekernel.CreateIn)(inMsg.Consume(fusekernel.CreateInSize(protocol)))
+		if in == nil {
+			err = errors.New("Corrupt OpCreate")
+			return
+		}
+
+		name := inMsg.ConsumeBytes(inMsg.Len())
+		i := bytes.IndexByte(name, '\x00')
+		if i < 0 {
+			err = errors.New("Corrupt OpCreate")
+			return
+		}
+		name = name[:i]
+
+		o = &fuseops.CreateFileOp{
+			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:   string(name),
+			Mode:   convertFileMode(in.Mode),
+		}
+
+	case fusekernel.OpSymlink:
+		// The message is "newName\0target\0".
+		names := inMsg.ConsumeBytes(inMsg.Len())
+		if len(names) == 0 || names[len(names)-1] != 0 {
+			err = errors.New("Corrupt OpSymlink")
+			return
+		}
+		i := bytes.IndexByte(names, '\x00')
+		if i < 0 {
+			err = errors.New("Corrupt OpSymlink")
+			return
+		}
+		newName, target := names[0:i], names[i+1:len(names)-1]
+
+		o = &fuseops.CreateSymlinkOp{
+			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:   string(newName),
+			Target: string(target),
+		}
+
+	case fusekernel.OpRename:
+		type input fusekernel.RenameIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpRename")
+			return
+		}
+
+		names := inMsg.ConsumeBytes(inMsg.Len())
+		// names should be "old\x00new\x00"
+		if len(names) < 4 {
+			err = errors.New("Corrupt OpRename")
+			return
+		}
+		if names[len(names)-1] != '\x00' {
+			err = errors.New("Corrupt OpRename")
+			return
+		}
+		i := bytes.IndexByte(names, '\x00')
+		if i < 0 {
+			err = errors.New("Corrupt OpRename")
+			return
+		}
+		oldName, newName := names[:i], names[i+1:len(names)-1]
+
+		o = &fuseops.RenameOp{
+			OldParent: fuseops.InodeID(inMsg.Header().Nodeid),
+			OldName:   string(oldName),
+			NewParent: fuseops.InodeID(in.Newdir),
+			NewName:   string(newName),
+		}
+
+	case fusekernel.OpUnlink:
+		buf := inMsg.ConsumeBytes(inMsg.Len())
+		n := len(buf)
+		if n == 0 || buf[n-1] != '\x00' {
+			err = errors.New("Corrupt OpUnlink")
+			return
+		}
+
+		o = &fuseops.UnlinkOp{
+			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:   string(buf[:n-1]),
+		}
+
+	case fusekernel.OpRmdir:
+		buf := inMsg.ConsumeBytes(inMsg.Len())
+		n := len(buf)
+		if n == 0 || buf[n-1] != '\x00' {
+			err = errors.New("Corrupt OpRmdir")
+			return
+		}
+
+		o = &fuseops.RmDirOp{
+			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:   string(buf[:n-1]),
+		}
+
+	case fusekernel.OpOpen:
+		o = &fuseops.OpenFileOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+		}
+
+	case fusekernel.OpOpendir:
+		o = &fuseops.OpenDirOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+		}
+
+	case fusekernel.OpRead:
+		in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol)))
+		if in == nil {
+			err = errors.New("Corrupt OpRead")
+			return
+		}
+
+		to := &fuseops.ReadFileOp{
+			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
+			Handle: fuseops.HandleID(in.Fh),
+			Offset: int64(in.Offset),
+		}
+		o = to
+
+		readSize := int(in.Size)
+		p := outMsg.GrowNoZero(readSize)
+		if p == nil {
+			err = fmt.Errorf("Can't grow for %d-byte read", readSize)
+			return
+		}
+
+		sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
+		sh.Data = uintptr(p)
+		sh.Len = readSize
+		sh.Cap = readSize
+
+	case fusekernel.OpReaddir:
+		in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol)))
+		if in == nil {
+			err = errors.New("Corrupt OpReaddir")
+			return
+		}
+
+		to := &fuseops.ReadDirOp{
+			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
+			Handle: fuseops.HandleID(in.Fh),
+			Offset: fuseops.DirOffset(in.Offset),
+		}
+		o = to
+
+		readSize := int(in.Size)
+		p := outMsg.GrowNoZero(readSize)
+		if p == nil {
+			err = fmt.Errorf("Can't grow for %d-byte read", readSize)
+			return
+		}
+
+		sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
+		sh.Data = uintptr(p)
+		sh.Len = readSize
+		sh.Cap = readSize
+
+	case fusekernel.OpRelease:
+		type input fusekernel.ReleaseIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpRelease")
+			return
+		}
+
+		o = &fuseops.ReleaseFileHandleOp{
+			Handle: fuseops.HandleID(in.Fh),
+		}
+
+	case fusekernel.OpReleasedir:
+		type input fusekernel.ReleaseIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpReleasedir")
+			return
+		}
+
+		o = &fuseops.ReleaseDirHandleOp{
+			Handle: fuseops.HandleID(in.Fh),
+		}
+
+	case fusekernel.OpWrite:
+		in := (*fusekernel.WriteIn)(inMsg.Consume(fusekernel.WriteInSize(protocol)))
+		if in == nil {
+			err = errors.New("Corrupt OpWrite")
+			return
+		}
+
+		buf := inMsg.ConsumeBytes(inMsg.Len())
+		if len(buf) < int(in.Size) {
+			err = errors.New("Corrupt OpWrite")
+			return
+		}
+
+		o = &fuseops.WriteFileOp{
+			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
+			Handle: fuseops.HandleID(in.Fh),
+			Data:   buf,
+			Offset: int64(in.Offset),
+		}
+
+	case fusekernel.OpFsync:
+		type input fusekernel.FsyncIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpFsync")
+			return
+		}
+
+		o = &fuseops.SyncFileOp{
+			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
+			Handle: fuseops.HandleID(in.Fh),
+		}
+
+	case fusekernel.OpFlush:
+		type input fusekernel.FlushIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpFlush")
+			return
+		}
+
+		o = &fuseops.FlushFileOp{
+			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
+			Handle: fuseops.HandleID(in.Fh),
+		}
+
+	case fusekernel.OpReadlink:
+		o = &fuseops.ReadSymlinkOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+		}
+
+	case fusekernel.OpStatfs:
+		o = &fuseops.StatFSOp{}
+
+	case fusekernel.OpInterrupt:
+		type input fusekernel.InterruptIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpInterrupt")
+			return
+		}
+
+		o = &interruptOp{
+			FuseID: in.Unique,
+		}
+
+	case fusekernel.OpInit:
+		type input fusekernel.InitIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpInit")
+			return
+		}
+
+		o = &initOp{
+			Kernel:       fusekernel.Protocol{in.Major, in.Minor},
+			MaxReadahead: in.MaxReadahead,
+			Flags:        fusekernel.InitFlags(in.Flags),
+		}
+
+	case fusekernel.OpRemovexattr:
+		buf := inMsg.ConsumeBytes(inMsg.Len())
+		n := len(buf)
+		if n == 0 || buf[n-1] != '\x00' {
+			err = errors.New("Corrupt OpRemovexattr")
+			return
+		}
+
+		o = &fuseops.RemoveXattrOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:  string(buf[:n-1]),
+		}
+
+	case fusekernel.OpGetxattr:
+		type input fusekernel.GetxattrIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpGetxattr")
+			return
+		}
+
+		name := inMsg.ConsumeBytes(inMsg.Len())
+		i := bytes.IndexByte(name, '\x00')
+		if i < 0 {
+			err = errors.New("Corrupt OpGetxattr")
+			return
+		}
+		name = name[:i]
+
+		to := &fuseops.GetXattrOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:  string(name),
+		}
+		o = to
+
+		readSize := int(in.Size)
+		p := outMsg.GrowNoZero(readSize)
+		if p == nil {
+			err = fmt.Errorf("Can't grow for %d-byte read", readSize)
+			return
+		}
+
+		sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
+		sh.Data = uintptr(p)
+		sh.Len = readSize
+		sh.Cap = readSize
+
+	case fusekernel.OpListxattr:
+		type input fusekernel.ListxattrIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpListxattr")
+			return
+		}
+
+		to := &fuseops.ListXattrOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+		}
+		o = to
+
+		readSize := int(in.Size)
+		if readSize != 0 {
+			p := outMsg.GrowNoZero(readSize)
+			if p == nil {
+				err = fmt.Errorf("Can't grow for %d-byte read", readSize)
+				return
+			}
+			sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
+			sh.Data = uintptr(p)
+			sh.Len = readSize
+			sh.Cap = readSize
+		}
+	case fusekernel.OpSetxattr:
+		type input fusekernel.SetxattrIn
+		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
+		if in == nil {
+			err = errors.New("Corrupt OpSetxattr")
+			return
+		}
+
+		payload := inMsg.ConsumeBytes(inMsg.Len())
+		// payload should be "name\x00value"
+		if len(payload) < 3 {
+			err = errors.New("Corrupt OpSetxattr")
+			return
+		}
+		i := bytes.IndexByte(payload, '\x00')
+		if i < 0 {
+			err = errors.New("Corrupt OpSetxattr")
+			return
+		}
+
+		name, value := payload[:i], payload[i+1:len(payload)]
+
+		o = &fuseops.SetXattrOp{
+			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
+			Name:  string(name),
+			Value: value,
+			Flags: in.Flags,
+		}
+
+	default:
+		o = &unknownOp{
+			OpCode: inMsg.Header().Opcode,
+			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
+		}
+	}
+
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// Outgoing messages
+////////////////////////////////////////////////////////////////////////
+
+// Fill in the response that should be sent to the kernel, or set noResponse if
+// the op requires no response.
+func (c *Connection) kernelResponse(
+	m *buffer.OutMessage,
+	fuseID uint64,
+	op interface{},
+	opErr error) (noResponse bool) {
+	h := m.OutHeader()
+	h.Unique = fuseID
+
+	// Special case: handle the ops for which the kernel expects no response.
+	// interruptOp .
+	switch op.(type) {
+	case *fuseops.ForgetInodeOp:
+		noResponse = true
+		return
+
+	case *interruptOp:
+		noResponse = true
+		return
+	}
+
+	// If the user returned the error, fill in the error field of the outgoing
+	// message header.
+	if opErr != nil {
+		handled := false
+
+		if opErr == syscall.ERANGE {
+			switch o := op.(type) {
+			case *fuseops.GetXattrOp:
+				writeXattrSize(m, uint32(o.BytesRead))
+				handled = true
+			case *fuseops.ListXattrOp:
+				writeXattrSize(m, uint32(o.BytesRead))
+				handled = true
+			}
+		}
+
+		if !handled {
+			m.OutHeader().Error = -int32(syscall.EIO)
+			if errno, ok := opErr.(syscall.Errno); ok {
+				m.OutHeader().Error = -int32(errno)
+			}
+
+			// Special case: for some types, convertInMessage grew the message in order
+			// to obtain a destination buffer. Make sure that we shrink back to just
+			// the header, because on OS X the kernel otherwise returns EINVAL when we
+			// attempt to write an error response with a length that extends beyond the
+			// header.
+			m.ShrinkTo(buffer.OutMessageHeaderSize)
+		}
+	}
+
+	// Otherwise, fill in the rest of the response.
+	if opErr == nil {
+		c.kernelResponseForOp(m, op)
+	}
+
+	h.Len = uint32(m.Len())
+	return
+}
+
+// Like kernelResponse, but assumes the user replied with a nil error to the
+// op.
+func (c *Connection) kernelResponseForOp(
+	m *buffer.OutMessage,
+	op interface{}) {
+	// Create the appropriate output message
+	switch o := op.(type) {
+	case *fuseops.LookUpInodeOp:
+		size := int(fusekernel.EntryOutSize(c.protocol))
+		out := (*fusekernel.EntryOut)(m.Grow(size))
+		convertChildInodeEntry(&o.Entry, out)
+
+	case *fuseops.GetInodeAttributesOp:
+		size := int(fusekernel.AttrOutSize(c.protocol))
+		out := (*fusekernel.AttrOut)(m.Grow(size))
+		out.AttrValid, out.AttrValidNsec = convertExpirationTime(
+			o.AttributesExpiration)
+		convertAttributes(o.Inode, &o.Attributes, &out.Attr)
+
+	case *fuseops.SetInodeAttributesOp:
+		size := int(fusekernel.AttrOutSize(c.protocol))
+		out := (*fusekernel.AttrOut)(m.Grow(size))
+		out.AttrValid, out.AttrValidNsec = convertExpirationTime(
+			o.AttributesExpiration)
+		convertAttributes(o.Inode, &o.Attributes, &out.Attr)
+
+	case *fuseops.MkDirOp:
+		size := int(fusekernel.EntryOutSize(c.protocol))
+		out := (*fusekernel.EntryOut)(m.Grow(size))
+		convertChildInodeEntry(&o.Entry, out)
+
+	case *fuseops.MkNodeOp:
+		size := int(fusekernel.EntryOutSize(c.protocol))
+		out := (*fusekernel.EntryOut)(m.Grow(size))
+		convertChildInodeEntry(&o.Entry, out)
+
+	case *fuseops.CreateFileOp:
+		eSize := int(fusekernel.EntryOutSize(c.protocol))
+
+		e := (*fusekernel.EntryOut)(m.Grow(eSize))
+		convertChildInodeEntry(&o.Entry, e)
+
+		oo := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
+		oo.Fh = uint64(o.Handle)
+
+	case *fuseops.CreateSymlinkOp:
+		size := int(fusekernel.EntryOutSize(c.protocol))
+		out := (*fusekernel.EntryOut)(m.Grow(size))
+		convertChildInodeEntry(&o.Entry, out)
+
+	case *fuseops.RenameOp:
+		// Empty response
+
+	case *fuseops.RmDirOp:
+		// Empty response
+
+	case *fuseops.UnlinkOp:
+		// Empty response
+
+	case *fuseops.OpenDirOp:
+		out := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
+		out.Fh = uint64(o.Handle)
+
+	case *fuseops.ReadDirOp:
+		// convertInMessage already set up the destination buffer to be at the end
+		// of the out message. We need only shrink to the right size based on how
+		// much the user read.
+		m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
+
+	case *fuseops.ReleaseDirHandleOp:
+		// Empty response
+
+	case *fuseops.OpenFileOp:
+		out := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
+		out.Fh = uint64(o.Handle)
+
+		if o.KeepPageCache {
+			out.OpenFlags |= uint32(fusekernel.OpenKeepCache)
+		}
+
+		if o.UseDirectIO {
+			out.OpenFlags |= uint32(fusekernel.OpenDirectIO)
+		}
+
+	case *fuseops.ReadFileOp:
+		// convertInMessage already set up the destination buffer to be at the end
+		// of the out message. We need only shrink to the right size based on how
+		// much the user read.
+		m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
+
+	case *fuseops.WriteFileOp:
+		out := (*fusekernel.WriteOut)(m.Grow(int(unsafe.Sizeof(fusekernel.WriteOut{}))))
+		out.Size = uint32(len(o.Data))
+
+	case *fuseops.SyncFileOp:
+		// Empty response
+
+	case *fuseops.FlushFileOp:
+		// Empty response
+
+	case *fuseops.ReleaseFileHandleOp:
+		// Empty response
+
+	case *fuseops.ReadSymlinkOp:
+		m.AppendString(o.Target)
+
+	case *fuseops.StatFSOp:
+		out := (*fusekernel.StatfsOut)(m.Grow(int(unsafe.Sizeof(fusekernel.StatfsOut{}))))
+		out.St.Blocks = o.Blocks
+		out.St.Bfree = o.BlocksFree
+		out.St.Bavail = o.BlocksAvailable
+		out.St.Files = o.Inodes
+		out.St.Ffree = o.InodesFree
+
+		// The posix spec for sys/statvfs.h (http://goo.gl/LktgrF) defines the
+		// following fields of statvfs, among others:
+		//
+		//     f_bsize    File system block size.
+		//     f_frsize   Fundamental file system block size.
+		//     f_blocks   Total number of blocks on file system in units of f_frsize.
+		//
+		// It appears as though f_bsize was the only thing supported by most unixes
+		// originally, but then f_frsize was added when new sorts of file systems
+		// came about. Quoth The Linux Programming Interface by Michael Kerrisk
+		// (https://goo.gl/5LZMxQ):
+		//
+		//     For most Linux file systems, the values of f_bsize and f_frsize are
+		//     the same. However, some file systems support the notion of block
+		//     fragments, which can be used to allocate a smaller unit of storage
+		//     at the end of the file if if a full block is not required. This
+		//     avoids the waste of space that would otherwise occur if a full block
+		//     was allocated. On such file systems, f_frsize is the size of a
+		//     fragment, and f_bsize is the size of a whole block. (The notion of
+		//     fragments in UNIX file systems first appeared in the early 1980s
+		//     with the 4.2BSD Fast File System.)
+		//
+		// Confusingly, it appears as though osxfuse surfaces fuse_kstatfs::bsize
+		// as statfs::f_iosize (of advisory use only), and fuse_kstatfs::frsize as
+		// statfs::f_bsize (which affects free space display in the Finder).
+		out.St.Bsize = o.IoSize
+		out.St.Frsize = o.BlockSize
+
+	case *fuseops.RemoveXattrOp:
+		// Empty response
+
+	case *fuseops.GetXattrOp:
+		// convertInMessage already set up the destination buffer to be at the end
+		// of the out message. We need only shrink to the right size based on how
+		// much the user read.
+		if o.BytesRead == 0 {
+			writeXattrSize(m, uint32(o.BytesRead))
+		} else {
+			m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
+		}
+
+	case *fuseops.ListXattrOp:
+		if o.BytesRead == 0 {
+			writeXattrSize(m, uint32(o.BytesRead))
+		} else {
+			m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
+		}
+
+	case *fuseops.SetXattrOp:
+		// Empty response
+
+	case *initOp:
+		out := (*fusekernel.InitOut)(m.Grow(int(unsafe.Sizeof(fusekernel.InitOut{}))))
+
+		out.Major = o.Library.Major
+		out.Minor = o.Library.Minor
+		out.MaxReadahead = o.MaxReadahead
+		out.Flags = uint32(o.Flags)
+		out.MaxWrite = o.MaxWrite
+
+	default:
+		panic(fmt.Sprintf("Unexpected op: %#v", op))
+	}
+
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// General conversions
+////////////////////////////////////////////////////////////////////////
+
+func convertTime(t time.Time) (secs uint64, nsec uint32) {
+	totalNano := t.UnixNano()
+	secs = uint64(totalNano / 1e9)
+	nsec = uint32(totalNano % 1e9)
+	return
+}
+
+func convertAttributes(
+	inodeID fuseops.InodeID,
+	in *fuseops.InodeAttributes,
+	out *fusekernel.Attr) {
+	out.Ino = uint64(inodeID)
+	out.Size = in.Size
+	out.Atime, out.AtimeNsec = convertTime(in.Atime)
+	out.Mtime, out.MtimeNsec = convertTime(in.Mtime)
+	out.Ctime, out.CtimeNsec = convertTime(in.Ctime)
+	out.SetCrtime(convertTime(in.Crtime))
+	out.Nlink = in.Nlink
+	out.Uid = in.Uid
+	out.Gid = in.Gid
+	// round up to the nearest 512 boundary
+	out.Blocks = (in.Size + 512 - 1) / 512
+
+	// Set the mode.
+	out.Mode = uint32(in.Mode) & 0777
+	switch {
+	default:
+		out.Mode |= syscall.S_IFREG
+	case in.Mode&os.ModeDir != 0:
+		out.Mode |= syscall.S_IFDIR
+	case in.Mode&os.ModeDevice != 0:
+		if in.Mode&os.ModeCharDevice != 0 {
+			out.Mode |= syscall.S_IFCHR
+		} else {
+			out.Mode |= syscall.S_IFBLK
+		}
+	case in.Mode&os.ModeNamedPipe != 0:
+		out.Mode |= syscall.S_IFIFO
+	case in.Mode&os.ModeSymlink != 0:
+		out.Mode |= syscall.S_IFLNK
+	case in.Mode&os.ModeSocket != 0:
+		out.Mode |= syscall.S_IFSOCK
+	}
+}
+
+// Convert an absolute cache expiration time to a relative time from now for
+// consumption by the fuse kernel module.
+func convertExpirationTime(t time.Time) (secs uint64, nsecs uint32) {
+	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
+	// counts of nanoseconds (cf. http://goo.gl/EJupJV). So negative durations
+	// are right out. There is no need to cap the positive magnitude, because
+	// 2^64 seconds is well longer than the 2^63 ns range of time.Duration.
+	d := t.Sub(time.Now())
+	if d > 0 {
+		secs = uint64(d / time.Second)
+		nsecs = uint32((d % time.Second) / time.Nanosecond)
+	}
+
+	return
+}
+
+func convertChildInodeEntry(
+	in *fuseops.ChildInodeEntry,
+	out *fusekernel.EntryOut) {
+	out.Nodeid = uint64(in.Child)
+	out.Generation = uint64(in.Generation)
+	out.EntryValid, out.EntryValidNsec = convertExpirationTime(in.EntryExpiration)
+	out.AttrValid, out.AttrValidNsec = convertExpirationTime(in.AttributesExpiration)
+
+	convertAttributes(in.Child, &in.Attributes, &out.Attr)
+}
+
+func convertFileMode(unixMode uint32) os.FileMode {
+	mode := os.FileMode(unixMode & 0777)
+	switch unixMode & syscall.S_IFMT {
+	case syscall.S_IFREG:
+		// nothing
+	case syscall.S_IFDIR:
+		mode |= os.ModeDir
+	case syscall.S_IFCHR:
+		mode |= os.ModeCharDevice | os.ModeDevice
+	case syscall.S_IFBLK:
+		mode |= os.ModeDevice
+	case syscall.S_IFIFO:
+		mode |= os.ModeNamedPipe
+	case syscall.S_IFLNK:
+		mode |= os.ModeSymlink
+	case syscall.S_IFSOCK:
+		mode |= os.ModeSocket
+	default:
+		// no idea
+		mode |= os.ModeDevice
+	}
+	if unixMode&syscall.S_ISUID != 0 {
+		mode |= os.ModeSetuid
+	}
+	if unixMode&syscall.S_ISGID != 0 {
+		mode |= os.ModeSetgid
+	}
+	return mode
+}
+
+func writeXattrSize(m *buffer.OutMessage, size uint32) {
+	out := (*fusekernel.GetxattrOut)(m.Grow(int(unsafe.Sizeof(fusekernel.GetxattrOut{}))))
+	out.Size = size
+}

+ 129 - 0
src/vendor/github.com/jacobsa/fuse/debug.go

@@ -0,0 +1,129 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+import (
+	"fmt"
+	"reflect"
+	"strings"
+
+	"github.com/jacobsa/fuse/fuseops"
+)
+
+// Decide on the name of the given op.
+func opName(op interface{}) string {
+	// We expect all ops to be pointers.
+	t := reflect.TypeOf(op).Elem()
+
+	// Strip the "Op" from "FooOp".
+	return strings.TrimSuffix(t.Name(), "Op")
+}
+
+func describeRequest(op interface{}) (s string) {
+	v := reflect.ValueOf(op).Elem()
+
+	// We will set up a comma-separated list of components.
+	var components []string
+	addComponent := func(format string, v ...interface{}) {
+		components = append(components, fmt.Sprintf(format, v...))
+	}
+
+	// Include an inode number, if available.
+	if f := v.FieldByName("Inode"); f.IsValid() {
+		addComponent("inode %v", f.Interface())
+	}
+
+	// Include a parent inode number, if available.
+	if f := v.FieldByName("Parent"); f.IsValid() {
+		addComponent("parent %v", f.Interface())
+	}
+
+	// Include a name, if available.
+	if f := v.FieldByName("Name"); f.IsValid() {
+		addComponent("name %q", f.Interface())
+	}
+
+	// Handle special cases.
+	switch typed := op.(type) {
+	case *interruptOp:
+		addComponent("fuseid 0x%08x", typed.FuseID)
+
+	case *unknownOp:
+		addComponent("opcode %d", typed.OpCode)
+
+	case *fuseops.SetInodeAttributesOp:
+		if typed.Size != nil {
+			addComponent("size %d", *typed.Size)
+		}
+
+		if typed.Mode != nil {
+			addComponent("mode %v", *typed.Mode)
+		}
+
+		if typed.Atime != nil {
+			addComponent("atime %v", *typed.Atime)
+		}
+
+		if typed.Mtime != nil {
+			addComponent("mtime %v", *typed.Mtime)
+		}
+
+	case *fuseops.ReadFileOp:
+		addComponent("handle %d", typed.Handle)
+		addComponent("offset %d", typed.Offset)
+		addComponent("%d bytes", len(typed.Dst))
+
+	case *fuseops.WriteFileOp:
+		addComponent("handle %d", typed.Handle)
+		addComponent("offset %d", typed.Offset)
+		addComponent("%d bytes", len(typed.Data))
+
+	case *fuseops.RemoveXattrOp:
+		addComponent("name %s", typed.Name)
+
+	case *fuseops.GetXattrOp:
+		addComponent("name %s", typed.Name)
+
+	case *fuseops.SetXattrOp:
+		addComponent("name %s", typed.Name)
+	}
+
+	// Use just the name if there is no extra info.
+	if len(components) == 0 {
+		return opName(op)
+	}
+
+	// Otherwise, include the extra info.
+	return fmt.Sprintf("%s (%s)", opName(op), strings.Join(components, ", "))
+}
+
+func describeResponse(op interface{}) string {
+	v := reflect.ValueOf(op).Elem()
+
+	// We will set up a comma-separated list of components.
+	var components []string
+	addComponent := func(format string, v ...interface{}) {
+		components = append(components, fmt.Sprintf(format, v...))
+	}
+
+	// Include a resulting inode number, if available.
+	if f := v.FieldByName("Entry"); f.IsValid() {
+		if entry, ok := f.Interface().(fuseops.ChildInodeEntry); ok {
+			addComponent("inode %v", entry.Child)
+		}
+	}
+
+	return fmt.Sprintf("%s", strings.Join(components, ", "))
+}

+ 36 - 0
src/vendor/github.com/jacobsa/fuse/doc.go

@@ -0,0 +1,36 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package fuse enables writing and mounting user-space file systems.
+//
+// The primary elements of interest are:
+//
+//  *  The fuseops package, which defines the operations that fuse might send
+//     to your userspace daemon.
+//
+//  *  The Server interface, which your daemon must implement.
+//
+//  *  fuseutil.NewFileSystemServer, which offers a convenient way to implement
+//     the Server interface.
+//
+//  *  Mount, a function that allows for mounting a Server as a file system.
+//
+// Make sure to see the examples in the sub-packages of samples/, which double
+// as tests for this package: http://godoc.org/github.com/jacobsa/fuse/samples
+//
+// In order to use this package to mount file systems on OS X, the system must
+// have FUSE for OS X installed (see http://osxfuse.github.io/). Do note that
+// there are several OS X-specific oddities; grep through the documentation for
+// more info.
+package fuse

+ 30 - 0
src/vendor/github.com/jacobsa/fuse/errors.go

@@ -0,0 +1,30 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+import "syscall"
+
+const (
+	// Errors corresponding to kernel error numbers. These may be treated
+	// specially by Connection.Reply.
+	EEXIST    = syscall.EEXIST
+	EINVAL    = syscall.EINVAL
+	EIO       = syscall.EIO
+	ENOATTR   = syscall.ENODATA
+	ENOENT    = syscall.ENOENT
+	ENOSYS    = syscall.ENOSYS
+	ENOTDIR   = syscall.ENOTDIR
+	ENOTEMPTY = syscall.ENOTEMPTY
+)

+ 70 - 0
src/vendor/github.com/jacobsa/fuse/freelists.go

@@ -0,0 +1,70 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+import (
+	"unsafe"
+
+	"github.com/jacobsa/fuse/internal/buffer"
+)
+
+////////////////////////////////////////////////////////////////////////
+// buffer.InMessage
+////////////////////////////////////////////////////////////////////////
+
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) getInMessage() (x *buffer.InMessage) {
+	c.mu.Lock()
+	x = (*buffer.InMessage)(c.inMessages.Get())
+	c.mu.Unlock()
+
+	if x == nil {
+		x = new(buffer.InMessage)
+	}
+
+	return
+}
+
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) putInMessage(x *buffer.InMessage) {
+	c.mu.Lock()
+	c.inMessages.Put(unsafe.Pointer(x))
+	c.mu.Unlock()
+}
+
+////////////////////////////////////////////////////////////////////////
+// buffer.OutMessage
+////////////////////////////////////////////////////////////////////////
+
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) getOutMessage() (x *buffer.OutMessage) {
+	c.mu.Lock()
+	x = (*buffer.OutMessage)(c.outMessages.Get())
+	c.mu.Unlock()
+
+	if x == nil {
+		x = new(buffer.OutMessage)
+	}
+	x.Reset()
+
+	return
+}
+
+// LOCKS_EXCLUDED(c.mu)
+func (c *Connection) putOutMessage(x *buffer.OutMessage) {
+	c.mu.Lock()
+	c.outMessages.Put(unsafe.Pointer(x))
+	c.mu.Unlock()
+}

+ 23 - 0
src/vendor/github.com/jacobsa/fuse/fsutil/fdatasync_darwin.go

@@ -0,0 +1,23 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsutil
+
+import "os"
+
+const FdatasyncSupported = false
+
+func fdatasync(f *os.File) error {
+	panic("We require FdatasyncSupported be true.")
+}

+ 26 - 0
src/vendor/github.com/jacobsa/fuse/fsutil/fdatasync_linux.go

@@ -0,0 +1,26 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsutil
+
+import (
+	"os"
+	"syscall"
+)
+
+const FdatasyncSupported = true
+
+func fdatasync(f *os.File) error {
+	return syscall.Fdatasync(int(f.Fd()))
+}

+ 57 - 0
src/vendor/github.com/jacobsa/fuse/fsutil/fsutil.go

@@ -0,0 +1,57 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsutil
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+)
+
+// Create a temporary file with the same semantics as ioutil.TempFile, but
+// ensure that it is unlinked before returning so that it does not persist
+// after the process exits.
+//
+// Warning: this is not production-quality code, and should only be used for
+// testing purposes. In particular, there is a race between creating and
+// unlinking by name.
+func AnonymousFile(dir string) (f *os.File, err error) {
+	// Choose a prefix based on the binary name.
+	prefix := path.Base(os.Args[0])
+
+	// Create the file.
+	f, err = ioutil.TempFile(dir, prefix)
+	if err != nil {
+		err = fmt.Errorf("TempFile: %v", err)
+		return
+	}
+
+	// Unlink it.
+	err = os.Remove(f.Name())
+	if err != nil {
+		err = fmt.Errorf("Remove: %v", err)
+		return
+	}
+
+	return
+}
+
+// Call fdatasync on the supplied file.
+//
+// REQUIRES: FdatasyncSupported is true.
+func Fdatasync(f *os.File) error {
+	return fdatasync(f)
+}

+ 17 - 0
src/vendor/github.com/jacobsa/fuse/fuseops/doc.go

@@ -0,0 +1,17 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package fuseops contains ops that may be returned by fuse.Connection.ReadOp.
+// See documentation in that package for more.
+package fuseops

+ 847 - 0
src/vendor/github.com/jacobsa/fuse/fuseops/ops.go

@@ -0,0 +1,847 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuseops
+
+import (
+	"os"
+	"time"
+)
+
+////////////////////////////////////////////////////////////////////////
+// File system
+////////////////////////////////////////////////////////////////////////
+
+// Return statistics about the file system's capacity and available resources.
+//
+// Called by statfs(2) and friends:
+//
+//     * (https://goo.gl/Xi1lDr) sys_statfs called user_statfs, which calls
+//        vfs_statfs, which calls statfs_by_dentry.
+//
+//     * (https://goo.gl/VAIOwU) statfs_by_dentry calls the superblock
+//       operation statfs, which in our case points at
+//       fuse_statfs (cf. https://goo.gl/L7BTM3)
+//
+//     * (https://goo.gl/Zn7Sgl) fuse_statfs sends a statfs op, then uses
+//       convert_fuse_statfs to convert the response in a straightforward
+//       manner.
+//
+// This op is particularly important on OS X: if you don't implement it, the
+// file system will not successfully mount. If you don't model a sane amount of
+// free space, the Finder will refuse to copy files into the file system.
+type StatFSOp struct {
+	// The size of the file system's blocks. This may be used, in combination
+	// with the block counts below,  by callers of statfs(2) to infer the file
+	// system's capacity and space availability.
+	//
+	// On Linux this is surfaced as statfs::f_frsize, matching the posix standard
+	// (http://goo.gl/LktgrF), which says that f_blocks and friends are in units
+	// of f_frsize. On OS X this is surfaced as statfs::f_bsize, which plays the
+	// same roll.
+	//
+	// It appears as though the original intent of statvfs::f_frsize in the posix
+	// standard was to support a smaller addressable unit than statvfs::f_bsize
+	// (cf. The Linux Programming Interface by Michael Kerrisk,
+	// https://goo.gl/5LZMxQ). Therefore users should probably arrange for this
+	// to be no larger than IoSize.
+	//
+	// On Linux this can be any value, and will be faithfully returned to the
+	// caller of statfs(2) (see the code walk above). On OS X it appears that
+	// only powers of 2 in the range [2^7, 2^20] are preserved, and a value of
+	// zero is treated as 4096.
+	//
+	// This interface does not distinguish between blocks and block fragments.
+	BlockSize uint32
+
+	// The total number of blocks in the file system, the number of unused
+	// blocks, and the count of the latter that are available for use by non-root
+	// users.
+	//
+	// For each category, the corresponding number of bytes is derived by
+	// multiplying by BlockSize.
+	Blocks          uint64
+	BlocksFree      uint64
+	BlocksAvailable uint64
+
+	// The preferred size of writes to and reads from the file system, in bytes.
+	// This may affect clients that use statfs(2) to size buffers correctly. It
+	// does not appear to influence the size of writes sent from the kernel to
+	// the file system daemon.
+	//
+	// On Linux this is surfaced as statfs::f_bsize, and on OS X as
+	// statfs::f_iosize. Both are documented in `man 2 statfs` as "optimal
+	// transfer block size".
+	//
+	// On Linux this can be any value. On OS X it appears that only powers of 2
+	// in the range [2^12, 2^25] are faithfully preserved, and a value of zero is
+	// treated as 65536.
+	IoSize uint32
+
+	// The total number of inodes in the file system, and how many remain free.
+	Inodes     uint64
+	InodesFree uint64
+}
+
+////////////////////////////////////////////////////////////////////////
+// Inodes
+////////////////////////////////////////////////////////////////////////
+
+// Look up a child by name within a parent directory. The kernel sends this
+// when resolving user paths to dentry structs, which are then cached.
+type LookUpInodeOp struct {
+	// The ID of the directory inode to which the child belongs.
+	Parent InodeID
+
+	// The name of the child of interest, relative to the parent. For example, in
+	// this directory structure:
+	//
+	//     foo/
+	//         bar/
+	//             baz
+	//
+	// the file system may receive a request to look up the child named "bar" for
+	// the parent foo/.
+	Name string
+
+	// The resulting entry. Must be filled out by the file system.
+	//
+	// The lookup count for the inode is implicitly incremented. See notes on
+	// ForgetInodeOp for more information.
+	Entry ChildInodeEntry
+}
+
+// Refresh the attributes for an inode whose ID was previously returned in a
+// LookUpInodeOp. The kernel sends this when the FUSE VFS layer's cache of
+// inode attributes is stale. This is controlled by the AttributesExpiration
+// field of ChildInodeEntry, etc.
+type GetInodeAttributesOp struct {
+	// The inode of interest.
+	Inode InodeID
+
+	// Set by the file system: attributes for the inode, and the time at which
+	// they should expire. See notes on ChildInodeEntry.AttributesExpiration for
+	// more.
+	Attributes           InodeAttributes
+	AttributesExpiration time.Time
+}
+
+// Change attributes for an inode.
+//
+// The kernel sends this for obvious cases like chmod(2), and for less obvious
+// cases like ftrunctate(2).
+type SetInodeAttributesOp struct {
+	// The inode of interest.
+	Inode InodeID
+
+	// The attributes to modify, or nil for attributes that don't need a change.
+	Size  *uint64
+	Mode  *os.FileMode
+	Atime *time.Time
+	Mtime *time.Time
+
+	// Set by the file system: the new attributes for the inode, and the time at
+	// which they should expire. See notes on
+	// ChildInodeEntry.AttributesExpiration for more.
+	Attributes           InodeAttributes
+	AttributesExpiration time.Time
+}
+
+// Decrement the reference count for an inode ID previously issued by the file
+// system.
+//
+// The comments for the ops that implicitly increment the reference count
+// contain a note of this (but see also the note about the root inode below).
+// For example, LookUpInodeOp and MkDirOp. The authoritative source is the
+// libfuse documentation, which states that any op that returns
+// fuse_reply_entry fuse_reply_create implicitly increments (cf.
+// http://goo.gl/o5C7Dx).
+//
+// If the reference count hits zero, the file system can forget about that ID
+// entirely, and even re-use it in future responses. The kernel guarantees that
+// it will not otherwise use it again.
+//
+// The reference count corresponds to fuse_inode::nlookup
+// (http://goo.gl/ut48S4). Some examples of where the kernel manipulates it:
+//
+//  *  (http://goo.gl/vPD9Oh) Any caller to fuse_iget increases the count.
+//  *  (http://goo.gl/B6tTTC) fuse_lookup_name calls fuse_iget.
+//  *  (http://goo.gl/IlcxWv) fuse_create_open calls fuse_iget.
+//  *  (http://goo.gl/VQMQul) fuse_dentry_revalidate increments after
+//     revalidating.
+//
+// In contrast to all other inodes, RootInodeID begins with an implicit
+// lookup count of one, without a corresponding op to increase it. (There
+// could be no such op, because the root cannot be referred to by name.) Code
+// walk:
+//
+//  *  (http://goo.gl/gWAheU) fuse_fill_super calls fuse_get_root_inode.
+//
+//  *  (http://goo.gl/AoLsbb) fuse_get_root_inode calls fuse_iget without
+//     sending any particular request.
+//
+//  *  (http://goo.gl/vPD9Oh) fuse_iget increments nlookup.
+//
+// File systems should tolerate but not rely on receiving forget ops for
+// remaining inodes when the file system unmounts, including the root inode.
+// Rather they should take fuse.Connection.ReadOp returning io.EOF as
+// implicitly decrementing all lookup counts to zero.
+type ForgetInodeOp struct {
+	// The inode whose reference count should be decremented.
+	Inode InodeID
+
+	// The amount to decrement the reference count.
+	N uint64
+}
+
+////////////////////////////////////////////////////////////////////////
+// Inode creation
+////////////////////////////////////////////////////////////////////////
+
+// Create a directory inode as a child of an existing directory inode. The
+// kernel sends this in response to a mkdir(2) call.
+//
+// The Linux kernel appears to verify the name doesn't already exist (mkdir
+// calls mkdirat calls user_path_create calls filename_create, which verifies:
+// http://goo.gl/FZpLu5). Indeed, the tests in samples/memfs that call in
+// parallel appear to bear this out. But osxfuse does not appear to guarantee
+// this (cf. https://goo.gl/PqzZDv). And if names may be created outside of the
+// kernel's control, it doesn't matter what the kernel does anyway.
+//
+// Therefore the file system should return EEXIST if the name already exists.
+type MkDirOp struct {
+	// The ID of parent directory inode within which to create the child.
+	Parent InodeID
+
+	// The name of the child to create, and the mode with which to create it.
+	Name string
+	Mode os.FileMode
+
+	// Set by the file system: information about the inode that was created.
+	//
+	// The lookup count for the inode is implicitly incremented. See notes on
+	// ForgetInodeOp for more information.
+	Entry ChildInodeEntry
+}
+
+// Create a file inode as a child of an existing directory inode. The kernel
+// sends this in response to a mknod(2) call. It may also send it in special
+// cases such as an NFS export (cf. https://goo.gl/HiLfnK). It is more typical
+// to see CreateFileOp, which is received for an open(2) that creates a file.
+//
+// The Linux kernel appears to verify the name doesn't already exist (mknod
+// calls sys_mknodat calls user_path_create calls filename_create, which
+// verifies: http://goo.gl/FZpLu5). But osxfuse may not guarantee this, as with
+// mkdir(2). And if names may be created outside of the kernel's control, it
+// doesn't matter what the kernel does anyway.
+//
+// Therefore the file system should return EEXIST if the name already exists.
+type MkNodeOp struct {
+	// The ID of parent directory inode within which to create the child.
+	Parent InodeID
+
+	// The name of the child to create, and the mode with which to create it.
+	Name string
+	Mode os.FileMode
+
+	// Set by the file system: information about the inode that was created.
+	//
+	// The lookup count for the inode is implicitly incremented. See notes on
+	// ForgetInodeOp for more information.
+	Entry ChildInodeEntry
+}
+
+// Create a file inode and open it.
+//
+// The kernel sends this when the user asks to open a file with the O_CREAT
+// flag and the kernel has observed that the file doesn't exist. (See for
+// example lookup_open, http://goo.gl/PlqE9d). However, osxfuse doesn't appear
+// to make this check atomically (cf. https://goo.gl/PqzZDv). And if names may
+// be created outside of the kernel's control, it doesn't matter what the
+// kernel does anyway.
+//
+// Therefore the file system should return EEXIST if the name already exists.
+type CreateFileOp struct {
+	// The ID of parent directory inode within which to create the child file.
+	Parent InodeID
+
+	// The name of the child to create, and the mode with which to create it.
+	Name string
+	Mode os.FileMode
+
+	// Set by the file system: information about the inode that was created.
+	//
+	// The lookup count for the inode is implicitly incremented. See notes on
+	// ForgetInodeOp for more information.
+	Entry ChildInodeEntry
+
+	// Set by the file system: an opaque ID that will be echoed in follow-up
+	// calls for this file using the same struct file in the kernel. In practice
+	// this usually means follow-up calls using the file descriptor returned by
+	// open(2).
+	//
+	// The handle may be supplied in future ops like ReadFileOp that contain a
+	// file handle. The file system must ensure this ID remains valid until a
+	// later call to ReleaseFileHandle.
+	Handle HandleID
+}
+
+// Create a symlink inode. If the name already exists, the file system should
+// return EEXIST (cf. the notes on CreateFileOp and MkDirOp).
+type CreateSymlinkOp struct {
+	// The ID of parent directory inode within which to create the child symlink.
+	Parent InodeID
+
+	// The name of the symlink to create.
+	Name string
+
+	// The target of the symlink.
+	Target string
+
+	// Set by the file system: information about the symlink inode that was
+	// created.
+	//
+	// The lookup count for the inode is implicitly incremented. See notes on
+	// ForgetInodeOp for more information.
+	Entry ChildInodeEntry
+}
+
+////////////////////////////////////////////////////////////////////////
+// Unlinking
+////////////////////////////////////////////////////////////////////////
+
+// Rename a file or directory, given the IDs of the original parent directory
+// and the new one (which may be the same).
+//
+// In Linux, this is called by vfs_rename (https://goo.gl/eERItT), which is
+// called by sys_renameat2 (https://goo.gl/fCC9qC).
+//
+// The kernel takes care of ensuring that the source and destination are not
+// identical (in which case it does nothing), that the rename is not across
+// file system boundaries, and that the destination doesn't already exist with
+// the wrong type. Some subtleties that the file system must care about:
+//
+//  *  If the new name is an existing directory, the file system must ensure it
+//     is empty before replacing it, returning ENOTEMPTY otherwise. (This is
+//     per the posix spec: http://goo.gl/4XtT79)
+//
+//  *  The rename must be atomic from the point of view of an observer of the
+//     new name. That is, if the new name already exists, there must be no
+//     point at which it doesn't exist.
+//
+//  *  It is okay for the new name to be modified before the old name is
+//     removed; these need not be atomic. In fact, the Linux man page
+//     explicitly says this is likely (cf. https://goo.gl/Y1wVZc).
+//
+//  *  Linux bends over backwards (https://goo.gl/pLDn3r) to ensure that
+//     neither the old nor the new parent can be concurrently modified. But
+//     it's not clear whether OS X does this, and in any case it doesn't matter
+//     for file systems that may be modified remotely. Therefore a careful file
+//     system implementor should probably ensure if possible that the unlink
+//     step in the "link new name, unlink old name" process doesn't unlink a
+//     different inode than the one that was linked to the new name. Still,
+//     posix and the man pages are imprecise about the actual semantics of a
+//     rename if it's not atomic, so it is probably not disastrous to be loose
+//     about this.
+//
+type RenameOp struct {
+	// The old parent directory, and the name of the entry within it to be
+	// relocated.
+	OldParent InodeID
+	OldName   string
+
+	// The new parent directory, and the name of the entry to be created or
+	// overwritten within it.
+	NewParent InodeID
+	NewName   string
+}
+
+// Unlink a directory from its parent. Because directories cannot have a link
+// count above one, this means the directory inode should be deleted as well
+// once the kernel sends ForgetInodeOp.
+//
+// The file system is responsible for checking that the directory is empty.
+//
+// Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf)
+type RmDirOp struct {
+	// The ID of parent directory inode, and the name of the directory being
+	// removed within it.
+	Parent InodeID
+	Name   string
+}
+
+// Unlink a file or symlink from its parent. If this brings the inode's link
+// count to zero, the inode should be deleted once the kernel sends
+// ForgetInodeOp. It may still be referenced before then if a user still has
+// the file open.
+//
+// Sample implementation in ext2: ext2_unlink (http://goo.gl/hY6r6C)
+type UnlinkOp struct {
+	// The ID of parent directory inode, and the name of the entry being removed
+	// within it.
+	Parent InodeID
+	Name   string
+}
+
+////////////////////////////////////////////////////////////////////////
+// Directory handles
+////////////////////////////////////////////////////////////////////////
+
+// Open a directory inode.
+//
+// On Linux the sends this when setting up a struct file for a particular inode
+// with type directory, usually in response to an open(2) call from a
+// user-space process. On OS X it may not be sent for every open(2) (cf.
+// https://github.com/osxfuse/osxfuse/issues/199).
+type OpenDirOp struct {
+	// The ID of the inode to be opened.
+	Inode InodeID
+
+	// Set by the file system: an opaque ID that will be echoed in follow-up
+	// calls for this directory using the same struct file in the kernel. In
+	// practice this usually means follow-up calls using the file descriptor
+	// returned by open(2).
+	//
+	// The handle may be supplied in future ops like ReadDirOp that contain a
+	// directory handle. The file system must ensure this ID remains valid until
+	// a later call to ReleaseDirHandle.
+	Handle HandleID
+}
+
+// Read entries from a directory previously opened with OpenDir.
+type ReadDirOp struct {
+	// The directory inode that we are reading, and the handle previously
+	// returned by OpenDir when opening that inode.
+	Inode  InodeID
+	Handle HandleID
+
+	// The offset within the directory at which to read.
+	//
+	// Warning: this field is not necessarily a count of bytes. Its legal values
+	// are defined by the results returned in ReadDirResponse. See the notes
+	// below and the notes on that struct.
+	//
+	// In the Linux kernel this ultimately comes from file::f_pos, which starts
+	// at zero and is set by llseek and by the final consumed result returned by
+	// each call to ReadDir:
+	//
+	//  *  (http://goo.gl/2nWJPL) iterate_dir, which is called by getdents(2) and
+	//     readdir(2), sets dir_context::pos to file::f_pos before calling
+	//     f_op->iterate, and then does the opposite assignment afterward.
+	//
+	//  *  (http://goo.gl/rTQVSL) fuse_readdir, which implements iterate for fuse
+	//     directories, passes dir_context::pos as the offset to fuse_read_fill,
+	//     which passes it on to user-space. fuse_readdir later calls
+	//     parse_dirfile with the same context.
+	//
+	//  *  (http://goo.gl/vU5ukv) For each returned result (except perhaps the
+	//     last, which may be truncated by the page boundary), parse_dirfile
+	//     updates dir_context::pos with fuse_dirent::off.
+	//
+	// It is affected by the Posix directory stream interfaces in the following
+	// manner:
+	//
+	//  *  (http://goo.gl/fQhbyn, http://goo.gl/ns1kDF) opendir initially causes
+	//     filepos to be set to zero.
+	//
+	//  *  (http://goo.gl/ezNKyR, http://goo.gl/xOmDv0) readdir allows the user
+	//     to iterate through the directory one entry at a time. As each entry is
+	//     consumed, its d_off field is stored in __dirstream::filepos.
+	//
+	//  *  (http://goo.gl/WEOXG8, http://goo.gl/rjSXl3) telldir allows the user
+	//     to obtain the d_off field from the most recently returned entry.
+	//
+	//  *  (http://goo.gl/WG3nDZ, http://goo.gl/Lp0U6W) seekdir allows the user
+	//     to seek backward to an offset previously returned by telldir. It
+	//     stores the new offset in filepos, and calls llseek to update the
+	//     kernel's struct file.
+	//
+	//  *  (http://goo.gl/gONQhz, http://goo.gl/VlrQkc) rewinddir allows the user
+	//     to go back to the beginning of the directory, obtaining a fresh view.
+	//     It updates filepos and calls llseek to update the kernel's struct
+	//     file.
+	//
+	// Unfortunately, FUSE offers no way to intercept seeks
+	// (http://goo.gl/H6gEXa), so there is no way to cause seekdir or rewinddir
+	// to fail. Additionally, there is no way to distinguish an explicit
+	// rewinddir followed by readdir from the initial readdir, or a rewinddir
+	// from a seekdir to the value returned by telldir just after opendir.
+	//
+	// Luckily, Posix is vague about what the user will see if they seek
+	// backwards, and requires the user not to seek to an old offset after a
+	// rewind. The only requirement on freshness is that rewinddir results in
+	// something that looks like a newly-opened directory. So FUSE file systems
+	// may e.g. cache an entire fresh listing for each ReadDir with a zero
+	// offset, and return array offsets into that cached listing.
+	Offset DirOffset
+
+	// The destination buffer, whose length gives the size of the read.
+	//
+	// The output data should consist of a sequence of FUSE directory entries in
+	// the format generated by fuse_add_direntry (http://goo.gl/qCcHCV), which is
+	// consumed by parse_dirfile (http://goo.gl/2WUmD2). Use fuseutil.WriteDirent
+	// to generate this data.
+	//
+	// Each entry returned exposes a directory offset to the user that may later
+	// show up in ReadDirRequest.Offset. See notes on that field for more
+	// information.
+	Dst []byte
+
+	// Set by the file system: the number of bytes read into Dst.
+	//
+	// It is okay for this to be less than len(Dst) if there are not enough
+	// entries available or the final entry would not fit.
+	//
+	// Zero means that the end of the directory has been reached. This is
+	// unambiguous because NAME_MAX (https://goo.gl/ZxzKaE) plus the size of
+	// fuse_dirent (https://goo.gl/WO8s3F) plus the 8-byte alignment of
+	// FUSE_DIRENT_ALIGN (http://goo.gl/UziWvH) is less than the read size of
+	// PAGE_SIZE used by fuse_readdir (cf. https://goo.gl/VajtS2).
+	BytesRead int
+}
+
+// Release a previously-minted directory handle. The kernel sends this when
+// there are no more references to an open directory: all file descriptors are
+// closed and all memory mappings are unmapped.
+//
+// The kernel guarantees that the handle ID will not be used in further ops
+// sent to the file system (unless it is reissued by the file system).
+//
+// Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do).
+type ReleaseDirHandleOp struct {
+	// The handle ID to be released. The kernel guarantees that this ID will not
+	// be used in further calls to the file system (unless it is reissued by the
+	// file system).
+	Handle HandleID
+}
+
+////////////////////////////////////////////////////////////////////////
+// File handles
+////////////////////////////////////////////////////////////////////////
+
+// Open a file inode.
+//
+// On Linux the sends this when setting up a struct file for a particular inode
+// with type file, usually in response to an open(2) call from a user-space
+// process. On OS X it may not be sent for every open(2)
+// (cf.https://github.com/osxfuse/osxfuse/issues/199).
+type OpenFileOp struct {
+	// The ID of the inode to be opened.
+	Inode InodeID
+
+	// An opaque ID that will be echoed in follow-up calls for this file using
+	// the same struct file in the kernel. In practice this usually means
+	// follow-up calls using the file descriptor returned by open(2).
+	//
+	// The handle may be supplied in future ops like ReadFileOp that contain a
+	// file handle. The file system must ensure this ID remains valid until a
+	// later call to ReleaseFileHandle.
+	Handle HandleID
+
+	// By default, fuse invalidates the kernel's page cache for an inode when a
+	// new file handle is opened for that inode (cf. https://goo.gl/2rZ9uk). The
+	// intent appears to be to allow users to "see" content that has changed
+	// remotely on a networked file system by re-opening the file.
+	//
+	// For file systems where this is not a concern because all modifications for
+	// a particular inode go through the kernel, set this field to true to
+	// disable this behavior.
+	//
+	// (More discussion: http://goo.gl/cafzWF)
+	//
+	// Note that on OS X it appears that the behavior is always as if this field
+	// is set to true, regardless of its value, at least for files opened in the
+	// same mode. (Cf. https://github.com/osxfuse/osxfuse/issues/223)
+	KeepPageCache bool
+
+	// Whether to use direct IO for this file handle. By default, the kernel
+	// suppresses what it sees as redundant operations (including reads beyond
+	// the precomputed EOF).
+	//
+	// Enabling direct IO ensures that all client operations reach the fuse
+	// layer. This allows for filesystems whose file sizes are not known in
+	// advance, for example, because contents are generated on the fly.
+	UseDirectIO bool
+}
+
+// Read data from a file previously opened with CreateFile or OpenFile.
+//
+// Note that this op is not sent for every call to read(2) by the end user;
+// some reads may be served by the page cache. See notes on WriteFileOp for
+// more.
+type ReadFileOp struct {
+	// The file inode that we are reading, and the handle previously returned by
+	// CreateFile or OpenFile when opening that inode.
+	Inode  InodeID
+	Handle HandleID
+
+	// The offset within the file at which to read.
+	Offset int64
+
+	// The destination buffer, whose length gives the size of the read.
+	Dst []byte
+
+	// Set by the file system: the number of bytes read.
+	//
+	// The FUSE documentation requires that exactly the requested number of bytes
+	// be returned, except in the case of EOF or error (http://goo.gl/ZgfBkF).
+	// This appears to be because it uses file mmapping machinery
+	// (http://goo.gl/SGxnaN) to read a page at a time. It appears to understand
+	// where EOF is by checking the inode size (http://goo.gl/0BkqKD), returned
+	// by a previous call to LookUpInode, GetInodeAttributes, etc.
+	//
+	// If direct IO is enabled, semantics should match those of read(2).
+	BytesRead int
+}
+
+// Write data to a file previously opened with CreateFile or OpenFile.
+//
+// When the user writes data using write(2), the write goes into the page
+// cache and the page is marked dirty. Later the kernel may write back the
+// page via the FUSE VFS layer, causing this op to be sent:
+//
+//  *  The kernel calls address_space_operations::writepage when a dirty page
+//     needs to be written to backing store (cf. http://goo.gl/Ezbewg). Fuse
+//     sets this to fuse_writepage (cf. http://goo.gl/IeNvLT).
+//
+//  *  (http://goo.gl/Eestuy) fuse_writepage calls fuse_writepage_locked.
+//
+//  *  (http://goo.gl/RqYIxY) fuse_writepage_locked makes a write request to
+//     the userspace server.
+//
+// Note that the kernel *will* ensure that writes are received and acknowledged
+// by the file system before sending a FlushFileOp when closing the file
+// descriptor to which they were written. Cf. the notes on
+// fuse.MountConfig.DisableWritebackCaching.
+//
+// (See also http://goo.gl/ocdTdM, fuse-devel thread "Fuse guarantees on
+// concurrent requests".)
+type WriteFileOp struct {
+	// The file inode that we are modifying, and the handle previously returned
+	// by CreateFile or OpenFile when opening that inode.
+	Inode  InodeID
+	Handle HandleID
+
+	// The offset at which to write the data below.
+	//
+	// The man page for pwrite(2) implies that aside from changing the file
+	// handle's offset, using pwrite is equivalent to using lseek(2) and then
+	// write(2). The man page for lseek(2) says the following:
+	//
+	// "The lseek() function allows the file offset to be set beyond the end of
+	// the file (but this does not change the size of the file). If data is later
+	// written at this point, subsequent reads of the data in the gap (a "hole")
+	// return null bytes (aq\0aq) until data is actually written into the gap."
+	//
+	// It is therefore reasonable to assume that the kernel is looking for
+	// the following semantics:
+	//
+	// *   If the offset is less than or equal to the current size, extend the
+	//     file as necessary to fit any data that goes past the end of the file.
+	//
+	// *   If the offset is greater than the current size, extend the file
+	//     with null bytes until it is not, then do the above.
+	//
+	Offset int64
+
+	// The data to write.
+	//
+	// The FUSE documentation requires that exactly the number of bytes supplied
+	// be written, except on error (http://goo.gl/KUpwwn). This appears to be
+	// because it uses file mmapping machinery (http://goo.gl/SGxnaN) to write a
+	// page at a time.
+	Data []byte
+}
+
+// Synchronize the current contents of an open file to storage.
+//
+// vfs.txt documents this as being called for by the fsync(2) system call
+// (cf. http://goo.gl/j9X8nB). Code walk for that case:
+//
+//  *  (http://goo.gl/IQkWZa) sys_fsync calls do_fsync, calls vfs_fsync, calls
+//     vfs_fsync_range.
+//
+//  *  (http://goo.gl/5L2SMy) vfs_fsync_range calls f_op->fsync.
+//
+// Note that this is also sent by fdatasync(2) (cf. http://goo.gl/01R7rF), and
+// may be sent for msync(2) with the MS_SYNC flag (see the notes on
+// FlushFileOp).
+//
+// See also: FlushFileOp, which may perform a similar function when closing a
+// file (but which is not used in "real" file systems).
+type SyncFileOp struct {
+	// The file and handle being sync'd.
+	Inode  InodeID
+	Handle HandleID
+}
+
+// Flush the current state of an open file to storage upon closing a file
+// descriptor.
+//
+// vfs.txt documents this as being sent for each close(2) system call (cf.
+// http://goo.gl/FSkbrq). Code walk for that case:
+//
+//  *  (http://goo.gl/e3lv0e) sys_close calls __close_fd, calls filp_close.
+//  *  (http://goo.gl/nI8fxD) filp_close calls f_op->flush (fuse_flush).
+//
+// But note that this is also sent in other contexts where a file descriptor is
+// closed, such as dup2(2) (cf. http://goo.gl/NQDvFS). In the case of close(2),
+// a flush error is returned to the user. For dup2(2), it is not.
+//
+// One potentially significant case where this may not be sent is mmap'd files,
+// where the behavior is complicated:
+//
+//  *  munmap(2) does not cause flushes (cf. http://goo.gl/j8B9g0).
+//
+//  *  On OS X, if a user modifies a mapped file via the mapping before
+//     closing the file with close(2), the WriteFileOps for the modifications
+//     may not be received before the FlushFileOp for the close(2) (cf.
+//     https://github.com/osxfuse/osxfuse/issues/202). It appears that this may
+//     be fixed in osxfuse 3 (cf. https://goo.gl/rtvbko).
+//
+//  *  However, you safely can arrange for writes via a mapping to be
+//     flushed by calling msync(2) followed by close(2). On OS X msync(2)
+//     will cause a WriteFileOps to go through and close(2) will cause a
+//     FlushFile as usual (cf. http://goo.gl/kVmNcx). On Linux, msync(2) does
+//     nothing unless you set the MS_SYNC flag, in which case it causes a
+//     SyncFileOp to be sent (cf. http://goo.gl/P3mErk).
+//
+// In summary: if you make data durable in both FlushFile and SyncFile, then
+// your users can get safe behavior from mapped files on both operating systems
+// by calling msync(2) with MS_SYNC, followed by munmap(2), followed by
+// close(2). On Linux, the msync(2) is optional (cf. http://goo.gl/EIhAxv and
+// the notes on WriteFileOp).
+//
+// Because of cases like dup2(2), FlushFileOps are not necessarily one to one
+// with OpenFileOps. They should not be used for reference counting, and the
+// handle must remain valid even after the flush op is received (use
+// ReleaseFileHandleOp for disposing of it).
+//
+// Typical "real" file systems do not implement this, presumably relying on
+// the kernel to write out the page cache to the block device eventually.
+// They can get away with this because a later open(2) will see the same
+// data. A file system that writes to remote storage however probably wants
+// to at least schedule a real flush, and maybe do it immediately in order to
+// return any errors that occur.
+type FlushFileOp struct {
+	// The file and handle being flushed.
+	Inode  InodeID
+	Handle HandleID
+}
+
+// Release a previously-minted file handle. The kernel calls this when there
+// are no more references to an open file: all file descriptors are closed
+// and all memory mappings are unmapped.
+//
+// The kernel guarantees that the handle ID will not be used in further calls
+// to the file system (unless it is reissued by the file system).
+//
+// Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do).
+type ReleaseFileHandleOp struct {
+	// The handle ID to be released. The kernel guarantees that this ID will not
+	// be used in further calls to the file system (unless it is reissued by the
+	// file system).
+	Handle HandleID
+}
+
+////////////////////////////////////////////////////////////////////////
+// Reading symlinks
+////////////////////////////////////////////////////////////////////////
+
+// Read the target of a symlink inode.
+type ReadSymlinkOp struct {
+	// The symlink inode that we are reading.
+	Inode InodeID
+
+	// Set by the file system: the target of the symlink.
+	Target string
+}
+
+////////////////////////////////////////////////////////////////////////
+// eXtended attributes
+////////////////////////////////////////////////////////////////////////
+
+// Remove an extended attribute.
+//
+// This is sent in response to removexattr(2). Return ENOATTR if the
+// extended attribute does not exist.
+type RemoveXattrOp struct {
+	// The inode that we are removing an extended attribute from.
+	Inode InodeID
+
+	// The name of the extended attribute.
+	Name string
+}
+
+// Get an extended attribute.
+//
+// This is sent in response to getxattr(2). Return ENOATTR if the
+// extended attribute does not exist.
+type GetXattrOp struct {
+	// The inode whose extended attribute we are reading.
+	Inode InodeID
+
+	// The name of the extended attribute.
+	Name string
+
+	// The destination buffer.  If the size is too small for the
+	// value, the ERANGE error should be sent.
+	Dst []byte
+
+	// Set by the file system: the number of bytes read into Dst, or
+	// the number of bytes that would have been read into Dst if Dst was
+	// big enough (return ERANGE in this case).
+	BytesRead int
+}
+
+// List all the extended attributes for a file.
+//
+// This is sent in response to listxattr(2).
+type ListXattrOp struct {
+	// The inode whose extended attributes we are listing.
+	Inode InodeID
+
+	// The destination buffer.  If the size is too small for the
+	// value, the ERANGE error should be sent.
+	//
+	// The output data should consist of a sequence of NUL-terminated strings,
+	// one for each xattr.
+	Dst []byte
+
+	// Set by the file system: the number of bytes read into Dst, or
+	// the number of bytes that would have been read into Dst if Dst was
+	// big enough (return ERANGE in this case).
+	BytesRead int
+}
+
+// Set an extended attribute.
+//
+// This is sent in response to setxattr(2). Return ENOSPC if there is
+// insufficient space remaining to store the extended attribute.
+type SetXattrOp struct {
+	// The inode whose extended attribute we are setting.
+	Inode InodeID
+
+	// The name of the extended attribute
+	Name string
+
+	// The value to for the extened attribute.
+	Value []byte
+
+	// If Flags is 0x1, and the attribute exists already, EEXIST should be returned.
+	// If Flags is 0x2, and the attribute does not exist, ENOATTR should be returned.
+	// If Flags is 0x0, the extended attribute will be created if need be, or will
+	// simply replace the value if the attribute exists.
+	Flags uint32
+}

+ 222 - 0
src/vendor/github.com/jacobsa/fuse/fuseops/simple_types.go

@@ -0,0 +1,222 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuseops
+
+import (
+	"fmt"
+	"os"
+	"time"
+
+	"github.com/jacobsa/fuse/internal/fusekernel"
+)
+
+// InodeID is a 64-bit number used to uniquely identify a file or directory in
+// the file system. File systems may mint inode IDs with any value except for
+// RootInodeID.
+//
+// This corresponds to struct inode::i_no in the VFS layer.
+// (Cf. http://goo.gl/tvYyQt)
+type InodeID uint64
+
+// RootInodeID is a distinguished inode ID that identifies the root of the file
+// system, e.g. in an OpenDirOp or LookUpInodeOp. Unlike all other inode IDs,
+// which are minted by the file system, the FUSE VFS layer may send a request
+// for this ID without the file system ever having referenced it in a previous
+// response.
+const RootInodeID = 1
+
+func init() {
+	// Make sure the constant above is correct. We do this at runtime rather than
+	// defining the constant in terms of fusekernel.RootID for two reasons:
+	//
+	//  1. Users can more clearly see that the root ID is low and can therefore
+	//     be used as e.g. an array index, with space reserved up to the root.
+	//
+	//  2. The constant can be untyped and can therefore more easily be used as
+	//     an array index.
+	//
+	if RootInodeID != fusekernel.RootID {
+		panic(
+			fmt.Sprintf(
+				"Oops, RootInodeID is wrong: %v vs. %v",
+				RootInodeID,
+				fusekernel.RootID))
+	}
+}
+
+// InodeAttributes contains attributes for a file or directory inode. It
+// corresponds to struct inode (cf. http://goo.gl/tvYyQt).
+type InodeAttributes struct {
+	Size uint64
+
+	// The number of incoming hard links to this inode.
+	Nlink uint32
+
+	// The mode of the inode. This is exposed to the user in e.g. the result of
+	// fstat(2).
+	//
+	// Note that in contrast to the defaults for FUSE, this package mounts file
+	// systems in a manner such that the kernel checks inode permissions in the
+	// standard posix way. This is implemented by setting the default_permissions
+	// mount option (cf. http://goo.gl/1LxOop and http://goo.gl/1pTjuk).
+	//
+	// For example, in the case of mkdir:
+	//
+	//  *  (http://goo.gl/JkdxDI) sys_mkdirat calls inode_permission.
+	//
+	//  *  (...) inode_permission eventually calls do_inode_permission.
+	//
+	//  *  (http://goo.gl/aGCsmZ) calls i_op->permission, which is
+	//     fuse_permission (cf. http://goo.gl/VZ9beH).
+	//
+	//  *  (http://goo.gl/5kqUKO) fuse_permission doesn't do anything at all for
+	//     several code paths if FUSE_DEFAULT_PERMISSIONS is unset. In contrast,
+	//     if that flag *is* set, then it calls generic_permission.
+	//
+	Mode os.FileMode
+
+	// Time information. See `man 2 stat` for full details.
+	Atime  time.Time // Time of last access
+	Mtime  time.Time // Time of last modification
+	Ctime  time.Time // Time of last modification to inode
+	Crtime time.Time // Time of creation (OS X only)
+
+	// Ownership information
+	Uid uint32
+	Gid uint32
+}
+
+func (a *InodeAttributes) DebugString() string {
+	return fmt.Sprintf(
+		"%d %d %v %d %d",
+		a.Size,
+		a.Nlink,
+		a.Mode,
+		a.Uid,
+		a.Gid)
+}
+
+// GenerationNumber represents a generation of an inode. It is irrelevant for
+// file systems that won't be exported over NFS. For those that will and that
+// reuse inode IDs when they become free, the generation number must change
+// when an ID is reused.
+//
+// This corresponds to struct inode::i_generation in the VFS layer.
+// (Cf. http://goo.gl/tvYyQt)
+//
+// Some related reading:
+//
+//     http://fuse.sourceforge.net/doxygen/structfuse__entry__param.html
+//     http://stackoverflow.com/q/11071996/1505451
+//     http://goo.gl/CqvwyX
+//     http://julipedia.meroh.net/2005/09/nfs-file-handles.html
+//     http://goo.gl/wvo3MB
+//
+type GenerationNumber uint64
+
+// HandleID is an opaque 64-bit number used to identify a particular open
+// handle to a file or directory.
+//
+// This corresponds to fuse_file_info::fh.
+type HandleID uint64
+
+// DirOffset is an offset into an open directory handle. This is opaque to
+// FUSE, and can be used for whatever purpose the file system desires. See
+// notes on ReadDirOp.Offset for details.
+type DirOffset uint64
+
+// ChildInodeEntry contains information about a child inode within its parent
+// directory. It is shared by LookUpInodeOp, MkDirOp, CreateFileOp, etc, and is
+// consumed by the kernel in order to set up a dcache entry.
+type ChildInodeEntry struct {
+	// The ID of the child inode. The file system must ensure that the returned
+	// inode ID remains valid until a later ForgetInodeOp.
+	Child InodeID
+
+	// A generation number for this incarnation of the inode with the given ID.
+	// See comments on type GenerationNumber for more.
+	Generation GenerationNumber
+
+	// Current attributes for the child inode.
+	//
+	// When creating a new inode, the file system is responsible for initializing
+	// and recording (where supported) attributes like time information,
+	// ownership information, etc.
+	//
+	// Ownership information in particular must be set to something reasonable or
+	// by default root will own everything and unprivileged users won't be able
+	// to do anything useful. In traditional file systems in the kernel, the
+	// function inode_init_owner (http://goo.gl/5qavg8) contains the
+	// standards-compliant logic for this.
+	Attributes InodeAttributes
+
+	// The FUSE VFS layer in the kernel maintains a cache of file attributes,
+	// used whenever up to date information about size, mode, etc. is needed.
+	//
+	// For example, this is the abridged call chain for fstat(2):
+	//
+	//  *  (http://goo.gl/tKBH1p) fstat calls vfs_fstat.
+	//  *  (http://goo.gl/3HeITq) vfs_fstat eventuall calls vfs_getattr_nosec.
+	//  *  (http://goo.gl/DccFQr) vfs_getattr_nosec calls i_op->getattr.
+	//  *  (http://goo.gl/dpKkst) fuse_getattr calls fuse_update_attributes.
+	//  *  (http://goo.gl/yNlqPw) fuse_update_attributes uses the values in the
+	//     struct inode if allowed, otherwise calling out to the user-space code.
+	//
+	// In addition to obvious cases like fstat, this is also used in more subtle
+	// cases like updating size information before seeking (http://goo.gl/2nnMFa)
+	// or reading (http://goo.gl/FQSWs8).
+	//
+	// Most 'real' file systems do not set inode_operations::getattr, and
+	// therefore vfs_getattr_nosec calls generic_fillattr which simply grabs the
+	// information from the inode struct. This makes sense because these file
+	// systems cannot spontaneously change; all modifications go through the
+	// kernel which can update the inode struct as appropriate.
+	//
+	// In contrast, a FUSE file system may have spontaneous changes, so it calls
+	// out to user space to fetch attributes. However this is expensive, so the
+	// FUSE layer in the kernel caches the attributes if requested.
+	//
+	// This field controls when the attributes returned in this response and
+	// stashed in the struct inode should be re-queried. Leave at the zero value
+	// to disable caching.
+	//
+	// More reading:
+	//     http://stackoverflow.com/q/21540315/1505451
+	AttributesExpiration time.Time
+
+	// The time until which the kernel may maintain an entry for this name to
+	// inode mapping in its dentry cache. After this time, it will revalidate the
+	// dentry.
+	//
+	// As in the discussion of attribute caching above, unlike real file systems,
+	// FUSE file systems may spontaneously change their name -> inode mapping.
+	// Therefore the FUSE VFS layer uses dentry_operations::d_revalidate
+	// (http://goo.gl/dVea0h) to intercept lookups and revalidate by calling the
+	// user-space LookUpInode method. However the latter may be slow, so it
+	// caches the entries until the time defined by this field.
+	//
+	// Example code walk:
+	//
+	//     * (http://goo.gl/M2G3tO) lookup_dcache calls d_revalidate if enabled.
+	//     * (http://goo.gl/ef0Elu) fuse_dentry_revalidate just uses the dentry's
+	//     inode if fuse_dentry_time(entry) hasn't passed. Otherwise it sends a
+	//     lookup request.
+	//
+	// Leave at the zero value to disable caching.
+	//
+	// Beware: this value is ignored on OS X, where entry caching is disabled by
+	// default. See notes on MountConfig.EnableVnodeCaching for more.
+	EntryExpiration time.Time
+}

+ 367 - 0
src/vendor/github.com/jacobsa/fuse/fusetesting/parallel.go

@@ -0,0 +1,367 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fusetesting
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"runtime"
+	"sync/atomic"
+	"time"
+
+	. "github.com/jacobsa/ogletest"
+	"github.com/jacobsa/syncutil"
+	"golang.org/x/net/context"
+)
+
+// Run an ogletest test that checks expectations for parallel calls to open(2)
+// with O_CREAT.
+func RunCreateInParallelTest_NoTruncate(
+	ctx context.Context,
+	dir string) {
+	// Ensure that we get parallelism for this test.
+	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))
+
+	// Try for awhile to see if anything breaks.
+	const duration = 500 * time.Millisecond
+	startTime := time.Now()
+	for time.Since(startTime) < duration {
+		filename := path.Join(dir, "foo")
+
+		// Set up a function that opens the file with O_CREATE and then appends a
+		// byte to it.
+		worker := func(id byte) (err error) {
+			f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
+			if err != nil {
+				err = fmt.Errorf("Worker %d: Open: %v", id, err)
+				return
+			}
+
+			defer f.Close()
+
+			_, err = f.Write([]byte{id})
+			if err != nil {
+				err = fmt.Errorf("Worker %d: Write: %v", id, err)
+				return
+			}
+
+			return
+		}
+
+		// Run several workers in parallel.
+		const numWorkers = 16
+		b := syncutil.NewBundle(ctx)
+		for i := 0; i < numWorkers; i++ {
+			id := byte(i)
+			b.Add(func(ctx context.Context) (err error) {
+				err = worker(id)
+				return
+			})
+		}
+
+		err := b.Join()
+		AssertEq(nil, err)
+
+		// Read the contents of the file. We should see each worker's ID once.
+		contents, err := ioutil.ReadFile(filename)
+		AssertEq(nil, err)
+
+		idsSeen := make(map[byte]struct{})
+		for i, _ := range contents {
+			id := contents[i]
+			AssertLt(id, numWorkers)
+
+			if _, ok := idsSeen[id]; ok {
+				AddFailure("Duplicate ID: %d", id)
+			}
+
+			idsSeen[id] = struct{}{}
+		}
+
+		AssertEq(numWorkers, len(idsSeen))
+
+		// Delete the file.
+		err = os.Remove(filename)
+		AssertEq(nil, err)
+	}
+}
+
+// Run an ogletest test that checks expectations for parallel calls to open(2)
+// with O_CREAT|O_TRUNC.
+func RunCreateInParallelTest_Truncate(
+	ctx context.Context,
+	dir string) {
+	// Ensure that we get parallelism for this test.
+	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))
+
+	// Try for awhile to see if anything breaks.
+	const duration = 500 * time.Millisecond
+	startTime := time.Now()
+	for time.Since(startTime) < duration {
+		filename := path.Join(dir, "foo")
+
+		// Set up a function that opens the file with O_CREATE and O_TRUNC and then
+		// appends a byte to it.
+		worker := func(id byte) (err error) {
+			f, err := os.OpenFile(
+				filename,
+				os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_TRUNC,
+				0600)
+
+			if err != nil {
+				err = fmt.Errorf("Worker %d: Open: %v", id, err)
+				return
+			}
+
+			defer f.Close()
+
+			_, err = f.Write([]byte{id})
+			if err != nil {
+				err = fmt.Errorf("Worker %d: Write: %v", id, err)
+				return
+			}
+
+			return
+		}
+
+		// Run several workers in parallel.
+		const numWorkers = 16
+		b := syncutil.NewBundle(ctx)
+		for i := 0; i < numWorkers; i++ {
+			id := byte(i)
+			b.Add(func(ctx context.Context) (err error) {
+				err = worker(id)
+				return
+			})
+		}
+
+		err := b.Join()
+		AssertEq(nil, err)
+
+		// Read the contents of the file. We should see at least one ID (the last
+		// one that truncated), and at most all of them.
+		contents, err := ioutil.ReadFile(filename)
+		AssertEq(nil, err)
+
+		idsSeen := make(map[byte]struct{})
+		for i, _ := range contents {
+			id := contents[i]
+			AssertLt(id, numWorkers)
+
+			if _, ok := idsSeen[id]; ok {
+				AddFailure("Duplicate ID: %d", id)
+			}
+
+			idsSeen[id] = struct{}{}
+		}
+
+		AssertGe(len(idsSeen), 1)
+		AssertLe(len(idsSeen), numWorkers)
+
+		// Delete the file.
+		err = os.Remove(filename)
+		AssertEq(nil, err)
+	}
+}
+
+// Run an ogletest test that checks expectations for parallel calls to open(2)
+// with O_CREAT|O_EXCL.
+func RunCreateInParallelTest_Exclusive(
+	ctx context.Context,
+	dir string) {
+	// Ensure that we get parallelism for this test.
+	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))
+
+	// Try for awhile to see if anything breaks.
+	const duration = 500 * time.Millisecond
+	startTime := time.Now()
+	for time.Since(startTime) < duration {
+		filename := path.Join(dir, "foo")
+
+		// Set up a function that opens the file with O_CREATE and O_EXCL, and then
+		// appends a byte to it if it was successfully opened.
+		var openCount uint64
+		worker := func(id byte) (err error) {
+			f, err := os.OpenFile(
+				filename,
+				os.O_CREATE|os.O_EXCL|os.O_WRONLY|os.O_APPEND,
+				0600)
+
+			// If we failed to open due to the file already existing, just leave.
+			if os.IsExist(err) {
+				err = nil
+				return
+			}
+
+			// Propgate other errors.
+			if err != nil {
+				err = fmt.Errorf("Worker %d: Open: %v", id, err)
+				return
+			}
+
+			atomic.AddUint64(&openCount, 1)
+			defer f.Close()
+
+			_, err = f.Write([]byte{id})
+			if err != nil {
+				err = fmt.Errorf("Worker %d: Write: %v", id, err)
+				return
+			}
+
+			return
+		}
+
+		// Run several workers in parallel.
+		const numWorkers = 16
+		b := syncutil.NewBundle(ctx)
+		for i := 0; i < numWorkers; i++ {
+			id := byte(i)
+			b.Add(func(ctx context.Context) (err error) {
+				err = worker(id)
+				return
+			})
+		}
+
+		err := b.Join()
+		AssertEq(nil, err)
+
+		// Exactly one worker should have opened successfully.
+		AssertEq(1, openCount)
+
+		// Read the contents of the file. It should contain that one worker's ID.
+		contents, err := ioutil.ReadFile(filename)
+		AssertEq(nil, err)
+
+		AssertEq(1, len(contents))
+		AssertLt(contents[0], numWorkers)
+
+		// Delete the file.
+		err = os.Remove(filename)
+		AssertEq(nil, err)
+	}
+}
+
+// Run an ogletest test that checks expectations for parallel calls to mkdir(2).
+func RunMkdirInParallelTest(
+	ctx context.Context,
+	dir string) {
+	// Ensure that we get parallelism for this test.
+	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))
+
+	// Try for awhile to see if anything breaks.
+	const duration = 500 * time.Millisecond
+	startTime := time.Now()
+	for time.Since(startTime) < duration {
+		filename := path.Join(dir, "foo")
+
+		// Set up a function that creates the directory, ignoring EEXIST errors.
+		worker := func(id byte) (err error) {
+			err = os.Mkdir(filename, 0700)
+
+			if os.IsExist(err) {
+				err = nil
+			}
+
+			if err != nil {
+				err = fmt.Errorf("Worker %d: Mkdir: %v", id, err)
+				return
+			}
+
+			return
+		}
+
+		// Run several workers in parallel.
+		const numWorkers = 16
+		b := syncutil.NewBundle(ctx)
+		for i := 0; i < numWorkers; i++ {
+			id := byte(i)
+			b.Add(func(ctx context.Context) (err error) {
+				err = worker(id)
+				return
+			})
+		}
+
+		err := b.Join()
+		AssertEq(nil, err)
+
+		// The directory should have been created, once.
+		entries, err := ReadDirPicky(dir)
+		AssertEq(nil, err)
+		AssertEq(1, len(entries))
+		AssertEq("foo", entries[0].Name())
+
+		// Delete the directory.
+		err = os.Remove(filename)
+		AssertEq(nil, err)
+	}
+}
+
+// Run an ogletest test that checks expectations for parallel calls to
+// symlink(2).
+func RunSymlinkInParallelTest(
+	ctx context.Context,
+	dir string) {
+	// Ensure that we get parallelism for this test.
+	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))
+
+	// Try for awhile to see if anything breaks.
+	const duration = 500 * time.Millisecond
+	startTime := time.Now()
+	for time.Since(startTime) < duration {
+		filename := path.Join(dir, "foo")
+
+		// Set up a function that creates the symlink, ignoring EEXIST errors.
+		worker := func(id byte) (err error) {
+			err = os.Symlink("blah", filename)
+
+			if os.IsExist(err) {
+				err = nil
+			}
+
+			if err != nil {
+				err = fmt.Errorf("Worker %d: Symlink: %v", id, err)
+				return
+			}
+
+			return
+		}
+
+		// Run several workers in parallel.
+		const numWorkers = 16
+		b := syncutil.NewBundle(ctx)
+		for i := 0; i < numWorkers; i++ {
+			id := byte(i)
+			b.Add(func(ctx context.Context) (err error) {
+				err = worker(id)
+				return
+			})
+		}
+
+		err := b.Join()
+		AssertEq(nil, err)
+
+		// The symlink should have been created, once.
+		entries, err := ReadDirPicky(dir)
+		AssertEq(nil, err)
+		AssertEq(1, len(entries))
+		AssertEq("foo", entries[0].Name())
+
+		// Delete the directory.
+		err = os.Remove(filename)
+		AssertEq(nil, err)
+	}
+}

+ 76 - 0
src/vendor/github.com/jacobsa/fuse/fusetesting/readdir.go

@@ -0,0 +1,76 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fusetesting
+
+import (
+	"fmt"
+	"os"
+	"path"
+	"sort"
+)
+
+type sortedEntries []os.FileInfo
+
+func (f sortedEntries) Len() int           { return len(f) }
+func (f sortedEntries) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
+func (f sortedEntries) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
+
+// Read the directory with the given name and return a list of directory
+// entries, sorted by name.
+//
+// Unlike ioutil.ReadDir (cf. http://goo.gl/i0nNP4), this function does not
+// silently ignore "file not found" errors when stat'ing the names read from
+// the directory.
+func ReadDirPicky(dirname string) (entries []os.FileInfo, err error) {
+	// Open the directory.
+	f, err := os.Open(dirname)
+	if err != nil {
+		err = fmt.Errorf("Open: %v", err)
+		return
+	}
+
+	// Don't forget to close it later.
+	defer func() {
+		closeErr := f.Close()
+		if closeErr != nil && err == nil {
+			err = fmt.Errorf("Close: %v", closeErr)
+		}
+	}()
+
+	// Read all of the names from the directory.
+	names, err := f.Readdirnames(-1)
+	if err != nil {
+		err = fmt.Errorf("Readdirnames: %v", err)
+		return
+	}
+
+	// Stat each one.
+	for _, name := range names {
+		var fi os.FileInfo
+
+		fi, err = os.Lstat(path.Join(dirname, name))
+		if err != nil {
+			err = fmt.Errorf("Lstat(%s): %v", name, err)
+			return
+		}
+
+		entries = append(entries, fi)
+	}
+
+	// Sort the entries by name.
+	sort.Sort(sortedEntries(entries))
+
+	return
+}

+ 125 - 0
src/vendor/github.com/jacobsa/fuse/fusetesting/stat.go

@@ -0,0 +1,125 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fusetesting
+
+import (
+	"fmt"
+	"os"
+	"reflect"
+	"syscall"
+	"time"
+
+	"github.com/jacobsa/oglematchers"
+)
+
+// Match os.FileInfo values that specify an mtime equal to the given time.
+func MtimeIs(expected time.Time) oglematchers.Matcher {
+	return oglematchers.NewMatcher(
+		func(c interface{}) error { return mtimeIsWithin(c, expected, 0) },
+		fmt.Sprintf("mtime is %v", expected))
+}
+
+// Like MtimeIs, but allows for a tolerance.
+func MtimeIsWithin(expected time.Time, d time.Duration) oglematchers.Matcher {
+	return oglematchers.NewMatcher(
+		func(c interface{}) error { return mtimeIsWithin(c, expected, d) },
+		fmt.Sprintf("mtime is within %v of %v", d, expected))
+}
+
+func mtimeIsWithin(c interface{}, expected time.Time, d time.Duration) error {
+	fi, ok := c.(os.FileInfo)
+	if !ok {
+		return fmt.Errorf("which is of type %v", reflect.TypeOf(c))
+	}
+
+	// Check ModTime().
+	diff := fi.ModTime().Sub(expected)
+	absDiff := diff
+	if absDiff < 0 {
+		absDiff = -absDiff
+	}
+
+	if !(absDiff < d) {
+		return fmt.Errorf("which has mtime %v, off by %v", fi.ModTime(), diff)
+	}
+
+	return nil
+}
+
+// Match os.FileInfo values that specify a file birth time within the supplied
+// radius of the given time. On platforms where there is no birth time
+// available, match all os.FileInfo values.
+func BirthtimeIsWithin(
+	expected time.Time,
+	d time.Duration) oglematchers.Matcher {
+	return oglematchers.NewMatcher(
+		func(c interface{}) error { return birthtimeIsWithin(c, expected, d) },
+		fmt.Sprintf("birthtime is within %v of %v", d, expected))
+}
+
+func birthtimeIsWithin(
+	c interface{},
+	expected time.Time,
+	d time.Duration) error {
+	fi, ok := c.(os.FileInfo)
+	if !ok {
+		return fmt.Errorf("which is of type %v", reflect.TypeOf(c))
+	}
+
+	t, ok := extractBirthtime(fi.Sys())
+	if !ok {
+		return nil
+	}
+
+	diff := t.Sub(expected)
+	absDiff := diff
+	if absDiff < 0 {
+		absDiff = -absDiff
+	}
+
+	if !(absDiff < d) {
+		return fmt.Errorf("which has birth time %v, off by %v", t, diff)
+	}
+
+	return nil
+}
+
+// Extract time information from the supplied file info. Panic on platforms
+// where this is not possible.
+func GetTimes(fi os.FileInfo) (atime, ctime, mtime time.Time) {
+	return getTimes(fi.Sys().(*syscall.Stat_t))
+}
+
+// Match os.FileInfo values that specify a number of links equal to the given
+// number. On platforms where there is no nlink field available, match all
+// os.FileInfo values.
+func NlinkIs(expected uint64) oglematchers.Matcher {
+	return oglematchers.NewMatcher(
+		func(c interface{}) error { return nlinkIs(c, expected) },
+		fmt.Sprintf("nlink is %v", expected))
+}
+
+func nlinkIs(c interface{}, expected uint64) error {
+	fi, ok := c.(os.FileInfo)
+	if !ok {
+		return fmt.Errorf("which is of type %v", reflect.TypeOf(c))
+	}
+
+	if actual, ok := extractNlink(fi.Sys()); ok && actual != expected {
+		return fmt.Errorf("which has nlink == %v", actual)
+	}
+
+	return nil
+}

+ 45 - 0
src/vendor/github.com/jacobsa/fuse/fusetesting/stat_darwin.go

@@ -0,0 +1,45 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fusetesting
+
+import (
+	"syscall"
+	"time"
+)
+
+func extractMtime(sys interface{}) (mtime time.Time, ok bool) {
+	mtime = time.Unix(sys.(*syscall.Stat_t).Mtimespec.Unix())
+	ok = true
+	return
+}
+
+func extractBirthtime(sys interface{}) (birthtime time.Time, ok bool) {
+	birthtime = time.Unix(sys.(*syscall.Stat_t).Birthtimespec.Unix())
+	ok = true
+	return
+}
+
+func extractNlink(sys interface{}) (nlink uint64, ok bool) {
+	nlink = uint64(sys.(*syscall.Stat_t).Nlink)
+	ok = true
+	return
+}
+
+func getTimes(stat *syscall.Stat_t) (atime, ctime, mtime time.Time) {
+	atime = time.Unix(stat.Atimespec.Unix())
+	ctime = time.Unix(stat.Ctimespec.Unix())
+	mtime = time.Unix(stat.Mtimespec.Unix())
+	return
+}

+ 43 - 0
src/vendor/github.com/jacobsa/fuse/fusetesting/stat_linux.go

@@ -0,0 +1,43 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fusetesting
+
+import (
+	"syscall"
+	"time"
+)
+
+func extractMtime(sys interface{}) (mtime time.Time, ok bool) {
+	mtime = time.Unix(sys.(*syscall.Stat_t).Mtim.Unix())
+	ok = true
+	return
+}
+
+func extractBirthtime(sys interface{}) (birthtime time.Time, ok bool) {
+	return
+}
+
+func extractNlink(sys interface{}) (nlink uint64, ok bool) {
+	nlink = sys.(*syscall.Stat_t).Nlink
+	ok = true
+	return
+}
+
+func getTimes(stat *syscall.Stat_t) (atime, ctime, mtime time.Time) {
+	atime = time.Unix(stat.Atim.Unix())
+	ctime = time.Unix(stat.Ctim.Unix())
+	mtime = time.Unix(stat.Mtim.Unix())
+	return
+}

+ 105 - 0
src/vendor/github.com/jacobsa/fuse/fuseutil/dirent.go

@@ -0,0 +1,105 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuseutil
+
+import (
+	"syscall"
+	"unsafe"
+
+	"github.com/jacobsa/fuse/fuseops"
+)
+
+type DirentType uint32
+
+const (
+	DT_Unknown   DirentType = 0
+	DT_Socket    DirentType = syscall.DT_SOCK
+	DT_Link      DirentType = syscall.DT_LNK
+	DT_File      DirentType = syscall.DT_REG
+	DT_Block     DirentType = syscall.DT_BLK
+	DT_Directory DirentType = syscall.DT_DIR
+	DT_Char      DirentType = syscall.DT_CHR
+	DT_FIFO      DirentType = syscall.DT_FIFO
+)
+
+// A struct representing an entry within a directory file, describing a child.
+// See notes on fuseops.ReadDirOp and on WriteDirent for details.
+type Dirent struct {
+	// The (opaque) offset within the directory file of the entry following this
+	// one. See notes on fuseops.ReadDirOp.Offset for details.
+	Offset fuseops.DirOffset
+
+	// The inode of the child file or directory, and its name within the parent.
+	Inode fuseops.InodeID
+	Name  string
+
+	// The type of the child. The zero value (DT_Unknown) is legal, but means
+	// that the kernel will need to call GetAttr when the type is needed.
+	Type DirentType
+}
+
+// Write the supplied directory entry intto the given buffer in the format
+// expected in fuseops.ReadFileOp.Data, returning the number of bytes written.
+// Return zero if the entry would not fit.
+func WriteDirent(buf []byte, d Dirent) (n int) {
+	// We want to write bytes with the layout of fuse_dirent
+	// (http://goo.gl/BmFxob) in host order. The struct must be aligned according
+	// to FUSE_DIRENT_ALIGN (http://goo.gl/UziWvH), which dictates 8-byte
+	// alignment.
+	type fuse_dirent struct {
+		ino     uint64
+		off     uint64
+		namelen uint32
+		type_   uint32
+		name    [0]byte
+	}
+
+	const direntAlignment = 8
+	const direntSize = 8 + 8 + 4 + 4
+
+	// Compute the number of bytes of padding we'll need to maintain alignment
+	// for the next entry.
+	var padLen int
+	if len(d.Name)%direntAlignment != 0 {
+		padLen = direntAlignment - (len(d.Name) % direntAlignment)
+	}
+
+	// Do we have enough room?
+	totalLen := direntSize + len(d.Name) + padLen
+	if totalLen > len(buf) {
+		return
+	}
+
+	// Write the header.
+	de := fuse_dirent{
+		ino:     uint64(d.Inode),
+		off:     uint64(d.Offset),
+		namelen: uint32(len(d.Name)),
+		type_:   uint32(d.Type),
+	}
+
+	n += copy(buf[n:], (*[direntSize]byte)(unsafe.Pointer(&de))[:])
+
+	// Write the name afterward.
+	n += copy(buf[n:], d.Name)
+
+	// Add any necessary padding.
+	if padLen != 0 {
+		var padding [direntAlignment]byte
+		n += copy(buf[n:], padding[:padLen])
+	}
+
+	return
+}

+ 16 - 0
src/vendor/github.com/jacobsa/fuse/fuseutil/doc.go

@@ -0,0 +1,16 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Types and functions that make it easier to work with package fuse.
+package fuseutil

+ 208 - 0
src/vendor/github.com/jacobsa/fuse/fuseutil/file_system.go

@@ -0,0 +1,208 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuseutil
+
+import (
+	"io"
+	"sync"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+)
+
+// An interface with a method for each op type in the fuseops package. This can
+// be used in conjunction with NewFileSystemServer to avoid writing a "dispatch
+// loop" that switches on op types, instead receiving typed method calls
+// directly.
+//
+// The FileSystem implementation should not call Connection.Reply, instead
+// returning the error with which the caller should respond.
+//
+// See NotImplementedFileSystem for a convenient way to embed default
+// implementations for methods you don't care about.
+type FileSystem interface {
+	StatFS(context.Context, *fuseops.StatFSOp) error
+	LookUpInode(context.Context, *fuseops.LookUpInodeOp) error
+	GetInodeAttributes(context.Context, *fuseops.GetInodeAttributesOp) error
+	SetInodeAttributes(context.Context, *fuseops.SetInodeAttributesOp) error
+	ForgetInode(context.Context, *fuseops.ForgetInodeOp) error
+	MkDir(context.Context, *fuseops.MkDirOp) error
+	MkNode(context.Context, *fuseops.MkNodeOp) error
+	CreateFile(context.Context, *fuseops.CreateFileOp) error
+	CreateSymlink(context.Context, *fuseops.CreateSymlinkOp) error
+	Rename(context.Context, *fuseops.RenameOp) error
+	RmDir(context.Context, *fuseops.RmDirOp) error
+	Unlink(context.Context, *fuseops.UnlinkOp) error
+	OpenDir(context.Context, *fuseops.OpenDirOp) error
+	ReadDir(context.Context, *fuseops.ReadDirOp) error
+	ReleaseDirHandle(context.Context, *fuseops.ReleaseDirHandleOp) error
+	OpenFile(context.Context, *fuseops.OpenFileOp) error
+	ReadFile(context.Context, *fuseops.ReadFileOp) error
+	WriteFile(context.Context, *fuseops.WriteFileOp) error
+	SyncFile(context.Context, *fuseops.SyncFileOp) error
+	FlushFile(context.Context, *fuseops.FlushFileOp) error
+	ReleaseFileHandle(context.Context, *fuseops.ReleaseFileHandleOp) error
+	ReadSymlink(context.Context, *fuseops.ReadSymlinkOp) error
+	RemoveXattr(context.Context, *fuseops.RemoveXattrOp) error
+	GetXattr(context.Context, *fuseops.GetXattrOp) error
+	ListXattr(context.Context, *fuseops.ListXattrOp) error
+	SetXattr(context.Context, *fuseops.SetXattrOp) error
+
+	// Regard all inodes (including the root inode) as having their lookup counts
+	// decremented to zero, and clean up any resources associated with the file
+	// system. No further calls to the file system will be made.
+	Destroy()
+}
+
+// Create a fuse.Server that handles ops by calling the associated FileSystem
+// method.Respond with the resulting error. Unsupported ops are responded to
+// directly with ENOSYS.
+//
+// Each call to a FileSystem method is made on its own goroutine, and is free
+// to block.
+//
+// (It is safe to naively process ops concurrently because the kernel
+// guarantees to serialize operations that the user expects to happen in order,
+// cf. http://goo.gl/jnkHPO, fuse-devel thread "Fuse guarantees on concurrent
+// requests").
+func NewFileSystemServer(fs FileSystem) fuse.Server {
+	return &fileSystemServer{
+		fs: fs,
+	}
+}
+
+type fileSystemServer struct {
+	fs          FileSystem
+	opsInFlight sync.WaitGroup
+}
+
+func (s *fileSystemServer) ServeOps(c *fuse.Connection) {
+	// When we are done, we clean up by waiting for all in-flight ops then
+	// destroying the file system.
+	defer func() {
+		s.opsInFlight.Wait()
+		s.fs.Destroy()
+	}()
+
+	for {
+		ctx, op, err := c.ReadOp()
+		if err == io.EOF {
+			break
+		}
+
+		if err != nil {
+			panic(err)
+		}
+
+		s.opsInFlight.Add(1)
+		go s.handleOp(c, ctx, op)
+	}
+}
+
+func (s *fileSystemServer) handleOp(
+	c *fuse.Connection,
+	ctx context.Context,
+	op interface{}) {
+	defer s.opsInFlight.Done()
+
+	// Dispatch to the appropriate method.
+	var err error
+	switch typed := op.(type) {
+	default:
+		err = fuse.ENOSYS
+
+	case *fuseops.StatFSOp:
+		err = s.fs.StatFS(ctx, typed)
+
+	case *fuseops.LookUpInodeOp:
+		err = s.fs.LookUpInode(ctx, typed)
+
+	case *fuseops.GetInodeAttributesOp:
+		err = s.fs.GetInodeAttributes(ctx, typed)
+
+	case *fuseops.SetInodeAttributesOp:
+		err = s.fs.SetInodeAttributes(ctx, typed)
+
+	case *fuseops.ForgetInodeOp:
+		err = s.fs.ForgetInode(ctx, typed)
+
+	case *fuseops.MkDirOp:
+		err = s.fs.MkDir(ctx, typed)
+
+	case *fuseops.MkNodeOp:
+		err = s.fs.MkNode(ctx, typed)
+
+	case *fuseops.CreateFileOp:
+		err = s.fs.CreateFile(ctx, typed)
+
+	case *fuseops.CreateSymlinkOp:
+		err = s.fs.CreateSymlink(ctx, typed)
+
+	case *fuseops.RenameOp:
+		err = s.fs.Rename(ctx, typed)
+
+	case *fuseops.RmDirOp:
+		err = s.fs.RmDir(ctx, typed)
+
+	case *fuseops.UnlinkOp:
+		err = s.fs.Unlink(ctx, typed)
+
+	case *fuseops.OpenDirOp:
+		err = s.fs.OpenDir(ctx, typed)
+
+	case *fuseops.ReadDirOp:
+		err = s.fs.ReadDir(ctx, typed)
+
+	case *fuseops.ReleaseDirHandleOp:
+		err = s.fs.ReleaseDirHandle(ctx, typed)
+
+	case *fuseops.OpenFileOp:
+		err = s.fs.OpenFile(ctx, typed)
+
+	case *fuseops.ReadFileOp:
+		err = s.fs.ReadFile(ctx, typed)
+
+	case *fuseops.WriteFileOp:
+		err = s.fs.WriteFile(ctx, typed)
+
+	case *fuseops.SyncFileOp:
+		err = s.fs.SyncFile(ctx, typed)
+
+	case *fuseops.FlushFileOp:
+		err = s.fs.FlushFile(ctx, typed)
+
+	case *fuseops.ReleaseFileHandleOp:
+		err = s.fs.ReleaseFileHandle(ctx, typed)
+
+	case *fuseops.ReadSymlinkOp:
+		err = s.fs.ReadSymlink(ctx, typed)
+
+	case *fuseops.RemoveXattrOp:
+		err = s.fs.RemoveXattr(ctx, typed)
+
+	case *fuseops.GetXattrOp:
+		err = s.fs.GetXattr(ctx, typed)
+
+	case *fuseops.ListXattrOp:
+		err = s.fs.ListXattr(ctx, typed)
+
+	case *fuseops.SetXattrOp:
+		err = s.fs.SetXattr(ctx, typed)
+	}
+
+	c.Reply(ctx, err)
+}

+ 215 - 0
src/vendor/github.com/jacobsa/fuse/fuseutil/not_implemented_file_system.go

@@ -0,0 +1,215 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuseutil
+
+import (
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"golang.org/x/net/context"
+)
+
+// A FileSystem that responds to all ops with fuse.ENOSYS. Embed this in your
+// struct to inherit default implementations for the methods you don't care
+// about, ensuring your struct will continue to implement FileSystem even as
+// new methods are added.
+type NotImplementedFileSystem struct {
+}
+
+var _ FileSystem = &NotImplementedFileSystem{}
+
+func (fs *NotImplementedFileSystem) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) SetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.SetInodeAttributesOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) ForgetInode(
+	ctx context.Context,
+	op *fuseops.ForgetInodeOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) MkDir(
+	ctx context.Context,
+	op *fuseops.MkDirOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) MkNode(
+	ctx context.Context,
+	op *fuseops.MkNodeOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) CreateFile(
+	ctx context.Context,
+	op *fuseops.CreateFileOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) CreateSymlink(
+	ctx context.Context,
+	op *fuseops.CreateSymlinkOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) Rename(
+	ctx context.Context,
+	op *fuseops.RenameOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) RmDir(
+	ctx context.Context,
+	op *fuseops.RmDirOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) Unlink(
+	ctx context.Context,
+	op *fuseops.UnlinkOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) OpenDir(
+	ctx context.Context,
+	op *fuseops.OpenDirOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) ReadDir(
+	ctx context.Context,
+	op *fuseops.ReadDirOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) ReleaseDirHandle(
+	ctx context.Context,
+	op *fuseops.ReleaseDirHandleOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) ReadFile(
+	ctx context.Context,
+	op *fuseops.ReadFileOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) WriteFile(
+	ctx context.Context,
+	op *fuseops.WriteFileOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) SyncFile(
+	ctx context.Context,
+	op *fuseops.SyncFileOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) FlushFile(
+	ctx context.Context,
+	op *fuseops.FlushFileOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) ReleaseFileHandle(
+	ctx context.Context,
+	op *fuseops.ReleaseFileHandleOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) ReadSymlink(
+	ctx context.Context,
+	op *fuseops.ReadSymlinkOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) RemoveXattr(
+	ctx context.Context,
+	op *fuseops.RemoveXattrOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) GetXattr(
+	ctx context.Context,
+	op *fuseops.GetXattrOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) ListXattr(
+	ctx context.Context,
+	op *fuseops.ListXattrOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) SetXattr(
+	ctx context.Context,
+	op *fuseops.SetXattrOp) (err error) {
+	err = fuse.ENOSYS
+	return
+}
+
+func (fs *NotImplementedFileSystem) Destroy() {
+}

+ 115 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/in_message.go

@@ -0,0 +1,115 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package buffer
+
+import (
+	"fmt"
+	"io"
+	"syscall"
+	"unsafe"
+
+	"github.com/jacobsa/fuse/internal/fusekernel"
+)
+
+// All requests read from the kernel, without data, are shorter than
+// this.
+const pageSize = 4096
+
+func init() {
+	// Confirm the page size.
+	if syscall.Getpagesize() != pageSize {
+		panic(fmt.Sprintf("Page size is unexpectedly %d", syscall.Getpagesize()))
+	}
+}
+
+// We size the buffer to have enough room for a fuse request plus data
+// associated with a write request.
+const bufSize = pageSize + MaxWriteSize
+
+// An incoming message from the kernel, including leading fusekernel.InHeader
+// struct. Provides storage for messages and convenient access to their
+// contents.
+type InMessage struct {
+	remaining []byte
+	storage   [bufSize]byte
+}
+
+// Initialize with the data read by a single call to r.Read. The first call to
+// Consume will consume the bytes directly after the fusekernel.InHeader
+// struct.
+func (m *InMessage) Init(r io.Reader) (err error) {
+	n, err := r.Read(m.storage[:])
+	if err != nil {
+		return
+	}
+
+	// Make sure the message is long enough.
+	const headerSize = unsafe.Sizeof(fusekernel.InHeader{})
+	if uintptr(n) < headerSize {
+		err = fmt.Errorf("Unexpectedly read only %d bytes.", n)
+		return
+	}
+
+	m.remaining = m.storage[headerSize:n]
+
+	// Check the header's length.
+	if int(m.Header().Len) != n {
+		err = fmt.Errorf(
+			"Header says %d bytes, but we read %d",
+			m.Header().Len,
+			n)
+
+		return
+	}
+
+	return
+}
+
+// Return a reference to the header read in the most recent call to Init.
+func (m *InMessage) Header() (h *fusekernel.InHeader) {
+	h = (*fusekernel.InHeader)(unsafe.Pointer(&m.storage[0]))
+	return
+}
+
+// Return the number of bytes left to consume.
+func (m *InMessage) Len() uintptr {
+	return uintptr(len(m.remaining))
+}
+
+// Consume the next n bytes from the message, returning a nil pointer if there
+// are fewer than n bytes available.
+func (m *InMessage) Consume(n uintptr) (p unsafe.Pointer) {
+	if m.Len() == 0 || n > m.Len() {
+		return
+	}
+
+	p = unsafe.Pointer(&m.remaining[0])
+	m.remaining = m.remaining[n:]
+
+	return
+}
+
+// Equivalent to Consume, except returns a slice of bytes. The result will be
+// nil if Consume would fail.
+func (m *InMessage) ConsumeBytes(n uintptr) (b []byte) {
+	if n > m.Len() {
+		return
+	}
+
+	b = m.remaining[:n]
+	m.remaining = m.remaining[n:]
+
+	return
+}

+ 21 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/in_message_darwin.go

@@ -0,0 +1,21 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package buffer
+
+// The maximum fuse write request size that InMessage can acommodate.
+//
+// Experimentally, OS X appears to cap the size of writes to 1 MiB, regardless
+// of whether a larger size is specified in the mount options.
+const MaxWriteSize = 1 << 20

+ 21 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/in_message_linux.go

@@ -0,0 +1,21 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package buffer
+
+// The maximum fuse write request size that InMessage can acommodate.
+//
+// Experimentally, Linux appears to refuse to honor a MaxWrite setting in an
+// INIT response of more than 128 KiB.
+const MaxWriteSize = 1 << 17

+ 167 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/out_message.go

@@ -0,0 +1,167 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package buffer
+
+import (
+	"fmt"
+	"log"
+	"reflect"
+	"unsafe"
+
+	"github.com/jacobsa/fuse/internal/fusekernel"
+)
+
+// OutMessageHeaderSize is the size of the leading header in every
+// properly-constructed OutMessage. Reset brings the message back to this size.
+const OutMessageHeaderSize = int(unsafe.Sizeof(fusekernel.OutHeader{}))
+
+// OutMessage provides a mechanism for constructing a single contiguous fuse
+// message from multiple segments, where the first segment is always a
+// fusekernel.OutHeader message.
+//
+// Must be initialized with Reset.
+type OutMessage struct {
+	// The offset into payload to which we're currently writing.
+	payloadOffset int
+
+	header  fusekernel.OutHeader
+	payload [MaxReadSize]byte
+}
+
+// Make sure that the header and payload are contiguous.
+func init() {
+	a := unsafe.Offsetof(OutMessage{}.header) + uintptr(OutMessageHeaderSize)
+	b := unsafe.Offsetof(OutMessage{}.payload)
+
+	if a != b {
+		log.Panicf(
+			"header ends at offset %d, but payload starts at offset %d",
+			a, b)
+	}
+}
+
+// Reset resets m so that it's ready to be used again. Afterward, the contents
+// are solely a zeroed fusekernel.OutHeader struct.
+func (m *OutMessage) Reset() {
+	// Ideally we'd like to write:
+	//
+	//     m.payloadOffset = 0
+	//     m.header = fusekernel.OutHeader{}
+	//
+	// But Go 1.8 beta 2 generates bad code for this
+	// (https://golang.org/issue/18370). Encourage it to generate the same code
+	// as Go 1.7.4 did.
+	//if unsafe.Offsetof(m.payload) != 24 {
+	//	panic("unexpected OutMessage layout")
+	//}
+
+	//a := (*[3]uint64)(unsafe.Pointer(m))
+	//a[0] = 0
+	//a[1] = 0
+	//a[2] = 0
+
+	m.payloadOffset = 0
+	m.header = fusekernel.OutHeader{}
+}
+
+// OutHeader returns a pointer to the header at the start of the message.
+func (m *OutMessage) OutHeader() *fusekernel.OutHeader {
+	return &m.header
+}
+
+// Grow grows m's buffer by the given number of bytes, returning a pointer to
+// the start of the new segment, which is guaranteed to be zeroed. If there is
+// insufficient space, it returns nil.
+func (m *OutMessage) Grow(n int) (p unsafe.Pointer) {
+	p = m.GrowNoZero(n)
+	if p != nil {
+		memclr(p, uintptr(n))
+	}
+
+	return
+}
+
+// GrowNoZero is equivalent to Grow, except the new segment is not zeroed. Use
+// with caution!
+func (m *OutMessage) GrowNoZero(n int) (p unsafe.Pointer) {
+	// Will we overflow the buffer?
+	o := m.payloadOffset
+	if len(m.payload)-o < n {
+		return
+	}
+
+	p = unsafe.Pointer(uintptr(unsafe.Pointer(&m.payload)) + uintptr(o))
+	m.payloadOffset = o + n
+
+	return
+}
+
+// ShrinkTo shrinks m to the given size. It panics if the size is greater than
+// Len() or less than OutMessageHeaderSize.
+func (m *OutMessage) ShrinkTo(n int) {
+	if n < OutMessageHeaderSize || n > m.Len() {
+		panic(fmt.Sprintf(
+			"ShrinkTo(%d) out of range (current Len: %d)",
+			n,
+			m.Len()))
+	}
+
+	m.payloadOffset = n - OutMessageHeaderSize
+}
+
+// Append is equivalent to growing by len(src), then copying src over the new
+// segment. Int panics if there is not enough room available.
+func (m *OutMessage) Append(src []byte) {
+	p := m.GrowNoZero(len(src))
+	if p == nil {
+		panic(fmt.Sprintf("Can't grow %d bytes", len(src)))
+	}
+
+	sh := (*reflect.SliceHeader)(unsafe.Pointer(&src))
+	memmove(p, unsafe.Pointer(sh.Data), uintptr(sh.Len))
+
+	return
+}
+
+// AppendString is like Append, but accepts string input.
+func (m *OutMessage) AppendString(src string) {
+	p := m.GrowNoZero(len(src))
+	if p == nil {
+		panic(fmt.Sprintf("Can't grow %d bytes", len(src)))
+	}
+
+	sh := (*reflect.StringHeader)(unsafe.Pointer(&src))
+	memmove(p, unsafe.Pointer(sh.Data), uintptr(sh.Len))
+
+	return
+}
+
+// Len returns the current size of the message, including the leading header.
+func (m *OutMessage) Len() int {
+	return OutMessageHeaderSize + m.payloadOffset
+}
+
+// Bytes returns a reference to the current contents of the buffer, including
+// the leading header.
+func (m *OutMessage) Bytes() []byte {
+	l := m.Len()
+	sh := reflect.SliceHeader{
+		Data: uintptr(unsafe.Pointer(&m.header)),
+		Len:  l,
+		Cap:  l,
+	}
+
+	return *(*[]byte)(unsafe.Pointer(&sh))
+}

+ 21 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/out_message_darwin.go

@@ -0,0 +1,21 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package buffer
+
+// The maximum read size that we expect to ever see from the kernel, used for
+// calculating the size of out messages.
+//
+// Experimentally determined on OS X.
+const MaxReadSize = 1 << 20

+ 21 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/out_message_linux.go

@@ -0,0 +1,21 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package buffer
+
+// The maximum read size that we expect to ever see from the kernel, used for
+// calculating the size of out messages.
+//
+// For 4 KiB pages, this is 128 KiB (cf. https://goo.gl/HOiEYo)
+const MaxReadSize = 1 << 17

+ 357 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/out_message_test.go

@@ -0,0 +1,357 @@
+package buffer
+
+import (
+	"bytes"
+	"crypto/rand"
+	"fmt"
+	"io"
+	"reflect"
+	"testing"
+	"unsafe"
+
+	"github.com/jacobsa/fuse/internal/fusekernel"
+	"github.com/kylelemons/godebug/pretty"
+)
+
+func toByteSlice(p unsafe.Pointer, n int) []byte {
+	sh := reflect.SliceHeader{
+		Data: uintptr(p),
+		Len:  n,
+		Cap:  n,
+	}
+
+	return *(*[]byte)(unsafe.Pointer(&sh))
+}
+
+// fillWithGarbage writes random data to [p, p+n).
+func fillWithGarbage(p unsafe.Pointer, n int) (err error) {
+	b := toByteSlice(p, n)
+	_, err = io.ReadFull(rand.Reader, b)
+	return
+}
+
+func randBytes(n int) (b []byte, err error) {
+	b = make([]byte, n)
+	_, err = io.ReadFull(rand.Reader, b)
+	return
+}
+
+// findNonZero finds the offset of the first non-zero byte in [p, p+n). If
+// none, it returns n.
+func findNonZero(p unsafe.Pointer, n int) int {
+	b := toByteSlice(p, n)
+	for i, x := range b {
+		if x != 0 {
+			return i
+		}
+	}
+
+	return n
+}
+
+func TestMemclr(t *testing.T) {
+	// All sizes up to 32 bytes.
+	var sizes []int
+	for i := 0; i <= 32; i++ {
+		sizes = append(sizes, i)
+	}
+
+	// And a few hand-chosen sizes.
+	sizes = append(sizes, []int{
+		39, 41, 64, 127, 128, 129,
+		1<<20 - 1,
+		1 << 20,
+		1<<20 + 1,
+	}...)
+
+	// For each size, fill a buffer with random bytes and then zero it.
+	for _, size := range sizes {
+		size := size
+		t.Run(fmt.Sprintf("size=%d", size), func(t *testing.T) {
+			// Generate
+			b, err := randBytes(size)
+			if err != nil {
+				t.Fatalf("randBytes: %v", err)
+			}
+
+			// Clear
+			var p unsafe.Pointer
+			if len(b) != 0 {
+				p = unsafe.Pointer(&b[0])
+			}
+
+			memclr(p, uintptr(len(b)))
+
+			// Check
+			if i := findNonZero(p, len(b)); i != len(b) {
+				t.Fatalf("non-zero byte at offset %d", i)
+			}
+		})
+	}
+}
+
+func TestOutMessageAppend(t *testing.T) {
+	var om OutMessage
+	om.Reset()
+
+	// Append some payload.
+	const wantPayloadStr = "tacoburrito"
+	wantPayload := []byte(wantPayloadStr)
+	om.Append(wantPayload[:4])
+	om.Append(wantPayload[4:])
+
+	// The result should be a zeroed header followed by the desired payload.
+	const wantLen = OutMessageHeaderSize + len(wantPayloadStr)
+
+	if got, want := om.Len(), wantLen; got != want {
+		t.Errorf("om.Len() = %d, want %d", got, want)
+	}
+
+	b := om.Bytes()
+	if got, want := len(b), wantLen; got != want {
+		t.Fatalf("len(om.Bytes()) = %d, want %d", got, want)
+	}
+
+	want := append(
+		make([]byte, OutMessageHeaderSize),
+		wantPayload...)
+
+	if !bytes.Equal(b, want) {
+		t.Error("messages differ")
+	}
+}
+
+func TestOutMessageAppendString(t *testing.T) {
+	var om OutMessage
+	om.Reset()
+
+	// Append some payload.
+	const wantPayload = "tacoburrito"
+	om.AppendString(wantPayload[:4])
+	om.AppendString(wantPayload[4:])
+
+	// The result should be a zeroed header followed by the desired payload.
+	const wantLen = OutMessageHeaderSize + len(wantPayload)
+
+	if got, want := om.Len(), wantLen; got != want {
+		t.Errorf("om.Len() = %d, want %d", got, want)
+	}
+
+	b := om.Bytes()
+	if got, want := len(b), wantLen; got != want {
+		t.Fatalf("len(om.Bytes()) = %d, want %d", got, want)
+	}
+
+	want := append(
+		make([]byte, OutMessageHeaderSize),
+		wantPayload...)
+
+	if !bytes.Equal(b, want) {
+		t.Error("messages differ")
+	}
+}
+
+func TestOutMessageShrinkTo(t *testing.T) {
+	// Set up a buffer with some payload.
+	var om OutMessage
+	om.Reset()
+	om.AppendString("taco")
+	om.AppendString("burrito")
+
+	// Shrink it.
+	om.ShrinkTo(OutMessageHeaderSize + len("taco"))
+
+	// The result should be a zeroed header followed by "taco".
+	const wantLen = OutMessageHeaderSize + len("taco")
+
+	if got, want := om.Len(), wantLen; got != want {
+		t.Errorf("om.Len() = %d, want %d", got, want)
+	}
+
+	b := om.Bytes()
+	if got, want := len(b), wantLen; got != want {
+		t.Fatalf("len(om.Bytes()) = %d, want %d", got, want)
+	}
+
+	want := append(
+		make([]byte, OutMessageHeaderSize),
+		"taco"...)
+
+	if !bytes.Equal(b, want) {
+		t.Error("messages differ")
+	}
+}
+
+func TestOutMessageHeader(t *testing.T) {
+	var om OutMessage
+	om.Reset()
+
+	// Fill in the header.
+	want := fusekernel.OutHeader{
+		Len:    0xdeadbeef,
+		Error:  -31231917,
+		Unique: 0xcafebabeba5eba11,
+	}
+
+	h := om.OutHeader()
+	if h == nil {
+		t.Fatal("OutHeader returned nil")
+	}
+
+	*h = want
+
+	// Check that the result is as expected.
+	b := om.Bytes()
+	if len(b) != int(unsafe.Sizeof(want)) {
+		t.Fatalf("unexpected length %d; want %d", len(b), unsafe.Sizeof(want))
+	}
+
+	got := *(*fusekernel.OutHeader)(unsafe.Pointer(&b[0]))
+	if diff := pretty.Compare(got, want); diff != "" {
+		t.Errorf("diff -got +want:\n%s", diff)
+	}
+}
+
+func TestOutMessageReset(t *testing.T) {
+	var om OutMessage
+	h := om.OutHeader()
+
+	const trials = 10
+	for i := 0; i < trials; i++ {
+		// Fill the header with garbage.
+		err := fillWithGarbage(unsafe.Pointer(h), int(unsafe.Sizeof(*h)))
+		if err != nil {
+			t.Fatalf("fillWithGarbage: %v", err)
+		}
+
+		// Ensure a non-zero payload length.
+		if p := om.GrowNoZero(128); p == nil {
+			t.Fatal("GrowNoZero failed")
+		}
+
+		// Reset.
+		om.Reset()
+
+		// Check that the length was updated.
+		if got, want := om.Len(), OutMessageHeaderSize; got != want {
+			t.Fatalf("om.Len() = %d, want %d", got, want)
+		}
+
+		// Check that the header was zeroed.
+		if h.Len != 0 {
+			t.Fatalf("non-zero Len %v", h.Len)
+		}
+
+		if h.Error != 0 {
+			t.Fatalf("non-zero Error %v", h.Error)
+		}
+
+		if h.Unique != 0 {
+			t.Fatalf("non-zero Unique %v", h.Unique)
+		}
+	}
+}
+
+func TestOutMessageGrow(t *testing.T) {
+	var om OutMessage
+	om.Reset()
+
+	// Set up garbage where the payload will soon be.
+	const payloadSize = 1234
+	{
+		p := om.GrowNoZero(payloadSize)
+		if p == nil {
+			t.Fatal("GrowNoZero failed")
+		}
+
+		err := fillWithGarbage(p, payloadSize)
+		if err != nil {
+			t.Fatalf("fillWithGarbage: %v", err)
+		}
+
+		om.ShrinkTo(OutMessageHeaderSize)
+	}
+
+	// Call Grow.
+	if p := om.Grow(payloadSize); p == nil {
+		t.Fatal("Grow failed")
+	}
+
+	// Check the resulting length in two ways.
+	const wantLen = payloadSize + OutMessageHeaderSize
+	if got, want := om.Len(), wantLen; got != want {
+		t.Errorf("om.Len() = %d, want %d", got)
+	}
+
+	b := om.Bytes()
+	if got, want := len(b), wantLen; got != want {
+		t.Fatalf("len(om.Len()) = %d, want %d", got)
+	}
+
+	// Check that the payload was zeroed.
+	for i, x := range b[OutMessageHeaderSize:] {
+		if x != 0 {
+			t.Fatalf("non-zero byte 0x%02x at payload offset %d", x, i)
+		}
+	}
+}
+
+func BenchmarkOutMessageReset(b *testing.B) {
+	// A single buffer, which should fit in some level of CPU cache.
+	b.Run("Single buffer", func(b *testing.B) {
+		var om OutMessage
+		for i := 0; i < b.N; i++ {
+			om.Reset()
+		}
+
+		b.SetBytes(int64(unsafe.Offsetof(om.payload)))
+	})
+
+	// Many megabytes worth of buffers, which should defeat the CPU cache.
+	b.Run("Many buffers", func(b *testing.B) {
+		// The number of messages; intentionally a power of two.
+		const numMessages = 128
+
+		var oms [numMessages]OutMessage
+		if s := unsafe.Sizeof(oms); s < 128<<20 {
+			panic(fmt.Sprintf("Array is too small; total size: %d", s))
+		}
+
+		for i := 0; i < b.N; i++ {
+			oms[i%numMessages].Reset()
+		}
+
+		b.SetBytes(int64(unsafe.Offsetof(oms[0].payload)))
+	})
+}
+
+func BenchmarkOutMessageGrowShrink(b *testing.B) {
+	// A single buffer, which should fit in some level of CPU cache.
+	b.Run("Single buffer", func(b *testing.B) {
+		var om OutMessage
+		for i := 0; i < b.N; i++ {
+			om.Grow(MaxReadSize)
+			om.ShrinkTo(OutMessageHeaderSize)
+		}
+
+		b.SetBytes(int64(MaxReadSize))
+	})
+
+	// Many megabytes worth of buffers, which should defeat the CPU cache.
+	b.Run("Many buffers", func(b *testing.B) {
+		// The number of messages; intentionally a power of two.
+		const numMessages = 128
+
+		var oms [numMessages]OutMessage
+		if s := unsafe.Sizeof(oms); s < 128<<20 {
+			panic(fmt.Sprintf("Array is too small; total size: %d", s))
+		}
+
+		for i := 0; i < b.N; i++ {
+			oms[i%numMessages].Grow(MaxReadSize)
+			oms[i%numMessages].ShrinkTo(OutMessageHeaderSize)
+		}
+
+		b.SetBytes(int64(MaxReadSize))
+	})
+}

+ 29 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/runtime.go

@@ -0,0 +1,29 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package buffer
+
+import "unsafe"
+
+//go:noescape
+
+// Zero the n bytes starting at p.
+//
+// REQUIRES: the region does not contain any Go pointers.
+func memclr(p unsafe.Pointer, n uintptr)
+
+//go:noescape
+
+// Copy from src to dst, allowing overlap.
+func memmove(dst unsafe.Pointer, src unsafe.Pointer, n uintptr)

+ 40 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/runtime_go1.8.s

@@ -0,0 +1,40 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build amd64 arm64 ppc64 ppc64le arm
+// +build go1.8
+
+// Assembly code isn't subject to visibility restrictions, so we can jump
+// directly into package runtime.
+//
+// Technique copied from here:
+//     https://github.com/golang/go/blob/d8c6dac/src/os/signal/sig.s
+
+#include "textflag.h"
+
+#ifdef GOARCH_arm
+#define JMP B
+#endif
+#ifdef GOARCH_ppc64
+#define JMP BR
+#endif
+#ifdef GOARCH_ppc64le
+#define JMP BR
+#endif
+
+TEXT ·memclr(SB),NOSPLIT,$0-16
+	JMP runtime·memclrNoHeapPointers(SB)
+
+TEXT ·memmove(SB),NOSPLIT,$0-24
+	JMP runtime·memmove(SB)

+ 40 - 0
src/vendor/github.com/jacobsa/fuse/internal/buffer/runtime_other.s

@@ -0,0 +1,40 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build amd64 arm64 ppc64 ppc64le arm
+// +build !go1.8
+
+// Assembly code isn't subject to visibility restrictions, so we can jump
+// directly into package runtime.
+//
+// Technique copied from here:
+//     https://github.com/golang/go/blob/d8c6dac/src/os/signal/sig.s
+
+#include "textflag.h"
+
+#ifdef GOARCH_arm
+#define JMP B
+#endif
+#ifdef GOARCH_ppc64
+#define JMP BR
+#endif
+#ifdef GOARCH_ppc64le
+#define JMP BR
+#endif
+
+TEXT ·memclr(SB),NOSPLIT,$0-16
+	JMP runtime·memclr(SB)
+
+TEXT ·memmove(SB),NOSPLIT,$0-24
+	JMP runtime·memmove(SB)

+ 40 - 0
src/vendor/github.com/jacobsa/fuse/internal/freelist/freelist.go

@@ -0,0 +1,40 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package freelist
+
+import "unsafe"
+
+// A freelist for arbitrary pointers. Not safe for concurrent access.
+type Freelist struct {
+	list []unsafe.Pointer
+}
+
+// Get an element from the freelist, returning nil if empty.
+func (fl *Freelist) Get() (p unsafe.Pointer) {
+	l := len(fl.list)
+	if l == 0 {
+		return
+	}
+
+	p = fl.list[l-1]
+	fl.list = fl.list[:l-1]
+
+	return
+}
+
+// Contribute an element back to the freelist.
+func (fl *Freelist) Put(p unsafe.Pointer) {
+	fl.list = append(fl.list, p)
+}

+ 771 - 0
src/vendor/github.com/jacobsa/fuse/internal/fusekernel/fuse_kernel.go

@@ -0,0 +1,771 @@
+// 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 fusekernel
+
+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) GetPosition() uint32 {
+	return 0
+}
+
+type getxattrInCommon struct {
+	Size    uint32
+	Padding uint32
+}
+
+func (getxattrInCommon) GetPosition() uint32 {
+	return 0
+}
+
+type GetxattrOut struct {
+	Size    uint32
+	Padding uint32
+}
+
+type ListxattrIn 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
+}

+ 88 - 0
src/vendor/github.com/jacobsa/fuse/internal/fusekernel/fuse_kernel_darwin.go

@@ -0,0 +1,88 @@
+package fusekernel
+
+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) GetPosition() uint32 {
+	return g.Position
+}
+
+type SetxattrIn struct {
+	setxattrInCommon
+
+	// OS X only
+	Position uint32
+	Padding  uint32
+}
+
+func (s *SetxattrIn) GetPosition() uint32 {
+	return s.Position
+}

+ 70 - 0
src/vendor/github.com/jacobsa/fuse/internal/fusekernel/fuse_kernel_linux.go

@@ -0,0 +1,70 @@
+package fusekernel
+
+import "time"
+
+type Attr struct {
+	Ino       uint64
+	Size      uint64
+	Blocks    uint64
+	Atime     uint64
+	Mtime     uint64
+	Ctime     uint64
+	AtimeNsec uint32
+	MtimeNsec uint32
+	CtimeNsec uint32
+	Mode      uint32
+	Nlink     uint32
+	Uid       uint32
+	Gid       uint32
+	Rdev      uint32
+	Blksize   uint32
+	padding   uint32
+}
+
+func (a *Attr) Crtime() time.Time {
+	return time.Time{}
+}
+
+func (a *Attr) SetCrtime(s uint64, ns uint32) {
+	// Ignored on Linux.
+}
+
+func (a *Attr) SetFlags(f uint32) {
+	// Ignored on Linux.
+}
+
+type SetattrIn struct {
+	setattrInCommon
+}
+
+func (in *SetattrIn) BkupTime() time.Time {
+	return time.Time{}
+}
+
+func (in *SetattrIn) Chgtime() time.Time {
+	return time.Time{}
+}
+
+func (in *SetattrIn) Flags() uint32 {
+	return 0
+}
+
+func openFlags(flags uint32) OpenFlags {
+	// on amd64, the 32-bit O_LARGEFILE flag is always seen;
+	// on i386, the flag probably depends on the app
+	// requesting, but in any case should be utterly
+	// uninteresting to us here; our kernel protocol messages
+	// are not directly related to the client app's kernel
+	// API/ABI
+	flags &^= 0x8000
+
+	return OpenFlags(flags)
+}
+
+type GetxattrIn struct {
+	getxattrInCommon
+}
+
+type SetxattrIn struct {
+	setxattrInCommon
+}

+ 1 - 0
src/vendor/github.com/jacobsa/fuse/internal/fusekernel/fuse_kernel_std.go

@@ -0,0 +1 @@
+package fusekernel

+ 75 - 0
src/vendor/github.com/jacobsa/fuse/internal/fusekernel/protocol.go

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

+ 102 - 0
src/vendor/github.com/jacobsa/fuse/mount.go

@@ -0,0 +1,102 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+import (
+	"fmt"
+	"os"
+
+	"golang.org/x/net/context"
+)
+
+// Server is an interface for any type that knows how to serve ops read from a
+// connection.
+type Server interface {
+	// Read and serve ops from the supplied connection until EOF. Do not return
+	// until all operations have been responded to. Must not be called more than
+	// once.
+	ServeOps(*Connection)
+}
+
+// Mount attempts to mount a file system on the given directory, using the
+// supplied Server to serve connection requests. It blocks until the file
+// system is successfully mounted.
+func Mount(
+	dir string,
+	server Server,
+	config *MountConfig) (mfs *MountedFileSystem, err error) {
+	// Sanity check: make sure the mount point exists and is a directory. This
+	// saves us from some confusing errors later on OS X.
+	fi, err := os.Stat(dir)
+	switch {
+	case os.IsNotExist(err):
+		return
+
+	case err != nil:
+		err = fmt.Errorf("Statting mount point: %v", err)
+		return
+
+	case !fi.IsDir():
+		err = fmt.Errorf("Mount point %s is not a directory", dir)
+		return
+	}
+
+	// Initialize the struct.
+	mfs = &MountedFileSystem{
+		dir:                 dir,
+		joinStatusAvailable: make(chan struct{}),
+	}
+
+	// Begin the mounting process, which will continue in the background.
+	ready := make(chan error, 1)
+	dev, err := mount(dir, config, ready)
+	if err != nil {
+		err = fmt.Errorf("mount: %v", err)
+		return
+	}
+
+	// Choose a parent context for ops.
+	cfgCopy := *config
+	if cfgCopy.OpContext == nil {
+		cfgCopy.OpContext = context.Background()
+	}
+
+	// Create a Connection object wrapping the device.
+	connection, err := newConnection(
+		cfgCopy,
+		config.DebugLogger,
+		config.ErrorLogger,
+		dev)
+
+	if err != nil {
+		err = fmt.Errorf("newConnection: %v", err)
+		return
+	}
+
+	// Serve the connection in the background. When done, set the join status.
+	go func() {
+		server.ServeOps(connection)
+		mfs.joinStatus = connection.close()
+		close(mfs.joinStatusAvailable)
+	}()
+
+	// Wait for the mount process to complete.
+	if err = <-ready; err != nil {
+		err = fmt.Errorf("mount (background): %v", err)
+		return
+	}
+
+	return
+}

+ 232 - 0
src/vendor/github.com/jacobsa/fuse/mount_config.go

@@ -0,0 +1,232 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+import (
+	"fmt"
+	"log"
+	"runtime"
+	"strings"
+
+	"golang.org/x/net/context"
+)
+
+// Optional configuration accepted by Mount.
+type MountConfig struct {
+	// The context from which every op read from the connetion by the sever
+	// should inherit. If nil, context.Background() will be used.
+	OpContext context.Context
+
+	// If non-empty, the name of the file system as displayed by e.g. `mount`.
+	// This is important because the `umount` command requires root privileges if
+	// it doesn't agree with /etc/fstab.
+	FSName string
+
+	// Mount the file system in read-only mode. File modes will appear as normal,
+	// but opening a file for writing and metadata operations like chmod,
+	// chtimes, etc. will fail.
+	ReadOnly bool
+
+	// A logger to use for logging errors. All errors are logged, with the
+	// exception of a few blacklisted errors that are expected. If nil, no error
+	// logging is performed.
+	ErrorLogger *log.Logger
+
+	// A logger to use for logging debug information. If nil, no debug logging is
+	// performed.
+	DebugLogger *log.Logger
+
+	// Linux only. OS X always behaves as if writeback caching is disabled.
+	//
+	// By default on Linux we allow the kernel to perform writeback caching
+	// (cf. http://goo.gl/LdZzo1):
+	//
+	// *   When the user calls write(2), the kernel sticks the user's data into
+	//     its page cache. Only later does it call through to the file system,
+	//     potentially after coalescing multiple small user writes.
+	//
+	// *   The file system may receive multiple write ops from the kernel
+	//     concurrently if there is a lot of page cache data to flush.
+	//
+	// *   Write performance may be significantly improved due to the user and
+	//     the kernel not waiting for serial round trips to the file system. This
+	//     is especially true if the user makes tiny writes.
+	//
+	// *   close(2) (and anything else calling f_op->flush) causes all dirty
+	//     pages to be written out before it proceeds to send a FlushFileOp
+	//     (cf. https://goo.gl/TMrY6X).
+	//
+	// *   Similarly, close(2) causes the kernel to send a setattr request
+	//     filling in the mtime if any dirty pages were flushed, since the time
+	//     at which the pages were written to the file system can't be trusted.
+	//
+	// *   close(2) (and anything else calling f_op->flush) writes out all dirty
+	//     pages, then sends a setattr request with an appropriate mtime for
+	//     those writes if there were any, and only then proceeds to send a
+	//     flush.
+	//
+	//     Code walk:
+	//
+	//     *   (https://goo.gl/zTIZQ9) fuse_flush calls write_inode_now before
+	//         calling the file system. The latter eventually calls into
+	//         __writeback_single_inode.
+	//
+	//     *   (https://goo.gl/L7Z2w5) __writeback_single_inode calls
+	//         do_writepages, which writes out any dirty pages.
+	//
+	//     *   (https://goo.gl/DOPgla) __writeback_single_inode later calls
+	//         write_inode, which calls into the superblock op struct's write_inode
+	//         member. For fuse, this is fuse_write_inode
+	//         (cf. https://goo.gl/eDSKOX).
+	//
+	//     *   (https://goo.gl/PbkGA1) fuse_write_inode calls fuse_flush_times.
+	//
+	//     *   (https://goo.gl/ig8x9V) fuse_flush_times sends a setttr request
+	//         for setting the inode's mtime.
+	//
+	// However, this brings along some caveats:
+	//
+	// *   The file system must handle SetInodeAttributesOp or close(2) will fail,
+	//     due to the call chain into fuse_flush_times listed above.
+	//
+	// *   The kernel caches mtime and ctime regardless of whether the file
+	//     system tells it to do so, disregarding the result of further getattr
+	//     requests (cf. https://goo.gl/3ZZMUw, https://goo.gl/7WtQUp). It
+	//     appears this may be true of the file size, too. Writeback caching may
+	//     therefore not be suitable for file systems where these attributes can
+	//     spontaneously change for reasons the kernel doesn't observe. See
+	//     http://goo.gl/V5WQCN for more discussion.
+	//
+	// Setting DisableWritebackCaching disables this behavior. Instead the file
+	// system is called one or more times for each write(2), and the user's
+	// syscall doesn't return until the file system returns.
+	DisableWritebackCaching bool
+
+	// OS X only.
+	//
+	// Normally on OS X we mount with the novncache option
+	// (cf. http://goo.gl/1pTjuk), which disables entry caching in the kernel.
+	// This is because osxfuse does not honor the entry expiration values we
+	// return to it, instead caching potentially forever (cf.
+	// http://goo.gl/8yR0Ie), and it is probably better to fail to cache than to
+	// cache for too long, since the latter is more likely to hide consistency
+	// bugs that are difficult to detect and diagnose.
+	//
+	// This field disables the use of novncache, restoring entry caching. Beware:
+	// the value of ChildInodeEntry.EntryExpiration is ignored by the kernel, and
+	// entries will be cached for an arbitrarily long time.
+	EnableVnodeCaching bool
+
+	// OS X only.
+	//
+	// The name of the mounted volume, as displayed in the Finder. If empty, a
+	// default name involving the string 'osxfuse' is used.
+	VolumeName string
+
+	// Additional key=value options to pass unadulterated to the underlying mount
+	// command. See `man 8 mount`, the fuse documentation, etc. for
+	// system-specific information.
+	//
+	// For expert use only! May invalidate other guarantees made in the
+	// documentation for this package.
+	Options map[string]string
+}
+
+// Create a map containing all of the key=value mount options to be given to
+// the mount helper.
+func (c *MountConfig) toMap() (opts map[string]string) {
+	isDarwin := runtime.GOOS == "darwin"
+	opts = make(map[string]string)
+
+	// Enable permissions checking in the kernel. See the comments on
+	// InodeAttributes.Mode.
+	opts["default_permissions"] = ""
+
+	// HACK(jacobsa): Work around what appears to be a bug in systemd v219, as
+	// shipped in Ubuntu 15.04, where it automatically unmounts any file system
+	// that doesn't set an explicit name.
+	//
+	// When Ubuntu contains systemd v220, this workaround should be removed and
+	// the systemd bug reopened if the problem persists.
+	//
+	// Cf. https://github.com/bazil/fuse/issues/89
+	// Cf. https://bugs.freedesktop.org/show_bug.cgi?id=90907
+	fsname := c.FSName
+	if runtime.GOOS == "linux" && fsname == "" {
+		fsname = "some_fuse_file_system"
+	}
+
+	// Special file system name?
+	if fsname != "" {
+		opts["fsname"] = fsname
+	}
+
+	// Read only?
+	if c.ReadOnly {
+		opts["ro"] = ""
+	}
+
+	// Handle OS X options.
+	if isDarwin {
+		if !c.EnableVnodeCaching {
+			opts["novncache"] = ""
+		}
+
+		if c.VolumeName != "" {
+			// Cf. https://github.com/osxfuse/osxfuse/wiki/Mount-options#volname
+			opts["volname"] = c.VolumeName
+		}
+	}
+
+	// OS X: disable the use of "Apple Double" (._foo and .DS_Store) files, which
+	// just add noise to debug output and can have significant cost on
+	// network-based file systems.
+	//
+	// Cf. https://github.com/osxfuse/osxfuse/wiki/Mount-options
+	if isDarwin {
+		opts["noappledouble"] = ""
+	}
+
+	// Last but not least: other user-supplied options.
+	for k, v := range c.Options {
+		opts[k] = v
+	}
+
+	return
+}
+
+func escapeOptionsKey(s string) (res string) {
+	res = s
+	res = strings.Replace(res, `\`, `\\`, -1)
+	res = strings.Replace(res, `,`, `\,`, -1)
+	return
+}
+
+// Create an options string suitable for passing to the mount helper.
+func (c *MountConfig) toOptionsString() string {
+	var components []string
+	for k, v := range c.toMap() {
+		k = escapeOptionsKey(k)
+
+		component := k
+		if v != "" {
+			component = fmt.Sprintf("%s=%s", k, v)
+		}
+
+		components = append(components, component)
+	}
+
+	return strings.Join(components, ",")
+}

+ 217 - 0
src/vendor/github.com/jacobsa/fuse/mount_darwin.go

@@ -0,0 +1,217 @@
+package fuse
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"os"
+	"os/exec"
+	"strconv"
+	"strings"
+	"syscall"
+
+	"github.com/jacobsa/fuse/internal/buffer"
+)
+
+var errNoAvail = errors.New("no available fuse devices")
+var errNotLoaded = errors.New("osxfuse is not loaded")
+
+// errOSXFUSENotFound is returned from Mount when the OSXFUSE installation is
+// not detected. Make sure OSXFUSE is installed.
+var errOSXFUSENotFound = errors.New("cannot locate OSXFUSE")
+
+// osxfuseInstallation describes the paths used by an installed OSXFUSE
+// version.
+type osxfuseInstallation struct {
+	// Prefix for the device file. At mount time, an incrementing number is
+	// suffixed until a free FUSE device is found.
+	DevicePrefix string
+
+	// Path of the load helper, used to load the kernel extension if no device
+	// files are found.
+	Load string
+
+	// Path of the mount helper, used for the actual mount operation.
+	Mount string
+
+	// Environment variable used to pass the path to the executable calling the
+	// mount helper.
+	DaemonVar string
+}
+
+var (
+	osxfuseInstallations = []osxfuseInstallation{
+		// v3
+		{
+			DevicePrefix: "/dev/osxfuse",
+			Load:         "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse",
+			Mount:        "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse",
+			DaemonVar:    "MOUNT_OSXFUSE_DAEMON_PATH",
+		},
+
+		// v2
+		{
+			DevicePrefix: "/dev/osxfuse",
+			Load:         "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs",
+			Mount:        "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs",
+			DaemonVar:    "MOUNT_FUSEFS_DAEMON_PATH",
+		},
+	}
+)
+
+func loadOSXFUSE(bin string) error {
+	cmd := exec.Command(bin)
+	cmd.Dir = "/"
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	err := cmd.Run()
+	return err
+}
+
+func openOSXFUSEDev(devPrefix string) (dev *os.File, err error) {
+	// Try each device name.
+	for i := uint64(0); ; i++ {
+		path := devPrefix + strconv.FormatUint(i, 10)
+		dev, err = os.OpenFile(path, os.O_RDWR, 0000)
+		if os.IsNotExist(err) {
+			if i == 0 {
+				// Not even the first device was found. Fuse must not be loaded.
+				err = errNotLoaded
+				return
+			}
+
+			// Otherwise we've run out of kernel-provided devices
+			err = errNoAvail
+			return
+		}
+
+		if err2, ok := err.(*os.PathError); ok && err2.Err == syscall.EBUSY {
+			// This device is in use; try the next one.
+			continue
+		}
+
+		return
+	}
+}
+
+func callMount(
+	bin string,
+	daemonVar string,
+	dir string,
+	cfg *MountConfig,
+	dev *os.File,
+	ready chan<- error) (err error) {
+
+	// The mount helper doesn't understand any escaping.
+	for k, v := range cfg.toMap() {
+		if strings.Contains(k, ",") || strings.Contains(v, ",") {
+			return fmt.Errorf(
+				"mount options cannot contain commas on darwin: %q=%q",
+				k,
+				v)
+		}
+	}
+
+	// Call the mount helper, passing in the device file and saving output into a
+	// buffer.
+	cmd := exec.Command(
+		bin,
+		"-o", cfg.toOptionsString(),
+		// 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(buffer.MaxWriteSize, 10),
+		// refers to fd passed in cmd.ExtraFiles
+		"3",
+		dir,
+	)
+	cmd.ExtraFiles = []*os.File{dev}
+	cmd.Env = os.Environ()
+	// OSXFUSE <3.3.0
+	cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
+	// OSXFUSE >=3.3.0
+	cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=")
+
+	daemon := os.Args[0]
+	if daemonVar != "" {
+		cmd.Env = append(cmd.Env, daemonVar+"="+daemon)
+	}
+
+	var buf bytes.Buffer
+	cmd.Stdout = &buf
+	cmd.Stderr = &buf
+
+	err = cmd.Start()
+	if err != nil {
+		return
+	}
+
+	// In the background, wait for the command to complete.
+	go func() {
+		err := cmd.Wait()
+		if err != nil {
+			if buf.Len() > 0 {
+				output := buf.Bytes()
+				output = bytes.TrimRight(output, "\n")
+				err = fmt.Errorf("%v: %s", err, output)
+			}
+		}
+
+		ready <- err
+	}()
+
+	return
+}
+
+// Begin the process of mounting at the given directory, returning a connection
+// to the kernel. Mounting continues in the background, and is complete when an
+// error is written to the supplied channel. The file system may need to
+// service the connection in order for mounting to complete.
+func mount(
+	dir string,
+	cfg *MountConfig,
+	ready chan<- error) (dev *os.File, err error) {
+	// Find the version of osxfuse installed on this machine.
+	for _, loc := range osxfuseInstallations {
+		if _, err := os.Stat(loc.Mount); os.IsNotExist(err) {
+			// try the other locations
+			continue
+		}
+
+		// Open the device.
+		dev, err = openOSXFUSEDev(loc.DevicePrefix)
+
+		// Special case: we may need to explicitly load osxfuse. Load it, then
+		// try again.
+		if err == errNotLoaded {
+			err = loadOSXFUSE(loc.Load)
+			if err != nil {
+				err = fmt.Errorf("loadOSXFUSE: %v", err)
+				return
+			}
+
+			dev, err = openOSXFUSEDev(loc.DevicePrefix)
+		}
+
+		// Propagate errors.
+		if err != nil {
+			err = fmt.Errorf("openOSXFUSEDev: %v", err)
+			return
+		}
+
+		// Call the mount binary with the device.
+		err = callMount(loc.Mount, loc.DaemonVar, dir, cfg, dev, ready)
+		if err != nil {
+			dev.Close()
+			err = fmt.Errorf("callMount: %v", err)
+			return
+		}
+
+		return
+	}
+
+	err = errOSXFUSENotFound
+	return
+}

+ 113 - 0
src/vendor/github.com/jacobsa/fuse/mount_linux.go

@@ -0,0 +1,113 @@
+package fuse
+
+import (
+	"bytes"
+	"fmt"
+	"net"
+	"os"
+	"os/exec"
+	"syscall"
+)
+
+// Begin the process of mounting at the given directory, returning a connection
+// to the kernel. Mounting continues in the background, and is complete when an
+// error is written to the supplied channel. The file system may need to
+// service the connection in order for mounting to complete.
+func mount(
+	dir string,
+	cfg *MountConfig,
+	ready chan<- error) (dev *os.File, err error) {
+	// On linux, mounting is never delayed.
+	ready <- nil
+
+	// Create a socket pair.
+	fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0)
+	if err != nil {
+		err = fmt.Errorf("Socketpair: %v", err)
+		return
+	}
+
+	// Wrap the sockets into os.File objects that we will pass off to fusermount.
+	writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes")
+	defer writeFile.Close()
+
+	readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads")
+	defer readFile.Close()
+
+	// Start fusermount, passing it a buffer in which to write stderr.
+	var stderr bytes.Buffer
+
+	cmd := exec.Command(
+		"fusermount",
+		"-o", cfg.toOptionsString(),
+		"--",
+		dir,
+	)
+
+	cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3")
+	cmd.ExtraFiles = []*os.File{writeFile}
+	cmd.Stderr = &stderr
+
+	// Run the command.
+	err = cmd.Run()
+	if err != nil {
+		err = fmt.Errorf("running fusermount: %v\n\nstderr:\n%s", err, stderr.Bytes())
+		return
+	}
+
+	// Wrap the socket file in a connection.
+	c, err := net.FileConn(readFile)
+	if err != nil {
+		err = fmt.Errorf("FileConn: %v", err)
+		return
+	}
+	defer c.Close()
+
+	// We expect to have a Unix domain socket.
+	uc, ok := c.(*net.UnixConn)
+	if !ok {
+		err = fmt.Errorf("Expected UnixConn, got %T", c)
+		return
+	}
+
+	// Read a message.
+	buf := make([]byte, 32) // expect 1 byte
+	oob := make([]byte, 32) // expect 24 bytes
+	_, oobn, _, _, err := uc.ReadMsgUnix(buf, oob)
+	if err != nil {
+		err = fmt.Errorf("ReadMsgUnix: %v", err)
+		return
+	}
+
+	// Parse the message.
+	scms, err := syscall.ParseSocketControlMessage(oob[:oobn])
+	if err != nil {
+		err = fmt.Errorf("ParseSocketControlMessage: %v", err)
+		return
+	}
+
+	// We expect one message.
+	if len(scms) != 1 {
+		err = fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms)
+		return
+	}
+
+	scm := scms[0]
+
+	// Pull out the FD returned by fusermount
+	gotFds, err := syscall.ParseUnixRights(&scm)
+	if err != nil {
+		err = fmt.Errorf("syscall.ParseUnixRights: %v", err)
+		return
+	}
+
+	if len(gotFds) != 1 {
+		err = fmt.Errorf("wanted 1 fd; got %#v", gotFds)
+		return
+	}
+
+	// Turn the FD into an os.File.
+	dev = os.NewFile(uintptr(gotFds[0]), "/dev/fuse")
+
+	return
+}

+ 142 - 0
src/vendor/github.com/jacobsa/fuse/mount_test.go

@@ -0,0 +1,142 @@
+package fuse_test
+
+import (
+	"io/ioutil"
+	"os"
+	"path"
+	"runtime"
+	"strings"
+	"testing"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+)
+
+////////////////////////////////////////////////////////////////////////
+// minimalFS
+////////////////////////////////////////////////////////////////////////
+
+// A minimal fuseutil.FileSystem that can successfully mount but do nothing
+// else.
+type minimalFS struct {
+	fuseutil.NotImplementedFileSystem
+}
+
+func (fs *minimalFS) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////
+
+func TestSuccessfulMount(t *testing.T) {
+	ctx := context.Background()
+
+	// Set up a temporary directory.
+	dir, err := ioutil.TempDir("", "mount_test")
+	if err != nil {
+		t.Fatal("ioutil.TempDir: %v", err)
+	}
+
+	defer os.RemoveAll(dir)
+
+	// Mount.
+	fs := &minimalFS{}
+	mfs, err := fuse.Mount(
+		dir,
+		fuseutil.NewFileSystemServer(fs),
+		&fuse.MountConfig{})
+
+	if err != nil {
+		t.Fatalf("fuse.Mount: %v", err)
+	}
+
+	defer func() {
+		if err := mfs.Join(ctx); err != nil {
+			t.Errorf("Joining: %v", err)
+		}
+	}()
+
+	defer fuse.Unmount(mfs.Dir())
+}
+
+func TestNonEmptyMountPoint(t *testing.T) {
+	ctx := context.Background()
+
+	// osxfuse appears to be happy to mount over a non-empty mount point.
+	//
+	// We leave this test in for Linux, because it tickles the behavior of
+	// fusermount writing to stderr and exiting with an error code. We want to
+	// make sure that a descriptive error makes it back to the user.
+	if runtime.GOOS == "darwin" {
+		return
+	}
+
+	// Set up a temporary directory.
+	dir, err := ioutil.TempDir("", "mount_test")
+	if err != nil {
+		t.Fatal("ioutil.TempDir: %v", err)
+	}
+
+	defer os.RemoveAll(dir)
+
+	// Add a file within it.
+	err = ioutil.WriteFile(path.Join(dir, "foo"), []byte{}, 0600)
+	if err != nil {
+		t.Fatalf("ioutil.WriteFile: %v", err)
+	}
+
+	// Attempt to mount.
+	fs := &minimalFS{}
+	mfs, err := fuse.Mount(
+		dir,
+		fuseutil.NewFileSystemServer(fs),
+		&fuse.MountConfig{})
+
+	if err == nil {
+		fuse.Unmount(mfs.Dir())
+		mfs.Join(ctx)
+		t.Fatal("fuse.Mount returned nil")
+	}
+
+	const want = "not empty"
+	if got := err.Error(); !strings.Contains(got, want) {
+		t.Errorf("Unexpected error: %v", got)
+	}
+}
+
+func TestNonexistentMountPoint(t *testing.T) {
+	ctx := context.Background()
+
+	// Set up a temporary directory.
+	dir, err := ioutil.TempDir("", "mount_test")
+	if err != nil {
+		t.Fatal("ioutil.TempDir: %v", err)
+	}
+
+	defer os.RemoveAll(dir)
+
+	// Attempt to mount into a sub-directory that doesn't exist.
+	fs := &minimalFS{}
+	mfs, err := fuse.Mount(
+		path.Join(dir, "foo"),
+		fuseutil.NewFileSystemServer(fs),
+		&fuse.MountConfig{})
+
+	if err == nil {
+		fuse.Unmount(mfs.Dir())
+		mfs.Join(ctx)
+		t.Fatal("fuse.Mount returned nil")
+	}
+
+	const want = "no such file"
+	if got := err.Error(); !strings.Contains(got, want) {
+		t.Errorf("Unexpected error: %v", got)
+	}
+}

+ 49 - 0
src/vendor/github.com/jacobsa/fuse/mounted_file_system.go

@@ -0,0 +1,49 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+import "golang.org/x/net/context"
+
+// MountedFileSystem represents the status of a mount operation, with a method
+// that waits for unmounting.
+type MountedFileSystem struct {
+	dir string
+
+	// The result to return from Join. Not valid until the channel is closed.
+	joinStatus          error
+	joinStatusAvailable chan struct{}
+}
+
+// Dir returns the directory on which the file system is mounted (or where we
+// attempted to mount it.)
+func (mfs *MountedFileSystem) Dir() string {
+	return mfs.dir
+}
+
+// Join blocks until a mounted file system has been unmounted. It does not
+// return successfully until all ops read from the connection have been
+// responded to (i.e. the file system server has finished processing all
+// in-flight ops).
+//
+// The return value will be non-nil if anything unexpected happened while
+// serving. May be called multiple times.
+func (mfs *MountedFileSystem) Join(ctx context.Context) error {
+	select {
+	case <-mfs.joinStatusAvailable:
+		return mfs.joinStatus
+	case <-ctx.Done():
+		return ctx.Err()
+	}
+}

+ 46 - 0
src/vendor/github.com/jacobsa/fuse/ops.go

@@ -0,0 +1,46 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+import (
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/internal/fusekernel"
+)
+
+// A sentinel used for unknown ops. The user is expected to respond with a
+// non-nil error.
+type unknownOp struct {
+	OpCode uint32
+	Inode  fuseops.InodeID
+}
+
+// Causes us to cancel the associated context.
+type interruptOp struct {
+	FuseID uint64
+}
+
+// Required in order to mount on Linux and OS X.
+type initOp struct {
+	// In
+	Kernel fusekernel.Protocol
+
+	// In/out
+	Flags fusekernel.InitFlags
+
+	// Out
+	Library      fusekernel.Protocol
+	MaxReadahead uint32
+	MaxWrite     uint32
+}

+ 378 - 0
src/vendor/github.com/jacobsa/fuse/samples/cachingfs/caching_fs.go

@@ -0,0 +1,378 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cachingfs
+
+import (
+	"crypto/rand"
+	"fmt"
+	"io"
+	"os"
+	"time"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+	"github.com/jacobsa/syncutil"
+)
+
+const (
+	// Sizes of the files according to the file system.
+	FooSize = 123
+	BarSize = 456
+)
+
+// A file system with a fixed structure that looks like this:
+//
+//     foo
+//     dir/
+//         bar
+//
+// The file system is configured with durations that specify how long to allow
+// inode entries and attributes to be cached, used when responding to fuse
+// requests. It also exposes methods for renumbering inodes and updating mtimes
+// that are useful in testing that these durations are honored.
+//
+// Each file responds to reads with random contents. SetKeepCache can be used
+// to control whether the response to OpenFileOp tells the kernel to keep the
+// file's data in the page cache or not.
+type CachingFS interface {
+	fuseutil.FileSystem
+
+	// Return the current inode ID of the file/directory with the given name.
+	FooID() fuseops.InodeID
+	DirID() fuseops.InodeID
+	BarID() fuseops.InodeID
+
+	// Cause the inode IDs to change to values that have never before been used.
+	RenumberInodes()
+
+	// Cause further queries for the attributes of inodes to use the supplied
+	// time as the inode's mtime.
+	SetMtime(mtime time.Time)
+
+	// Instruct the file system whether or not to reply to OpenFileOp with
+	// FOPEN_KEEP_CACHE set.
+	SetKeepCache(keep bool)
+}
+
+// Create a file system that issues cacheable responses according to the
+// following rules:
+//
+//  *  LookUpInodeResponse.Entry.EntryExpiration is set according to
+//     lookupEntryTimeout.
+//
+//  *  GetInodeAttributesResponse.AttributesExpiration is set according to
+//     getattrTimeout.
+//
+//  *  Nothing else is marked cacheable. (In particular, the attributes
+//     returned by LookUpInode are not cacheable.)
+//
+func NewCachingFS(
+	lookupEntryTimeout time.Duration,
+	getattrTimeout time.Duration) (fs CachingFS, err error) {
+	roundUp := func(n fuseops.InodeID) fuseops.InodeID {
+		return numInodes * ((n + numInodes - 1) / numInodes)
+	}
+
+	cfs := &cachingFS{
+		lookupEntryTimeout: lookupEntryTimeout,
+		getattrTimeout:     getattrTimeout,
+		baseID:             roundUp(fuseops.RootInodeID + 1),
+		mtime:              time.Now(),
+	}
+
+	cfs.mu = syncutil.NewInvariantMutex(cfs.checkInvariants)
+
+	fs = cfs
+	return
+}
+
+const (
+	// Inode IDs are issued such that "foo" always receives an ID that is
+	// congruent to fooOffset modulo numInodes, etc.
+	fooOffset = iota
+	dirOffset
+	barOffset
+	numInodes
+)
+
+type cachingFS struct {
+	fuseutil.NotImplementedFileSystem
+
+	/////////////////////////
+	// Constant data
+	/////////////////////////
+
+	lookupEntryTimeout time.Duration
+	getattrTimeout     time.Duration
+
+	/////////////////////////
+	// Mutable state
+	/////////////////////////
+
+	mu syncutil.InvariantMutex
+
+	// GUARDED_BY(mu)
+	keepPageCache bool
+
+	// The current ID of the lowest numbered non-root inode.
+	//
+	// INVARIANT: baseID > fuseops.RootInodeID
+	// INVARIANT: baseID % numInodes == 0
+	//
+	// GUARDED_BY(mu)
+	baseID fuseops.InodeID
+
+	// GUARDED_BY(mu)
+	mtime time.Time
+}
+
+////////////////////////////////////////////////////////////////////////
+// Helpers
+////////////////////////////////////////////////////////////////////////
+
+func (fs *cachingFS) checkInvariants() {
+	// INVARIANT: baseID > fuseops.RootInodeID
+	// INVARIANT: baseID % numInodes == 0
+	if fs.baseID <= fuseops.RootInodeID || fs.baseID%numInodes != 0 {
+		panic(fmt.Sprintf("Bad baseID: %v", fs.baseID))
+	}
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *cachingFS) fooID() fuseops.InodeID {
+	return fs.baseID + fooOffset
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *cachingFS) dirID() fuseops.InodeID {
+	return fs.baseID + dirOffset
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *cachingFS) barID() fuseops.InodeID {
+	return fs.baseID + barOffset
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *cachingFS) rootAttrs() fuseops.InodeAttributes {
+	return fuseops.InodeAttributes{
+		Mode:  os.ModeDir | 0777,
+		Mtime: fs.mtime,
+	}
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *cachingFS) fooAttrs() fuseops.InodeAttributes {
+	return fuseops.InodeAttributes{
+		Nlink: 1,
+		Size:  FooSize,
+		Mode:  0777,
+		Mtime: fs.mtime,
+	}
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *cachingFS) dirAttrs() fuseops.InodeAttributes {
+	return fuseops.InodeAttributes{
+		Nlink: 1,
+		Mode:  os.ModeDir | 0777,
+		Mtime: fs.mtime,
+	}
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *cachingFS) barAttrs() fuseops.InodeAttributes {
+	return fuseops.InodeAttributes{
+		Nlink: 1,
+		Size:  BarSize,
+		Mode:  0777,
+		Mtime: fs.mtime,
+	}
+}
+
+////////////////////////////////////////////////////////////////////////
+// Public interface
+////////////////////////////////////////////////////////////////////////
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *cachingFS) FooID() fuseops.InodeID {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	return fs.fooID()
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *cachingFS) DirID() fuseops.InodeID {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	return fs.dirID()
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *cachingFS) BarID() fuseops.InodeID {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	return fs.barID()
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *cachingFS) RenumberInodes() {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	fs.baseID += numInodes
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *cachingFS) SetMtime(mtime time.Time) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	fs.mtime = mtime
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *cachingFS) SetKeepCache(keep bool) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	fs.keepPageCache = keep
+}
+
+////////////////////////////////////////////////////////////////////////
+// FileSystem methods
+////////////////////////////////////////////////////////////////////////
+
+func (fs *cachingFS) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	return
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *cachingFS) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Find the ID and attributes.
+	var id fuseops.InodeID
+	var attrs fuseops.InodeAttributes
+
+	switch op.Name {
+	case "foo":
+		// Parent must be the root.
+		if op.Parent != fuseops.RootInodeID {
+			err = fuse.ENOENT
+			return
+		}
+
+		id = fs.fooID()
+		attrs = fs.fooAttrs()
+
+	case "dir":
+		// Parent must be the root.
+		if op.Parent != fuseops.RootInodeID {
+			err = fuse.ENOENT
+			return
+		}
+
+		id = fs.dirID()
+		attrs = fs.dirAttrs()
+
+	case "bar":
+		// Parent must be dir.
+		if op.Parent == fuseops.RootInodeID || op.Parent%numInodes != dirOffset {
+			err = fuse.ENOENT
+			return
+		}
+
+		id = fs.barID()
+		attrs = fs.barAttrs()
+
+	default:
+		err = fuse.ENOENT
+		return
+	}
+
+	// Fill in the response.
+	op.Entry.Child = id
+	op.Entry.Attributes = attrs
+	op.Entry.EntryExpiration = time.Now().Add(fs.lookupEntryTimeout)
+
+	return
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *cachingFS) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Figure out which inode the request is for.
+	var attrs fuseops.InodeAttributes
+
+	switch {
+	case op.Inode == fuseops.RootInodeID:
+		attrs = fs.rootAttrs()
+
+	case op.Inode%numInodes == fooOffset:
+		attrs = fs.fooAttrs()
+
+	case op.Inode%numInodes == dirOffset:
+		attrs = fs.dirAttrs()
+
+	case op.Inode%numInodes == barOffset:
+		attrs = fs.barAttrs()
+	}
+
+	// Fill in the response.
+	op.Attributes = attrs
+	op.AttributesExpiration = time.Now().Add(fs.getattrTimeout)
+
+	return
+}
+
+func (fs *cachingFS) OpenDir(
+	ctx context.Context,
+	op *fuseops.OpenDirOp) (err error) {
+	return
+}
+
+func (fs *cachingFS) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	op.KeepPageCache = fs.keepPageCache
+
+	return
+}
+
+func (fs *cachingFS) ReadFile(
+	ctx context.Context,
+	op *fuseops.ReadFileOp) (err error) {
+	op.BytesRead, err = io.ReadFull(rand.Reader, op.Dst)
+	return
+}

+ 725 - 0
src/vendor/github.com/jacobsa/fuse/samples/cachingfs/caching_fs_test.go

@@ -0,0 +1,725 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cachingfs_test
+
+import (
+	"bytes"
+	"io/ioutil"
+	"os"
+	"path"
+	"runtime"
+	"syscall"
+	"testing"
+	"time"
+
+	"github.com/jacobsa/fuse/fuseutil"
+	"github.com/jacobsa/fuse/samples"
+	"github.com/jacobsa/fuse/samples/cachingfs"
+	. "github.com/jacobsa/oglematchers"
+	. "github.com/jacobsa/ogletest"
+	"github.com/jacobsa/timeutil"
+)
+
+func TestCachingFS(t *testing.T) { RunTests(t) }
+
+////////////////////////////////////////////////////////////////////////
+// Boilerplate
+////////////////////////////////////////////////////////////////////////
+
+type cachingFSTest struct {
+	samples.SampleTest
+
+	fs           cachingfs.CachingFS
+	initialMtime time.Time
+}
+
+var _ TearDownInterface = &cachingFSTest{}
+
+func (t *cachingFSTest) setUp(
+	ti *TestInfo,
+	lookupEntryTimeout time.Duration,
+	getattrTimeout time.Duration) {
+	var err error
+
+	// We assert things about whether or not mtimes are cached, but writeback
+	// caching causes them to always be cached. Turn it off.
+	t.MountConfig.DisableWritebackCaching = true
+
+	// Create the file system.
+	t.fs, err = cachingfs.NewCachingFS(lookupEntryTimeout, getattrTimeout)
+	AssertEq(nil, err)
+
+	t.Server = fuseutil.NewFileSystemServer(t.fs)
+
+	// Mount it.
+	t.SampleTest.SetUp(ti)
+
+	// Set up the mtime.
+	t.initialMtime = time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local)
+	t.fs.SetMtime(t.initialMtime)
+}
+
+func (t *cachingFSTest) statAll() (foo, dir, bar os.FileInfo) {
+	var err error
+
+	foo, err = os.Stat(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	dir, err = os.Stat(path.Join(t.Dir, "dir"))
+	AssertEq(nil, err)
+
+	bar, err = os.Stat(path.Join(t.Dir, "dir/bar"))
+	AssertEq(nil, err)
+
+	return
+}
+
+func (t *cachingFSTest) openFiles() (foo, dir, bar *os.File) {
+	var err error
+
+	foo, err = os.Open(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	dir, err = os.Open(path.Join(t.Dir, "dir"))
+	AssertEq(nil, err)
+
+	bar, err = os.Open(path.Join(t.Dir, "dir/bar"))
+	AssertEq(nil, err)
+
+	return
+}
+
+func (t *cachingFSTest) statFiles(
+	f, g, h *os.File) (foo, dir, bar os.FileInfo) {
+	var err error
+
+	foo, err = f.Stat()
+	AssertEq(nil, err)
+
+	dir, err = g.Stat()
+	AssertEq(nil, err)
+
+	bar, err = h.Stat()
+	AssertEq(nil, err)
+
+	return
+}
+
+func getInodeID(fi os.FileInfo) uint64 {
+	return fi.Sys().(*syscall.Stat_t).Ino
+}
+
+////////////////////////////////////////////////////////////////////////
+// Basics
+////////////////////////////////////////////////////////////////////////
+
+type BasicsTest struct {
+	cachingFSTest
+}
+
+var _ SetUpInterface = &BasicsTest{}
+
+func init() { RegisterTestSuite(&BasicsTest{}) }
+
+func (t *BasicsTest) SetUp(ti *TestInfo) {
+	const (
+		lookupEntryTimeout = 0
+		getattrTimeout     = 0
+	)
+
+	t.cachingFSTest.setUp(ti, lookupEntryTimeout, getattrTimeout)
+}
+
+func (t *BasicsTest) StatNonexistent() {
+	names := []string{
+		"blah",
+		"bar",
+		"dir/blah",
+		"dir/dir",
+		"dir/foo",
+	}
+
+	for _, n := range names {
+		_, err := os.Stat(path.Join(t.Dir, n))
+
+		AssertNe(nil, err)
+		ExpectTrue(os.IsNotExist(err), "n: %s, err: %v", n, err)
+	}
+}
+
+func (t *BasicsTest) StatFoo() {
+	fi, err := os.Stat(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	ExpectEq("foo", fi.Name())
+	ExpectEq(cachingfs.FooSize, fi.Size())
+	ExpectEq(0777, fi.Mode())
+	ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime))
+	ExpectFalse(fi.IsDir())
+	ExpectEq(t.fs.FooID(), getInodeID(fi))
+	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
+}
+
+func (t *BasicsTest) StatDir() {
+	fi, err := os.Stat(path.Join(t.Dir, "dir"))
+	AssertEq(nil, err)
+
+	ExpectEq("dir", fi.Name())
+	ExpectEq(os.ModeDir|0777, fi.Mode())
+	ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime))
+	ExpectTrue(fi.IsDir())
+	ExpectEq(t.fs.DirID(), getInodeID(fi))
+	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
+}
+
+func (t *BasicsTest) StatBar() {
+	fi, err := os.Stat(path.Join(t.Dir, "dir/bar"))
+	AssertEq(nil, err)
+
+	ExpectEq("bar", fi.Name())
+	ExpectEq(cachingfs.BarSize, fi.Size())
+	ExpectEq(0777, fi.Mode())
+	ExpectThat(fi.ModTime(), timeutil.TimeEq(t.initialMtime))
+	ExpectFalse(fi.IsDir())
+	ExpectEq(t.fs.BarID(), getInodeID(fi))
+	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
+}
+
+////////////////////////////////////////////////////////////////////////
+// No caching
+////////////////////////////////////////////////////////////////////////
+
+type NoCachingTest struct {
+	cachingFSTest
+}
+
+var _ SetUpInterface = &NoCachingTest{}
+
+func init() { RegisterTestSuite(&NoCachingTest{}) }
+
+func (t *NoCachingTest) SetUp(ti *TestInfo) {
+	const (
+		lookupEntryTimeout = 0
+		getattrTimeout     = 0
+	)
+
+	t.cachingFSTest.setUp(ti, lookupEntryTimeout, getattrTimeout)
+}
+
+func (t *NoCachingTest) StatStat() {
+	fooBefore, dirBefore, barBefore := t.statAll()
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// Make sure everything matches.
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
+
+	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
+	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
+	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
+}
+
+func (t *NoCachingTest) StatRenumberStat() {
+	t.statAll()
+	t.fs.RenumberInodes()
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// We should see the new inode IDs, because the entries should not have been
+	// cached.
+	ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
+	ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
+	ExpectEq(t.fs.BarID(), getInodeID(barAfter))
+}
+
+func (t *NoCachingTest) StatMtimeStat() {
+	newMtime := t.initialMtime.Add(time.Second)
+
+	t.statAll()
+	t.fs.SetMtime(newMtime)
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// We should see the new mtimes, because the attributes should not have been
+	// cached.
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
+}
+
+func (t *NoCachingTest) StatRenumberMtimeStat() {
+	newMtime := t.initialMtime.Add(time.Second)
+
+	t.statAll()
+	t.fs.RenumberInodes()
+	t.fs.SetMtime(newMtime)
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// We should see the new inode IDs and mtimes, because nothing should have
+	// been cached.
+	ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
+	ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
+	ExpectEq(t.fs.BarID(), getInodeID(barAfter))
+
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
+}
+
+////////////////////////////////////////////////////////////////////////
+// Entry caching
+////////////////////////////////////////////////////////////////////////
+
+type EntryCachingTest struct {
+	cachingFSTest
+	lookupEntryTimeout time.Duration
+}
+
+var _ SetUpInterface = &EntryCachingTest{}
+
+func init() { RegisterTestSuite(&EntryCachingTest{}) }
+
+func (t *EntryCachingTest) SetUp(ti *TestInfo) {
+	t.lookupEntryTimeout = 250 * time.Millisecond
+	t.SampleTest.MountConfig.EnableVnodeCaching = true
+
+	t.cachingFSTest.setUp(ti, t.lookupEntryTimeout, 0)
+}
+
+func (t *EntryCachingTest) StatStat() {
+	fooBefore, dirBefore, barBefore := t.statAll()
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// Make sure everything matches.
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
+
+	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
+	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
+	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
+}
+
+func (t *EntryCachingTest) StatRenumberStat() {
+	fooBefore, dirBefore, barBefore := t.statAll()
+	t.fs.RenumberInodes()
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// We should still see the old inode IDs, because the inode entries should
+	// have been cached.
+	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
+	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
+	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
+
+	// But after waiting for the entry cache to expire, we should see the new
+	// IDs.
+	//
+	// Note that the cache is not guaranteed to expire on darwin. See notes on
+	// fuse.MountConfig.EnableVnodeCaching.
+	if runtime.GOOS != "darwin" {
+		time.Sleep(2 * t.lookupEntryTimeout)
+		fooAfter, dirAfter, barAfter = t.statAll()
+
+		ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
+		ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
+		ExpectEq(t.fs.BarID(), getInodeID(barAfter))
+	}
+}
+
+func (t *EntryCachingTest) StatMtimeStat() {
+	newMtime := t.initialMtime.Add(time.Second)
+
+	t.statAll()
+	t.fs.SetMtime(newMtime)
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// We should see the new mtimes, because the attributes should not have been
+	// cached.
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
+}
+
+func (t *EntryCachingTest) StatRenumberMtimeStat() {
+	newMtime := t.initialMtime.Add(time.Second)
+
+	fooBefore, dirBefore, barBefore := t.statAll()
+	t.fs.RenumberInodes()
+	t.fs.SetMtime(newMtime)
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// We should still see the old inode IDs, because the inode entries should
+	// have been cached. But the attributes should not have been.
+	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
+	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
+	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
+
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
+
+	// After waiting for the entry cache to expire, we should see fresh
+	// everything.
+	//
+	// Note that the cache is not guaranteed to expire on darwin. See notes on
+	// fuse.MountConfig.EnableVnodeCaching.
+	if runtime.GOOS != "darwin" {
+		time.Sleep(2 * t.lookupEntryTimeout)
+		fooAfter, dirAfter, barAfter = t.statAll()
+
+		ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
+		ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
+		ExpectEq(t.fs.BarID(), getInodeID(barAfter))
+
+		ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
+		ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
+		ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
+	}
+}
+
+////////////////////////////////////////////////////////////////////////
+// Attribute caching
+////////////////////////////////////////////////////////////////////////
+
+type AttributeCachingTest struct {
+	cachingFSTest
+	getattrTimeout time.Duration
+}
+
+var _ SetUpInterface = &AttributeCachingTest{}
+
+func init() { RegisterTestSuite(&AttributeCachingTest{}) }
+
+func (t *AttributeCachingTest) SetUp(ti *TestInfo) {
+	t.getattrTimeout = 250 * time.Millisecond
+	t.cachingFSTest.setUp(ti, 0, t.getattrTimeout)
+}
+
+func (t *AttributeCachingTest) StatStat() {
+	fooBefore, dirBefore, barBefore := t.statAll()
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// Make sure everything matches.
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
+
+	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
+	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
+	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
+}
+
+func (t *AttributeCachingTest) StatRenumberStat() {
+	t.statAll()
+	t.fs.RenumberInodes()
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// We should see the new inode IDs, because the entries should not have been
+	// cached.
+	ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
+	ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
+	ExpectEq(t.fs.BarID(), getInodeID(barAfter))
+}
+
+func (t *AttributeCachingTest) StatMtimeStat_ViaPath() {
+	newMtime := t.initialMtime.Add(time.Second)
+
+	t.statAll()
+	t.fs.SetMtime(newMtime)
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// Since we don't have entry caching enabled, the call above had to look up
+	// the entry again. With the lookup we returned new attributes, so it's
+	// possible that the mtime will be fresh. On Linux it appears to be, and on
+	// OS X it appears to not be.
+	m := AnyOf(timeutil.TimeEq(newMtime), timeutil.TimeEq(t.initialMtime))
+	ExpectThat(fooAfter.ModTime(), m)
+	ExpectThat(dirAfter.ModTime(), m)
+	ExpectThat(barAfter.ModTime(), m)
+}
+
+func (t *AttributeCachingTest) StatMtimeStat_ViaFileDescriptor() {
+	newMtime := t.initialMtime.Add(time.Second)
+
+	// Open everything, fixing a particular inode number for each.
+	foo, dir, bar := t.openFiles()
+	defer func() {
+		foo.Close()
+		dir.Close()
+		bar.Close()
+	}()
+
+	fooBefore, dirBefore, barBefore := t.statFiles(foo, dir, bar)
+	t.fs.SetMtime(newMtime)
+	fooAfter, dirAfter, barAfter := t.statFiles(foo, dir, bar)
+
+	// We should still see the old cached mtime.
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
+
+	// After waiting for the attribute cache to expire, we should see the fresh
+	// mtime.
+	time.Sleep(2 * t.getattrTimeout)
+	fooAfter, dirAfter, barAfter = t.statFiles(foo, dir, bar)
+
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
+}
+
+func (t *AttributeCachingTest) StatRenumberMtimeStat_ViaPath() {
+	newMtime := t.initialMtime.Add(time.Second)
+
+	t.statAll()
+	t.fs.RenumberInodes()
+	t.fs.SetMtime(newMtime)
+	fooAfter, dirAfter, barAfter := t.statAll()
+
+	// We should see new everything, because this is the first time the new
+	// inodes have been encountered. Entries for the old ones should not have
+	// been cached, because we have entry caching disabled.
+	ExpectEq(t.fs.FooID(), getInodeID(fooAfter))
+	ExpectEq(t.fs.DirID(), getInodeID(dirAfter))
+	ExpectEq(t.fs.BarID(), getInodeID(barAfter))
+
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
+}
+
+func (t *AttributeCachingTest) StatRenumberMtimeStat_ViaFileDescriptor() {
+	newMtime := t.initialMtime.Add(time.Second)
+
+	// Open everything, fixing a particular inode number for each.
+	foo, dir, bar := t.openFiles()
+	defer func() {
+		foo.Close()
+		dir.Close()
+		bar.Close()
+	}()
+
+	fooBefore, dirBefore, barBefore := t.statFiles(foo, dir, bar)
+	t.fs.RenumberInodes()
+	t.fs.SetMtime(newMtime)
+	fooAfter, dirAfter, barAfter := t.statFiles(foo, dir, bar)
+
+	// We should still see the old cached mtime with the old inode ID.
+	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
+	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
+	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
+
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(fooBefore.ModTime()))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(dirBefore.ModTime()))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(barBefore.ModTime()))
+
+	// After waiting for the attribute cache to expire, we should see the fresh
+	// mtime, still with the old inode ID.
+	time.Sleep(2 * t.getattrTimeout)
+	fooAfter, dirAfter, barAfter = t.statFiles(foo, dir, bar)
+
+	ExpectEq(getInodeID(fooBefore), getInodeID(fooAfter))
+	ExpectEq(getInodeID(dirBefore), getInodeID(dirAfter))
+	ExpectEq(getInodeID(barBefore), getInodeID(barAfter))
+
+	ExpectThat(fooAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(dirAfter.ModTime(), timeutil.TimeEq(newMtime))
+	ExpectThat(barAfter.ModTime(), timeutil.TimeEq(newMtime))
+}
+
+////////////////////////////////////////////////////////////////////////
+// Page cache
+////////////////////////////////////////////////////////////////////////
+
+type PageCacheTest struct {
+	cachingFSTest
+}
+
+var _ SetUpInterface = &PageCacheTest{}
+
+func init() { RegisterTestSuite(&PageCacheTest{}) }
+
+func (t *PageCacheTest) SetUp(ti *TestInfo) {
+	const (
+		lookupEntryTimeout = 0
+		getattrTimeout     = 0
+	)
+
+	t.cachingFSTest.setUp(ti, lookupEntryTimeout, getattrTimeout)
+}
+
+func (t *PageCacheTest) SingleFileHandle_NoKeepCache() {
+	t.fs.SetKeepCache(false)
+
+	// Open the file.
+	f, err := os.Open(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	defer f.Close()
+
+	// Read its contents once.
+	f.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c1, err := ioutil.ReadAll(f)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c1))
+
+	// And again.
+	f.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c2, err := ioutil.ReadAll(f)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c2))
+
+	// We should have seen the same contents each time.
+	ExpectTrue(bytes.Equal(c1, c2))
+}
+
+func (t *PageCacheTest) SingleFileHandle_KeepCache() {
+	t.fs.SetKeepCache(true)
+
+	// Open the file.
+	f, err := os.Open(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	defer f.Close()
+
+	// Read its contents once.
+	f.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c1, err := ioutil.ReadAll(f)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c1))
+
+	// And again.
+	f.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c2, err := ioutil.ReadAll(f)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c2))
+
+	// We should have seen the same contents each time.
+	ExpectTrue(bytes.Equal(c1, c2))
+}
+
+func (t *PageCacheTest) TwoFileHandles_NoKeepCache() {
+	t.fs.SetKeepCache(false)
+
+	// SetKeepCache(false) doesn't work on OS X. See the notes on
+	// OpenFileOp.KeepPageCache.
+	if runtime.GOOS == "darwin" {
+		return
+	}
+
+	// Open the file.
+	f1, err := os.Open(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	defer f1.Close()
+
+	// Read its contents once.
+	f1.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c1, err := ioutil.ReadAll(f1)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c1))
+
+	// Open a second handle.
+	f2, err := os.Open(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	defer f2.Close()
+
+	// We should see different contents if we read from that handle, due to the
+	// cache being invalidated at the time of opening.
+	f2.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c2, err := ioutil.ReadAll(f2)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c2))
+
+	ExpectFalse(bytes.Equal(c1, c2))
+
+	// Another read from the second handle should give the same result as the
+	// first one from that handle.
+	f2.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c3, err := ioutil.ReadAll(f2)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c3))
+
+	ExpectTrue(bytes.Equal(c2, c3))
+
+	// And another read from the first handle should give the same result yet
+	// again.
+	f1.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c4, err := ioutil.ReadAll(f1)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c4))
+
+	ExpectTrue(bytes.Equal(c2, c4))
+}
+
+func (t *PageCacheTest) TwoFileHandles_KeepCache() {
+	t.fs.SetKeepCache(true)
+
+	// Open the file.
+	f1, err := os.Open(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	defer f1.Close()
+
+	// Read its contents once.
+	f1.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c1, err := ioutil.ReadAll(f1)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c1))
+
+	// Open a second handle.
+	f2, err := os.Open(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	defer f2.Close()
+
+	// We should see the same contents when we read via the second handle.
+	f2.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c2, err := ioutil.ReadAll(f2)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c2))
+
+	ExpectTrue(bytes.Equal(c1, c2))
+
+	// Ditto if we read again from the first.
+	f1.Seek(0, 0)
+	AssertEq(nil, err)
+
+	c3, err := ioutil.ReadAll(f1)
+	AssertEq(nil, err)
+	AssertEq(cachingfs.FooSize, len(c3))
+
+	ExpectTrue(bytes.Equal(c1, c3))
+}

+ 284 - 0
src/vendor/github.com/jacobsa/fuse/samples/dynamicfs/dynamic_fs.go

@@ -0,0 +1,284 @@
+package dynamicfs
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"strings"
+	"sync"
+	"time"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+	"github.com/jacobsa/timeutil"
+)
+
+// Create a file system that contains 2 files (`age` and `weekday`) and no
+// directories. Every time the `age` file is opened, its contents are refreshed
+// to show the number of seconds elapsed since the file system was created (as
+// opposed to mounted). Every time the `weekday` file is opened, its contents
+// are refreshed to reflect the current weekday.
+//
+// The contents of both of these files is updated within the filesystem itself,
+// i.e., these changes do not go through the kernel. Additionally, file access
+// times are not updated and file size is not known in advance and is set to 0.
+// This simulates a filesystem that is backed by a dynamic data source where
+// file metadata is not necessarily known before the file is read. For example,
+// a filesystem backed by an expensive RPC or by a stream that's generated on
+// the fly might not know data size ahead of time.
+//
+// This implementation depends on direct IO in fuse. Without it, all read
+// operations are suppressed because the kernel detects that they read beyond
+// the end of the files.
+func NewDynamicFS(clock timeutil.Clock) (server fuse.Server, err error) {
+	createTime := clock.Now()
+	fs := &dynamicFS{
+		clock:       clock,
+		createTime:  createTime,
+		fileHandles: make(map[fuseops.HandleID]string),
+	}
+	server = fuseutil.NewFileSystemServer(fs)
+	return
+}
+
+type dynamicFS struct {
+	fuseutil.NotImplementedFileSystem
+	mu          sync.Mutex
+	clock       timeutil.Clock
+	createTime  time.Time
+	nextHandle  fuseops.HandleID
+	fileHandles map[fuseops.HandleID]string
+}
+
+const (
+	rootInode fuseops.InodeID = fuseops.RootInodeID + iota
+	ageInode
+	weekdayInode
+)
+
+type inodeInfo struct {
+	attributes fuseops.InodeAttributes
+
+	// File or directory?
+	dir bool
+
+	// For directories, children.
+	children []fuseutil.Dirent
+}
+
+// We have a fixed directory structure.
+var gInodeInfo = map[fuseops.InodeID]inodeInfo{
+	// root
+	rootInode: {
+		attributes: fuseops.InodeAttributes{
+			Nlink: 1,
+			Mode:  0555 | os.ModeDir,
+		},
+		dir: true,
+		children: []fuseutil.Dirent{
+			{
+				Offset: 1,
+				Inode:  ageInode,
+				Name:   "age",
+				Type:   fuseutil.DT_File,
+			},
+			{
+				Offset: 2,
+				Inode:  weekdayInode,
+				Name:   "weekday",
+				Type:   fuseutil.DT_File,
+			},
+		},
+	},
+
+	// age
+	ageInode: {
+		attributes: fuseops.InodeAttributes{
+			Nlink: 1,
+			Mode:  0444,
+		},
+	},
+
+	// weekday
+	weekdayInode: {
+		attributes: fuseops.InodeAttributes{
+			Nlink: 1,
+			Mode:  0444,
+			// Size left at 0.
+		},
+	},
+}
+
+func findChildInode(
+	name string,
+	children []fuseutil.Dirent) (inode fuseops.InodeID, err error) {
+	for _, e := range children {
+		if e.Name == name {
+			inode = e.Inode
+			return
+		}
+	}
+
+	err = fuse.ENOENT
+	return
+}
+
+func (fs *dynamicFS) findUnusedHandle() fuseops.HandleID {
+	// TODO: Mutex annotation?
+	handle := fs.nextHandle
+	for _, exists := fs.fileHandles[handle]; exists; _, exists = fs.fileHandles[handle] {
+		handle++
+	}
+	fs.nextHandle = handle + 1
+	return handle
+}
+
+func (fs *dynamicFS) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	// Find the info for this inode.
+	info, ok := gInodeInfo[op.Inode]
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+	// Copy over its attributes.
+	op.Attributes = info.attributes
+	return
+}
+
+func (fs *dynamicFS) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	// Find the info for the parent.
+	parentInfo, ok := gInodeInfo[op.Parent]
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+
+	// Find the child within the parent.
+	childInode, err := findChildInode(op.Name, parentInfo.children)
+	if err != nil {
+		return
+	}
+
+	// Copy over information.
+	op.Entry.Child = childInode
+	op.Entry.Attributes = gInodeInfo[childInode].attributes
+
+	return
+}
+
+func (fs *dynamicFS) OpenDir(
+	ctx context.Context,
+	op *fuseops.OpenDirOp) (err error) {
+	// Allow opening directory.
+	return
+}
+
+func (fs *dynamicFS) ReadDir(
+	ctx context.Context,
+	op *fuseops.ReadDirOp) (err error) {
+	// Find the info for this inode.
+	info, ok := gInodeInfo[op.Inode]
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+
+	if !info.dir {
+		err = fuse.EIO
+		return
+	}
+
+	entries := info.children
+
+	// Grab the range of interest.
+	if op.Offset > fuseops.DirOffset(len(entries)) {
+		err = fuse.EIO
+		return
+	}
+
+	entries = entries[op.Offset:]
+
+	// Resume at the specified offset into the array.
+	for _, e := range entries {
+		n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], e)
+		if n == 0 {
+			break
+		}
+
+		op.BytesRead += n
+	}
+
+	return
+}
+
+func (fs *dynamicFS) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+	var contents string
+	// Update file contents on (and only on) open.
+	switch op.Inode {
+	case ageInode:
+		now := fs.clock.Now()
+		ageInSeconds := int(now.Sub(fs.createTime).Seconds())
+		contents = fmt.Sprintf("This filesystem is %d seconds old.", ageInSeconds)
+	case weekdayInode:
+		contents = fmt.Sprintf("Today is %s.", fs.clock.Now().Weekday())
+	default:
+		err = fuse.EINVAL
+		return
+	}
+	handle := fs.findUnusedHandle()
+	fs.fileHandles[handle] = contents
+	op.UseDirectIO = true
+	op.Handle = handle
+	return
+}
+
+func (fs *dynamicFS) ReadFile(
+	ctx context.Context,
+	op *fuseops.ReadFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+	contents, ok := fs.fileHandles[op.Handle]
+	if !ok {
+		log.Printf("ReadFile: no open file handle: %d", op.Handle)
+		err = fuse.EIO
+		return
+	}
+	reader := strings.NewReader(contents)
+	op.BytesRead, err = reader.ReadAt(op.Dst, op.Offset)
+	if err == io.EOF {
+		err = nil
+	}
+	return
+}
+
+func (fs *dynamicFS) ReleaseFileHandle(
+	ctx context.Context,
+	op *fuseops.ReleaseFileHandleOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+	_, ok := fs.fileHandles[op.Handle]
+	if !ok {
+		log.Printf("ReleaseFileHandle: bad handle: %d", op.Handle)
+		err = fuse.EIO
+		return
+	}
+	delete(fs.fileHandles, op.Handle)
+	return
+}
+
+func (fs *dynamicFS) StatFS(ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	return
+}

+ 181 - 0
src/vendor/github.com/jacobsa/fuse/samples/dynamicfs/dynamic_fs_test.go

@@ -0,0 +1,181 @@
+package dynamicfs_test
+
+import (
+	"testing"
+
+	"github.com/jacobsa/fuse/fusetesting"
+	"github.com/jacobsa/fuse/samples"
+	"github.com/jacobsa/fuse/samples/dynamicfs"
+
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"syscall"
+	"time"
+
+	. "github.com/jacobsa/oglematchers"
+	. "github.com/jacobsa/ogletest"
+)
+
+func TestDynamicFS(t *testing.T) { RunTests(t) }
+
+type DynamicFSTest struct {
+	samples.SampleTest
+}
+
+func init() {
+	RegisterTestSuite(&DynamicFSTest{})
+}
+
+var gCreateTime = time.Date(2017, 5, 4, 14, 53, 10, 0, time.UTC)
+
+func (t *DynamicFSTest) SetUp(ti *TestInfo) {
+	var err error
+	t.Clock.SetTime(gCreateTime)
+	t.Server, err = dynamicfs.NewDynamicFS(&t.Clock)
+	AssertEq(nil, err)
+	t.SampleTest.SetUp(ti)
+}
+
+func (t *DynamicFSTest) ReadDir_Root() {
+	entries, err := fusetesting.ReadDirPicky(t.Dir)
+	AssertEq(nil, err)
+	AssertEq(2, len(entries))
+
+	var fi os.FileInfo
+	fi = entries[0]
+	ExpectEq("age", fi.Name())
+	ExpectEq(0, fi.Size())
+	ExpectEq(0444, fi.Mode())
+	ExpectFalse(fi.IsDir())
+
+	fi = entries[1]
+	ExpectEq("weekday", fi.Name())
+	ExpectEq(0, fi.Size())
+	ExpectEq(0444, fi.Mode())
+	ExpectFalse(fi.IsDir())
+}
+
+func (t *DynamicFSTest) ReadDir_NonExistent() {
+	_, err := fusetesting.ReadDirPicky(path.Join(t.Dir, "nosuchfile"))
+
+	AssertNe(nil, err)
+	ExpectThat(err, Error(HasSubstr("no such file")))
+}
+
+func (t *DynamicFSTest) Stat_Age() {
+	fi, err := os.Stat(path.Join(t.Dir, "age"))
+	AssertEq(nil, err)
+
+	ExpectEq("age", fi.Name())
+	ExpectEq(0, fi.Size())
+	ExpectEq(0444, fi.Mode())
+	ExpectFalse(fi.IsDir())
+	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
+}
+
+func (t *DynamicFSTest) Stat_Weekday() {
+	fi, err := os.Stat(path.Join(t.Dir, "weekday"))
+	AssertEq(nil, err)
+
+	ExpectEq("weekday", fi.Name())
+	ExpectEq(0, fi.Size())
+	ExpectEq(0444, fi.Mode())
+	ExpectFalse(fi.IsDir())
+	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
+}
+
+func (t *DynamicFSTest) Stat_NonExistent() {
+	_, err := os.Stat(path.Join(t.Dir, "nosuchfile"))
+
+	AssertNe(nil, err)
+	ExpectThat(err, Error(HasSubstr("no such file")))
+}
+
+func (t *DynamicFSTest) ReadFile_AgeZero() {
+	t.Clock.SetTime(gCreateTime)
+	slice, err := ioutil.ReadFile(path.Join(t.Dir, "age"))
+
+	AssertEq(nil, err)
+	ExpectEq("This filesystem is 0 seconds old.", string(slice))
+}
+
+func (t *DynamicFSTest) ReadFile_Age1000() {
+	t.Clock.SetTime(gCreateTime.Add(1000 * time.Second))
+	slice, err := ioutil.ReadFile(path.Join(t.Dir, "age"))
+
+	AssertEq(nil, err)
+	ExpectEq("This filesystem is 1000 seconds old.", string(slice))
+}
+
+func (t *DynamicFSTest) ReadFile_WeekdayNow() {
+	now := t.Clock.Now()
+	// Does simulated clock advance itself by default?
+	// Manually set time to ensure it's frozen.
+	t.Clock.SetTime(now)
+	slice, err := ioutil.ReadFile(path.Join(t.Dir, "weekday"))
+
+	AssertEq(nil, err)
+	ExpectEq(fmt.Sprintf("Today is %s.", now.Weekday().String()), string(slice))
+}
+
+func (t *DynamicFSTest) ReadFile_WeekdayCreateTime() {
+	t.Clock.SetTime(gCreateTime)
+	slice, err := ioutil.ReadFile(path.Join(t.Dir, "weekday"))
+
+	AssertEq(nil, err)
+	ExpectEq(fmt.Sprintf("Today is %s.", gCreateTime.Weekday().String()), string(slice))
+}
+
+func (t *DynamicFSTest) ReadFile_AgeUnchangedForHandle() {
+	t.Clock.SetTime(gCreateTime.Add(100 * time.Second))
+	var err error
+	var file *os.File
+	file, err = os.Open(path.Join(t.Dir, "age"))
+	AssertEq(nil, err)
+
+	// Ensure that all reads from the same handle return the contents created at
+	// file open time.
+	func(file *os.File) {
+		defer file.Close()
+
+		var expectedContents string
+		var buffer bytes.Buffer
+		var bytesRead int64
+
+		expectedContents = "This filesystem is 100 seconds old."
+		bytesRead, err = buffer.ReadFrom(file)
+		AssertEq(nil, err)
+		ExpectEq(len(expectedContents), bytesRead)
+		ExpectEq(expectedContents, buffer.String())
+
+		t.Clock.SetTime(gCreateTime.Add(1000 * time.Second))
+		// Seek back to the beginning of the file. The contents should be unchanged
+		// for the life of the file handle.
+		_, err = file.Seek(0, 0)
+		AssertEq(nil, err)
+
+		buffer.Reset()
+		bytesRead, err = buffer.ReadFrom(file)
+		AssertEq(nil, err)
+		ExpectEq(len(expectedContents), bytesRead)
+		ExpectEq(expectedContents, buffer.String())
+	}(file)
+
+	// The clock was advanced while the old handle was open. The content change
+	// should be reflected by the new handle.
+	file, err = os.Open(path.Join(t.Dir, "age"))
+	AssertEq(nil, err)
+	func(file *os.File) {
+		defer file.Close()
+
+		expectedContents := "This filesystem is 1000 seconds old."
+		buffer := bytes.Buffer{}
+		bytesRead, err := buffer.ReadFrom(file)
+		AssertEq(nil, err)
+		ExpectEq(len(expectedContents), bytesRead)
+		ExpectEq(expectedContents, buffer.String())
+	}(file)
+}

+ 221 - 0
src/vendor/github.com/jacobsa/fuse/samples/errorfs/error_fs.go

@@ -0,0 +1,221 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package errorfs
+
+import (
+	"fmt"
+	"os"
+	"reflect"
+	"sync"
+	"syscall"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+)
+
+const FooContents = "xxxx"
+
+const fooInodeID = fuseops.RootInodeID + 1
+
+var fooAttrs = fuseops.InodeAttributes{
+	Nlink: 1,
+	Size:  uint64(len(FooContents)),
+	Mode:  0444,
+}
+
+// A file system whose sole contents are a file named "foo" containing the
+// string defined by FooContents.
+//
+// The file system can be configured to returned canned errors for particular
+// operations using the method SetError.
+type FS interface {
+	fuseutil.FileSystem
+
+	// Cause the file system to return the supplied error for all future
+	// operations matching the supplied type.
+	SetError(t reflect.Type, err syscall.Errno)
+}
+
+func New() (fs FS, err error) {
+	fs = &errorFS{
+		errors: make(map[reflect.Type]syscall.Errno),
+	}
+
+	return
+}
+
+type errorFS struct {
+	fuseutil.NotImplementedFileSystem
+
+	mu sync.Mutex
+
+	// GUARDED_BY(mu)
+	errors map[reflect.Type]syscall.Errno
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *errorFS) SetError(t reflect.Type, err syscall.Errno) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	fs.errors[t] = err
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *errorFS) transformError(op interface{}, err *error) bool {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	cannedErr, ok := fs.errors[reflect.TypeOf(op)]
+	if ok {
+		*err = cannedErr
+		return true
+	}
+
+	return false
+}
+
+////////////////////////////////////////////////////////////////////////
+// File system methods
+////////////////////////////////////////////////////////////////////////
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *errorFS) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	if fs.transformError(op, &err) {
+		return
+	}
+
+	// Figure out which inode the request is for.
+	switch {
+	case op.Inode == fuseops.RootInodeID:
+		op.Attributes = fuseops.InodeAttributes{
+			Mode: os.ModeDir | 0777,
+		}
+
+	case op.Inode == fooInodeID:
+		op.Attributes = fooAttrs
+
+	default:
+		err = fmt.Errorf("Unknown inode: %d", op.Inode)
+		return
+	}
+
+	return
+}
+
+func (fs *errorFS) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	return
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *errorFS) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	if fs.transformError(op, &err) {
+		return
+	}
+
+	// Is this a known inode?
+	if !(op.Parent == fuseops.RootInodeID && op.Name == "foo") {
+		err = syscall.ENOENT
+		return
+	}
+
+	op.Entry.Child = fooInodeID
+	op.Entry.Attributes = fooAttrs
+
+	return
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *errorFS) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	if fs.transformError(op, &err) {
+		return
+	}
+
+	if op.Inode != fooInodeID {
+		err = fmt.Errorf("Unsupported inode ID: %d", op.Inode)
+		return
+	}
+
+	return
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *errorFS) ReadFile(
+	ctx context.Context,
+	op *fuseops.ReadFileOp) (err error) {
+	if fs.transformError(op, &err) {
+		return
+	}
+
+	if op.Inode != fooInodeID || op.Offset != 0 {
+		err = fmt.Errorf("Unexpected request: %#v", op)
+		return
+	}
+
+	op.BytesRead = copy(op.Dst, FooContents)
+
+	return
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *errorFS) OpenDir(
+	ctx context.Context,
+	op *fuseops.OpenDirOp) (err error) {
+	if fs.transformError(op, &err) {
+		return
+	}
+
+	if op.Inode != fuseops.RootInodeID {
+		err = fmt.Errorf("Unsupported inode ID: %d", op.Inode)
+		return
+	}
+
+	return
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *errorFS) ReadDir(
+	ctx context.Context,
+	op *fuseops.ReadDirOp) (err error) {
+	if fs.transformError(op, &err) {
+		return
+	}
+
+	if op.Inode != fuseops.RootInodeID || op.Offset != 0 {
+		err = fmt.Errorf("Unexpected request: %#v", op)
+		return
+	}
+
+	op.BytesRead = fuseutil.WriteDirent(
+		op.Dst,
+		fuseutil.Dirent{
+			Offset: 0,
+			Inode:  fooInodeID,
+			Name:   "foo",
+			Type:   fuseutil.DT_File,
+		})
+
+	return
+}

+ 106 - 0
src/vendor/github.com/jacobsa/fuse/samples/errorfs/error_fs_test.go

@@ -0,0 +1,106 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package errorfs_test
+
+import (
+	"io/ioutil"
+	"os"
+	"path"
+	"reflect"
+	"syscall"
+	"testing"
+
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+	"github.com/jacobsa/fuse/samples"
+	"github.com/jacobsa/fuse/samples/errorfs"
+	. "github.com/jacobsa/oglematchers"
+	. "github.com/jacobsa/ogletest"
+)
+
+func TestErrorFS(t *testing.T) { RunTests(t) }
+
+////////////////////////////////////////////////////////////////////////
+// Boilerplate
+////////////////////////////////////////////////////////////////////////
+
+type ErrorFSTest struct {
+	samples.SampleTest
+	fs errorfs.FS
+}
+
+func init() { RegisterTestSuite(&ErrorFSTest{}) }
+
+var _ SetUpInterface = &ErrorFSTest{}
+var _ TearDownInterface = &ErrorFSTest{}
+
+func (t *ErrorFSTest) SetUp(ti *TestInfo) {
+	var err error
+
+	// Create the file system.
+	t.fs, err = errorfs.New()
+	AssertEq(nil, err)
+
+	t.Server = fuseutil.NewFileSystemServer(t.fs)
+
+	// Mount it.
+	t.SampleTest.SetUp(ti)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////
+
+func (t *ErrorFSTest) OpenFile() {
+	t.fs.SetError(reflect.TypeOf(&fuseops.OpenFileOp{}), syscall.EOWNERDEAD)
+
+	f, err := os.Open(path.Join(t.Dir, "foo"))
+	defer f.Close()
+	ExpectThat(err, Error(MatchesRegexp("open.*: .*owner died")))
+}
+
+func (t *ErrorFSTest) ReadFile() {
+	t.fs.SetError(reflect.TypeOf(&fuseops.ReadFileOp{}), syscall.EOWNERDEAD)
+
+	// Open
+	f, err := os.Open(path.Join(t.Dir, "foo"))
+	defer f.Close()
+	AssertEq(nil, err)
+
+	// Read
+	_, err = ioutil.ReadAll(f)
+	ExpectThat(err, Error(MatchesRegexp("read.*: .*owner died")))
+}
+
+func (t *ErrorFSTest) OpenDir() {
+	t.fs.SetError(reflect.TypeOf(&fuseops.OpenDirOp{}), syscall.EOWNERDEAD)
+
+	f, err := os.Open(t.Dir)
+	defer f.Close()
+	ExpectThat(err, Error(MatchesRegexp("open.*: .*owner died")))
+}
+
+func (t *ErrorFSTest) ReadDir() {
+	t.fs.SetError(reflect.TypeOf(&fuseops.ReadDirOp{}), syscall.EOWNERDEAD)
+
+	// Open
+	f, err := os.Open(t.Dir)
+	defer f.Close()
+	AssertEq(nil, err)
+
+	// Read
+	_, err = f.Readdirnames(1)
+	ExpectThat(err, Error(MatchesRegexp("read.*: .*owner died")))
+}

+ 338 - 0
src/vendor/github.com/jacobsa/fuse/samples/flushfs/flush_fs.go

@@ -0,0 +1,338 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package flushfs
+
+import (
+	"fmt"
+	"os"
+	"sync"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+)
+
+// Create a file system whose sole contents are a file named "foo" and a
+// directory named "bar".
+//
+// The file may be opened for reading and/or writing. Its initial contents are
+// empty. Whenever a flush or fsync is received, the supplied function will be
+// called with the current contents of the file and its status returned.
+//
+// The directory cannot be modified.
+func NewFileSystem(
+	reportFlush func(string) error,
+	reportFsync func(string) error) (server fuse.Server, err error) {
+	fs := &flushFS{
+		reportFlush: reportFlush,
+		reportFsync: reportFsync,
+	}
+
+	server = fuseutil.NewFileSystemServer(fs)
+	return
+}
+
+const (
+	fooID = fuseops.RootInodeID + 1 + iota
+	barID
+)
+
+type flushFS struct {
+	fuseutil.NotImplementedFileSystem
+
+	reportFlush func(string) error
+	reportFsync func(string) error
+
+	mu          sync.Mutex
+	fooContents []byte // GUARDED_BY(mu)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Helpers
+////////////////////////////////////////////////////////////////////////
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *flushFS) rootAttributes() fuseops.InodeAttributes {
+	return fuseops.InodeAttributes{
+		Nlink: 1,
+		Mode:  0777 | os.ModeDir,
+	}
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *flushFS) fooAttributes() fuseops.InodeAttributes {
+	return fuseops.InodeAttributes{
+		Nlink: 1,
+		Mode:  0777,
+		Size:  uint64(len(fs.fooContents)),
+	}
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *flushFS) barAttributes() fuseops.InodeAttributes {
+	return fuseops.InodeAttributes{
+		Nlink: 1,
+		Mode:  0777 | os.ModeDir,
+	}
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *flushFS) getAttributes(id fuseops.InodeID) (
+	attrs fuseops.InodeAttributes,
+	err error) {
+	switch id {
+	case fuseops.RootInodeID:
+		attrs = fs.rootAttributes()
+		return
+
+	case fooID:
+		attrs = fs.fooAttributes()
+		return
+
+	case barID:
+		attrs = fs.barAttributes()
+		return
+
+	default:
+		err = fuse.ENOENT
+		return
+	}
+}
+
+////////////////////////////////////////////////////////////////////////
+// FileSystem methods
+////////////////////////////////////////////////////////////////////////
+
+func (fs *flushFS) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	return
+}
+
+func (fs *flushFS) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Sanity check.
+	if op.Parent != fuseops.RootInodeID {
+		err = fuse.ENOENT
+		return
+	}
+
+	// Set up the entry.
+	switch op.Name {
+	case "foo":
+		op.Entry = fuseops.ChildInodeEntry{
+			Child:      fooID,
+			Attributes: fs.fooAttributes(),
+		}
+
+	case "bar":
+		op.Entry = fuseops.ChildInodeEntry{
+			Child:      barID,
+			Attributes: fs.barAttributes(),
+		}
+
+	default:
+		err = fuse.ENOENT
+		return
+	}
+
+	return
+}
+
+func (fs *flushFS) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	op.Attributes, err = fs.getAttributes(op.Inode)
+	return
+}
+
+func (fs *flushFS) SetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.SetInodeAttributesOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Ignore any changes and simply return existing attributes.
+	op.Attributes, err = fs.getAttributes(op.Inode)
+
+	return
+}
+
+func (fs *flushFS) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Sanity check.
+	if op.Inode != fooID {
+		err = fuse.ENOSYS
+		return
+	}
+
+	return
+}
+
+func (fs *flushFS) ReadFile(
+	ctx context.Context,
+	op *fuseops.ReadFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Ensure the offset is in range.
+	if op.Offset > int64(len(fs.fooContents)) {
+		return
+	}
+
+	// Read what we can.
+	op.BytesRead = copy(op.Dst, fs.fooContents[op.Offset:])
+
+	return
+}
+
+func (fs *flushFS) WriteFile(
+	ctx context.Context,
+	op *fuseops.WriteFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Ensure that the contents slice is long enough.
+	newLen := int(op.Offset) + len(op.Data)
+	if len(fs.fooContents) < newLen {
+		padding := make([]byte, newLen-len(fs.fooContents))
+		fs.fooContents = append(fs.fooContents, padding...)
+	}
+
+	// Copy in the data.
+	n := copy(fs.fooContents[op.Offset:], op.Data)
+
+	// Sanity check.
+	if n != len(op.Data) {
+		panic(fmt.Sprintf("Unexpected short copy: %v", n))
+	}
+
+	return
+}
+
+func (fs *flushFS) SyncFile(
+	ctx context.Context,
+	op *fuseops.SyncFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	err = fs.reportFsync(string(fs.fooContents))
+	return
+}
+
+func (fs *flushFS) FlushFile(
+	ctx context.Context,
+	op *fuseops.FlushFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	err = fs.reportFlush(string(fs.fooContents))
+	return
+}
+
+func (fs *flushFS) OpenDir(
+	ctx context.Context,
+	op *fuseops.OpenDirOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Sanity check.
+	switch op.Inode {
+	case fuseops.RootInodeID:
+	case barID:
+
+	default:
+		err = fuse.ENOENT
+		return
+	}
+
+	return
+}
+
+func (fs *flushFS) ReadDir(
+	ctx context.Context,
+	op *fuseops.ReadDirOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Create the appropriate listing.
+	var dirents []fuseutil.Dirent
+
+	switch op.Inode {
+	case fuseops.RootInodeID:
+		dirents = []fuseutil.Dirent{
+			fuseutil.Dirent{
+				Offset: 1,
+				Inode:  fooID,
+				Name:   "foo",
+				Type:   fuseutil.DT_File,
+			},
+
+			fuseutil.Dirent{
+				Offset: 2,
+				Inode:  barID,
+				Name:   "bar",
+				Type:   fuseutil.DT_Directory,
+			},
+		}
+
+	case barID:
+
+	default:
+		err = fmt.Errorf("Unexpected inode: %v", op.Inode)
+		return
+	}
+
+	// If the offset is for the end of the listing, we're done. Otherwise we
+	// expect it to be for the start.
+	switch op.Offset {
+	case fuseops.DirOffset(len(dirents)):
+		return
+
+	case 0:
+
+	default:
+		err = fmt.Errorf("Unexpected offset: %v", op.Offset)
+		return
+	}
+
+	// Fill in the listing.
+	for _, de := range dirents {
+		n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], de)
+
+		// We don't support doing this in anything more than one shot.
+		if n == 0 {
+			err = fmt.Errorf("Couldn't fit listing in %v bytes", len(op.Dst))
+			return
+		}
+
+		op.BytesRead += n
+	}
+
+	return
+}

File diff suppressed because it is too large
+ 1051 - 0
src/vendor/github.com/jacobsa/fuse/samples/flushfs/flush_fs_test.go


+ 389 - 0
src/vendor/github.com/jacobsa/fuse/samples/forgetfs/forget_fs.go

@@ -0,0 +1,389 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package forgetfs
+
+import (
+	"fmt"
+	"os"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+	"github.com/jacobsa/syncutil"
+)
+
+// Create a file system whose sole contents are a file named "foo" and a
+// directory named "bar".
+//
+// The file "foo" may be opened for reading and/or writing, but reads and
+// writes aren't supported. Additionally, any non-existent file or directory
+// name may be created within any directory, but the resulting inode will
+// appear to have been unlinked immediately.
+//
+// The file system maintains reference counts for the inodes involved. It will
+// panic if a reference count becomes negative or if an inode ID is re-used
+// after we expect it to be dead. Its Check method may be used to check that
+// there are no inodes with unexpected reference counts remaining, after
+// unmounting.
+func NewFileSystem() (fs *ForgetFS) {
+	// Set up the actual file system.
+	impl := &fsImpl{
+		inodes: map[fuseops.InodeID]*inode{
+			cannedID_Root: &inode{
+				attributes: fuseops.InodeAttributes{
+					Nlink: 1,
+					Mode:  0777 | os.ModeDir,
+				},
+			},
+			cannedID_Foo: &inode{
+				attributes: fuseops.InodeAttributes{
+					Nlink: 1,
+					Mode:  0777,
+				},
+			},
+			cannedID_Bar: &inode{
+				attributes: fuseops.InodeAttributes{
+					Nlink: 1,
+					Mode:  0777 | os.ModeDir,
+				},
+			},
+		},
+		nextInodeID: cannedID_Next,
+	}
+
+	// The root inode starts with a lookup count of one.
+	impl.inodes[cannedID_Root].IncrementLookupCount()
+
+	// The canned inodes are supposed to be stable from the user's point of view,
+	// so we should allow them to be looked up at any point even if the kernel
+	// has balanced its lookups with its forgets. Ensure that they never go to
+	// zero until the file system is destroyed.
+	impl.inodes[cannedID_Foo].IncrementLookupCount()
+	impl.inodes[cannedID_Bar].IncrementLookupCount()
+
+	// Set up the mutex.
+	impl.mu = syncutil.NewInvariantMutex(impl.checkInvariants)
+
+	// Set up a wrapper that exposes only certain methods.
+	fs = &ForgetFS{
+		impl:   impl,
+		server: fuseutil.NewFileSystemServer(impl),
+	}
+
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// ForgetFS
+////////////////////////////////////////////////////////////////////////
+
+type ForgetFS struct {
+	impl   *fsImpl
+	server fuse.Server
+}
+
+func (fs *ForgetFS) ServeOps(c *fuse.Connection) {
+	fs.server.ServeOps(c)
+}
+
+// Panic if there are any inodes that have a non-zero reference count. For use
+// after unmounting.
+func (fs *ForgetFS) Check() {
+	fs.impl.Check()
+}
+
+////////////////////////////////////////////////////////////////////////
+// Actual implementation
+////////////////////////////////////////////////////////////////////////
+
+const (
+	cannedID_Root = fuseops.RootInodeID + iota
+	cannedID_Foo
+	cannedID_Bar
+	cannedID_Next
+)
+
+type fsImpl struct {
+	fuseutil.NotImplementedFileSystem
+
+	/////////////////////////
+	// Mutable state
+	/////////////////////////
+
+	mu syncutil.InvariantMutex
+
+	// An index of inode by ID, for all IDs we have issued.
+	//
+	// GUARDED_BY(mu)
+	inodes map[fuseops.InodeID]*inode
+
+	// The next ID to issue.
+	//
+	// INVARIANT: For each k in inodes, k < nextInodeID
+	//
+	// GUARDED_BY(mu)
+	nextInodeID fuseops.InodeID
+}
+
+////////////////////////////////////////////////////////////////////////
+// inode
+////////////////////////////////////////////////////////////////////////
+
+type inode struct {
+	attributes fuseops.InodeAttributes
+
+	// The current lookup count.
+	lookupCount uint64
+
+	// true if lookupCount has ever been positive.
+	lookedUp bool
+}
+
+func (in *inode) Forgotten() bool {
+	return in.lookedUp && in.lookupCount == 0
+}
+
+func (in *inode) IncrementLookupCount() {
+	in.lookupCount++
+	in.lookedUp = true
+}
+
+func (in *inode) DecrementLookupCount(n uint64) {
+	if in.lookupCount < n {
+		panic(fmt.Sprintf(
+			"Overly large decrement: %v, %v",
+			in.lookupCount,
+			n))
+	}
+
+	in.lookupCount -= n
+}
+
+// Decrement the lookup count to zero.
+func (in *inode) Destroy() {
+	in.DecrementLookupCount(in.lookupCount)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Helpers
+////////////////////////////////////////////////////////////////////////
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *fsImpl) checkInvariants() {
+	// INVARIANT: For each k in inodes, k < nextInodeID
+	for k, _ := range fs.inodes {
+		if !(k < fs.nextInodeID) {
+			panic("Unexpectedly large inode ID")
+		}
+	}
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *fsImpl) Check() {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	for k, v := range fs.inodes {
+		if v.lookupCount != 0 {
+			panic(fmt.Sprintf("Inode %v has lookup count %v", k, v.lookupCount))
+		}
+	}
+}
+
+// Look up the inode and verify it hasn't been forgotten.
+//
+// LOCKS_REQUIRED(fs.mu)
+func (fs *fsImpl) findInodeByID(id fuseops.InodeID) (in *inode) {
+	in = fs.inodes[id]
+	if in == nil {
+		panic(fmt.Sprintf("Unknown inode: %v", id))
+	}
+
+	if in.Forgotten() {
+		panic(fmt.Sprintf("Forgotten inode: %v", id))
+	}
+
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// FileSystem methods
+////////////////////////////////////////////////////////////////////////
+
+func (fs *fsImpl) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	return
+}
+
+func (fs *fsImpl) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Make sure the parent exists and has not been forgotten.
+	_ = fs.findInodeByID(op.Parent)
+
+	// Handle the names we support.
+	var childID fuseops.InodeID
+	switch {
+	case op.Parent == cannedID_Root && op.Name == "foo":
+		childID = cannedID_Foo
+
+	case op.Parent == cannedID_Root && op.Name == "bar":
+		childID = cannedID_Bar
+
+	default:
+		err = fuse.ENOENT
+		return
+	}
+
+	// Look up the child.
+	child := fs.findInodeByID(childID)
+	child.IncrementLookupCount()
+
+	// Return an appropriate entry.
+	op.Entry = fuseops.ChildInodeEntry{
+		Child:      childID,
+		Attributes: child.attributes,
+	}
+
+	return
+}
+
+func (fs *fsImpl) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Find the inode, verifying that it has not been forgotten.
+	in := fs.findInodeByID(op.Inode)
+
+	// Return appropriate attributes.
+	op.Attributes = in.attributes
+
+	return
+}
+
+func (fs *fsImpl) ForgetInode(
+	ctx context.Context,
+	op *fuseops.ForgetInodeOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Find the inode and decrement its count.
+	in := fs.findInodeByID(op.Inode)
+	in.DecrementLookupCount(op.N)
+
+	return
+}
+
+func (fs *fsImpl) MkDir(
+	ctx context.Context,
+	op *fuseops.MkDirOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Make sure the parent exists and has not been forgotten.
+	_ = fs.findInodeByID(op.Parent)
+
+	// Mint a child inode.
+	childID := fs.nextInodeID
+	fs.nextInodeID++
+
+	child := &inode{
+		attributes: fuseops.InodeAttributes{
+			Nlink: 0,
+			Mode:  0777 | os.ModeDir,
+		},
+	}
+
+	fs.inodes[childID] = child
+	child.IncrementLookupCount()
+
+	// Return an appropriate entry.
+	op.Entry = fuseops.ChildInodeEntry{
+		Child:      childID,
+		Attributes: child.attributes,
+	}
+
+	return
+}
+
+func (fs *fsImpl) CreateFile(
+	ctx context.Context,
+	op *fuseops.CreateFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Make sure the parent exists and has not been forgotten.
+	_ = fs.findInodeByID(op.Parent)
+
+	// Mint a child inode.
+	childID := fs.nextInodeID
+	fs.nextInodeID++
+
+	child := &inode{
+		attributes: fuseops.InodeAttributes{
+			Nlink: 0,
+			Mode:  0777,
+		},
+	}
+
+	fs.inodes[childID] = child
+	child.IncrementLookupCount()
+
+	// Return an appropriate entry.
+	op.Entry = fuseops.ChildInodeEntry{
+		Child:      childID,
+		Attributes: child.attributes,
+	}
+
+	return
+}
+
+func (fs *fsImpl) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Verify that the inode has not been forgotten.
+	_ = fs.findInodeByID(op.Inode)
+
+	return
+}
+
+func (fs *fsImpl) OpenDir(
+	ctx context.Context,
+	op *fuseops.OpenDirOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Verify that the inode has not been forgotten.
+	_ = fs.findInodeByID(op.Inode)
+
+	return
+}
+
+func (fs *fsImpl) Destroy() {
+	for _, in := range fs.inodes {
+		in.Destroy()
+	}
+}

+ 167 - 0
src/vendor/github.com/jacobsa/fuse/samples/forgetfs/forget_fs_test.go

@@ -0,0 +1,167 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package forgetfs_test
+
+import (
+	"io"
+	"os"
+	"path"
+	"testing"
+
+	"github.com/jacobsa/fuse/samples"
+	"github.com/jacobsa/fuse/samples/forgetfs"
+	. "github.com/jacobsa/ogletest"
+)
+
+func TestForgetFS(t *testing.T) { RunTests(t) }
+
+////////////////////////////////////////////////////////////////////////
+// Boilerplate
+////////////////////////////////////////////////////////////////////////
+
+type ForgetFSTest struct {
+	samples.SampleTest
+	fs *forgetfs.ForgetFS
+}
+
+func init() { RegisterTestSuite(&ForgetFSTest{}) }
+
+func (t *ForgetFSTest) SetUp(ti *TestInfo) {
+	t.fs = forgetfs.NewFileSystem()
+	t.Server = t.fs
+	t.SampleTest.SetUp(ti)
+}
+
+func (t *ForgetFSTest) TearDown() {
+	// Unmount.
+	t.SampleTest.TearDown()
+
+	// Crash if anything is left.
+	t.fs.Check()
+}
+
+////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////
+
+func (t *ForgetFSTest) Open_Foo() {
+	var err error
+
+	f, err := os.Open(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	err = f.Close()
+	AssertEq(nil, err)
+}
+
+func (t *ForgetFSTest) Open_Bar() {
+	var err error
+
+	f, err := os.Open(path.Join(t.Dir, "bar"))
+	AssertEq(nil, err)
+
+	err = f.Close()
+	AssertEq(nil, err)
+}
+
+func (t *ForgetFSTest) Open_ManyTimes() {
+	// Set up a slice of files that will be closed when we're done.
+	var toClose []io.Closer
+	defer func() {
+		for _, c := range toClose {
+			ExpectEq(nil, c.Close())
+		}
+	}()
+
+	// Open foo many times.
+	for i := 0; i < 100; i++ {
+		f, err := os.Open(path.Join(t.Dir, "foo"))
+		AssertEq(nil, err)
+		toClose = append(toClose, f)
+	}
+
+	// Open bar many times.
+	for i := 0; i < 100; i++ {
+		f, err := os.Open(path.Join(t.Dir, "bar"))
+		AssertEq(nil, err)
+		toClose = append(toClose, f)
+	}
+}
+
+func (t *ForgetFSTest) Stat_Foo() {
+	var fi os.FileInfo
+	var err error
+
+	fi, err = os.Stat(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+	AssertEq("foo", fi.Name())
+	AssertEq(os.FileMode(0777), fi.Mode())
+}
+
+func (t *ForgetFSTest) Stat_Bar() {
+	var fi os.FileInfo
+	var err error
+
+	fi, err = os.Stat(path.Join(t.Dir, "bar"))
+	AssertEq(nil, err)
+	AssertEq("bar", fi.Name())
+	AssertEq(0777|os.ModeDir, fi.Mode())
+}
+
+func (t *ForgetFSTest) Stat_ManyTimes() {
+	var err error
+
+	// Stat foo many times.
+	for i := 0; i < 100; i++ {
+		_, err = os.Stat(path.Join(t.Dir, "foo"))
+		AssertEq(nil, err)
+	}
+
+	// Stat bar many times.
+	for i := 0; i < 100; i++ {
+		_, err = os.Stat(path.Join(t.Dir, "bar"))
+		AssertEq(nil, err)
+	}
+}
+
+func (t *ForgetFSTest) CreateFile() {
+	// Create and close many files within the root.
+	for i := 0; i < 100; i++ {
+		f, err := os.Create(path.Join(t.Dir, "blah"))
+		AssertEq(nil, err)
+		AssertEq(nil, f.Close())
+	}
+
+	// Create and close many files within the sub-directory.
+	for i := 0; i < 100; i++ {
+		f, err := os.Create(path.Join(t.Dir, "bar", "blah"))
+		AssertEq(nil, err)
+		AssertEq(nil, f.Close())
+	}
+}
+
+func (t *ForgetFSTest) MkDir() {
+	// Create many directories within the root.
+	for i := 0; i < 100; i++ {
+		err := os.Mkdir(path.Join(t.Dir, "blah"), 0777)
+		AssertEq(nil, err)
+	}
+
+	// Create many directories within the sub-directory.
+	for i := 0; i < 100; i++ {
+		err := os.Mkdir(path.Join(t.Dir, "bar", "blah"), 0777)
+		AssertEq(nil, err)
+	}
+}

+ 269 - 0
src/vendor/github.com/jacobsa/fuse/samples/hellofs/hello_fs.go

@@ -0,0 +1,269 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hellofs
+
+import (
+	"io"
+	"os"
+	"strings"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+	"github.com/jacobsa/timeutil"
+)
+
+// Create a file system with a fixed structure that looks like this:
+//
+//     hello
+//     dir/
+//         world
+//
+// Each file contains the string "Hello, world!".
+func NewHelloFS(clock timeutil.Clock) (server fuse.Server, err error) {
+	fs := &helloFS{
+		Clock: clock,
+	}
+
+	server = fuseutil.NewFileSystemServer(fs)
+	return
+}
+
+type helloFS struct {
+	fuseutil.NotImplementedFileSystem
+
+	Clock timeutil.Clock
+}
+
+const (
+	rootInode fuseops.InodeID = fuseops.RootInodeID + iota
+	helloInode
+	dirInode
+	worldInode
+)
+
+type inodeInfo struct {
+	attributes fuseops.InodeAttributes
+
+	// File or directory?
+	dir bool
+
+	// For directories, children.
+	children []fuseutil.Dirent
+}
+
+// We have a fixed directory structure.
+var gInodeInfo = map[fuseops.InodeID]inodeInfo{
+	// root
+	rootInode: inodeInfo{
+		attributes: fuseops.InodeAttributes{
+			Nlink: 1,
+			Mode:  0555 | os.ModeDir,
+		},
+		dir: true,
+		children: []fuseutil.Dirent{
+			fuseutil.Dirent{
+				Offset: 1,
+				Inode:  helloInode,
+				Name:   "hello",
+				Type:   fuseutil.DT_File,
+			},
+			fuseutil.Dirent{
+				Offset: 2,
+				Inode:  dirInode,
+				Name:   "dir",
+				Type:   fuseutil.DT_Directory,
+			},
+		},
+	},
+
+	// hello
+	helloInode: inodeInfo{
+		attributes: fuseops.InodeAttributes{
+			Nlink: 1,
+			Mode:  0444,
+			Size:  uint64(len("Hello, world!")),
+		},
+	},
+
+	// dir
+	dirInode: inodeInfo{
+		attributes: fuseops.InodeAttributes{
+			Nlink: 1,
+			Mode:  0555 | os.ModeDir,
+		},
+		dir: true,
+		children: []fuseutil.Dirent{
+			fuseutil.Dirent{
+				Offset: 1,
+				Inode:  worldInode,
+				Name:   "world",
+				Type:   fuseutil.DT_File,
+			},
+		},
+	},
+
+	// world
+	worldInode: inodeInfo{
+		attributes: fuseops.InodeAttributes{
+			Nlink: 1,
+			Mode:  0444,
+			Size:  uint64(len("Hello, world!")),
+		},
+	},
+}
+
+func findChildInode(
+	name string,
+	children []fuseutil.Dirent) (inode fuseops.InodeID, err error) {
+	for _, e := range children {
+		if e.Name == name {
+			inode = e.Inode
+			return
+		}
+	}
+
+	err = fuse.ENOENT
+	return
+}
+
+func (fs *helloFS) patchAttributes(
+	attr *fuseops.InodeAttributes) {
+	now := fs.Clock.Now()
+	attr.Atime = now
+	attr.Mtime = now
+	attr.Crtime = now
+}
+
+func (fs *helloFS) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	return
+}
+
+func (fs *helloFS) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	// Find the info for the parent.
+	parentInfo, ok := gInodeInfo[op.Parent]
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+
+	// Find the child within the parent.
+	childInode, err := findChildInode(op.Name, parentInfo.children)
+	if err != nil {
+		return
+	}
+
+	// Copy over information.
+	op.Entry.Child = childInode
+	op.Entry.Attributes = gInodeInfo[childInode].attributes
+
+	// Patch attributes.
+	fs.patchAttributes(&op.Entry.Attributes)
+
+	return
+}
+
+func (fs *helloFS) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	// Find the info for this inode.
+	info, ok := gInodeInfo[op.Inode]
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+
+	// Copy over its attributes.
+	op.Attributes = info.attributes
+
+	// Patch attributes.
+	fs.patchAttributes(&op.Attributes)
+
+	return
+}
+
+func (fs *helloFS) OpenDir(
+	ctx context.Context,
+	op *fuseops.OpenDirOp) (err error) {
+	// Allow opening any directory.
+	return
+}
+
+func (fs *helloFS) ReadDir(
+	ctx context.Context,
+	op *fuseops.ReadDirOp) (err error) {
+	// Find the info for this inode.
+	info, ok := gInodeInfo[op.Inode]
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+
+	if !info.dir {
+		err = fuse.EIO
+		return
+	}
+
+	entries := info.children
+
+	// Grab the range of interest.
+	if op.Offset > fuseops.DirOffset(len(entries)) {
+		err = fuse.EIO
+		return
+	}
+
+	entries = entries[op.Offset:]
+
+	// Resume at the specified offset into the array.
+	for _, e := range entries {
+		n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], e)
+		if n == 0 {
+			break
+		}
+
+		op.BytesRead += n
+	}
+
+	return
+}
+
+func (fs *helloFS) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	// Allow opening any file.
+	return
+}
+
+func (fs *helloFS) ReadFile(
+	ctx context.Context,
+	op *fuseops.ReadFileOp) (err error) {
+	// Let io.ReaderAt deal with the semantics.
+	reader := strings.NewReader("Hello, world!")
+
+	op.BytesRead, err = reader.ReadAt(op.Dst, op.Offset)
+
+	// Special case: FUSE doesn't expect us to return io.EOF.
+	if err == io.EOF {
+		err = nil
+	}
+
+	return
+}

+ 226 - 0
src/vendor/github.com/jacobsa/fuse/samples/hellofs/hello_fs_test.go

@@ -0,0 +1,226 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hellofs_test
+
+import (
+	"io"
+	"io/ioutil"
+	"os"
+	"path"
+	"syscall"
+	"testing"
+
+	"github.com/jacobsa/fuse/fusetesting"
+	"github.com/jacobsa/fuse/samples"
+	"github.com/jacobsa/fuse/samples/hellofs"
+	. "github.com/jacobsa/oglematchers"
+	. "github.com/jacobsa/ogletest"
+)
+
+func TestHelloFS(t *testing.T) { RunTests(t) }
+
+////////////////////////////////////////////////////////////////////////
+// Boilerplate
+////////////////////////////////////////////////////////////////////////
+
+type HelloFSTest struct {
+	samples.SampleTest
+}
+
+func init() { RegisterTestSuite(&HelloFSTest{}) }
+
+func (t *HelloFSTest) SetUp(ti *TestInfo) {
+	var err error
+
+	t.Server, err = hellofs.NewHelloFS(&t.Clock)
+	AssertEq(nil, err)
+
+	t.SampleTest.SetUp(ti)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Test functions
+////////////////////////////////////////////////////////////////////////
+
+func (t *HelloFSTest) ReadDir_Root() {
+	entries, err := fusetesting.ReadDirPicky(t.Dir)
+
+	AssertEq(nil, err)
+	AssertEq(2, len(entries))
+	var fi os.FileInfo
+
+	// dir
+	fi = entries[0]
+	ExpectEq("dir", fi.Name())
+	ExpectEq(0, fi.Size())
+	ExpectEq(os.ModeDir|0555, fi.Mode())
+	ExpectEq(0, t.Clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
+	ExpectTrue(fi.IsDir())
+
+	// hello
+	fi = entries[1]
+	ExpectEq("hello", fi.Name())
+	ExpectEq(len("Hello, world!"), fi.Size())
+	ExpectEq(0444, fi.Mode())
+	ExpectEq(0, t.Clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
+	ExpectFalse(fi.IsDir())
+}
+
+func (t *HelloFSTest) ReadDir_Dir() {
+	entries, err := fusetesting.ReadDirPicky(path.Join(t.Dir, "dir"))
+
+	AssertEq(nil, err)
+	AssertEq(1, len(entries))
+	var fi os.FileInfo
+
+	// world
+	fi = entries[0]
+	ExpectEq("world", fi.Name())
+	ExpectEq(len("Hello, world!"), fi.Size())
+	ExpectEq(0444, fi.Mode())
+	ExpectEq(0, t.Clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
+	ExpectFalse(fi.IsDir())
+}
+
+func (t *HelloFSTest) ReadDir_NonExistent() {
+	_, err := fusetesting.ReadDirPicky(path.Join(t.Dir, "foobar"))
+
+	AssertNe(nil, err)
+	ExpectThat(err, Error(HasSubstr("no such file")))
+}
+
+func (t *HelloFSTest) Stat_Hello() {
+	fi, err := os.Stat(path.Join(t.Dir, "hello"))
+	AssertEq(nil, err)
+
+	ExpectEq("hello", fi.Name())
+	ExpectEq(len("Hello, world!"), fi.Size())
+	ExpectEq(0444, fi.Mode())
+	ExpectEq(0, t.Clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
+	ExpectFalse(fi.IsDir())
+	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
+}
+
+func (t *HelloFSTest) Stat_Dir() {
+	fi, err := os.Stat(path.Join(t.Dir, "dir"))
+	AssertEq(nil, err)
+
+	ExpectEq("dir", fi.Name())
+	ExpectEq(0, fi.Size())
+	ExpectEq(0555|os.ModeDir, fi.Mode())
+	ExpectEq(0, t.Clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
+	ExpectTrue(fi.IsDir())
+	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
+}
+
+func (t *HelloFSTest) Stat_World() {
+	fi, err := os.Stat(path.Join(t.Dir, "dir/world"))
+	AssertEq(nil, err)
+
+	ExpectEq("world", fi.Name())
+	ExpectEq(len("Hello, world!"), fi.Size())
+	ExpectEq(0444, fi.Mode())
+	ExpectEq(0, t.Clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
+	ExpectFalse(fi.IsDir())
+	ExpectEq(1, fi.Sys().(*syscall.Stat_t).Nlink)
+}
+
+func (t *HelloFSTest) Stat_NonExistent() {
+	_, err := os.Stat(path.Join(t.Dir, "foobar"))
+
+	AssertNe(nil, err)
+	ExpectThat(err, Error(HasSubstr("no such file")))
+}
+
+func (t *HelloFSTest) ReadFile_Hello() {
+	slice, err := ioutil.ReadFile(path.Join(t.Dir, "hello"))
+
+	AssertEq(nil, err)
+	ExpectEq("Hello, world!", string(slice))
+}
+
+func (t *HelloFSTest) ReadFile_Dir() {
+	_, err := ioutil.ReadFile(path.Join(t.Dir, "dir"))
+
+	AssertNe(nil, err)
+	ExpectThat(err, Error(HasSubstr("is a directory")))
+}
+
+func (t *HelloFSTest) ReadFile_World() {
+	slice, err := ioutil.ReadFile(path.Join(t.Dir, "dir/world"))
+
+	AssertEq(nil, err)
+	ExpectEq("Hello, world!", string(slice))
+}
+
+func (t *HelloFSTest) OpenAndRead() {
+	var buf []byte = make([]byte, 1024)
+	var n int
+	var off int64
+	var err error
+
+	// Open the file.
+	f, err := os.Open(path.Join(t.Dir, "hello"))
+	defer func() {
+		if f != nil {
+			ExpectEq(nil, f.Close())
+		}
+	}()
+
+	AssertEq(nil, err)
+
+	// Seeking shouldn't affect the random access reads below.
+	_, err = f.Seek(7, 0)
+	AssertEq(nil, err)
+
+	// Random access reads
+	n, err = f.ReadAt(buf[:2], 0)
+	AssertEq(nil, err)
+	ExpectEq(2, n)
+	ExpectEq("He", string(buf[:n]))
+
+	n, err = f.ReadAt(buf[:2], int64(len("Hel")))
+	AssertEq(nil, err)
+	ExpectEq(2, n)
+	ExpectEq("lo", string(buf[:n]))
+
+	n, err = f.ReadAt(buf[:3], int64(len("Hello, wo")))
+	AssertEq(nil, err)
+	ExpectEq(3, n)
+	ExpectEq("rld", string(buf[:n]))
+
+	// Read beyond end.
+	n, err = f.ReadAt(buf[:3], int64(len("Hello, world")))
+	AssertEq(io.EOF, err)
+	ExpectEq(1, n)
+	ExpectEq("!", string(buf[:n]))
+
+	// Seek then read the rest.
+	off, err = f.Seek(int64(len("Hel")), 0)
+	AssertEq(nil, err)
+	AssertEq(len("Hel"), off)
+
+	n, err = io.ReadFull(f, buf[:len("lo, world!")])
+	AssertEq(nil, err)
+	ExpectEq(len("lo, world!"), n)
+	ExpectEq("lo, world!", string(buf[:n]))
+}
+
+func (t *HelloFSTest) Open_NonExistent() {
+	_, err := os.Open(path.Join(t.Dir, "foobar"))
+
+	AssertNe(nil, err)
+	ExpectThat(err, Error(HasSubstr("no such file")))
+}

+ 153 - 0
src/vendor/github.com/jacobsa/fuse/samples/in_process.go

@@ -0,0 +1,153 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package samples
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+	"time"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/ogletest"
+	"github.com/jacobsa/timeutil"
+	"golang.org/x/net/context"
+)
+
+// A struct that implements common behavior needed by tests in the samples/
+// directory. Use it as an embedded field in your test fixture, calling its
+// SetUp method from your SetUp method after setting the Server field.
+type SampleTest struct {
+	// The server under test and the configuration with which it should be
+	// mounted. These must be set by the user of this type before calling SetUp;
+	// all the other fields below are set by SetUp itself.
+	Server      fuse.Server
+	MountConfig fuse.MountConfig
+
+	// A context object that can be used for long-running operations.
+	Ctx context.Context
+
+	// A clock with a fixed initial time. The test's set up method may use this
+	// to wire the server with a clock, if desired.
+	Clock timeutil.SimulatedClock
+
+	// The directory at which the file system is mounted.
+	Dir string
+
+	// Anothing non-nil in this slice will be closed by TearDown. The test will
+	// fail if closing fails.
+	ToClose []io.Closer
+
+	mfs *fuse.MountedFileSystem
+}
+
+// Mount t.Server and initialize the other exported fields of the struct.
+// Panics on error.
+//
+// REQUIRES: t.Server has been set.
+func (t *SampleTest) SetUp(ti *ogletest.TestInfo) {
+	cfg := t.MountConfig
+	if *fDebug {
+		cfg.DebugLogger = log.New(os.Stderr, "fuse: ", 0)
+	}
+
+	err := t.initialize(ti.Ctx, t.Server, &cfg)
+	if err != nil {
+		panic(err)
+	}
+}
+
+// Like SetUp, but doens't panic.
+func (t *SampleTest) initialize(
+	ctx context.Context,
+	server fuse.Server,
+	config *fuse.MountConfig) (err error) {
+	// Initialize the context used by the test.
+	t.Ctx = ctx
+
+	// Make the server share that context, if the test hasn't already set some
+	// other one.
+	if config.OpContext == nil {
+		config.OpContext = ctx
+	}
+
+	// Initialize the clock.
+	t.Clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))
+
+	// Set up a temporary directory.
+	t.Dir, err = ioutil.TempDir("", "sample_test")
+	if err != nil {
+		err = fmt.Errorf("TempDir: %v", err)
+		return
+	}
+
+	// Mount the file system.
+	t.mfs, err = fuse.Mount(t.Dir, server, config)
+	if err != nil {
+		err = fmt.Errorf("Mount: %v", err)
+		return
+	}
+
+	return
+}
+
+// Unmount the file system and clean up. Panics on error.
+func (t *SampleTest) TearDown() {
+	err := t.destroy()
+	if err != nil {
+		panic(err)
+	}
+}
+
+// Like TearDown, but doesn't panic.
+func (t *SampleTest) destroy() (err error) {
+	// Close what is necessary.
+	for _, c := range t.ToClose {
+		if c == nil {
+			continue
+		}
+
+		ogletest.ExpectEq(nil, c.Close())
+	}
+
+	// Was the file system mounted?
+	if t.mfs == nil {
+		return
+	}
+
+	// Unmount the file system.
+	err = unmount(t.Dir)
+	if err != nil {
+		err = fmt.Errorf("unmount: %v", err)
+		return
+	}
+
+	// Unlink the mount point.
+	if err = os.Remove(t.Dir); err != nil {
+		err = fmt.Errorf("Unlinking mount point: %v", err)
+		return
+	}
+
+	// Join the file system.
+	err = t.mfs.Join(t.Ctx)
+	if err != nil {
+		err = fmt.Errorf("mfs.Join: %v", err)
+		return
+	}
+
+	return
+}

+ 209 - 0
src/vendor/github.com/jacobsa/fuse/samples/interruptfs/interrupt_fs.go

@@ -0,0 +1,209 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package interruptfs
+
+import (
+	"fmt"
+	"os"
+	"sync"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+)
+
+var rootAttrs = fuseops.InodeAttributes{
+	Nlink: 1,
+	Mode:  os.ModeDir | 0777,
+}
+
+const fooID = fuseops.RootInodeID + 1
+
+var fooAttrs = fuseops.InodeAttributes{
+	Nlink: 1,
+	Mode:  0777,
+	Size:  1234,
+}
+
+// A file system containing exactly one file, named "foo". ReadFile and
+// FlushFile ops can be made to hang until interrupted. Exposes a method for
+// synchronizing with the arrival of a read or a flush.
+//
+// Must be created with New.
+type InterruptFS struct {
+	fuseutil.NotImplementedFileSystem
+
+	mu sync.Mutex
+
+	blockForReads   bool // GUARDED_BY(mu)
+	blockForFlushes bool // GUARDED_BY(mu)
+
+	// Must hold the mutex when closing these.
+	readReceived  chan struct{}
+	flushReceived chan struct{}
+}
+
+func New() (fs *InterruptFS) {
+	fs = &InterruptFS{
+		readReceived:  make(chan struct{}),
+		flushReceived: make(chan struct{}),
+	}
+
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// Public interface
+////////////////////////////////////////////////////////////////////////
+
+// Block until the first read is received.
+func (fs *InterruptFS) WaitForFirstRead() {
+	<-fs.readReceived
+}
+
+// Block until the first flush is received.
+func (fs *InterruptFS) WaitForFirstFlush() {
+	<-fs.flushReceived
+}
+
+// Enable blocking until interrupted for the next (and subsequent) read ops.
+func (fs *InterruptFS) EnableReadBlocking() {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	fs.blockForReads = true
+}
+
+// Enable blocking until interrupted for the next (and subsequent) flush ops.
+func (fs *InterruptFS) EnableFlushBlocking() {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	fs.blockForFlushes = true
+}
+
+////////////////////////////////////////////////////////////////////////
+// FileSystem methods
+////////////////////////////////////////////////////////////////////////
+
+func (fs *InterruptFS) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	return
+}
+
+func (fs *InterruptFS) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	// We support only one parent.
+	if op.Parent != fuseops.RootInodeID {
+		err = fmt.Errorf("Unexpected parent: %v", op.Parent)
+		return
+	}
+
+	// We support only one name.
+	if op.Name != "foo" {
+		err = fuse.ENOENT
+		return
+	}
+
+	// Fill in the response.
+	op.Entry.Child = fooID
+	op.Entry.Attributes = fooAttrs
+
+	return
+}
+
+func (fs *InterruptFS) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	switch op.Inode {
+	case fuseops.RootInodeID:
+		op.Attributes = rootAttrs
+
+	case fooID:
+		op.Attributes = fooAttrs
+
+	default:
+		err = fmt.Errorf("Unexpected inode ID: %v", op.Inode)
+		return
+	}
+
+	return
+}
+
+func (fs *InterruptFS) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	return
+}
+
+func (fs *InterruptFS) ReadFile(
+	ctx context.Context,
+	op *fuseops.ReadFileOp) (err error) {
+	fs.mu.Lock()
+	shouldBlock := fs.blockForReads
+
+	// Signal that a read has been received, if this is the first.
+	select {
+	case <-fs.readReceived:
+	default:
+		close(fs.readReceived)
+	}
+	fs.mu.Unlock()
+
+	// Wait for cancellation if enabled.
+	if shouldBlock {
+		done := ctx.Done()
+		if done == nil {
+			panic("Expected non-nil channel.")
+		}
+
+		<-done
+		err = ctx.Err()
+	}
+
+	return
+}
+
+func (fs *InterruptFS) FlushFile(
+	ctx context.Context,
+	op *fuseops.FlushFileOp) (err error) {
+	fs.mu.Lock()
+	shouldBlock := fs.blockForFlushes
+
+	// Signal that a flush has been received, if this is the first.
+	select {
+	case <-fs.flushReceived:
+	default:
+		close(fs.flushReceived)
+	}
+	fs.mu.Unlock()
+
+	// Wait for cancellation if enabled.
+	if shouldBlock {
+		done := ctx.Done()
+		if done == nil {
+			panic("Expected non-nil channel.")
+		}
+
+		<-done
+		err = ctx.Err()
+	}
+
+	return
+}

+ 156 - 0
src/vendor/github.com/jacobsa/fuse/samples/interruptfs/interrupt_fs_test.go

@@ -0,0 +1,156 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package interruptfs_test
+
+import (
+	"bytes"
+	"os"
+	"os/exec"
+	"path"
+	"testing"
+	"time"
+
+	"github.com/jacobsa/fuse/fuseutil"
+	"github.com/jacobsa/fuse/samples"
+	"github.com/jacobsa/fuse/samples/interruptfs"
+	. "github.com/jacobsa/oglematchers"
+	. "github.com/jacobsa/ogletest"
+)
+
+func TestInterruptFS(t *testing.T) { RunTests(t) }
+
+////////////////////////////////////////////////////////////////////////
+// Boilerplate
+////////////////////////////////////////////////////////////////////////
+
+type InterruptFSTest struct {
+	samples.SampleTest
+	fs *interruptfs.InterruptFS
+}
+
+func init() { RegisterTestSuite(&InterruptFSTest{}) }
+
+var _ SetUpInterface = &InterruptFSTest{}
+var _ TearDownInterface = &InterruptFSTest{}
+
+func (t *InterruptFSTest) SetUp(ti *TestInfo) {
+	var err error
+
+	// Create the file system.
+	t.fs = interruptfs.New()
+	AssertEq(nil, err)
+
+	t.Server = fuseutil.NewFileSystemServer(t.fs)
+
+	// Mount it.
+	t.SampleTest.SetUp(ti)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Test functions
+////////////////////////////////////////////////////////////////////////
+
+func (t *InterruptFSTest) StatFoo() {
+	fi, err := os.Stat(path.Join(t.Dir, "foo"))
+	AssertEq(nil, err)
+
+	ExpectEq("foo", fi.Name())
+	ExpectEq(0777, fi.Mode())
+	ExpectFalse(fi.IsDir())
+}
+
+func (t *InterruptFSTest) InterruptedDuringRead() {
+	var err error
+	t.fs.EnableReadBlocking()
+
+	// Start a sub-process that attempts to read the file.
+	cmd := exec.Command("cat", path.Join(t.Dir, "foo"))
+
+	var cmdOutput bytes.Buffer
+	cmd.Stdout = &cmdOutput
+	cmd.Stderr = &cmdOutput
+
+	err = cmd.Start()
+	AssertEq(nil, err)
+
+	// Wait for the command in the background, writing to a channel when it is
+	// finished.
+	cmdErr := make(chan error)
+	go func() {
+		cmdErr <- cmd.Wait()
+	}()
+
+	// Wait for the read to make it to the file system.
+	t.fs.WaitForFirstRead()
+
+	// The command should be hanging on the read, and not yet have returned.
+	select {
+	case err = <-cmdErr:
+		AddFailure("Command returned early with error: %v", err)
+		AbortTest()
+
+	case <-time.After(10 * time.Millisecond):
+	}
+
+	// Send SIGINT.
+	cmd.Process.Signal(os.Interrupt)
+
+	// Now the command should return, with an appropriate error.
+	err = <-cmdErr
+	ExpectThat(err, Error(HasSubstr("signal")))
+	ExpectThat(err, Error(HasSubstr("interrupt")))
+}
+
+func (t *InterruptFSTest) InterruptedDuringFlush() {
+	var err error
+	t.fs.EnableFlushBlocking()
+
+	// Start a sub-process that attempts to read the file.
+	cmd := exec.Command("cat", path.Join(t.Dir, "foo"))
+
+	var cmdOutput bytes.Buffer
+	cmd.Stdout = &cmdOutput
+	cmd.Stderr = &cmdOutput
+
+	err = cmd.Start()
+	AssertEq(nil, err)
+
+	// Wait for the command in the background, writing to a channel when it is
+	// finished.
+	cmdErr := make(chan error)
+	go func() {
+		cmdErr <- cmd.Wait()
+	}()
+
+	// Wait for the flush to make it to the file system.
+	t.fs.WaitForFirstFlush()
+
+	// The command should be hanging on the flush, and not yet have returned.
+	select {
+	case err = <-cmdErr:
+		AddFailure("Command returned early with error: %v", err)
+		AbortTest()
+
+	case <-time.After(10 * time.Millisecond):
+	}
+
+	// Send SIGINT.
+	cmd.Process.Signal(os.Interrupt)
+
+	// Now the command should return, with an appropriate error.
+	err = <-cmdErr
+	ExpectThat(err, Error(HasSubstr("signal")))
+	ExpectThat(err, Error(HasSubstr("interrupt")))
+}

+ 384 - 0
src/vendor/github.com/jacobsa/fuse/samples/memfs/inode.go

@@ -0,0 +1,384 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package memfs
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"time"
+
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+)
+
+// Common attributes for files and directories.
+//
+// External synchronization is required.
+type inode struct {
+	/////////////////////////
+	// Mutable state
+	/////////////////////////
+
+	// The current attributes of this inode.
+	//
+	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
+	// INVARIANT: !(isDir() && isSymlink())
+	// INVARIANT: attrs.Size == len(contents)
+	attrs fuseops.InodeAttributes
+
+	// For directories, entries describing the children of the directory. Unused
+	// entries are of type DT_Unknown.
+	//
+	// This array can never be shortened, nor can its elements be moved, because
+	// we use its indices for Dirent.Offset, which is exposed to the user who
+	// might be calling readdir in a loop while concurrently modifying the
+	// directory. Unused entries can, however, be reused.
+	//
+	// INVARIANT: If !isDir(), len(entries) == 0
+	// INVARIANT: For each i, entries[i].Offset == i+1
+	// INVARIANT: Contains no duplicate names in used entries.
+	entries []fuseutil.Dirent
+
+	// For files, the current contents of the file.
+	//
+	// INVARIANT: If !isFile(), len(contents) == 0
+	contents []byte
+
+	// For symlinks, the target of the symlink.
+	//
+	// INVARIANT: If !isSymlink(), len(target) == 0
+	target string
+
+	// extended attributes and values
+	xattrs map[string][]byte
+}
+
+////////////////////////////////////////////////////////////////////////
+// Helpers
+////////////////////////////////////////////////////////////////////////
+
+// Create a new inode with the supplied attributes, which need not contain
+// time-related information (the inode object will take care of that).
+func newInode(
+	attrs fuseops.InodeAttributes) (in *inode) {
+	// Update time info.
+	now := time.Now()
+	attrs.Mtime = now
+	attrs.Crtime = now
+
+	// Create the object.
+	in = &inode{
+		attrs:  attrs,
+		xattrs: make(map[string][]byte),
+	}
+
+	return
+}
+
+func (in *inode) CheckInvariants() {
+	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
+	if !(in.attrs.Mode&^(os.ModePerm|os.ModeDir|os.ModeSymlink) == 0) {
+		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
+	}
+
+	// INVARIANT: !(isDir() && isSymlink())
+	if in.isDir() && in.isSymlink() {
+		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
+	}
+
+	// INVARIANT: attrs.Size == len(contents)
+	if in.attrs.Size != uint64(len(in.contents)) {
+		panic(fmt.Sprintf(
+			"Size mismatch: %d vs. %d",
+			in.attrs.Size,
+			len(in.contents)))
+	}
+
+	// INVARIANT: If !isDir(), len(entries) == 0
+	if !in.isDir() && len(in.entries) != 0 {
+		panic(fmt.Sprintf("Unexpected entries length: %d", len(in.entries)))
+	}
+
+	// INVARIANT: For each i, entries[i].Offset == i+1
+	for i, e := range in.entries {
+		if !(e.Offset == fuseops.DirOffset(i+1)) {
+			panic(fmt.Sprintf("Unexpected offset for index %d: %d", i, e.Offset))
+		}
+	}
+
+	// INVARIANT: Contains no duplicate names in used entries.
+	childNames := make(map[string]struct{})
+	for _, e := range in.entries {
+		if e.Type != fuseutil.DT_Unknown {
+			if _, ok := childNames[e.Name]; ok {
+				panic(fmt.Sprintf("Duplicate name: %s", e.Name))
+			}
+
+			childNames[e.Name] = struct{}{}
+		}
+	}
+
+	// INVARIANT: If !isFile(), len(contents) == 0
+	if !in.isFile() && len(in.contents) != 0 {
+		panic(fmt.Sprintf("Unexpected length: %d", len(in.contents)))
+	}
+
+	// INVARIANT: If !isSymlink(), len(target) == 0
+	if !in.isSymlink() && len(in.target) != 0 {
+		panic(fmt.Sprintf("Unexpected target length: %d", len(in.target)))
+	}
+
+	return
+}
+
+func (in *inode) isDir() bool {
+	return in.attrs.Mode&os.ModeDir != 0
+}
+
+func (in *inode) isSymlink() bool {
+	return in.attrs.Mode&os.ModeSymlink != 0
+}
+
+func (in *inode) isFile() bool {
+	return !(in.isDir() || in.isSymlink())
+}
+
+// Return the index of the child within in.entries, if it exists.
+//
+// REQUIRES: in.isDir()
+func (in *inode) findChild(name string) (i int, ok bool) {
+	if !in.isDir() {
+		panic("findChild called on non-directory.")
+	}
+
+	var e fuseutil.Dirent
+	for i, e = range in.entries {
+		if e.Name == name {
+			ok = true
+			return
+		}
+	}
+
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// Public methods
+////////////////////////////////////////////////////////////////////////
+
+// Return the number of children of the directory.
+//
+// REQUIRES: in.isDir()
+func (in *inode) Len() (n int) {
+	for _, e := range in.entries {
+		if e.Type != fuseutil.DT_Unknown {
+			n++
+		}
+	}
+
+	return
+}
+
+// Find an entry for the given child name and return its inode ID.
+//
+// REQUIRES: in.isDir()
+func (in *inode) LookUpChild(name string) (
+	id fuseops.InodeID,
+	typ fuseutil.DirentType,
+	ok bool) {
+	index, ok := in.findChild(name)
+	if ok {
+		id = in.entries[index].Inode
+		typ = in.entries[index].Type
+	}
+
+	return
+}
+
+// Add an entry for a child.
+//
+// REQUIRES: in.isDir()
+// REQUIRES: dt != fuseutil.DT_Unknown
+func (in *inode) AddChild(
+	id fuseops.InodeID,
+	name string,
+	dt fuseutil.DirentType) {
+	var index int
+
+	// Update the modification time.
+	in.attrs.Mtime = time.Now()
+
+	// No matter where we place the entry, make sure it has the correct Offset
+	// field.
+	defer func() {
+		in.entries[index].Offset = fuseops.DirOffset(index + 1)
+	}()
+
+	// Set up the entry.
+	e := fuseutil.Dirent{
+		Inode: id,
+		Name:  name,
+		Type:  dt,
+	}
+
+	// Look for a gap in which we can insert it.
+	for index = range in.entries {
+		if in.entries[index].Type == fuseutil.DT_Unknown {
+			in.entries[index] = e
+			return
+		}
+	}
+
+	// Append it to the end.
+	index = len(in.entries)
+	in.entries = append(in.entries, e)
+}
+
+// Remove an entry for a child.
+//
+// REQUIRES: in.isDir()
+// REQUIRES: An entry for the given name exists.
+func (in *inode) RemoveChild(name string) {
+	// Update the modification time.
+	in.attrs.Mtime = time.Now()
+
+	// Find the entry.
+	i, ok := in.findChild(name)
+	if !ok {
+		panic(fmt.Sprintf("Unknown child: %s", name))
+	}
+
+	// Mark it as unused.
+	in.entries[i] = fuseutil.Dirent{
+		Type:   fuseutil.DT_Unknown,
+		Offset: fuseops.DirOffset(i + 1),
+	}
+}
+
+// Serve a ReadDir request.
+//
+// REQUIRES: in.isDir()
+func (in *inode) ReadDir(p []byte, offset int) (n int) {
+	if !in.isDir() {
+		panic("ReadDir called on non-directory.")
+	}
+
+	for i := offset; i < len(in.entries); i++ {
+		e := in.entries[i]
+
+		// Skip unused entries.
+		if e.Type == fuseutil.DT_Unknown {
+			continue
+		}
+
+		tmp := fuseutil.WriteDirent(p[n:], in.entries[i])
+		if tmp == 0 {
+			break
+		}
+
+		n += tmp
+	}
+
+	return
+}
+
+// Read from the file's contents. See documentation for ioutil.ReaderAt.
+//
+// REQUIRES: in.isFile()
+func (in *inode) ReadAt(p []byte, off int64) (n int, err error) {
+	if !in.isFile() {
+		panic("ReadAt called on non-file.")
+	}
+
+	// Ensure the offset is in range.
+	if off > int64(len(in.contents)) {
+		err = io.EOF
+		return
+	}
+
+	// Read what we can.
+	n = copy(p, in.contents[off:])
+	if n < len(p) {
+		err = io.EOF
+	}
+
+	return
+}
+
+// Write to the file's contents. See documentation for ioutil.WriterAt.
+//
+// REQUIRES: in.isFile()
+func (in *inode) WriteAt(p []byte, off int64) (n int, err error) {
+	if !in.isFile() {
+		panic("WriteAt called on non-file.")
+	}
+
+	// Update the modification time.
+	in.attrs.Mtime = time.Now()
+
+	// Ensure that the contents slice is long enough.
+	newLen := int(off) + len(p)
+	if len(in.contents) < newLen {
+		padding := make([]byte, newLen-len(in.contents))
+		in.contents = append(in.contents, padding...)
+		in.attrs.Size = uint64(newLen)
+	}
+
+	// Copy in the data.
+	n = copy(in.contents[off:], p)
+
+	// Sanity check.
+	if n != len(p) {
+		panic(fmt.Sprintf("Unexpected short copy: %v", n))
+	}
+
+	return
+}
+
+// Update attributes from non-nil parameters.
+func (in *inode) SetAttributes(
+	size *uint64,
+	mode *os.FileMode,
+	mtime *time.Time) {
+	// Update the modification time.
+	in.attrs.Mtime = time.Now()
+
+	// Truncate?
+	if size != nil {
+		intSize := int(*size)
+
+		// Update contents.
+		if intSize <= len(in.contents) {
+			in.contents = in.contents[:intSize]
+		} else {
+			padding := make([]byte, intSize-len(in.contents))
+			in.contents = append(in.contents, padding...)
+		}
+
+		// Update attributes.
+		in.attrs.Size = *size
+	}
+
+	// Change mode?
+	if mode != nil {
+		in.attrs.Mode = *mode
+	}
+
+	// Change mtime?
+	if mtime != nil {
+		in.attrs.Mtime = *mtime
+	}
+}

+ 716 - 0
src/vendor/github.com/jacobsa/fuse/samples/memfs/memfs.go

@@ -0,0 +1,716 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package memfs
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"syscall"
+	"time"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+	"github.com/jacobsa/syncutil"
+)
+
+type memFS struct {
+	fuseutil.NotImplementedFileSystem
+
+	// The UID and GID that every inode receives.
+	uid uint32
+	gid uint32
+
+	/////////////////////////
+	// Mutable state
+	/////////////////////////
+
+	mu syncutil.InvariantMutex
+
+	// The collection of live inodes, indexed by ID. IDs of free inodes that may
+	// be re-used have nil entries. No ID less than fuseops.RootInodeID is ever
+	// used.
+	//
+	// All inodes are protected by the file system mutex.
+	//
+	// INVARIANT: For each inode in, in.CheckInvariants() does not panic.
+	// INVARIANT: len(inodes) > fuseops.RootInodeID
+	// INVARIANT: For all i < fuseops.RootInodeID, inodes[i] == nil
+	// INVARIANT: inodes[fuseops.RootInodeID] != nil
+	// INVARIANT: inodes[fuseops.RootInodeID].isDir()
+	inodes []*inode // GUARDED_BY(mu)
+
+	// A list of inode IDs within inodes available for reuse, not including the
+	// reserved IDs less than fuseops.RootInodeID.
+	//
+	// INVARIANT: This is all and only indices i of 'inodes' such that i >
+	// fuseops.RootInodeID and inodes[i] == nil
+	freeInodes []fuseops.InodeID // GUARDED_BY(mu)
+}
+
+// Create a file system that stores data and metadata in memory.
+//
+// The supplied UID/GID pair will own the root inode. This file system does no
+// permissions checking, and should therefore be mounted with the
+// default_permissions option.
+func NewMemFS(
+	uid uint32,
+	gid uint32) fuse.Server {
+	// Set up the basic struct.
+	fs := &memFS{
+		inodes: make([]*inode, fuseops.RootInodeID+1),
+		uid:    uid,
+		gid:    gid,
+	}
+
+	// Set up the root inode.
+	rootAttrs := fuseops.InodeAttributes{
+		Mode: 0700 | os.ModeDir,
+		Uid:  uid,
+		Gid:  gid,
+	}
+
+	fs.inodes[fuseops.RootInodeID] = newInode(rootAttrs)
+
+	// Set up invariant checking.
+	fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)
+
+	return fuseutil.NewFileSystemServer(fs)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Helpers
+////////////////////////////////////////////////////////////////////////
+
+func (fs *memFS) checkInvariants() {
+	// Check reserved inodes.
+	for i := 0; i < fuseops.RootInodeID; i++ {
+		if fs.inodes[i] != nil {
+			panic(fmt.Sprintf("Non-nil inode for ID: %v", i))
+		}
+	}
+
+	// Check the root inode.
+	if !fs.inodes[fuseops.RootInodeID].isDir() {
+		panic("Expected root to be a directory.")
+	}
+
+	// Build our own list of free IDs.
+	freeIDsEncountered := make(map[fuseops.InodeID]struct{})
+	for i := fuseops.RootInodeID + 1; i < len(fs.inodes); i++ {
+		inode := fs.inodes[i]
+		if inode == nil {
+			freeIDsEncountered[fuseops.InodeID(i)] = struct{}{}
+			continue
+		}
+	}
+
+	// Check fs.freeInodes.
+	if len(fs.freeInodes) != len(freeIDsEncountered) {
+		panic(
+			fmt.Sprintf(
+				"Length mismatch: %v vs. %v",
+				len(fs.freeInodes),
+				len(freeIDsEncountered)))
+	}
+
+	for _, id := range fs.freeInodes {
+		if _, ok := freeIDsEncountered[id]; !ok {
+			panic(fmt.Sprintf("Unexected free inode ID: %v", id))
+		}
+	}
+
+	// INVARIANT: For each inode in, in.CheckInvariants() does not panic.
+	for _, in := range fs.inodes {
+		in.CheckInvariants()
+	}
+}
+
+// Find the given inode. Panic if it doesn't exist.
+//
+// LOCKS_REQUIRED(fs.mu)
+func (fs *memFS) getInodeOrDie(id fuseops.InodeID) (inode *inode) {
+	inode = fs.inodes[id]
+	if inode == nil {
+		panic(fmt.Sprintf("Unknown inode: %v", id))
+	}
+
+	return
+}
+
+// Allocate a new inode, assigning it an ID that is not in use.
+//
+// LOCKS_REQUIRED(fs.mu)
+func (fs *memFS) allocateInode(
+	attrs fuseops.InodeAttributes) (id fuseops.InodeID, inode *inode) {
+	// Create the inode.
+	inode = newInode(attrs)
+
+	// Re-use a free ID if possible. Otherwise mint a new one.
+	numFree := len(fs.freeInodes)
+	if numFree != 0 {
+		id = fs.freeInodes[numFree-1]
+		fs.freeInodes = fs.freeInodes[:numFree-1]
+		fs.inodes[id] = inode
+	} else {
+		id = fuseops.InodeID(len(fs.inodes))
+		fs.inodes = append(fs.inodes, inode)
+	}
+
+	return
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *memFS) deallocateInode(id fuseops.InodeID) {
+	fs.freeInodes = append(fs.freeInodes, id)
+	fs.inodes[id] = nil
+}
+
+////////////////////////////////////////////////////////////////////////
+// FileSystem methods
+////////////////////////////////////////////////////////////////////////
+
+func (fs *memFS) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	return
+}
+
+func (fs *memFS) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Grab the parent directory.
+	inode := fs.getInodeOrDie(op.Parent)
+
+	// Does the directory have an entry with the given name?
+	childID, _, ok := inode.LookUpChild(op.Name)
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+
+	// Grab the child.
+	child := fs.getInodeOrDie(childID)
+
+	// Fill in the response.
+	op.Entry.Child = childID
+	op.Entry.Attributes = child.attrs
+
+	// We don't spontaneously mutate, so the kernel can cache as long as it wants
+	// (since it also handles invalidation).
+	op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
+	op.Entry.EntryExpiration = op.Entry.EntryExpiration
+
+	return
+}
+
+func (fs *memFS) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Grab the inode.
+	inode := fs.getInodeOrDie(op.Inode)
+
+	// Fill in the response.
+	op.Attributes = inode.attrs
+
+	// We don't spontaneously mutate, so the kernel can cache as long as it wants
+	// (since it also handles invalidation).
+	op.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
+
+	return
+}
+
+func (fs *memFS) SetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.SetInodeAttributesOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Grab the inode.
+	inode := fs.getInodeOrDie(op.Inode)
+
+	// Handle the request.
+	inode.SetAttributes(op.Size, op.Mode, op.Mtime)
+
+	// Fill in the response.
+	op.Attributes = inode.attrs
+
+	// We don't spontaneously mutate, so the kernel can cache as long as it wants
+	// (since it also handles invalidation).
+	op.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
+
+	return
+}
+
+func (fs *memFS) MkDir(
+	ctx context.Context,
+	op *fuseops.MkDirOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Grab the parent, which we will update shortly.
+	parent := fs.getInodeOrDie(op.Parent)
+
+	// Ensure that the name doesn't already exist, so we don't wind up with a
+	// duplicate.
+	_, _, exists := parent.LookUpChild(op.Name)
+	if exists {
+		err = fuse.EEXIST
+		return
+	}
+
+	// Set up attributes from the child.
+	childAttrs := fuseops.InodeAttributes{
+		Nlink: 1,
+		Mode:  op.Mode,
+		Uid:   fs.uid,
+		Gid:   fs.gid,
+	}
+
+	// Allocate a child.
+	childID, child := fs.allocateInode(childAttrs)
+
+	// Add an entry in the parent.
+	parent.AddChild(childID, op.Name, fuseutil.DT_Directory)
+
+	// Fill in the response.
+	op.Entry.Child = childID
+	op.Entry.Attributes = child.attrs
+
+	// We don't spontaneously mutate, so the kernel can cache as long as it wants
+	// (since it also handles invalidation).
+	op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
+	op.Entry.EntryExpiration = op.Entry.EntryExpiration
+
+	return
+}
+
+func (fs *memFS) MkNode(
+	ctx context.Context,
+	op *fuseops.MkNodeOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode)
+	return
+}
+
+// LOCKS_REQUIRED(fs.mu)
+func (fs *memFS) createFile(
+	parentID fuseops.InodeID,
+	name string,
+	mode os.FileMode) (entry fuseops.ChildInodeEntry, err error) {
+	// Grab the parent, which we will update shortly.
+	parent := fs.getInodeOrDie(parentID)
+
+	// Ensure that the name doesn't already exist, so we don't wind up with a
+	// duplicate.
+	_, _, exists := parent.LookUpChild(name)
+	if exists {
+		err = fuse.EEXIST
+		return
+	}
+
+	// Set up attributes for the child.
+	now := time.Now()
+	childAttrs := fuseops.InodeAttributes{
+		Nlink:  1,
+		Mode:   mode,
+		Atime:  now,
+		Mtime:  now,
+		Ctime:  now,
+		Crtime: now,
+		Uid:    fs.uid,
+		Gid:    fs.gid,
+	}
+
+	// Allocate a child.
+	childID, child := fs.allocateInode(childAttrs)
+
+	// Add an entry in the parent.
+	parent.AddChild(childID, name, fuseutil.DT_File)
+
+	// Fill in the response entry.
+	entry.Child = childID
+	entry.Attributes = child.attrs
+
+	// We don't spontaneously mutate, so the kernel can cache as long as it wants
+	// (since it also handles invalidation).
+	entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
+	entry.EntryExpiration = entry.AttributesExpiration
+
+	return
+}
+
+func (fs *memFS) CreateFile(
+	ctx context.Context,
+	op *fuseops.CreateFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode)
+	return
+}
+
+func (fs *memFS) CreateSymlink(
+	ctx context.Context,
+	op *fuseops.CreateSymlinkOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Grab the parent, which we will update shortly.
+	parent := fs.getInodeOrDie(op.Parent)
+
+	// Ensure that the name doesn't already exist, so we don't wind up with a
+	// duplicate.
+	_, _, exists := parent.LookUpChild(op.Name)
+	if exists {
+		err = fuse.EEXIST
+		return
+	}
+
+	// Set up attributes from the child.
+	now := time.Now()
+	childAttrs := fuseops.InodeAttributes{
+		Nlink:  1,
+		Mode:   0444 | os.ModeSymlink,
+		Atime:  now,
+		Mtime:  now,
+		Ctime:  now,
+		Crtime: now,
+		Uid:    fs.uid,
+		Gid:    fs.gid,
+	}
+
+	// Allocate a child.
+	childID, child := fs.allocateInode(childAttrs)
+
+	// Set up its target.
+	child.target = op.Target
+
+	// Add an entry in the parent.
+	parent.AddChild(childID, op.Name, fuseutil.DT_Link)
+
+	// Fill in the response entry.
+	op.Entry.Child = childID
+	op.Entry.Attributes = child.attrs
+
+	// We don't spontaneously mutate, so the kernel can cache as long as it wants
+	// (since it also handles invalidation).
+	op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
+	op.Entry.EntryExpiration = op.Entry.EntryExpiration
+
+	return
+}
+
+func (fs *memFS) Rename(
+	ctx context.Context,
+	op *fuseops.RenameOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Ask the old parent for the child's inode ID and type.
+	oldParent := fs.getInodeOrDie(op.OldParent)
+	childID, childType, ok := oldParent.LookUpChild(op.OldName)
+
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+
+	// If the new name exists already in the new parent, make sure it's not a
+	// non-empty directory, then delete it.
+	newParent := fs.getInodeOrDie(op.NewParent)
+	existingID, _, ok := newParent.LookUpChild(op.NewName)
+	if ok {
+		existing := fs.getInodeOrDie(existingID)
+
+		var buf [4096]byte
+		if existing.isDir() && existing.ReadDir(buf[:], 0) > 0 {
+			err = fuse.ENOTEMPTY
+			return
+		}
+
+		newParent.RemoveChild(op.NewName)
+	}
+
+	// Link the new name.
+	newParent.AddChild(
+		childID,
+		op.NewName,
+		childType)
+
+	// Finally, remove the old name from the old parent.
+	oldParent.RemoveChild(op.OldName)
+
+	return
+}
+
+func (fs *memFS) RmDir(
+	ctx context.Context,
+	op *fuseops.RmDirOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Grab the parent, which we will update shortly.
+	parent := fs.getInodeOrDie(op.Parent)
+
+	// Find the child within the parent.
+	childID, _, ok := parent.LookUpChild(op.Name)
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+
+	// Grab the child.
+	child := fs.getInodeOrDie(childID)
+
+	// Make sure the child is empty.
+	if child.Len() != 0 {
+		err = fuse.ENOTEMPTY
+		return
+	}
+
+	// Remove the entry within the parent.
+	parent.RemoveChild(op.Name)
+
+	// Mark the child as unlinked.
+	child.attrs.Nlink--
+
+	return
+}
+
+func (fs *memFS) Unlink(
+	ctx context.Context,
+	op *fuseops.UnlinkOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Grab the parent, which we will update shortly.
+	parent := fs.getInodeOrDie(op.Parent)
+
+	// Find the child within the parent.
+	childID, _, ok := parent.LookUpChild(op.Name)
+	if !ok {
+		err = fuse.ENOENT
+		return
+	}
+
+	// Grab the child.
+	child := fs.getInodeOrDie(childID)
+
+	// Remove the entry within the parent.
+	parent.RemoveChild(op.Name)
+
+	// Mark the child as unlinked.
+	child.attrs.Nlink--
+
+	return
+}
+
+func (fs *memFS) OpenDir(
+	ctx context.Context,
+	op *fuseops.OpenDirOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// We don't mutate spontaneosuly, so if the VFS layer has asked for an
+	// inode that doesn't exist, something screwed up earlier (a lookup, a
+	// cache invalidation, etc.).
+	inode := fs.getInodeOrDie(op.Inode)
+
+	if !inode.isDir() {
+		panic("Found non-dir.")
+	}
+
+	return
+}
+
+func (fs *memFS) ReadDir(
+	ctx context.Context,
+	op *fuseops.ReadDirOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Grab the directory.
+	inode := fs.getInodeOrDie(op.Inode)
+
+	// Serve the request.
+	op.BytesRead = inode.ReadDir(op.Dst, int(op.Offset))
+
+	return
+}
+
+func (fs *memFS) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// We don't mutate spontaneosuly, so if the VFS layer has asked for an
+	// inode that doesn't exist, something screwed up earlier (a lookup, a
+	// cache invalidation, etc.).
+	inode := fs.getInodeOrDie(op.Inode)
+
+	if !inode.isFile() {
+		panic("Found non-file.")
+	}
+
+	return
+}
+
+func (fs *memFS) ReadFile(
+	ctx context.Context,
+	op *fuseops.ReadFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Find the inode in question.
+	inode := fs.getInodeOrDie(op.Inode)
+
+	// Serve the request.
+	op.BytesRead, err = inode.ReadAt(op.Dst, op.Offset)
+
+	// Don't return EOF errors; we just indicate EOF to fuse using a short read.
+	if err == io.EOF {
+		err = nil
+	}
+
+	return
+}
+
+func (fs *memFS) WriteFile(
+	ctx context.Context,
+	op *fuseops.WriteFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Find the inode in question.
+	inode := fs.getInodeOrDie(op.Inode)
+
+	// Serve the request.
+	_, err = inode.WriteAt(op.Data, op.Offset)
+
+	return
+}
+
+func (fs *memFS) ReadSymlink(
+	ctx context.Context,
+	op *fuseops.ReadSymlinkOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	// Find the inode in question.
+	inode := fs.getInodeOrDie(op.Inode)
+
+	// Serve the request.
+	op.Target = inode.target
+
+	return
+}
+
+func (fs *memFS) GetXattr(ctx context.Context,
+	op *fuseops.GetXattrOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	inode := fs.getInodeOrDie(op.Inode)
+	if value, ok := inode.xattrs[op.Name]; ok {
+		op.BytesRead = len(value)
+		if len(op.Dst) >= len(value) {
+			copy(op.Dst, value)
+		} else {
+			err = syscall.ERANGE
+		}
+	} else {
+		err = fuse.ENOATTR
+	}
+
+	return
+}
+
+func (fs *memFS) ListXattr(ctx context.Context,
+	op *fuseops.ListXattrOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	inode := fs.getInodeOrDie(op.Inode)
+
+	dst := op.Dst[:]
+	for key := range inode.xattrs {
+		keyLen := len(key) + 1
+
+		if err == nil && len(dst) >= keyLen {
+			copy(dst, key)
+			dst = dst[keyLen:]
+		} else {
+			err = syscall.ERANGE
+		}
+		op.BytesRead += keyLen
+	}
+
+	return
+}
+
+func (fs *memFS) RemoveXattr(ctx context.Context,
+	op *fuseops.RemoveXattrOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+	inode := fs.getInodeOrDie(op.Inode)
+
+	if _, ok := inode.xattrs[op.Name]; ok {
+		delete(inode.xattrs, op.Name)
+	} else {
+		err = fuse.ENOATTR
+	}
+	return
+}
+
+func (fs *memFS) SetXattr(ctx context.Context,
+	op *fuseops.SetXattrOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+	inode := fs.getInodeOrDie(op.Inode)
+
+	_, ok := inode.xattrs[op.Name]
+
+	switch op.Flags {
+	case 0x1:
+		if ok {
+			err = fuse.EEXIST
+		}
+	case 0x2:
+		if !ok {
+			err = fuse.ENOATTR
+		}
+	}
+
+	if err == nil {
+		value := make([]byte, len(op.Value))
+		copy(value, op.Value)
+		inode.xattrs[op.Name] = value
+	}
+
+	return
+}

+ 5 - 0
src/vendor/github.com/jacobsa/fuse/samples/memfs/memfs_go18_test.go

@@ -0,0 +1,5 @@
+// +build go1.8
+
+package memfs_test
+
+const atLeastGo18 = true

+ 5 - 0
src/vendor/github.com/jacobsa/fuse/samples/memfs/memfs_others_test.go

@@ -0,0 +1,5 @@
+// +build !go1.8
+
+package memfs_test
+
+const atLeastGo18 = false

File diff suppressed because it is too large
+ 1781 - 0
src/vendor/github.com/jacobsa/fuse/samples/memfs/memfs_test.go


+ 447 - 0
src/vendor/github.com/jacobsa/fuse/samples/memfs/posix_test.go

@@ -0,0 +1,447 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Tests for the behavior of os.File objects on plain old posix file systems,
+// for use in verifying the intended behavior of memfs.
+
+package memfs_test
+
+import (
+	"io"
+	"io/ioutil"
+	"os"
+	"path"
+	"runtime"
+	"testing"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse/fusetesting"
+	. "github.com/jacobsa/oglematchers"
+	. "github.com/jacobsa/ogletest"
+)
+
+func TestPosix(t *testing.T) { RunTests(t) }
+
+////////////////////////////////////////////////////////////////////////
+// Helpers
+////////////////////////////////////////////////////////////////////////
+
+func getFileOffset(f *os.File) (offset int64, err error) {
+	const relativeToCurrent = 1
+	offset, err = f.Seek(0, relativeToCurrent)
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// Boilerplate
+////////////////////////////////////////////////////////////////////////
+
+type PosixTest struct {
+	ctx context.Context
+
+	// A temporary directory.
+	dir string
+
+	// Files to close when tearing down. Nil entries are skipped.
+	toClose []io.Closer
+}
+
+var _ SetUpInterface = &PosixTest{}
+var _ TearDownInterface = &PosixTest{}
+
+func init() { RegisterTestSuite(&PosixTest{}) }
+
+func (t *PosixTest) SetUp(ti *TestInfo) {
+	var err error
+
+	t.ctx = ti.Ctx
+
+	// Create a temporary directory.
+	t.dir, err = ioutil.TempDir("", "posix_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func (t *PosixTest) TearDown() {
+	// Close any files we opened.
+	for _, c := range t.toClose {
+		if c == nil {
+			continue
+		}
+
+		err := c.Close()
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	// Remove the temporary directory.
+	err := os.RemoveAll(t.dir)
+	if err != nil {
+		panic(err)
+	}
+}
+
+////////////////////////////////////////////////////////////////////////
+// Test functions
+////////////////////////////////////////////////////////////////////////
+
+func (t *PosixTest) WriteOverlapsEndOfFile() {
+	var err error
+	var n int
+
+	// Create a file.
+	f, err := os.Create(path.Join(t.dir, "foo"))
+	t.toClose = append(t.toClose, f)
+	AssertEq(nil, err)
+
+	// Make it 4 bytes long.
+	err = f.Truncate(4)
+	AssertEq(nil, err)
+
+	// Write the range [2, 6).
+	n, err = f.WriteAt([]byte("taco"), 2)
+	AssertEq(nil, err)
+	AssertEq(4, n)
+
+	// Read the full contents of the file.
+	contents, err := ioutil.ReadAll(f)
+	AssertEq(nil, err)
+	ExpectEq("\x00\x00taco", string(contents))
+}
+
+func (t *PosixTest) WriteStartsAtEndOfFile() {
+	var err error
+	var n int
+
+	// Create a file.
+	f, err := os.Create(path.Join(t.dir, "foo"))
+	t.toClose = append(t.toClose, f)
+	AssertEq(nil, err)
+
+	// Make it 2 bytes long.
+	err = f.Truncate(2)
+	AssertEq(nil, err)
+
+	// Write the range [2, 6).
+	n, err = f.WriteAt([]byte("taco"), 2)
+	AssertEq(nil, err)
+	AssertEq(4, n)
+
+	// Read the full contents of the file.
+	contents, err := ioutil.ReadAll(f)
+	AssertEq(nil, err)
+	ExpectEq("\x00\x00taco", string(contents))
+}
+
+func (t *PosixTest) WriteStartsPastEndOfFile() {
+	var err error
+	var n int
+
+	// Create a file.
+	f, err := os.Create(path.Join(t.dir, "foo"))
+	t.toClose = append(t.toClose, f)
+	AssertEq(nil, err)
+
+	// Write the range [2, 6).
+	n, err = f.WriteAt([]byte("taco"), 2)
+	AssertEq(nil, err)
+	AssertEq(4, n)
+
+	// Read the full contents of the file.
+	contents, err := ioutil.ReadAll(f)
+	AssertEq(nil, err)
+	ExpectEq("\x00\x00taco", string(contents))
+}
+
+func (t *PosixTest) WriteStartsPastEndOfFile_AppendMode() {
+	var err error
+	var n int
+
+	// Create a file.
+	f, err := os.OpenFile(
+		path.Join(t.dir, "foo"),
+		os.O_RDWR|os.O_APPEND|os.O_CREATE,
+		0600)
+
+	t.toClose = append(t.toClose, f)
+	AssertEq(nil, err)
+
+	// Write three bytes.
+	n, err = f.Write([]byte("111"))
+	AssertEq(nil, err)
+	AssertEq(3, n)
+
+	// Write at offset six.
+	n, err = f.WriteAt([]byte("222"), 6)
+	AssertEq(nil, err)
+	AssertEq(3, n)
+
+	// Read the full contents of the file.
+	//
+	// Linux's support for pwrite is buggy; the pwrite(2) man page says this:
+	//
+	//     POSIX requires that opening a file with the O_APPEND flag should have
+	//     no affect on the location at which pwrite() writes data.  However, on
+	//     Linux,  if  a  file  is opened with O_APPEND, pwrite() appends data to
+	//     the end of the file, regardless of the value of offset.
+	//
+	contents, err := ioutil.ReadFile(f.Name())
+	AssertEq(nil, err)
+
+	if runtime.GOOS == "linux" {
+		ExpectEq("111222", string(contents))
+	} else {
+		ExpectEq("111\x00\x00\x00222", string(contents))
+	}
+}
+
+func (t *PosixTest) WriteAtDoesntChangeOffset_NotAppendMode() {
+	var err error
+	var n int
+
+	// Create a file.
+	f, err := os.Create(path.Join(t.dir, "foo"))
+	t.toClose = append(t.toClose, f)
+	AssertEq(nil, err)
+
+	// Make it 16 bytes long.
+	err = f.Truncate(16)
+	AssertEq(nil, err)
+
+	// Seek to offset 4.
+	_, err = f.Seek(4, 0)
+	AssertEq(nil, err)
+
+	// Write the range [10, 14).
+	n, err = f.WriteAt([]byte("taco"), 2)
+	AssertEq(nil, err)
+	AssertEq(4, n)
+
+	// We should still be at offset 4.
+	offset, err := getFileOffset(f)
+	AssertEq(nil, err)
+	ExpectEq(4, offset)
+}
+
+func (t *PosixTest) WriteAtDoesntChangeOffset_AppendMode() {
+	var err error
+	var n int
+
+	// Create a file in append mode.
+	f, err := os.OpenFile(
+		path.Join(t.dir, "foo"),
+		os.O_RDWR|os.O_APPEND|os.O_CREATE,
+		0600)
+
+	t.toClose = append(t.toClose, f)
+	AssertEq(nil, err)
+
+	// Make it 16 bytes long.
+	err = f.Truncate(16)
+	AssertEq(nil, err)
+
+	// Seek to offset 4.
+	_, err = f.Seek(4, 0)
+	AssertEq(nil, err)
+
+	// Write the range [10, 14).
+	n, err = f.WriteAt([]byte("taco"), 2)
+	AssertEq(nil, err)
+	AssertEq(4, n)
+
+	// We should still be at offset 4.
+	offset, err := getFileOffset(f)
+	AssertEq(nil, err)
+	ExpectEq(4, offset)
+}
+
+func (t *PosixTest) AppendMode() {
+	var err error
+	var n int
+	var off int64
+	buf := make([]byte, 1024)
+
+	// Create a file with some contents.
+	fileName := path.Join(t.dir, "foo")
+	err = ioutil.WriteFile(fileName, []byte("Jello, "), 0600)
+	AssertEq(nil, err)
+
+	// Open the file in append mode.
+	f, err := os.OpenFile(fileName, os.O_RDWR|os.O_APPEND, 0600)
+	t.toClose = append(t.toClose, f)
+	AssertEq(nil, err)
+
+	// Seek to somewhere silly and then write.
+	off, err = f.Seek(2, 0)
+	AssertEq(nil, err)
+	AssertEq(2, off)
+
+	n, err = f.Write([]byte("world!"))
+	AssertEq(nil, err)
+	AssertEq(6, n)
+
+	// The offset should have been updated to point at the end of the file.
+	off, err = getFileOffset(f)
+	AssertEq(nil, err)
+	ExpectEq(13, off)
+
+	// A random write should still work, without updating the offset.
+	n, err = f.WriteAt([]byte("H"), 0)
+	AssertEq(nil, err)
+	AssertEq(1, n)
+
+	off, err = getFileOffset(f)
+	AssertEq(nil, err)
+	ExpectEq(13, off)
+
+	// Read back the contents of the file, which should be correct even though we
+	// seeked to a silly place before writing the world part.
+	//
+	// Linux's support for pwrite is buggy; the pwrite(2) man page says this:
+	//
+	//     POSIX requires that opening a file with the O_APPEND flag should have
+	//     no affect on the location at which pwrite() writes data.  However, on
+	//     Linux,  if  a  file  is opened with O_APPEND, pwrite() appends data to
+	//     the end of the file, regardless of the value of offset.
+	//
+	// So we allow either the POSIX result or the Linux result.
+	n, err = f.ReadAt(buf, 0)
+	AssertEq(io.EOF, err)
+
+	if runtime.GOOS == "linux" {
+		ExpectEq("Jello, world!H", string(buf[:n]))
+	} else {
+		ExpectEq("Hello, world!", string(buf[:n]))
+	}
+}
+
+func (t *PosixTest) ReadsPastEndOfFile() {
+	var err error
+	var n int
+	buf := make([]byte, 1024)
+
+	// Create a file.
+	f, err := os.Create(path.Join(t.dir, "foo"))
+	t.toClose = append(t.toClose, f)
+	AssertEq(nil, err)
+
+	// Give it some contents.
+	n, err = f.Write([]byte("taco"))
+	AssertEq(nil, err)
+	AssertEq(4, n)
+
+	// Read a range overlapping EOF.
+	n, err = f.ReadAt(buf[:4], 2)
+	AssertEq(io.EOF, err)
+	ExpectEq(2, n)
+	ExpectEq("co", string(buf[:n]))
+
+	// Read a range starting at EOF.
+	n, err = f.ReadAt(buf[:4], 4)
+	AssertEq(io.EOF, err)
+	ExpectEq(0, n)
+	ExpectEq("", string(buf[:n]))
+
+	// Read a range starting past EOF.
+	n, err = f.ReadAt(buf[:4], 100)
+	AssertEq(io.EOF, err)
+	ExpectEq(0, n)
+	ExpectEq("", string(buf[:n]))
+}
+
+func (t *PosixTest) HardLinkDirectory() {
+	dirName := path.Join(t.dir, "dir")
+
+	// Create a directory.
+	err := os.Mkdir(dirName, 0700)
+	AssertEq(nil, err)
+
+	// Attempt to hard-link it to a new name.
+	err = os.Link(dirName, path.Join(t.dir, "other"))
+
+	AssertNe(nil, err)
+	ExpectThat(err, Error(HasSubstr("link")))
+	ExpectThat(err, Error(HasSubstr("not permitted")))
+}
+
+func (t *PosixTest) RmdirWhileOpenedForReading() {
+	var err error
+
+	// Create a directory.
+	err = os.Mkdir(path.Join(t.dir, "dir"), 0700)
+	AssertEq(nil, err)
+
+	// Open the directory for reading.
+	f, err := os.Open(path.Join(t.dir, "dir"))
+	defer func() {
+		if f != nil {
+			ExpectEq(nil, f.Close())
+		}
+	}()
+
+	AssertEq(nil, err)
+
+	// Remove the directory.
+	err = os.Remove(path.Join(t.dir, "dir"))
+	AssertEq(nil, err)
+
+	// Create a new directory, with the same name even, and add some contents
+	// within it.
+	err = os.MkdirAll(path.Join(t.dir, "dir/foo"), 0700)
+	AssertEq(nil, err)
+
+	err = os.MkdirAll(path.Join(t.dir, "dir/bar"), 0700)
+	AssertEq(nil, err)
+
+	err = os.MkdirAll(path.Join(t.dir, "dir/baz"), 0700)
+	AssertEq(nil, err)
+
+	// We should still be able to stat the open file handle.
+	fi, err := f.Stat()
+	ExpectEq("dir", fi.Name())
+
+	// Attempt to read from the directory. This shouldn't see any junk from the
+	// new directory. It should either succeed with an empty result or should
+	// return ENOENT.
+	names, err := f.Readdirnames(0)
+
+	if err != nil {
+		ExpectThat(err, Error(HasSubstr("no such file")))
+	} else {
+		ExpectThat(names, ElementsAre())
+	}
+}
+
+func (t *PosixTest) CreateInParallel_NoTruncate() {
+	fusetesting.RunCreateInParallelTest_NoTruncate(t.ctx, t.dir)
+}
+
+func (t *PosixTest) CreateInParallel_Truncate() {
+	fusetesting.RunCreateInParallelTest_Truncate(t.ctx, t.dir)
+}
+
+func (t *PosixTest) CreateInParallel_Exclusive() {
+	fusetesting.RunCreateInParallelTest_Exclusive(t.ctx, t.dir)
+}
+
+func (t *PosixTest) MkdirInParallel() {
+	fusetesting.RunMkdirInParallelTest(t.ctx, t.dir)
+}
+
+func (t *PosixTest) SymlinkInParallel() {
+	fusetesting.RunSymlinkInParallelTest(t.ctx, t.dir)
+}

+ 163 - 0
src/vendor/github.com/jacobsa/fuse/samples/mount_sample/mount.go

@@ -0,0 +1,163 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// A simple tool for mounting sample file systems, used by the tests in
+// samples/.
+package main
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"runtime"
+	"syscall"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/samples/flushfs"
+	"golang.org/x/net/context"
+)
+
+var fType = flag.String("type", "", "The name of the samples/ sub-dir.")
+var fMountPoint = flag.String("mount_point", "", "Path to mount point.")
+var fReadyFile = flag.Uint64("ready_file", 0, "FD to signal when ready.")
+
+var fFlushesFile = flag.Uint64("flushfs.flushes_file", 0, "")
+var fFsyncsFile = flag.Uint64("flushfs.fsyncs_file", 0, "")
+var fFlushError = flag.Int("flushfs.flush_error", 0, "")
+var fFsyncError = flag.Int("flushfs.fsync_error", 0, "")
+
+var fReadOnly = flag.Bool("read_only", false, "Mount in read-only mode.")
+var fDebug = flag.Bool("debug", false, "Enable debug logging.")
+
+func makeFlushFS() (server fuse.Server, err error) {
+	// Check the flags.
+	if *fFlushesFile == 0 || *fFsyncsFile == 0 {
+		err = fmt.Errorf("You must set the flushfs flags.")
+		return
+	}
+
+	// Set up the files.
+	flushes := os.NewFile(uintptr(*fFlushesFile), "(flushes file)")
+	fsyncs := os.NewFile(uintptr(*fFsyncsFile), "(fsyncs file)")
+
+	// Set up errors.
+	var flushErr error
+	var fsyncErr error
+
+	if *fFlushError != 0 {
+		flushErr = syscall.Errno(*fFlushError)
+	}
+
+	if *fFsyncError != 0 {
+		fsyncErr = syscall.Errno(*fFsyncError)
+	}
+
+	// Report flushes and fsyncs by writing the contents followed by a newline.
+	report := func(f *os.File, outErr error) func(string) error {
+		return func(s string) (err error) {
+			buf := []byte(s)
+			buf = append(buf, '\n')
+
+			_, err = f.Write(buf)
+			if err != nil {
+				err = fmt.Errorf("Write: %v", err)
+				return
+			}
+
+			err = outErr
+			return
+		}
+	}
+
+	reportFlush := report(flushes, flushErr)
+	reportFsync := report(fsyncs, fsyncErr)
+
+	// Create the file system.
+	server, err = flushfs.NewFileSystem(reportFlush, reportFsync)
+
+	return
+}
+
+func makeFS() (server fuse.Server, err error) {
+	switch *fType {
+	default:
+		err = fmt.Errorf("Unknown FS type: %v", *fType)
+
+	case "flushfs":
+		server, err = makeFlushFS()
+	}
+
+	return
+}
+
+func getReadyFile() (f *os.File, err error) {
+	if *fReadyFile == 0 {
+		err = errors.New("You must set --ready_file.")
+		return
+	}
+
+	f = os.NewFile(uintptr(*fReadyFile), "(ready file)")
+	return
+}
+
+func main() {
+	flag.Parse()
+
+	// Allow parallelism in the file system implementation, to help flush out
+	// bugs like https://github.com/jacobsa/fuse/issues/4.
+	runtime.GOMAXPROCS(2)
+
+	// Grab the file to signal when ready.
+	readyFile, err := getReadyFile()
+	if err != nil {
+		log.Fatalf("getReadyFile:", err)
+	}
+
+	// Create an appropriate file system.
+	server, err := makeFS()
+	if err != nil {
+		log.Fatalf("makeFS: %v", err)
+	}
+
+	// Mount the file system.
+	if *fMountPoint == "" {
+		log.Fatalf("You must set --mount_point.")
+	}
+
+	cfg := &fuse.MountConfig{
+		ReadOnly: *fReadOnly,
+	}
+
+	if *fDebug {
+		cfg.DebugLogger = log.New(os.Stderr, "fuse: ", 0)
+	}
+
+	mfs, err := fuse.Mount(*fMountPoint, server, cfg)
+	if err != nil {
+		log.Fatalf("Mount: %v", err)
+	}
+
+	// Signal that it is ready.
+	_, err = readyFile.Write([]byte("x"))
+	if err != nil {
+		log.Fatalf("readyFile.Write: %v", err)
+	}
+
+	// Wait for it to be unmounted.
+	if err = mfs.Join(context.Background()); err != nil {
+		log.Fatalf("Join: %v", err)
+	}
+}

+ 184 - 0
src/vendor/github.com/jacobsa/fuse/samples/statfs/statfs.go

@@ -0,0 +1,184 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package statfs
+
+import (
+	"os"
+	"sync"
+
+	"golang.org/x/net/context"
+
+	"github.com/jacobsa/fuse"
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+)
+
+// A file system that allows orchestrating canned responses to statfs ops, for
+// testng out OS-specific statfs behavior.
+//
+// The file system allows opening and writing to any name that is a child of
+// the root inode, and keeps track of the most recent write size delivered by
+// the kernel (in order to test statfs response block size effects on write
+// size, if any).
+//
+// Safe for concurrent access.
+type FS interface {
+	fuseutil.FileSystem
+
+	// Set the canned response to be used for future statfs ops.
+	SetStatFSResponse(r fuseops.StatFSOp)
+
+	// Set the canned response to be used for future stat ops.
+	SetStatResponse(r fuseops.InodeAttributes)
+
+	// Return the size of the most recent write delivered by the kernel, or -1 if
+	// none.
+	MostRecentWriteSize() int
+}
+
+func New() (fs FS) {
+	fs = &statFS{
+		cannedStatResponse: fuseops.InodeAttributes{
+			Mode: 0666,
+		},
+		mostRecentWriteSize: -1,
+	}
+
+	return
+}
+
+const childInodeID = fuseops.RootInodeID + 1
+
+type statFS struct {
+	fuseutil.NotImplementedFileSystem
+
+	mu                  sync.Mutex
+	cannedResponse      fuseops.StatFSOp        // GUARDED_BY(mu)
+	cannedStatResponse  fuseops.InodeAttributes // GUARDED_BY(mu)
+	mostRecentWriteSize int                     // GUARDED_BY(mu)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Helpers
+////////////////////////////////////////////////////////////////////////
+
+func dirAttrs() fuseops.InodeAttributes {
+	return fuseops.InodeAttributes{
+		Mode: os.ModeDir | 0777,
+	}
+}
+
+func (fs *statFS) fileAttrs() fuseops.InodeAttributes {
+	return fs.cannedStatResponse
+}
+
+////////////////////////////////////////////////////////////////////////
+// Public interface
+////////////////////////////////////////////////////////////////////////
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *statFS) SetStatFSResponse(r fuseops.StatFSOp) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	fs.cannedResponse = r
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *statFS) SetStatResponse(r fuseops.InodeAttributes) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	fs.cannedStatResponse = r
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *statFS) MostRecentWriteSize() int {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	return fs.mostRecentWriteSize
+}
+
+////////////////////////////////////////////////////////////////////////
+// FileSystem methods
+////////////////////////////////////////////////////////////////////////
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *statFS) StatFS(
+	ctx context.Context,
+	op *fuseops.StatFSOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	*op = fs.cannedResponse
+	return
+}
+
+func (fs *statFS) LookUpInode(
+	ctx context.Context,
+	op *fuseops.LookUpInodeOp) (err error) {
+	// Only the root has children.
+	if op.Parent != fuseops.RootInodeID {
+		err = fuse.ENOENT
+		return
+	}
+
+	op.Entry.Child = childInodeID
+	op.Entry.Attributes = fs.fileAttrs()
+
+	return
+}
+
+func (fs *statFS) GetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.GetInodeAttributesOp) (err error) {
+	switch op.Inode {
+	case fuseops.RootInodeID:
+		op.Attributes = dirAttrs()
+
+	case childInodeID:
+		op.Attributes = fs.fileAttrs()
+
+	default:
+		err = fuse.ENOENT
+	}
+
+	return
+}
+
+func (fs *statFS) SetInodeAttributes(
+	ctx context.Context,
+	op *fuseops.SetInodeAttributesOp) (err error) {
+	// Ignore calls to truncate existing files when opening.
+	return
+}
+
+func (fs *statFS) OpenFile(
+	ctx context.Context,
+	op *fuseops.OpenFileOp) (err error) {
+	return
+}
+
+// LOCKS_EXCLUDED(fs.mu)
+func (fs *statFS) WriteFile(
+	ctx context.Context,
+	op *fuseops.WriteFileOp) (err error) {
+	fs.mu.Lock()
+	defer fs.mu.Unlock()
+
+	fs.mostRecentWriteSize = len(op.Data)
+	return
+}

+ 222 - 0
src/vendor/github.com/jacobsa/fuse/samples/statfs/statfs_darwin_test.go

@@ -0,0 +1,222 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package statfs_test
+
+import (
+	"fmt"
+	"math"
+	"regexp"
+	"syscall"
+
+	"github.com/jacobsa/fuse/fuseops"
+	. "github.com/jacobsa/ogletest"
+)
+
+// Sample output:
+//
+//     Filesystem  1024-blocks Used Available Capacity iused ifree %iused  Mounted on
+//     fake@bucket          32   16        16    50%       0     0  100%   /Users/jacobsa/tmp/mp
+//
+var gDfOutputRegexp = regexp.MustCompile(`^\S+\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s+\d+\s+\d+\s+\d+%.*$`)
+
+////////////////////////////////////////////////////////////////////////
+// Helpers
+////////////////////////////////////////////////////////////////////////
+
+func convertName(in []int8) (s string) {
+	var tmp []byte
+	for _, v := range in {
+		if v == 0 {
+			break
+		}
+
+		tmp = append(tmp, byte(v))
+	}
+
+	s = string(tmp)
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////
+
+func (t *StatFSTest) Syscall_ZeroValues() {
+	var err error
+	var stat syscall.Statfs_t
+
+	// Call without configuring a canned response, meaning the OS will see the
+	// zero value for each field. The assertions below act as documentation for
+	// the OS's behavior in this case.
+	err = syscall.Statfs(t.Dir, &stat)
+	AssertEq(nil, err)
+
+	ExpectEq(4096, stat.Bsize)
+	ExpectEq(65536, stat.Iosize)
+	ExpectEq(0, stat.Blocks)
+	ExpectEq(0, stat.Bfree)
+	ExpectEq(0, stat.Bavail)
+	ExpectEq(0, stat.Files)
+	ExpectEq(0, stat.Ffree)
+	ExpectEq("osxfuse", convertName(stat.Fstypename[:]))
+	ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
+	ExpectEq(fsName, convertName(stat.Mntfromname[:]))
+}
+
+func (t *StatFSTest) Syscall_NonZeroValues() {
+	var err error
+	var stat syscall.Statfs_t
+
+	// Set up the canned response.
+	canned := fuseops.StatFSOp{
+		BlockSize: 1 << 15,
+		IoSize:    1 << 16,
+
+		Blocks:          1<<51 + 3,
+		BlocksFree:      1<<43 + 5,
+		BlocksAvailable: 1<<41 + 7,
+
+		Inodes:     1<<59 + 11,
+		InodesFree: 1<<58 + 13,
+	}
+
+	t.fs.SetStatFSResponse(canned)
+
+	// Stat.
+	err = syscall.Statfs(t.Dir, &stat)
+	AssertEq(nil, err)
+
+	ExpectEq(canned.BlockSize, stat.Bsize)
+	ExpectEq(canned.IoSize, stat.Iosize)
+	ExpectEq(canned.Blocks, stat.Blocks)
+	ExpectEq(canned.BlocksFree, stat.Bfree)
+	ExpectEq(canned.BlocksAvailable, stat.Bavail)
+	ExpectEq(canned.Inodes, stat.Files)
+	ExpectEq(canned.InodesFree, stat.Ffree)
+	ExpectEq("osxfuse", convertName(stat.Fstypename[:]))
+	ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
+	ExpectEq(fsName, convertName(stat.Mntfromname[:]))
+}
+
+func (t *StatFSTest) BlockSizes() {
+	var err error
+
+	// Test a bunch of block sizes that the OS does or doesn't support
+	// faithfully, checking what it transforms them too.
+	testCases := []struct {
+		fsBlockSize   uint32
+		expectedBsize uint32
+	}{
+		0:  {0, 4096},
+		1:  {1, 128},
+		2:  {3, 128},
+		3:  {511, 512},
+		4:  {512, 512},
+		5:  {513, 1024},
+		6:  {1023, 1024},
+		7:  {1024, 1024},
+		8:  {4095, 4096},
+		9:  {1 << 16, 1 << 16},
+		10: {1 << 17, 1 << 17},
+		11: {1 << 18, 1 << 18},
+		12: {1 << 19, 1 << 19},
+
+		13: {1<<20 - 1, 1 << 20},
+		14: {1 << 20, 1 << 20},
+		15: {1<<20 + 1, 1 << 20},
+
+		16: {1 << 21, 1 << 20},
+		17: {1 << 22, 1 << 20},
+
+		18: {math.MaxInt32 - 1, 1 << 20},
+		19: {math.MaxInt32, 1 << 20},
+		20: {math.MaxInt32 + 1, 128},
+		21: {math.MaxInt32 + 1<<15, 1 << 15},
+		22: {math.MaxUint32, 1 << 20},
+	}
+
+	for i, tc := range testCases {
+		desc := fmt.Sprintf("Case %d: block size %d", i, tc.fsBlockSize)
+
+		// Set up.
+		canned := fuseops.StatFSOp{
+			BlockSize: tc.fsBlockSize,
+			Blocks:    10,
+		}
+
+		t.fs.SetStatFSResponse(canned)
+
+		// Check.
+		var stat syscall.Statfs_t
+		err = syscall.Statfs(t.Dir, &stat)
+		AssertEq(nil, err)
+
+		ExpectEq(tc.expectedBsize, stat.Bsize, "%s", desc)
+	}
+}
+
+func (t *StatFSTest) IoSizes() {
+	var err error
+
+	// Test a bunch of io sizes that the OS does or doesn't support faithfully,
+	// checking what it transforms them too.
+	testCases := []struct {
+		fsIoSize       uint32
+		expectedIosize uint32
+	}{
+		0: {0, 65536},
+		1: {1, 4096},
+		2: {3, 4096},
+		3: {4095, 4096},
+		4: {4096, 4096},
+		5: {4097, 8192},
+		6: {8191, 8192},
+		7: {8192, 8192},
+		8: {8193, 16384},
+
+		9:  {1 << 18, 1 << 18},
+		10: {1 << 20, 1 << 20},
+		11: {1 << 23, 1 << 23},
+
+		12: {1<<25 - 1, 1 << 25},
+		13: {1 << 25, 1 << 25},
+		14: {1<<25 + 1, 1 << 25},
+
+		15: {math.MaxInt32 - 1, 1 << 25},
+		16: {math.MaxInt32, 1 << 25},
+		17: {math.MaxInt32 + 1, 4096},
+		18: {math.MaxInt32 + 1<<15, 1 << 15},
+		19: {math.MaxUint32, 1 << 25},
+	}
+
+	for i, tc := range testCases {
+		desc := fmt.Sprintf("Case %d: IO size %d", i, tc.fsIoSize)
+
+		// Set up.
+		canned := fuseops.StatFSOp{
+			IoSize: tc.fsIoSize,
+			Blocks: 10,
+		}
+
+		t.fs.SetStatFSResponse(canned)
+
+		// Check.
+		var stat syscall.Statfs_t
+		err = syscall.Statfs(t.Dir, &stat)
+		AssertEq(nil, err)
+
+		ExpectEq(tc.expectedIosize, stat.Iosize, "%s", desc)
+	}
+}

+ 161 - 0
src/vendor/github.com/jacobsa/fuse/samples/statfs/statfs_linux_test.go

@@ -0,0 +1,161 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package statfs_test
+
+import (
+	"fmt"
+	"math"
+	"regexp"
+	"syscall"
+
+	"github.com/jacobsa/fuse/fuseops"
+	. "github.com/jacobsa/ogletest"
+)
+
+// Sample output:
+//
+//     Filesystem                  1K-blocks Used Available Use% Mounted on
+//     some_fuse_file_system       512       64   384       15%  /tmp/sample_test001288095
+//
+var gDfOutputRegexp = regexp.MustCompile(`^\S+\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%.*$`)
+
+////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////
+
+func (t *StatFSTest) Syscall_ZeroValues() {
+	var err error
+	var stat syscall.Statfs_t
+
+	// Call without configuring a canned response, meaning the OS will see the
+	// zero value for each field. The assertions below act as documentation for
+	// the OS's behavior in this case.
+	err = syscall.Statfs(t.Dir, &stat)
+	AssertEq(nil, err)
+
+	ExpectEq(0, stat.Bsize)
+	ExpectEq(0, stat.Frsize)
+	ExpectEq(0, stat.Blocks)
+	ExpectEq(0, stat.Bfree)
+	ExpectEq(0, stat.Bavail)
+	ExpectEq(0, stat.Files)
+	ExpectEq(0, stat.Ffree)
+}
+
+func (t *StatFSTest) Syscall_NonZeroValues() {
+	var err error
+	var stat syscall.Statfs_t
+
+	// Set up the canned response.
+	canned := fuseops.StatFSOp{
+		BlockSize: 1 << 15,
+		IoSize:    1 << 16,
+
+		Blocks:          1<<51 + 3,
+		BlocksFree:      1<<43 + 5,
+		BlocksAvailable: 1<<41 + 7,
+
+		Inodes:     1<<59 + 11,
+		InodesFree: 1<<58 + 13,
+	}
+
+	t.fs.SetStatFSResponse(canned)
+
+	// Stat.
+	err = syscall.Statfs(t.Dir, &stat)
+	AssertEq(nil, err)
+
+	ExpectEq(canned.BlockSize, stat.Frsize)
+	ExpectEq(canned.IoSize, stat.Bsize)
+	ExpectEq(canned.Blocks, stat.Blocks)
+	ExpectEq(canned.BlocksFree, stat.Bfree)
+	ExpectEq(canned.BlocksAvailable, stat.Bavail)
+	ExpectEq(canned.Inodes, stat.Files)
+	ExpectEq(canned.InodesFree, stat.Ffree)
+}
+
+func (t *StatFSTest) BlockSizes() {
+	var err error
+
+	// Test a bunch of weird block sizes that OS X would be cranky about.
+	blockSizes := []uint32{
+		0,
+		1,
+		3,
+		17,
+		1<<20 - 1,
+		1<<20 + 0,
+		1<<20 + 1,
+		math.MaxInt32,
+		math.MaxInt32 + 1,
+		math.MaxUint32,
+	}
+
+	for _, bs := range blockSizes {
+		desc := fmt.Sprintf("block size %d", bs)
+
+		// Set up.
+		canned := fuseops.StatFSOp{
+			BlockSize: bs,
+			Blocks:    10,
+		}
+
+		t.fs.SetStatFSResponse(canned)
+
+		// Check.
+		var stat syscall.Statfs_t
+		err = syscall.Statfs(t.Dir, &stat)
+		AssertEq(nil, err)
+
+		ExpectEq(bs, stat.Frsize, "%s", desc)
+	}
+}
+
+func (t *StatFSTest) IoSizes() {
+	var err error
+
+	// Test a bunch of weird IO sizes that OS X would be cranky about.
+	ioSizes := []uint32{
+		0,
+		1,
+		3,
+		17,
+		1<<20 - 1,
+		1<<20 + 0,
+		1<<20 + 1,
+		math.MaxInt32,
+		math.MaxInt32 + 1,
+		math.MaxUint32,
+	}
+
+	for _, bs := range ioSizes {
+		desc := fmt.Sprintf("IO size %d", bs)
+
+		// Set up.
+		canned := fuseops.StatFSOp{
+			IoSize: bs,
+			Blocks: 10,
+		}
+
+		t.fs.SetStatFSResponse(canned)
+
+		// Check.
+		var stat syscall.Statfs_t
+		err = syscall.Statfs(t.Dir, &stat)
+		AssertEq(nil, err)
+
+		ExpectEq(bs, stat.Bsize, "%s", desc)
+	}
+}

+ 228 - 0
src/vendor/github.com/jacobsa/fuse/samples/statfs/statfs_test.go

@@ -0,0 +1,228 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package statfs_test
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os/exec"
+	"path"
+	"path/filepath"
+	"runtime"
+	"strconv"
+	"syscall"
+	"testing"
+
+	"github.com/jacobsa/fuse/fuseops"
+	"github.com/jacobsa/fuse/fuseutil"
+	"github.com/jacobsa/fuse/samples"
+	"github.com/jacobsa/fuse/samples/statfs"
+	. "github.com/jacobsa/ogletest"
+)
+
+func TestStatFS(t *testing.T) { RunTests(t) }
+
+const fsName = "some_fs_name"
+const volumeName = "Some volume"
+
+////////////////////////////////////////////////////////////////////////
+// Helpers
+////////////////////////////////////////////////////////////////////////
+
+// Ask `df` for statistics about the file system's capacity and free space,
+// useful for checking that our reading of statfs(2) output matches the
+// system's. The output is not guaranteed to have resolution greater than 2^10
+// (1 KiB).
+func df(dir string) (capacity, used, available uint64, err error) {
+	// Call df with a block size of 1024 and capture its output.
+	cmd := exec.Command("df", dir)
+	cmd.Env = []string{"BLOCKSIZE=1024"}
+
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		return
+	}
+
+	// Scrape it.
+	for _, line := range bytes.Split(output, []byte{'\n'}) {
+		// Is this the line we're interested in?
+		if !bytes.Contains(line, []byte(dir)) {
+			continue
+		}
+
+		submatches := gDfOutputRegexp.FindSubmatch(line)
+		if submatches == nil {
+			err = fmt.Errorf("Unable to parse line: %q", line)
+			return
+		}
+
+		capacity, err = strconv.ParseUint(string(submatches[1]), 10, 64)
+		if err != nil {
+			return
+		}
+
+		used, err = strconv.ParseUint(string(submatches[2]), 10, 64)
+		if err != nil {
+			return
+		}
+
+		available, err = strconv.ParseUint(string(submatches[3]), 10, 64)
+		if err != nil {
+			return
+		}
+
+		// Scale appropriately based on the BLOCKSIZE set above.
+		capacity *= 1024
+		used *= 1024
+		available *= 1024
+
+		return
+	}
+
+	err = fmt.Errorf("Unable to parse df output:\n%s", output)
+	return
+}
+
+////////////////////////////////////////////////////////////////////////
+// Boilerplate
+////////////////////////////////////////////////////////////////////////
+
+type StatFSTest struct {
+	samples.SampleTest
+	fs statfs.FS
+
+	// t.Dir, with symlinks resolved and redundant path components removed.
+	canonicalDir string
+}
+
+var _ SetUpInterface = &StatFSTest{}
+var _ TearDownInterface = &StatFSTest{}
+
+func init() { RegisterTestSuite(&StatFSTest{}) }
+
+func (t *StatFSTest) SetUp(ti *TestInfo) {
+	var err error
+
+	// Writeback caching can ruin our measurement of the write sizes the kernel
+	// decides to give us, since it causes write acking to race against writes
+	// being issued from the client.
+	t.MountConfig.DisableWritebackCaching = true
+
+	// Configure names.
+	t.MountConfig.FSName = fsName
+	t.MountConfig.VolumeName = volumeName
+
+	// Create the file system.
+	t.fs = statfs.New()
+	t.Server = fuseutil.NewFileSystemServer(t.fs)
+
+	// Mount it.
+	t.SampleTest.SetUp(ti)
+
+	// Canonicalize the mount point.
+	t.canonicalDir, err = filepath.EvalSymlinks(t.Dir)
+	AssertEq(nil, err)
+	t.canonicalDir = path.Clean(t.canonicalDir)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////
+
+func (t *StatFSTest) CapacityAndFreeSpace() {
+	canned := fuseops.StatFSOp{
+		Blocks:          1024,
+		BlocksFree:      896,
+		BlocksAvailable: 768,
+
+		IoSize: 1024, // Shouldn't matter.
+	}
+
+	// Check that df agrees with us about a range of block sizes.
+	for log2BlockSize := uint(9); log2BlockSize <= 17; log2BlockSize++ {
+		bs := uint64(1) << log2BlockSize
+		desc := fmt.Sprintf("block size: %d (2^%d)", bs, log2BlockSize)
+
+		// Set up the canned response.
+		canned.BlockSize = uint32(bs)
+		t.fs.SetStatFSResponse(canned)
+
+		// Call df.
+		capacity, used, available, err := df(t.canonicalDir)
+		AssertEq(nil, err)
+
+		ExpectEq(bs*canned.Blocks, capacity, "%s", desc)
+		ExpectEq(bs*(canned.Blocks-canned.BlocksFree), used, "%s", desc)
+		ExpectEq(bs*canned.BlocksAvailable, available, "%s", desc)
+	}
+}
+
+func (t *StatFSTest) WriteSize() {
+	var err error
+
+	// Set up a smallish block size.
+	canned := fuseops.StatFSOp{
+		BlockSize:       8192,
+		IoSize:          16384,
+		Blocks:          1234,
+		BlocksFree:      1234,
+		BlocksAvailable: 1234,
+	}
+
+	t.fs.SetStatFSResponse(canned)
+
+	// Cause a large amount of date to be written.
+	err = ioutil.WriteFile(
+		path.Join(t.Dir, "foo"),
+		bytes.Repeat([]byte{'x'}, 1<<22),
+		0400)
+
+	AssertEq(nil, err)
+
+	// Despite the small block size, the OS shouldn't have given us pitifully
+	// small chunks of data.
+	switch runtime.GOOS {
+	case "linux":
+		ExpectEq(1<<17, t.fs.MostRecentWriteSize())
+
+	case "darwin":
+		ExpectEq(1<<20, t.fs.MostRecentWriteSize())
+
+	default:
+		AddFailure("Unhandled OS: %s", runtime.GOOS)
+	}
+}
+
+func (t *StatFSTest) StatBlocks() {
+	var err error
+	var stat syscall.Stat_t
+	const fileName = "foo"
+	const size = 1 << 22
+
+	err = ioutil.WriteFile(
+		path.Join(t.Dir, fileName),
+		bytes.Repeat([]byte{'x'}, size),
+		0400)
+	AssertEq(nil, err)
+
+	t.fs.SetStatResponse(fuseops.InodeAttributes{
+		Size: size,
+	})
+
+	err = syscall.Stat(path.Join(t.Dir, fileName), &stat)
+	AssertEq(nil, err)
+	ExpectEq(size/512, stat.Blocks)
+}

+ 366 - 0
src/vendor/github.com/jacobsa/fuse/samples/subprocess.go

@@ -0,0 +1,366 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package samples
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+	"os/exec"
+	"path"
+	"sync"
+
+	"github.com/jacobsa/ogletest"
+	"golang.org/x/net/context"
+)
+
+var fToolPath = flag.String(
+	"mount_sample",
+	"",
+	"Path to the mount_sample tool. If unset, we will compile it.")
+
+var fDebug = flag.Bool("debug", false, "If true, print fuse debug info.")
+
+// A struct that implements common behavior needed by tests in the samples/
+// directory where the file system is mounted by a subprocess. Use it as an
+// embedded field in your test fixture, calling its SetUp method from your
+// SetUp method after setting the MountType and MountFlags fields.
+type SubprocessTest struct {
+	// The type of the file system to mount. Must be recognized by mount_sample.
+	MountType string
+
+	// Additional flags to be passed to the mount_sample tool.
+	MountFlags []string
+
+	// A list of files to pass to mount_sample. The given string flag will be
+	// used to pass the file descriptor number.
+	MountFiles map[string]*os.File
+
+	// A context object that can be used for long-running operations.
+	Ctx context.Context
+
+	// The directory at which the file system is mounted.
+	Dir string
+
+	// Anothing non-nil in this slice will be closed by TearDown. The test will
+	// fail if closing fails.
+	ToClose []io.Closer
+
+	mountSampleErr <-chan error
+}
+
+// Mount the file system and initialize the other exported fields of the
+// struct. Panics on error.
+func (t *SubprocessTest) SetUp(ti *ogletest.TestInfo) {
+	err := t.initialize(ti.Ctx)
+	if err != nil {
+		panic(err)
+	}
+}
+
+// Private state for getToolPath.
+var getToolContents_Contents []byte
+var getToolContents_Err error
+var getToolContents_Once sync.Once
+
+// Implementation detail of getToolPath.
+func getToolContentsImpl() (contents []byte, err error) {
+	// Fast path: has the user set the flag?
+	if *fToolPath != "" {
+		contents, err = ioutil.ReadFile(*fToolPath)
+		if err != nil {
+			err = fmt.Errorf("Reading mount_sample contents: %v", err)
+			return
+		}
+
+		return
+	}
+
+	// Create a temporary directory into which we will compile the tool.
+	tempDir, err := ioutil.TempDir("", "sample_test")
+	if err != nil {
+		err = fmt.Errorf("TempDir: %v", err)
+		return
+	}
+
+	toolPath := path.Join(tempDir, "mount_sample")
+
+	// Ensure that we kill the temporary directory when we're finished here.
+	defer os.RemoveAll(tempDir)
+
+	// Run "go build".
+	cmd := exec.Command(
+		"go",
+		"build",
+		"-o",
+		toolPath,
+		"github.com/jacobsa/fuse/samples/mount_sample")
+
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		err = fmt.Errorf(
+			"mount_sample exited with %v, output:\n%s",
+			err,
+			string(output))
+
+		return
+	}
+
+	// Slurp the tool contents.
+	contents, err = ioutil.ReadFile(toolPath)
+	if err != nil {
+		err = fmt.Errorf("ReadFile: %v", err)
+		return
+	}
+
+	return
+}
+
+// Build the mount_sample tool if it has not yet been built for this process.
+// Return its contents.
+func getToolContents() (contents []byte, err error) {
+	// Get hold of the binary contents, if we haven't yet.
+	getToolContents_Once.Do(func() {
+		getToolContents_Contents, getToolContents_Err = getToolContentsImpl()
+	})
+
+	contents, err = getToolContents_Contents, getToolContents_Err
+	return
+}
+
+func waitForMountSample(
+	cmd *exec.Cmd,
+	errChan chan<- error,
+	stderr *bytes.Buffer) {
+	// However we exit, write the error to the channel.
+	var err error
+	defer func() {
+		errChan <- err
+	}()
+
+	// Wait for the command.
+	err = cmd.Wait()
+	if err == nil {
+		return
+	}
+
+	// Make exit errors nicer.
+	if exitErr, ok := err.(*exec.ExitError); ok {
+		err = fmt.Errorf(
+			"mount_sample exited with %v. Stderr:\n%s",
+			exitErr,
+			stderr.String())
+
+		return
+	}
+
+	err = fmt.Errorf("Waiting for mount_sample: %v", err)
+}
+
+func waitForReady(readyReader *os.File, c chan<- struct{}) {
+	_, err := readyReader.Read(make([]byte, 1))
+	if err != nil {
+		log.Printf("Readying from ready pipe: %v", err)
+		return
+	}
+
+	c <- struct{}{}
+}
+
+// Like SetUp, but doens't panic.
+func (t *SubprocessTest) initialize(ctx context.Context) (err error) {
+	// Initialize the context.
+	t.Ctx = ctx
+
+	// Set up a temporary directory.
+	t.Dir, err = ioutil.TempDir("", "sample_test")
+	if err != nil {
+		err = fmt.Errorf("TempDir: %v", err)
+		return
+	}
+
+	// Build/read the mount_sample tool.
+	toolContents, err := getToolContents()
+	if err != nil {
+		err = fmt.Errorf("getTooltoolContents: %v", err)
+		return
+	}
+
+	// Create a temporary file to hold the contents of the tool.
+	toolFile, err := ioutil.TempFile("", "sample_test")
+	if err != nil {
+		err = fmt.Errorf("TempFile: %v", err)
+		return
+	}
+
+	defer toolFile.Close()
+
+	// Ensure that it is deleted when we leave.
+	toolPath := toolFile.Name()
+	defer os.Remove(toolPath)
+
+	// Write out the tool contents and make them executable.
+	if _, err = toolFile.Write(toolContents); err != nil {
+		err = fmt.Errorf("toolFile.Write: %v", err)
+		return
+	}
+
+	if err = toolFile.Chmod(0500); err != nil {
+		err = fmt.Errorf("toolFile.Chmod: %v", err)
+		return
+	}
+
+	// Close the tool file to prevent "text file busy" errors below.
+	err = toolFile.Close()
+	toolFile = nil
+	if err != nil {
+		err = fmt.Errorf("toolFile.Close: %v", err)
+		return
+	}
+
+	// Set up basic args for the subprocess.
+	args := []string{
+		"--type",
+		t.MountType,
+		"--mount_point",
+		t.Dir,
+	}
+
+	args = append(args, t.MountFlags...)
+
+	// Set up a pipe for the "ready" status.
+	readyReader, readyWriter, err := os.Pipe()
+	if err != nil {
+		err = fmt.Errorf("Pipe: %v", err)
+		return
+	}
+
+	defer readyReader.Close()
+	defer readyWriter.Close()
+
+	t.MountFiles["ready_file"] = readyWriter
+
+	// Set up inherited files and appropriate flags.
+	var extraFiles []*os.File
+	for flag, file := range t.MountFiles {
+		// Cf. os/exec.Cmd.ExtraFiles
+		fd := 3 + len(extraFiles)
+
+		extraFiles = append(extraFiles, file)
+		args = append(args, "--"+flag)
+		args = append(args, fmt.Sprintf("%d", fd))
+	}
+
+	// Set up a command.
+	var stderr bytes.Buffer
+	mountCmd := exec.Command(toolPath, args...)
+	mountCmd.Stderr = &stderr
+	mountCmd.ExtraFiles = extraFiles
+
+	// Handle debug mode.
+	if *fDebug {
+		mountCmd.Stderr = os.Stderr
+		mountCmd.Args = append(mountCmd.Args, "--debug")
+	}
+
+	// Start the command.
+	if err = mountCmd.Start(); err != nil {
+		err = fmt.Errorf("mountCmd.Start: %v", err)
+		return
+	}
+
+	// Launch a goroutine that waits for it and returns its status.
+	mountSampleErr := make(chan error, 1)
+	go waitForMountSample(mountCmd, mountSampleErr, &stderr)
+
+	// Wait for the tool to say the file system is ready. In parallel, watch for
+	// the tool to fail.
+	readyChan := make(chan struct{}, 1)
+	go waitForReady(readyReader, readyChan)
+
+	select {
+	case <-readyChan:
+	case err = <-mountSampleErr:
+		return
+	}
+
+	// TearDown is no responsible for joining.
+	t.mountSampleErr = mountSampleErr
+
+	return
+}
+
+// Unmount the file system and clean up. Panics on error.
+func (t *SubprocessTest) TearDown() {
+	err := t.destroy()
+	if err != nil {
+		panic(err)
+	}
+}
+
+// Like TearDown, but doesn't panic.
+func (t *SubprocessTest) destroy() (err error) {
+	// Make sure we clean up after ourselves after everything else below.
+
+	// Close what is necessary.
+	for _, c := range t.ToClose {
+		if c == nil {
+			continue
+		}
+
+		ogletest.ExpectEq(nil, c.Close())
+	}
+
+	// If we didn't try to mount the file system, there's nothing further to do.
+	if t.mountSampleErr == nil {
+		return
+	}
+
+	// In the background, initiate an unmount.
+	unmountErrChan := make(chan error)
+	go func() {
+		unmountErrChan <- unmount(t.Dir)
+	}()
+
+	// Make sure we wait for the unmount, even if we've already returned early in
+	// error. Return its error if we haven't seen any other error.
+	defer func() {
+		// Wait.
+		unmountErr := <-unmountErrChan
+		if unmountErr != nil {
+			if err != nil {
+				log.Println("unmount:", unmountErr)
+				return
+			}
+
+			err = fmt.Errorf("unmount: %v", unmountErr)
+			return
+		}
+
+		// Clean up.
+		ogletest.ExpectEq(nil, os.Remove(t.Dir))
+	}()
+
+	// Wait for the subprocess.
+	if err = <-t.mountSampleErr; err != nil {
+		return
+	}
+
+	return
+}

+ 48 - 0
src/vendor/github.com/jacobsa/fuse/samples/unmount.go

@@ -0,0 +1,48 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package samples
+
+import (
+	"fmt"
+	"log"
+	"strings"
+	"time"
+
+	"github.com/jacobsa/fuse"
+)
+
+// Unmount the file system mounted at the supplied directory. Try again on
+// "resource busy" errors, which happen from time to time on OS X (due to weird
+// requests from the Finder) and when tests don't or can't synchronize all
+// events.
+func unmount(dir string) (err error) {
+	delay := 10 * time.Millisecond
+	for {
+		err = fuse.Unmount(dir)
+		if err == nil {
+			return
+		}
+
+		if strings.Contains(err.Error(), "resource busy") {
+			log.Println("Resource busy error while unmounting; trying again")
+			time.Sleep(delay)
+			delay = time.Duration(1.3 * float64(delay))
+			continue
+		}
+
+		err = fmt.Errorf("Unmount: %v", err)
+		return
+	}
+}

+ 21 - 0
src/vendor/github.com/jacobsa/fuse/unmount.go

@@ -0,0 +1,21 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuse
+
+// Unmount attempts to unmount the file system whose mount point is the
+// supplied directory.
+func Unmount(dir string) error {
+	return unmount(dir)
+}

+ 23 - 0
src/vendor/github.com/jacobsa/fuse/unmount_linux.go

@@ -0,0 +1,23 @@
+package fuse
+
+import (
+	"bytes"
+	"fmt"
+	"os/exec"
+)
+
+func unmount(dir string) (err error) {
+	// Call fusermount.
+	cmd := exec.Command("fusermount", "-u", dir)
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		if len(output) > 0 {
+			output = bytes.TrimRight(output, "\n")
+			err = fmt.Errorf("%v: %s", err, output)
+		}
+
+		return
+	}
+
+	return
+}

+ 18 - 0
src/vendor/github.com/jacobsa/fuse/unmount_std.go

@@ -0,0 +1,18 @@
+// +build !linux
+
+package fuse
+
+import (
+	"os"
+	"syscall"
+)
+
+func unmount(dir string) (err error) {
+	err = syscall.Unmount(dir, 0)
+	if err != nil {
+		err = &os.PathError{Op: "unmount", Path: dir, Err: err}
+		return
+	}
+
+	return
+}

+ 0 - 1
vendor/src/github.com/jacobsa/fuse

@@ -1 +0,0 @@
-Subproject commit fe7f3a55dcaa3a8f3d5ff6a85b16b62b7a2c446c