mount_config.go 4.67 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 fuse

import (
Aaron Jacobs's avatar
Aaron Jacobs committed
18
	"fmt"
19 20
	"log"
	"runtime"
Aaron Jacobs's avatar
Aaron Jacobs committed
21
	"strings"
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

	"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

	// 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

	// 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"
Aaron Jacobs's avatar
Aaron Jacobs committed
79
	opts = make(map[string]string)
80 81 82

	// Enable permissions checking in the kernel. See the comments on
	// InodeAttributes.Mode.
Aaron Jacobs's avatar
Aaron Jacobs committed
83
	opts["default_permissions"] = ""
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

	// 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 != "" {
Aaron Jacobs's avatar
Aaron Jacobs committed
101
		opts["fsname"] = fsname
102 103 104 105
	}

	// Read only?
	if c.ReadOnly {
Aaron Jacobs's avatar
Aaron Jacobs committed
106
		opts["ro"] = ""
107 108 109 110
	}

	// OS X: set novncache when appropriate.
	if isDarwin && !c.EnableVnodeCaching {
Aaron Jacobs's avatar
Aaron Jacobs committed
111
		opts["novncache"] = ""
112 113 114 115 116 117 118 119
	}

	// 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 {
Aaron Jacobs's avatar
Aaron Jacobs committed
120
		opts["noappledouble"] = ""
121 122 123 124
	}

	// Last but not least: other user-supplied options.
	for k, v := range c.Options {
Aaron Jacobs's avatar
Aaron Jacobs committed
125
		opts[k] = v
126 127 128 129
	}

	return
}
Aaron Jacobs's avatar
Aaron Jacobs committed
130

Aaron Jacobs's avatar
Aaron Jacobs committed
131 132 133 134 135 136 137
func escapeOptionsKey(s string) (res string) {
	res = s
	res = strings.Replace(res, `\`, `\\`, -1)
	res = strings.Replace(res, `,`, `\,`, -1)
	return
}

Aaron Jacobs's avatar
Aaron Jacobs committed
138 139
// Create an options string suitable for passing to the mount helper.
func (c *MountConfig) toOptionsString() string {
Aaron Jacobs's avatar
Aaron Jacobs committed
140 141 142 143 144 145 146 147 148 149 150 151 152
	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, ",")
Aaron Jacobs's avatar
Aaron Jacobs committed
153
}