summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/MANPAGE.md8
-rw-r--r--README.md2
-rw-r--r--fsck.go52
-rw-r--r--internal/fusefrontend/file.go2
-rw-r--r--internal/fusefrontend/fs.go18
-rw-r--r--internal/fusefrontend/fs_dir.go2
-rw-r--r--internal/fusefrontend/xattr.go1
7 files changed, 80 insertions, 5 deletions
diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md
index e20b786..1c7e7b9 100644
--- a/Documentation/MANPAGE.md
+++ b/Documentation/MANPAGE.md
@@ -19,6 +19,9 @@ SYNOPSIS
#### Change password
`gocryptfs -passwd [OPTIONS] CIPHERDIR`
+#### Check consistency
+`gocryptfs -fsck [OPTIONS] CIPHERDIR`
+
DESCRIPTION
===========
@@ -96,6 +99,10 @@ that uses built-in Go crypto.
Setting this option forces the filesystem to read-only and noexec.
+#### -fsck
+Check CIPHERDIR for consistency. If corruption is found, the
+exit code is 26.
+
#### -fsname string
Override the filesystem name (first column in df -T). Can also be
passed as "-o fsname=" and is equivalent to libfuse's option of the
@@ -351,6 +358,7 @@ EXIT CODES
22: password is empty (on "-init")
23: could not read gocryptfs.conf
24: could not write gocryptfs.conf (on "-init" or "-password")
+26: fsck found errors
other: please check the error message
SEE ALSO
diff --git a/README.md b/README.md
index 5b53061..ec8a8b6 100644
--- a/README.md
+++ b/README.md
@@ -158,6 +158,8 @@ vNEXT, in progress
([#218](https://github.com/rfjakob/gocryptfs/issues/218))
* Support extended attributes (xattr) in forward mode
([#217](https://github.com/rfjakob/gocryptfs/issues/217))
+* Add `-fsck` function
+ ([#191](https://github.com/rfjakob/gocryptfs/issues/191))
v1.4.4, 2018-03-18
* Overwrite secrets in memory with zeros as soon as possible
diff --git a/fsck.go b/fsck.go
index 9eff72e..d1dc7e5 100644
--- a/fsck.go
+++ b/fsck.go
@@ -6,6 +6,7 @@ import (
"path/filepath"
"sort"
"strings"
+ "sync"
"syscall"
"github.com/hanwen/go-fuse/fuse"
@@ -19,17 +20,34 @@ type fsckObj struct {
fs *fusefrontend.FS
// List of corrupt files
corruptList []string
+ // Protects corruptList
+ corruptListLock sync.Mutex
}
func (ck *fsckObj) markCorrupt(path string) {
+ ck.corruptListLock.Lock()
ck.corruptList = append(ck.corruptList, path)
+ ck.corruptListLock.Unlock()
}
// Recursively check dir for corruption
func (ck *fsckObj) dir(path string) {
//fmt.Printf("ck.dir %q\n", path)
ck.xattrs(path)
+ done := make(chan struct{})
+ go func() {
+ for {
+ select {
+ case item := <-ck.fs.CorruptItems:
+ fmt.Printf("fsck: corrupt entry in dir %q: %q\n", path, item)
+ ck.markCorrupt(filepath.Join(path, item))
+ case <-done:
+ return
+ }
+ }
+ }()
entries, status := ck.fs.OpenDir(path, nil)
+ done <- struct{}{}
if !status.Ok() {
ck.markCorrupt(path)
fmt.Printf("fsck: error opening dir %q: %v\n", path, status)
@@ -80,6 +98,19 @@ func (ck *fsckObj) file(path string) {
defer f.Release()
buf := make([]byte, fuse.MAX_KERNEL_WRITE)
var off int64
+ done := make(chan struct{})
+ go func() {
+ for {
+ select {
+ case item := <-ck.fs.CorruptItems:
+ fmt.Printf("fsck: corrupt file %q (inode %s)\n", path, item)
+ ck.markCorrupt(path)
+ case <-done:
+ return
+ }
+ }
+ }()
+ defer func() { done <- struct{}{} }()
for {
result, status := f.Read(buf, off)
if !status.Ok() {
@@ -97,7 +128,20 @@ func (ck *fsckObj) file(path string) {
// Check xattrs on file/dir at path
func (ck *fsckObj) xattrs(path string) {
+ done := make(chan struct{})
+ go func() {
+ for {
+ select {
+ case item := <-ck.fs.CorruptItems:
+ fmt.Printf("fsck: corrupt xattr name on file %q: %q\n", path, item)
+ ck.markCorrupt(path + " xattr:" + item)
+ case <-done:
+ return
+ }
+ }
+ }()
attrs, status := ck.fs.ListXAttr(path, nil)
+ done <- struct{}{}
if !status.Ok() {
fmt.Printf("fsck: error listing xattrs on %q: %v\n", path, status)
ck.markCorrupt(path)
@@ -120,19 +164,17 @@ func fsck(args *argContainer) {
args.allow_other = false
pfs, wipeKeys := initFuseFrontend(args)
fs := pfs.(*fusefrontend.FS)
+ fs.CorruptItems = make(chan string)
ck := fsckObj{
fs: fs,
}
ck.dir("")
wipeKeys()
if len(ck.corruptList) == 0 {
- fmt.Printf("fsck summary: no problems found")
+ fmt.Printf("fsck summary: no problems found\n")
return
}
- fmt.Printf("fsck summary: found %d corrupt files:\n", len(ck.corruptList))
- for _, path := range ck.corruptList {
- fmt.Printf(" %q\n", path)
- }
+ fmt.Printf("fsck summary: %d corrupt files\n", len(ck.corruptList))
os.Exit(exitcodes.FsckErrors)
}
diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go
index af13170..72319e9 100644
--- a/internal/fusefrontend/file.go
+++ b/internal/fusefrontend/file.go
@@ -4,6 +4,7 @@ package fusefrontend
import (
"bytes"
+ "fmt"
"io"
"log"
"os"
@@ -99,6 +100,7 @@ func (f *file) readFileID() ([]byte, error) {
if err == io.EOF && n != 0 {
tlog.Warn.Printf("readFileID %d: incomplete file, got %d instead of %d bytes",
f.qIno.Ino, n, readLen)
+ f.fs.reportCorruptItem(fmt.Sprint(f.qIno.Ino))
}
return nil, err
}
diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go
index 5f84541..8a3935f 100644
--- a/internal/fusefrontend/fs.go
+++ b/internal/fusefrontend/fs.go
@@ -38,6 +38,11 @@ type FS struct {
// This lock is used by openWriteOnlyFile() to block concurrent opens while
// it relaxes the permissions on a file.
openWriteOnlyLock sync.RWMutex
+ // CorruptItems is filled with file or xattr names that have been
+ // skipped (ignored) because they were corrupt. This is used by fsck
+ // to inform the user.
+ // Use the reportCorruptItem() function to push an item.
+ CorruptItems chan string
}
var _ pathfs.FileSystem = &FS{} // Verify that interface is implemented.
@@ -601,3 +606,16 @@ func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse
}
return fuse.ToStatus(syscall.Access(cPath, mode))
}
+
+func (fs *FS) reportCorruptItem(item string) {
+ if fs.CorruptItems == nil {
+ return
+ }
+ select {
+ case fs.CorruptItems <- item:
+ case <-time.After(1 * time.Second):
+ tlog.Warn.Printf("BUG: reportCorruptItem: timeout")
+ //debug.PrintStack()
+ return
+ }
+}
diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go
index e13afed..089429e 100644
--- a/internal/fusefrontend/fs_dir.go
+++ b/internal/fusefrontend/fs_dir.go
@@ -326,6 +326,7 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
if err != nil {
tlog.Warn.Printf("OpenDir %q: invalid entry %q: Could not read .name: %v",
cDirName, cName, err)
+ fs.reportCorruptItem(cName)
errorCount++
continue
}
@@ -338,6 +339,7 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
if err != nil {
tlog.Warn.Printf("OpenDir %q: invalid entry %q: %v",
cDirName, cName, err)
+ fs.reportCorruptItem(cName)
if runtime.GOOS == "darwin" && cName == dsStoreName {
// MacOS creates lots of these files. Log the warning but don't
// increment errorCount - does not warrant returning EIO.
diff --git a/internal/fusefrontend/xattr.go b/internal/fusefrontend/xattr.go
index 1d628b7..faaebd4 100644
--- a/internal/fusefrontend/xattr.go
+++ b/internal/fusefrontend/xattr.go
@@ -115,6 +115,7 @@ func (fs *FS) ListXAttr(path string, context *fuse.Context) ([]string, fuse.Stat
name, err := fs.decryptXattrName(curName)
if err != nil {
tlog.Warn.Printf("ListXAttr: invalid xattr name %q: %v", curName, err)
+ fs.reportCorruptItem(curName)
continue
}
names = append(names, name)