aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2018-04-03 21:19:44 +0200
committerJakob Unterwurzacher2018-04-03 21:24:48 +0200
commit4e5783591f9874e2c7336598c96ebd2c5840bd5b (patch)
tree589e9381ad3868eb1eb7dc384fdca4c18cda901b
parent8b443c8484f5fdcedbec2a689a7d66d2a277b26e (diff)
fsck: report skipped corrupt files
OpenDir and ListXAttr skip over corrupt entries, readFileID treats files the are too small as empty. This improves usability in the face of corruption, but hides the problem in a log message instead of putting it in the return code. Create a channel to report these corruptions to fsck so it can report them to the user. Also update the manpage and the changelog with the -fsck option. Closes https://github.com/rfjakob/gocryptfs/issues/191
-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)