Commit 21ed6eb9 authored by Jan Pfeifer's avatar Jan Pfeifer Committed by Han-Wen Nienhuys

Make hiding the READONLY link switchable in AutoUnionFs.

parent 97d9f0ed
...@@ -17,6 +17,9 @@ func main() { ...@@ -17,6 +17,9 @@ func main() {
branchcache_ttl := flag.Float64("branchcache_ttl", 5.0, "Branch cache TTL in seconds.") branchcache_ttl := flag.Float64("branchcache_ttl", 5.0, "Branch cache TTL in seconds.")
deldirname := flag.String( deldirname := flag.String(
"deletion_dirname", "GOUNIONFS_DELETIONS", "Directory name to use for deletions.") "deletion_dirname", "GOUNIONFS_DELETIONS", "Directory name to use for deletions.")
hide_readonly_link := flag.Bool("hide_readonly_link", true,
"Hides READONLY link from the top mountpoints. " +
"Enabled by default.")
flag.Parse() flag.Parse()
if *version { if *version {
...@@ -45,6 +48,7 @@ func main() { ...@@ -45,6 +48,7 @@ func main() {
PathNodeFsOptions: fuse.PathNodeFsOptions{ PathNodeFsOptions: fuse.PathNodeFsOptions{
ClientInodes: *hardlinks, ClientInodes: *hardlinks,
}, },
HideReadonly: *hide_readonly_link,
} }
fmt.Printf("AutoUnionFs - Go-FUSE Version %v.\n", fuse.Version()) fmt.Printf("AutoUnionFs - Go-FUSE Version %v.\n", fuse.Version())
......
...@@ -21,6 +21,7 @@ func main() { ...@@ -21,6 +21,7 @@ func main() {
branchcache_ttl := flag.Float64("branchcache_ttl", 5.0, "Branch cache TTL in seconds.") branchcache_ttl := flag.Float64("branchcache_ttl", 5.0, "Branch cache TTL in seconds.")
deldirname := flag.String( deldirname := flag.String(
"deletion_dirname", "GOUNIONFS_DELETIONS", "Directory name to use for deletions.") "deletion_dirname", "GOUNIONFS_DELETIONS", "Directory name to use for deletions.")
flag.Parse() flag.Parse()
if len(flag.Args()) < 2 { if len(flag.Args()) < 2 {
fmt.Println("Usage:\n unionfs MOUNTPOINT RW-DIRECTORY RO-DIRECTORY ...") fmt.Println("Usage:\n unionfs MOUNTPOINT RW-DIRECTORY RO-DIRECTORY ...")
......
...@@ -44,6 +44,9 @@ type AutoUnionFsOptions struct { ...@@ -44,6 +44,9 @@ type AutoUnionFsOptions struct {
// If set, run updateKnownFses() after mounting. // If set, run updateKnownFses() after mounting.
UpdateOnMount bool UpdateOnMount bool
// If set hides the _READONLY file.
HideReadonly bool
} }
const ( const (
...@@ -57,6 +60,9 @@ const ( ...@@ -57,6 +60,9 @@ const (
) )
func NewAutoUnionFs(directory string, options AutoUnionFsOptions) *AutoUnionFs { func NewAutoUnionFs(directory string, options AutoUnionFsOptions) *AutoUnionFs {
if options.HideReadonly {
options.HiddenFiles = append(options.HiddenFiles, _READONLY)
}
a := new(AutoUnionFs) a := new(AutoUnionFs)
a.knownFileSystems = make(map[string]knownFs) a.knownFileSystems = make(map[string]knownFs)
a.nameRootMap = make(map[string]string) a.nameRootMap = make(map[string]string)
......
...@@ -24,6 +24,7 @@ var testAOpts = AutoUnionFsOptions{ ...@@ -24,6 +24,7 @@ var testAOpts = AutoUnionFsOptions{
AttrTimeout: entryTtl, AttrTimeout: entryTtl,
NegativeTimeout: 0, NegativeTimeout: 0,
}, },
HideReadonly: true,
} }
func WriteFile(name string, contents string) { func WriteFile(name string, contents string) {
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"time" "time"
) )
func filePathHash(path string) string { func filePathHash(path string) string {
dir, base := filepath.Split(path) dir, base := filepath.Split(path)
...@@ -65,6 +66,9 @@ type UnionFs struct { ...@@ -65,6 +66,9 @@ type UnionFs struct {
// A file -> branch cache. // A file -> branch cache.
branchCache *TimedCache branchCache *TimedCache
// Map of files to hide.
hiddenFiles map[string]bool
options *UnionFsOptions options *UnionFsOptions
nodeFs *fuse.PathNodeFs nodeFs *fuse.PathNodeFs
} }
...@@ -73,6 +77,7 @@ type UnionFsOptions struct { ...@@ -73,6 +77,7 @@ type UnionFsOptions struct {
BranchCacheTTL time.Duration BranchCacheTTL time.Duration
DeletionCacheTTL time.Duration DeletionCacheTTL time.Duration
DeletionDirName string DeletionDirName string
HiddenFiles []string
} }
const ( const (
...@@ -96,6 +101,12 @@ func NewUnionFs(fileSystems []fuse.FileSystem, options UnionFsOptions) *UnionFs ...@@ -96,6 +101,12 @@ func NewUnionFs(fileSystems []fuse.FileSystem, options UnionFsOptions) *UnionFs
func(n string) (interface{}, bool) { return g.getBranchAttrNoCache(n), true }, func(n string) (interface{}, bool) { return g.getBranchAttrNoCache(n), true },
options.BranchCacheTTL) options.BranchCacheTTL)
g.branchCache.RecurringPurge() g.branchCache.RecurringPurge()
g.hiddenFiles = make(map[string]bool)
for _, name := range options.HiddenFiles {
g.hiddenFiles[name] = true
}
return g return g
} }
...@@ -659,7 +670,8 @@ func (me *UnionFs) Create(name string, flags uint32, mode uint32, context *fuse. ...@@ -659,7 +670,8 @@ func (me *UnionFs) Create(name string, flags uint32, mode uint32, context *fuse.
} }
func (me *UnionFs) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, s fuse.Status) { func (me *UnionFs) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, s fuse.Status) {
if name == _READONLY { _, hidden := me.hiddenFiles[name]
if hidden {
return nil, fuse.ENOENT return nil, fuse.ENOENT
} }
if name == _DROP_CACHE { if name == _DROP_CACHE {
...@@ -778,8 +790,9 @@ func (me *UnionFs) OpenDir(directory string, context *fuse.Context) (stream chan ...@@ -778,8 +790,9 @@ func (me *UnionFs) OpenDir(directory string, context *fuse.Context) (stream chan
} }
if directory == "" { if directory == "" {
delete(results, me.options.DeletionDirName) delete(results, me.options.DeletionDirName)
// HACK. for name, _ := range me.hiddenFiles {
delete(results, _READONLY) delete(results, name)
}
} }
stream = make(chan fuse.DirEntry, len(results)) stream = make(chan fuse.DirEntry, len(results))
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
"syscall" "syscall"
"testing" "testing"
"time" "time"
...@@ -25,6 +26,7 @@ var testOpts = UnionFsOptions{ ...@@ -25,6 +26,7 @@ var testOpts = UnionFsOptions{
DeletionCacheTTL: entryTtl, DeletionCacheTTL: entryTtl,
DeletionDirName: "DELETIONS", DeletionDirName: "DELETIONS",
BranchCacheTTL: entryTtl, BranchCacheTTL: entryTtl,
HiddenFiles: []string{ "hidden" },
} }
func freezeRo(dir string) { func freezeRo(dir string) {
...@@ -37,6 +39,8 @@ func freezeRo(dir string) { ...@@ -37,6 +39,8 @@ func freezeRo(dir string) {
CheckSuccess(err) CheckSuccess(err)
} }
// Creates 3 directories on a temporary dir: /mnt with the overlayed
// (unionfs) mount, rw with modifiable data, and ro on the bottom.
func setupUfs(t *testing.T) (workdir string, cleanup func()) { func setupUfs(t *testing.T) (workdir string, cleanup func()) {
// Make sure system setting does not affect test. // Make sure system setting does not affect test.
syscall.Umask(0) syscall.Umask(0)
...@@ -60,8 +64,8 @@ func setupUfs(t *testing.T) (workdir string, cleanup func()) { ...@@ -60,8 +64,8 @@ func setupUfs(t *testing.T) (workdir string, cleanup func()) {
// We configure timeouts are smaller, so we can check for // We configure timeouts are smaller, so we can check for
// UnionFs's cache consistency. // UnionFs's cache consistency.
opts := &fuse.FileSystemOptions{ opts := &fuse.FileSystemOptions{
EntryTimeout: entryTtl / 2, EntryTimeout: entryTtl / 2,
AttrTimeout: entryTtl / 2, AttrTimeout: entryTtl / 2,
NegativeTimeout: entryTtl / 2, NegativeTimeout: entryTtl / 2,
} }
...@@ -298,7 +302,7 @@ func TestUnionFsBasic(t *testing.T) { ...@@ -298,7 +302,7 @@ func TestUnionFsBasic(t *testing.T) {
names = dirNames(wd + "/rw") names = dirNames(wd + "/rw")
checkMapEq(t, names, map[string]bool{ checkMapEq(t, names, map[string]bool{
testOpts.DeletionDirName: true, testOpts.DeletionDirName: true,
"rw": true, "ro1": true, "rw": true, "ro1": true,
}) })
names = dirNames(wd + "/rw/" + testOpts.DeletionDirName) names = dirNames(wd + "/rw/" + testOpts.DeletionDirName)
if len(names) != 0 { if len(names) != 0 {
...@@ -742,6 +746,8 @@ func TestUnionFsRemoveAll(t *testing.T) { ...@@ -742,6 +746,8 @@ func TestUnionFsRemoveAll(t *testing.T) {
} }
} }
// Warning: test fails under coreutils < 8.0 because of non-posix behaviour
// of the "rm" tool -- which relies on behaviour that doesn't work in fuse.
func TestUnionFsRmRf(t *testing.T) { func TestUnionFsRmRf(t *testing.T) {
wd, clean := setupUfs(t) wd, clean := setupUfs(t)
defer clean() defer clean()
...@@ -757,6 +763,10 @@ func TestUnionFsRmRf(t *testing.T) { ...@@ -757,6 +763,10 @@ func TestUnionFsRmRf(t *testing.T) {
bin, err := exec.LookPath("rm") bin, err := exec.LookPath("rm")
CheckSuccess(err) CheckSuccess(err)
command := fmt.Sprintf("%s -f %s/mnt/dir", bin, wd);
log.Printf("Command: %s", command)
names, _ := Readdirnames(wd + "/mnt/dir")
log.Printf("Contents of %s/mnt/dir: %s", wd, strings.Join(names, ", "))
cmd := exec.Command(bin, "-rf", wd+"/mnt/dir") cmd := exec.Command(bin, "-rf", wd+"/mnt/dir")
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
...@@ -769,7 +779,7 @@ func TestUnionFsRmRf(t *testing.T) { ...@@ -769,7 +779,7 @@ func TestUnionFsRmRf(t *testing.T) {
} }
} }
names, err := Readdirnames(wd + "/rw/DELETIONS") names, err = Readdirnames(wd + "/rw/DELETIONS")
CheckSuccess(err) CheckSuccess(err)
if len(names) != 3 { if len(names) != 3 {
t.Fatal("unexpected names", names) t.Fatal("unexpected names", names)
...@@ -876,8 +886,8 @@ func TestUnionFsDisappearing(t *testing.T) { ...@@ -876,8 +886,8 @@ func TestUnionFsDisappearing(t *testing.T) {
ufs := NewUnionFs(fses, testOpts) ufs := NewUnionFs(fses, testOpts)
opts := &fuse.FileSystemOptions{ opts := &fuse.FileSystemOptions{
EntryTimeout: entryTtl, EntryTimeout: entryTtl,
AttrTimeout: entryTtl, AttrTimeout: entryTtl,
NegativeTimeout: entryTtl, NegativeTimeout: entryTtl,
} }
...@@ -1116,3 +1126,27 @@ func TestUnionFsPromoteDirTimeStamp(t *testing.T) { ...@@ -1116,3 +1126,27 @@ func TestUnionFsPromoteDirTimeStamp(t *testing.T) {
t.Errorf("Changed mode ro: %v, rw: %v", fRo.Mode(), fRw.Mode()) t.Errorf("Changed mode ro: %v, rw: %v", fRo.Mode(), fRw.Mode())
} }
} }
func TestUnionFsCheckHiddenFiles(t *testing.T) {
wd, clean := setupUfs(t)
defer clean()
err := ioutil.WriteFile(wd+"/ro/hidden", []byte("bla"), 0644)
CheckSuccess(err)
err = ioutil.WriteFile(wd+"/ro/not_hidden", []byte("bla"), 0644)
CheckSuccess(err)
freezeRo(wd + "/ro")
fi, _ := os.Lstat(wd + "/mnt/hidden")
if fi != nil {
t.Fatal("Lstat() should have failed", fi)
}
_, err = os.Lstat(wd + "/mnt/not_hidden")
CheckSuccess(err)
names, err := Readdirnames(wd + "/mnt")
CheckSuccess(err)
if len(names) != 1 || names[0] != "not_hidden" {
t.Fatal("unexpected names", names)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment