unionfs.go 15.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
package unionfs

import (
	"crypto/md5"
	"fmt"
	"github.com/hanwen/go-fuse/fuse"
	"log"
	"os"
	"syscall"
	"path"
	"path/filepath"
	"sync"
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
13
	"strings"
14
	"time"
15 16 17 18
)

// TODO(hanwen): is md5 sufficiently fast?
func filePathHash(path string) string {
19 20
	dir, base := filepath.Split(path)

21
	h := md5.New()
22
	h.Write([]byte(dir))
23 24 25

	// TODO(hanwen): should use a tighter format, so we can pack
	// more results in a readdir() roundtrip.
26
	return fmt.Sprintf("%x-%s", h.Sum()[:8], base)
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
}

/*

 UnionFs implements a user-space union file system, which is
 stateless but efficient even if the writable branch is on NFS.


 Assumptions:

 * It uses a list of branches, the first of which (index 0) is thought
 to be writable, and the rest read-only.

 * It assumes that the number of deleted files is small relative to
 the total tree size.


 Implementation notes.

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
46 47
 * It overlays arbitrary writable FileSystems with any number of
   readonly FileSystems.
48 49 50 51 52 53 54 55 56 57 58 59 60

 * Deleting a file will put a file named
 /DELETIONS/HASH-OF-FULL-FILENAME into the writable overlay,
 containing the full filename itself.

 This is optimized for NFS usage: we want to minimize the number of
 NFS operations, which are slow.  By putting all whiteouts in one
 place, we can cheaply fetch the list of all deleted files.  Even
 without caching on our side, the kernel's negative dentry cache can
 answer is-deleted queries quickly.

*/
type UnionFs struct {
61
	fuse.DefaultFileSystem
62

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
63
	name string
64

65
	// The same, but as interfaces.
66
	fileSystems []fuse.FileSystem
67

68
	cachingFileSystems []*CachingFileSystem
69

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
	// A file-existence cache.
	deletionCache *DirCache

	// A file -> branch cache.
	branchCache *TimedCache

	options *UnionFsOptions
}

type UnionFsOptions struct {
	BranchCacheTTLSecs   float64
	DeletionCacheTTLSecs float64
	DeletionDirName      string
}

85 86 87 88
const (
	_DROP_CACHE = ".drop_cache"
)

89
func NewUnionFs(name string, fileSystems []fuse.FileSystem, options UnionFsOptions) *UnionFs {
90
	g := new(UnionFs)
91
	g.name = name
92
	g.options = &options
93
	for i, fs := range fileSystems {
94
		if i > 0 {
95
			cfs := NewCachingFileSystem(fs, 0)
96 97 98 99 100
			g.cachingFileSystems = append(g.cachingFileSystems, cfs)
			fs = cfs
		}

		g.fileSystems = append(g.fileSystems, fs)
101 102
	}

103 104 105 106 107
	writable := g.fileSystems[0]
	fi, code := writable.GetAttr(options.DeletionDirName)
	if code == fuse.ENOENT {
		code = writable.Mkdir(options.DeletionDirName, 0755)
		fi, code = writable.GetAttr(options.DeletionDirName)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
108
	}
109
	if !code.Ok() || !fi.IsDirectory() {
110
		panic(fmt.Sprintf("could not create deletion path %v: %v",
111
			options.DeletionDirName, code))
112 113
	}

114
	g.deletionCache = NewDirCache(writable, options.DeletionDirName, int64(options.DeletionCacheTTLSecs*1e9))
115 116 117
	g.branchCache = NewTimedCache(
		func(n string) interface{} { return g.getBranchAttrNoCache(n) },
		int64(options.BranchCacheTTLSecs*1e9))
118
	g.branchCache.RecurringPurge()
119 120 121 122 123 124 125
	return g
}

////////////////
// Deal with all the caches.

