Skip to content

Commit 2988ccf

Browse files
committed
main, fusefrontend: add "-noprealloc" option
Preallocation is very slow on hdds that run btrfs. Give the user the option to disable it. This greatly speeds up small file operations but reduces the robustness against out-of-space errors. More info: #63
1 parent 3412405 commit 2988ccf

File tree

5 files changed

+28
-15
lines changed

5 files changed

+28
-15
lines changed

cli_args.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import (
1616
type argContainer struct {
1717
debug, init, zerokey, fusedebug, openssl, passwd, fg, version,
1818
plaintextnames, quiet, nosyslog, wpanic,
19-
longnames, allow_other, ro, reverse, aessiv, nonempty, raw64 bool
19+
longnames, allow_other, ro, reverse, aessiv, nonempty, raw64,
20+
noprealloc bool
2021
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
2122
memprofile, ko, passfile, ctlsock string
2223
// Configuration file name override
@@ -105,6 +106,7 @@ func parseCliOpts() (args argContainer) {
105106
flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption")
106107
flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories")
107108
flagSet.BoolVar(&args.raw64, "raw64", false, "Use unpadded base64 for file names")
109+
flagSet.BoolVar(&args.noprealloc, "noprealloc", false, "Disable preallocation before writing")
108110
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
109111
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
110112
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")
@@ -116,7 +118,7 @@ func parseCliOpts() (args argContainer) {
116118
flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+
117119
"successful mount - used internally for daemonization")
118120
flagSet.IntVar(&args.scryptn, "scryptn", configfile.ScryptDefaultLogN, "scrypt cost parameter logN. "+
119-
"Setting this to a lower value speeds up mounting but makes the password susceptible to brute-force attacks")
121+
"A lower value speeds up mounting but makes the password susceptible to brute-force attacks")
120122
// Ignored otions
121123
var dummyBool bool
122124
ignoreText := "(ignored for compatibility)"

internal/fusefrontend/args.go

+2
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ type Args struct {
2121
// Raw64 is true when RawURLEncoding (without padding) should be used for
2222
// file names
2323
Raw64 bool
24+
// NoPrealloc disables automatic preallocation before writing
25+
NoPrealloc bool
2426
}

internal/fusefrontend/file.go

+19-11
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ type file struct {
4949
// The opCount is used to judge whether "lastWrittenOffset" is still
5050
// guaranteed to be correct.
5151
lastOpCount uint64
52+
// Parent filesystem
53+
fs *FS
5254
}
5355

5456
// NewFile returns a new go-fuse File instance.
55-
func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) (nodefs.File, fuse.Status) {
57+
func NewFile(fd *os.File, writeOnly bool, fs *FS) (nodefs.File, fuse.Status) {
5658
var st syscall.Stat_t
5759
err := syscall.Fstat(int(fd.Fd()), &st)
5860
if err != nil {
@@ -65,10 +67,11 @@ func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) (no
6567
return &file{
6668
fd: fd,
6769
writeOnly: writeOnly,
68-
contentEnc: contentEnc,
70+
contentEnc: fs.contentEnc,
6971
devIno: di,
7072
fileTableEntry: t,
7173
loopbackFile: nodefs.NewLoopbackFile(fd),
74+
fs: fs,
7275
}, fuse.OK
7376
}
7477

@@ -107,10 +110,12 @@ func (f *file) createHeader() (fileID []byte, err error) {
107110
h := contentenc.RandomHeader()
108111
buf := h.Pack()
109112
// Prevent partially written (=corrupt) header by preallocating the space beforehand
110-
err = syscallcompat.EnospcPrealloc(int(f.fd.Fd()), 0, contentenc.HeaderLen)
111-
if err != nil {
112-
tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.devIno.ino, err.Error())
113-
return nil, err
113+
if !f.fs.args.NoPrealloc {
114+
err = syscallcompat.EnospcPrealloc(int(f.fd.Fd()), 0, contentenc.HeaderLen)
115+
if err != nil {
116+
tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.devIno.ino, err.Error())
117+
return nil, err
118+
}
114119
}
115120
// Actually write header
116121
_, err = f.fd.WriteAt(buf, 0)
@@ -285,19 +290,22 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
285290
writeChain[i] = blockData
286291
numOutBytes += len(blockData)
287292
}
288-
// Concatenenate all elements in the writeChain into one contigous buffer
293+
// Concatenenate all elements in the writeChain into one contiguous buffer
289294
tmp := make([]byte, numOutBytes)
290295
writeBuf := bytes.NewBuffer(tmp[:0])
291296
for _, w := range writeChain {
292297
writeBuf.Write(w)
293298
}
294299
// Preallocate so we cannot run out of space in the middle of the write.
295300
// This prevents partially written (=corrupt) blocks.
301+
var err error
296302
cOff := blocks[0].BlockCipherOff()
297-
err := syscallcompat.EnospcPrealloc(int(f.fd.Fd()), int64(cOff), int64(writeBuf.Len()))
298-
if err != nil {
299-
tlog.Warn.Printf("ino%d fh%d: doWrite: prealloc failed: %s", f.devIno.ino, f.intFd(), err.Error())
300-
return 0, fuse.ToStatus(err)
303+
if !f.fs.args.NoPrealloc {
304+
err = syscallcompat.EnospcPrealloc(int(f.fd.Fd()), int64(cOff), int64(writeBuf.Len()))
305+
if err != nil {
306+
tlog.Warn.Printf("ino%d fh%d: doWrite: prealloc failed: %s", f.devIno.ino, f.intFd(), err.Error())
307+
return 0, fuse.ToStatus(err)
308+
}
301309
}
302310
// Write
303311
_, err = f.fd.WriteAt(writeBuf.Bytes(), int64(cOff))

internal/fusefrontend/fs.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n
106106
return nil, fuse.ToStatus(err)
107107
}
108108

109-
return NewFile(f, writeOnly, fs.contentEnc)
109+
return NewFile(f, writeOnly, fs)
110110
}
111111

112112
// Create implements pathfs.Filesystem.
@@ -160,7 +160,7 @@ func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Conte
160160
tlog.Warn.Printf("Create: Chown failed: %v", err)
161161
}
162162
}
163-
return NewFile(fd, writeOnly, fs.contentEnc)
163+
return NewFile(fd, writeOnly, fs)
164164
}
165165

166166
// Chmod implements pathfs.Filesystem.

mount.go

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF
145145
CryptoBackend: cryptoBackend,
146146
ConfigCustom: args._configCustom,
147147
Raw64: args.raw64,
148+
NoPrealloc: args.noprealloc,
148149
}
149150
// confFile is nil when "-zerokey" or "-masterkey" was used
150151
if confFile != nil {

0 commit comments

Comments
 (0)