options.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package fuse
  2. import (
  3. "errors"
  4. "strings"
  5. )
  6. func dummyOption(conf *mountConfig) error {
  7. return nil
  8. }
  9. // mountConfig holds the configuration for a mount operation.
  10. // Use it by passing MountOption values to Mount.
  11. type mountConfig struct {
  12. options map[string]string
  13. maxReadahead uint32
  14. initFlags InitFlags
  15. osxfuseLocations []OSXFUSEPaths
  16. }
  17. func escapeComma(s string) string {
  18. s = strings.Replace(s, `\`, `\\`, -1)
  19. s = strings.Replace(s, `,`, `\,`, -1)
  20. return s
  21. }
  22. // getOptions makes a string of options suitable for passing to FUSE
  23. // mount flag `-o`. Returns an empty string if no options were set.
  24. // Any platform specific adjustments should happen before the call.
  25. func (m *mountConfig) getOptions() string {
  26. var opts []string
  27. for k, v := range m.options {
  28. k = escapeComma(k)
  29. if v != "" {
  30. k += "=" + escapeComma(v)
  31. }
  32. opts = append(opts, k)
  33. }
  34. return strings.Join(opts, ",")
  35. }
  36. type mountOption func(*mountConfig) error
  37. // MountOption is passed to Mount to change the behavior of the mount.
  38. type MountOption mountOption
  39. // FSName sets the file system name (also called source) that is
  40. // visible in the list of mounted file systems.
  41. //
  42. // FreeBSD ignores this option.
  43. func FSName(name string) MountOption {
  44. return func(conf *mountConfig) error {
  45. conf.options["fsname"] = name
  46. return nil
  47. }
  48. }
  49. // Subtype sets the subtype of the mount. The main type is always
  50. // `fuse`. The type in a list of mounted file systems will look like
  51. // `fuse.foo`.
  52. //
  53. // OS X ignores this option.
  54. // FreeBSD ignores this option.
  55. func Subtype(fstype string) MountOption {
  56. return func(conf *mountConfig) error {
  57. conf.options["subtype"] = fstype
  58. return nil
  59. }
  60. }
  61. // LocalVolume sets the volume to be local (instead of network),
  62. // changing the behavior of Finder, Spotlight, and such.
  63. //
  64. // OS X only. Others ignore this option.
  65. func LocalVolume() MountOption {
  66. return localVolume
  67. }
  68. // VolumeName sets the volume name shown in Finder.
  69. //
  70. // OS X only. Others ignore this option.
  71. func VolumeName(name string) MountOption {
  72. return volumeName(name)
  73. }
  74. // NoAppleDouble makes OSXFUSE disallow files with names used by OS X
  75. // to store extended attributes on file systems that do not support
  76. // them natively.
  77. //
  78. // Such file names are:
  79. //
  80. // ._*
  81. // .DS_Store
  82. //
  83. // OS X only. Others ignore this option.
  84. func NoAppleDouble() MountOption {
  85. return noAppleDouble
  86. }
  87. // NoAppleXattr makes OSXFUSE disallow extended attributes with the
  88. // prefix "com.apple.". This disables persistent Finder state and
  89. // other such information.
  90. //
  91. // OS X only. Others ignore this option.
  92. func NoAppleXattr() MountOption {
  93. return noAppleXattr
  94. }
  95. // ExclCreate causes O_EXCL flag to be set for only "truly" exclusive creates,
  96. // i.e. create calls for which the initiator explicitly set the O_EXCL flag.
  97. //
  98. // OSXFUSE expects all create calls to return EEXIST in case the file
  99. // already exists, regardless of whether O_EXCL was specified or not.
  100. // To ensure this behavior, it normally sets OpenExclusive for all
  101. // Create calls, regardless of whether the original call had it set.
  102. // For distributed filesystems, that may force every file create to be
  103. // a distributed consensus action, causing undesirable delays.
  104. //
  105. // This option makes the FUSE filesystem see the original flag value,
  106. // and better decide when to ensure global consensus.
  107. //
  108. // Note that returning EEXIST on existing file create is still
  109. // expected with OSXFUSE, regardless of the presence of the
  110. // OpenExclusive flag.
  111. //
  112. // For more information, see
  113. // https://github.com/osxfuse/osxfuse/issues/209
  114. //
  115. // OS X only. Others ignore this options.
  116. // Requires OSXFUSE 3.4.1 or newer.
  117. func ExclCreate() MountOption {
  118. return exclCreate
  119. }
  120. // DaemonTimeout sets the time in seconds between a request and a reply before
  121. // the FUSE mount is declared dead.
  122. //
  123. // OS X and FreeBSD only. Others ignore this option.
  124. func DaemonTimeout(name string) MountOption {
  125. return daemonTimeout(name)
  126. }
  127. var ErrCannotCombineAllowOtherAndAllowRoot = errors.New("cannot combine AllowOther and AllowRoot")
  128. // AllowOther allows other users to access the file system.
  129. //
  130. // Only one of AllowOther or AllowRoot can be used.
  131. func AllowOther() MountOption {
  132. return func(conf *mountConfig) error {
  133. if _, ok := conf.options["allow_root"]; ok {
  134. return ErrCannotCombineAllowOtherAndAllowRoot
  135. }
  136. conf.options["allow_other"] = ""
  137. return nil
  138. }
  139. }
  140. // AllowRoot allows other users to access the file system.
  141. //
  142. // Only one of AllowOther or AllowRoot can be used.
  143. //
  144. // FreeBSD ignores this option.
  145. func AllowRoot() MountOption {
  146. return func(conf *mountConfig) error {
  147. if _, ok := conf.options["allow_other"]; ok {
  148. return ErrCannotCombineAllowOtherAndAllowRoot
  149. }
  150. conf.options["allow_root"] = ""
  151. return nil
  152. }
  153. }
  154. // AllowDev enables interpreting character or block special devices on the
  155. // filesystem.
  156. func AllowDev() MountOption {
  157. return func(conf *mountConfig) error {
  158. conf.options["dev"] = ""
  159. return nil
  160. }
  161. }
  162. // AllowSUID allows set-user-identifier or set-group-identifier bits to take
  163. // effect.
  164. func AllowSUID() MountOption {
  165. return func(conf *mountConfig) error {
  166. conf.options["suid"] = ""
  167. return nil
  168. }
  169. }
  170. // DefaultPermissions makes the kernel enforce access control based on
  171. // the file mode (as in chmod).
  172. //
  173. // Without this option, the Node itself decides what is and is not
  174. // allowed. This is normally ok because FUSE file systems cannot be
  175. // accessed by other users without AllowOther/AllowRoot.
  176. //
  177. // FreeBSD ignores this option.
  178. func DefaultPermissions() MountOption {
  179. return func(conf *mountConfig) error {
  180. conf.options["default_permissions"] = ""
  181. return nil
  182. }
  183. }
  184. // ReadOnly makes the mount read-only.
  185. func ReadOnly() MountOption {
  186. return func(conf *mountConfig) error {
  187. conf.options["ro"] = ""
  188. return nil
  189. }
  190. }
  191. // MaxReadahead sets the number of bytes that can be prefetched for
  192. // sequential reads. The kernel can enforce a maximum value lower than
  193. // this.
  194. //
  195. // This setting makes the kernel perform speculative reads that do not
  196. // originate from any client process. This usually tremendously
  197. // improves read performance.
  198. func MaxReadahead(n uint32) MountOption {
  199. return func(conf *mountConfig) error {
  200. conf.maxReadahead = n
  201. return nil
  202. }
  203. }
  204. // AsyncRead enables multiple outstanding read requests for the same
  205. // handle. Without this, there is at most one request in flight at a
  206. // time.
  207. func AsyncRead() MountOption {
  208. return func(conf *mountConfig) error {
  209. conf.initFlags |= InitAsyncRead
  210. return nil
  211. }
  212. }
  213. // WritebackCache enables the kernel to buffer writes before sending
  214. // them to the FUSE server. Without this, writethrough caching is
  215. // used.
  216. func WritebackCache() MountOption {
  217. return func(conf *mountConfig) error {
  218. conf.initFlags |= InitWritebackCache
  219. return nil
  220. }
  221. }
  222. // OSXFUSEPaths describes the paths used by an installed OSXFUSE
  223. // version. See OSXFUSELocationV3 for typical values.
  224. type OSXFUSEPaths struct {
  225. // Prefix for the device file. At mount time, an incrementing
  226. // number is suffixed until a free FUSE device is found.
  227. DevicePrefix string
  228. // Path of the load helper, used to load the kernel extension if
  229. // no device files are found.
  230. Load string
  231. // Path of the mount helper, used for the actual mount operation.
  232. Mount string
  233. // Environment variable used to pass the path to the executable
  234. // calling the mount helper.
  235. DaemonVar string
  236. }
  237. // Default paths for OSXFUSE. See OSXFUSELocations.
  238. var (
  239. OSXFUSELocationV3 = OSXFUSEPaths{
  240. DevicePrefix: "/dev/osxfuse",
  241. Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse",
  242. Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse",
  243. DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH",
  244. }
  245. OSXFUSELocationV2 = OSXFUSEPaths{
  246. DevicePrefix: "/dev/osxfuse",
  247. Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs",
  248. Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs",
  249. DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH",
  250. }
  251. )
  252. // OSXFUSELocations sets where to look for OSXFUSE files. The
  253. // arguments are all the possible locations. The previous locations
  254. // are replaced.
  255. //
  256. // Without this option, OSXFUSELocationV3 and OSXFUSELocationV2 are
  257. // used.
  258. //
  259. // OS X only. Others ignore this option.
  260. func OSXFUSELocations(paths ...OSXFUSEPaths) MountOption {
  261. return func(conf *mountConfig) error {
  262. if len(paths) == 0 {
  263. return errors.New("must specify at least one location for OSXFUSELocations")
  264. }
  265. // replace previous values, but make a copy so there's no
  266. // worries about caller mutating their slice
  267. conf.osxfuseLocations = append(conf.osxfuseLocations[:0], paths...)
  268. return nil
  269. }
  270. }
  271. // AllowNonEmptyMount allows the mounting over a non-empty directory.
  272. //
  273. // The files in it will be shadowed by the freshly created mount. By
  274. // default these mounts are rejected to prevent accidental covering up
  275. // of data, which could for example prevent automatic backup.
  276. func AllowNonEmptyMount() MountOption {
  277. return func(conf *mountConfig) error {
  278. conf.options["nonempty"] = ""
  279. return nil
  280. }
  281. }