func (me *UnionFs) isDeleted(name string) bool {
126 127
	marker := me.deletionPath(name)
	haveCache, found := me.deletionCache.HasEntry(filepath.Base(marker))
128 129 130 131
	if haveCache {
		return found
	}

132 133 134 135 136 137 138 139
	_, code := me.fileSystems[0].GetAttr(marker)

	if code == fuse.OK {
		return true
	}
	if code == fuse.ENOENT {
		return false
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
140

141 142
	panic(fmt.Sprintf("Unexpected GetAttr return code %v %v", code, marker))
	return false
143 144
}

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
145
func (me *UnionFs) getBranch(name string) branchResult {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
146
	name = stripSlash(name)
147
	r := me.branchCache.Get(name)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
148
	return r.(branchResult)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
149 150
}

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
151
type branchResult struct {
152
	attr   *os.FileInfo
153 154 155 156
	code   fuse.Status
	branch int
}

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
157
func (me *UnionFs) getBranchAttrNoCache(name string) branchResult {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
158
	name = stripSlash(name)
159

160
	parent, base := path.Split(name)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
161
	parent = stripSlash(parent)
162

163 164
	parentBranch := 0
	if base != "" {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
165
		parentBranch = me.getBranch(parent).branch
166 167 168 169 170 171 172
	}
	for i, fs := range me.fileSystems {
		if i < parentBranch {
			continue
		}

		a, s := fs.GetAttr(name)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
173
		if s.Ok() {
174 175
			// Make all files appear writable
			a.Mode |= 0222
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
176
			return branchResult{
177 178 179 180 181 182 183 184 185 186
				attr:   a,
				code:   s,
				branch: i,
			}
		} else {
			if s != fuse.ENOENT {
				log.Printf("getattr: %v:  Got error %v from branch %v", name, s, i)
			}
		}
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
187
	return branchResult{nil, fuse.ENOENT, -1}
188 189 190 191 192 193
}

////////////////
// Deletion.

func (me *UnionFs) deletionPath(name string) string {
194
	return filepath.Join(me.options.DeletionDirName, filePathHash(name))
195 196 197
}

func (me *UnionFs) removeDeletion(name string) {
198 199
	marker := me.deletionPath(name)
	me.deletionCache.RemoveEntry(path.Base(marker))
200 201 202 203

	// os.Remove tries to be smart and issues a Remove() and
	// Rmdir() sequentially.  We want to skip the 2nd system call,
	// so use syscall.Unlink() directly.
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
204

205 206 207
	code := me.fileSystems[0].Unlink(marker)
	if !code.Ok() && code != fuse.ENOENT {
		log.Printf("error unlinking %s: %v", marker, code)
208 209 210 211
	}
}

func (me *UnionFs) putDeletion(name string) fuse.Status {
212 213
	marker := me.deletionPath(name)
	me.deletionCache.AddEntry(path.Base(marker))
214 215

	// Is there a WriteStringToFileOrDie ?
216 217 218
	writable := me.fileSystems[0]
	f, code := writable.Open(marker, uint32(os.O_TRUNC|os.O_WRONLY|os.O_CREATE))
	if !code.Ok() {
219
		log.Printf("could not create deletion file %v: %v",
220
			marker, code)
221 222
		return fuse.EPERM
	}
223 224 225 226 227 228
	defer f.Release()
	defer f.Flush()
	n, code := f.Write(&fuse.WriteIn{}, []byte(name))
	if int(n) != len(name) || !code.Ok() {
		panic(fmt.Sprintf("Error for writing %v: %v, %v (exp %v) %v", name, marker, n, len(name), code))
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
229

230 231 232 233 234 235
	return fuse.OK
}

////////////////
// Promotion.

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
236
func (me *UnionFs) Promote(name string, srcResult branchResult) fuse.Status {
237 238
	writable := me.fileSystems[0]
	sourceFs := me.fileSystems[srcResult.branch]
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
239 240 241

	// Promote directories.
	me.promoteDirsTo(name)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
242

243 244 245 246 247 248 249 250
	code := fuse.CopyFile(sourceFs, writable, name, name)
	if !code.Ok() {
		me.branchCache.GetFresh(name)
		return code
	} else {
		r := me.getBranch(name)
		r.branch = 0
		me.branchCache.Set(name, r)
251
	}
252

253
	return fuse.OK
254 255 256
}

////////////////////////////////////////////////////////////////
257
// Below: implement interface for a FileSystem.
258

259
func (me *UnionFs) Rmdir(path string) (code fuse.Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
260
	r := me.getBranch(path)
261 262 263
	if r.code != fuse.OK {
		return r.code
	}
264
	if !r.attr.IsDirectory() {
265 266
		return syscall.ENOTDIR
	}
267 268 269 270 271 272 273 274

	stream, code := me.OpenDir(path)
	found := false
	for _ = range stream {
		found = true
	}
	if found {
		return syscall.ENOTEMPTY
275 276
	}

277 278 279 280
	if r.branch > 0 {
		code = me.putDeletion(path)
		return code
	}
281 282 283 284 285
	code = me.fileSystems[0].Rmdir(path)
	if code != fuse.OK {
		return code
	}

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
286
	r = me.branchCache.GetFresh(path).(branchResult)
287 288 289 290 291
	if r.branch > 0 {
		code = me.putDeletion(path)
	}
	return code
}
292

293
func (me *UnionFs) Mkdir(path string, mode uint32) (code fuse.Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
294
	r := me.getBranch(path)
295 296 297
	if r.code != fuse.ENOENT {
		return syscall.EEXIST
	}
298

299
	code = me.promoteDirsTo(path)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
300
	if code.Ok() {
301 302
		code = me.fileSystems[0].Mkdir(path, mode)
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
303
	if code.Ok() {
304
		me.removeDeletion(path)
305
		attr := &os.FileInfo{
306
			Mode: fuse.S_IFDIR | mode | 0222,
307
		}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
308
		me.branchCache.Set(path, branchResult{attr, fuse.OK, 0})
309 310 311 312 313 314
	}
	return code
}

func (me *UnionFs) Symlink(pointedTo string, linkName string) (code fuse.Status) {
	code = me.fileSystems[0].Symlink(pointedTo, linkName)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
315
	if code.Ok() {
316
		me.removeDeletion(linkName)
317
		me.branchCache.GetFresh(linkName)
318 319 320 321
	}
	return code
}

322
func (me *UnionFs) Truncate(path string, offset uint64) (code fuse.Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
323
	r := me.getBranch(path)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
324
	if r.branch > 0 {
325 326
		code = me.Promote(path, r)
		r.branch = 0
327
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
328

329 330 331
	if code.Ok() {
		code = me.fileSystems[0].Truncate(path, offset)
	}
332
	if code.Ok() {
333
		r.attr.Size = int64(offset)
334 335 336
		now := time.Nanoseconds()
		r.attr.Mtime_ns = now
		r.attr.Ctime_ns = now
337 338 339
		me.branchCache.Set(path, r)
	}
	return code
340 341
}

342
func (me *UnionFs) Utimens(name string, atime uint64, mtime uint64) (code fuse.Status) {
343 344 345 346
	name = stripSlash(name)
	r := me.getBranch(name)

	code = r.code
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
347
	if code.Ok() && r.branch > 0 {
348 349 350
		code = me.Promote(name, r)
		r.branch = 0
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
351
	if code.Ok() {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
352
		code = me.fileSystems[0].Utimens(name, atime, mtime)
353
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
354
	if code.Ok() {
355 356
		r.attr.Atime_ns = int64(atime)
		r.attr.Mtime_ns = int64(mtime)
357
		r.attr.Ctime_ns = time.Nanoseconds()
358 359 360 361 362
		me.branchCache.Set(name, r)
	}
	return code
}

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
363 364 365 366 367 368
func (me *UnionFs) Chown(name string, uid uint32, gid uint32) (code fuse.Status) {
	name = stripSlash(name)
	r := me.getBranch(name)
	if r.attr == nil || r.code != fuse.OK {
		return r.code
	}
369

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
370 371 372
	if os.Geteuid() != 0 {
		return fuse.EPERM
	}
373

374
	if r.attr.Uid != int(uid) || r.attr.Gid != int(gid) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
375 376 377 378 379 380 381 382 383
		if r.branch > 0 {
			code := me.Promote(name, r)
			if code != fuse.OK {
				return code
			}
			r.branch = 0
		}
		me.fileSystems[0].Chown(name, uid, gid)
	}
384 385
	r.attr.Uid = int(uid)
	r.attr.Gid = int(gid)
386
	r.attr.Ctime_ns = time.Nanoseconds()
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
387 388 389 390
	me.branchCache.Set(name, r)
	return fuse.OK
}

391
func (me *UnionFs) Chmod(name string, mode uint32) (code fuse.Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
392
	name = stripSlash(name)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
393
	r := me.getBranch(name)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
394 395 396 397
	if r.attr == nil {
		return r.code
	}
	if r.code != fuse.OK {
398 399
		return r.code
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
400

401
	permMask := uint32(07777)
402 403 404

	// Always be writable.
	mode |= 0222
405 406 407 408
	oldMode := r.attr.Mode & permMask

	if oldMode != mode {
		if r.branch > 0 {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
409
			code := me.Promote(name, r)
410 411
			if code != fuse.OK {
				return code
412
			}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
413
			r.branch = 0
414 415 416
		}
		me.fileSystems[0].Chmod(name, mode)
	}
417
	r.attr.Mode = (r.attr.Mode &^ permMask) | mode
418
	r.attr.Ctime_ns = time.Nanoseconds()
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
419
	me.branchCache.Set(name, r)
420 421 422
	return fuse.OK
}

423
func (me *UnionFs) Access(name string, mode uint32) (code fuse.Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
424 425 426
	r := me.getBranch(name)
	if r.branch >= 0 {
		return me.fileSystems[r.branch].Access(name, mode)
427 428 429 430 431 432
	}

	return fuse.ENOENT
}

func (me *UnionFs) Unlink(name string) (code fuse.Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
433 434
	r := me.getBranch(name)
	if r.branch == 0 {
435 436 437 438
		code = me.fileSystems[0].Unlink(name)
		if code != fuse.OK {
			return code
		}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
439
		r = me.branchCache.GetFresh(name).(branchResult)
440 441
	}

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
442
	if r.branch > 0 {
443 444 445 446 447 448 449
		// It would be nice to do the putDeletion async.
		code = me.putDeletion(name)
	}
	return code
}

func (me *UnionFs) Readlink(name string) (out string, code fuse.Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
450 451 452
	r := me.getBranch(name)
	if r.branch >= 0 {
		return me.fileSystems[r.branch].Readlink(name)
453 454 455 456
	}
	return "", fuse.ENOENT
}

457 458
func IsDir(fs fuse.FileSystem, name string) bool {
	a, code := fs.GetAttr(name)
459
	return code.Ok() && a.IsDirectory()
460 461
}

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
462 463 464 465 466 467 468
func stripSlash(fn string) string {
	return strings.TrimRight(fn, string(filepath.Separator))
}

func (me *UnionFs) promoteDirsTo(filename string) fuse.Status {
	dirName, _ := filepath.Split(filename)
	dirName = stripSlash(dirName)
469

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
470
	var todo []string
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
471
	var results []branchResult
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
472
	for dirName != "" {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
473
		r := me.getBranch(dirName)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
474 475 476 477

		if r.code != fuse.OK {
			log.Println("path component does not exist", filename, dirName)
		}
478
		if !r.attr.IsDirectory() {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
479 480 481 482 483 484 485 486 487 488 489 490 491
			log.Println("path component is not a directory.", dirName, r)
			return fuse.EPERM
		}
		if r.branch == 0 {
			break
		}
		todo = append(todo, dirName)
		results = append(results, r)
		dirName, _ = filepath.Split(dirName)
		dirName = stripSlash(dirName)
	}

	for i, _ := range todo {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
492
		j := len(todo) - i - 1
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
493 494 495 496 497
		d := todo[j]
		log.Println("Promoting directory", d)
		code := me.fileSystems[0].Mkdir(d, 0755)
		if code != fuse.OK {
			log.Println("Error creating dir leading to path", d, code)
498 499
			return fuse.EPERM
		}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
500 501 502
		r := results[j]
		r.branch = 0
		me.branchCache.Set(d, r)
503 504 505 506
	}
	return fuse.OK
}

507
func (me *UnionFs) Create(name string, flags uint32, mode uint32) (fuseFile fuse.File, code fuse.Status) {
508
	writable := me.fileSystems[0]
509

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
510
	code = me.promoteDirsTo(name)
511 512 513
	if code != fuse.OK {
		return nil, code
	}
514
	fuseFile, code = writable.Create(name, flags, mode)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
515
	if code.Ok() {
516
		me.removeDeletion(name)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
517

518
		now := time.Nanoseconds()
519
		a := os.FileInfo{
520
			Mode:     fuse.S_IFREG | mode | 0222,
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
521 522
			Ctime_ns: now,
			Mtime_ns: now,
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
523
		}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
524
		me.branchCache.Set(name, branchResult{&a, fuse.OK, 0})
525 526 527 528
	}
	return fuseFile, code
}

529
func (me *UnionFs) GetAttr(name string) (a *os.FileInfo, s fuse.Status) {
530 531 532
	if name == _READONLY {
		return nil, fuse.ENOENT
	}
533
	if name == _DROP_CACHE {
534 535
		return &os.FileInfo{
			Mode: fuse.S_IFREG | 0777,
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
536
		},fuse.OK
537 538 539 540 541 542 543
	}
	if name == me.options.DeletionDirName {
		return nil, fuse.ENOENT
	}
	if me.isDeleted(name) {
		return nil, fuse.ENOENT
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
544
	r := me.getBranch(name)
545 546 547 548 549 550
	if r.branch < 0 {
		return nil, fuse.ENOENT
	}
	return r.attr, r.code
}

551
func (me *UnionFs) GetXAttr(name string, attr string) ([]byte, fuse.Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
552 553 554
	r := me.getBranch(name)
	if r.branch >= 0 {
		return me.fileSystems[r.branch].GetXAttr(name, attr)
555 556 557 558
	}
	return nil, fuse.ENOENT
}

559 560
func (me *UnionFs) OpenDir(directory string) (stream chan fuse.DirEntry, status fuse.Status) {
	dirBranch := me.getBranch(directory)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
561
	if dirBranch.branch < 0 {
562 563 564 565 566
		return nil, fuse.ENOENT
	}

	// We could try to use the cache, but we have a delay, so
	// might as well get the fresh results async.
567
	var wg sync.WaitGroup
568
	var deletions map[string]bool
569 570

	wg.Add(1)
571
	go func() {
572 573
		deletions = newDirnameMap(me.fileSystems[0], me.options.DeletionDirName)
		wg.Done()
574 575
	}()

576 577
	entries := make([]map[string]uint32, len(me.fileSystems))
	for i, _ := range me.fileSystems {
578 579 580
		entries[i] = make(map[string]uint32)
	}

581
	statuses := make([]fuse.Status, len(me.fileSystems))
582
	for i, l := range me.fileSystems {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
583
		if i >= dirBranch.branch {
584
			wg.Add(1)
585
			go func(j int, pfs fuse.FileSystem) {
586 587
				ch, s := pfs.OpenDir(directory)
				statuses[j] = s
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
588
				for s.Ok() {
589 590
					v, ok := <-ch
					if !ok {
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
						break
					}
					entries[j][v.Name] = v.Mode
				}
				wg.Done()
			}(i, l)
		}
	}

	wg.Wait()

	results := entries[0]

	// TODO(hanwen): should we do anything with the return
	// statuses?
	for i, m := range entries {
		if statuses[i] != fuse.OK {
			continue
		}
		if i == 0 {
			// We don't need to further process the first
			// branch: it has no deleted files.
			continue
		}
		for k, v := range m {
			_, ok := results[k]
			if ok {
				continue
			}

			deleted := deletions[filePathHash(filepath.Join(directory, k))]
			if !deleted {
				results[k] = v
			}
		}
	}
	if directory == "" {
		results[me.options.DeletionDirName] = 0, false
		// HACK.
630
		results[_READONLY] = 0, false
631 632
	}

633 634 635 636 637
	stream = make(chan fuse.DirEntry, len(results))
	for k, v := range results {
		stream <- fuse.DirEntry{
			Name: k,
			Mode: v,
638
		}
639 640
	}
	close(stream)
641 642 643
	return stream, fuse.OK
}

644
func (me *UnionFs) Rename(src string, dst string) (code fuse.Status) {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
645
	srcResult := me.getBranch(src)
646
	code = srcResult.code
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
647
	if code.Ok() {
648
		code = srcResult.code
649
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
650
	if code.Ok() && srcResult.branch > 0 {
651
		code = me.Promote(src, srcResult)
652
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
653
	if code.Ok() {
654
		code = me.promoteDirsTo(dst)
655
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
656
	if code.Ok() {
657 658
		code = me.fileSystems[0].Rename(src, dst)
	}
659

Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
660
	if code.Ok() {
661 662 663 664 665 666 667 668 669 670
		me.removeDeletion(dst)
		srcResult.branch = 0
		me.branchCache.Set(dst, srcResult)

		if srcResult.branch == 0 {
			srcResult := me.branchCache.GetFresh(src)
			if srcResult.(branchResult).branch > 0 {
				code = me.putDeletion(src)
			}
		} else {
671 672 673 674 675 676
			code = me.putDeletion(src)
		}
	}
	return code
}

677
func (me *UnionFs) DropCaches() {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
678 679 680 681 682 683
	log.Println("Forced cache drop on", me.name)
	me.branchCache.DropAll()
	me.deletionCache.DropCache()
	for _, fs := range me.cachingFileSystems {
		fs.DropCache()
	}
684 685
}

686
func (me *UnionFs) Open(name string, flags uint32) (fuseFile fuse.File, status fuse.Status) {
687
	if name == _DROP_CACHE {
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
688
		if flags&fuse.O_ANYWRITE != 0 {
689 690 691 692
			me.DropCaches()
		}
		return fuse.NewDevNullFile(), fuse.OK
	}
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
693
	r := me.getBranch(name)
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
694 695
	if flags&fuse.O_ANYWRITE != 0 && r.branch > 0 {
		code := me.Promote(name, r)
696 697
		if code != fuse.OK {
			return nil, code
698
		}
699 700 701
		r.branch = 0
		r.attr.Mtime_ns = time.Nanoseconds()
		me.branchCache.Set(name, r)
702
	}
703
	return me.fileSystems[r.branch].Open(name, uint32(flags))
704 705
}

706
func (me *UnionFs) Flush(name string) fuse.Status {
707
	// Refresh timestamps and size field.
708
	me.branchCache.GetFresh(name)
709
	return fuse.OK
Han-Wen Nienhuys's avatar
Han-Wen Nienhuys committed
710 711
}

712 713
func (me *UnionFs) Name() string {
	return me.name
714
}