fsck: add initial implementation
Most corruption cases except xattr should be covered. With test filesystem. The output is still pretty ugly. xattr support will be added in the next commits.
This commit is contained in:
parent
fb06c65ee9
commit
f28d85fad5
101
fsck.go
101
fsck.go
@ -1,9 +1,104 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/hanwen/go-fuse/fuse"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/exitcodes"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/fusefrontend"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fsck(args *argContainer) {
|
type fsckObj struct {
|
||||||
log.Panic("Not yet implemented")
|
fs *fusefrontend.FS
|
||||||
|
errorCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively check dir for corruption
|
||||||
|
func (ck *fsckObj) dir(path string) {
|
||||||
|
//fmt.Printf("ck.dir %q\n", path)
|
||||||
|
entries, status := ck.fs.OpenDir(path, nil)
|
||||||
|
if !status.Ok() {
|
||||||
|
fmt.Printf("fsck: error opening dir %q: %v\n", path, status)
|
||||||
|
ck.errorCount++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.Name == "." || entry.Name == ".." {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nextPath := filepath.Join(path, entry.Name)
|
||||||
|
filetype := entry.Mode & syscall.S_IFMT
|
||||||
|
//fmt.Printf(" %q %x\n", entry.Name, entry.Mode)
|
||||||
|
switch filetype {
|
||||||
|
case syscall.S_IFDIR:
|
||||||
|
ck.dir(nextPath)
|
||||||
|
case syscall.S_IFREG:
|
||||||
|
ck.file(nextPath)
|
||||||
|
case syscall.S_IFLNK:
|
||||||
|
ck.symlink(nextPath)
|
||||||
|
case syscall.S_IFIFO, syscall.S_IFSOCK, syscall.S_IFBLK, syscall.S_IFCHR:
|
||||||
|
// nothing to check
|
||||||
|
default:
|
||||||
|
fmt.Printf("fsck: unhandle file type %x\n", filetype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ck *fsckObj) symlink(path string) {
|
||||||
|
_, status := ck.fs.Readlink(path, nil)
|
||||||
|
if !status.Ok() {
|
||||||
|
fmt.Printf("fsck: error reading symlink %q: %v\n", path, status)
|
||||||
|
ck.errorCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check file for corruption
|
||||||
|
func (ck *fsckObj) file(path string) {
|
||||||
|
//fmt.Printf("ck.file %q\n", path)
|
||||||
|
f, status := ck.fs.Open(path, syscall.O_RDONLY, nil)
|
||||||
|
if !status.Ok() {
|
||||||
|
fmt.Printf("fsck: error opening file %q: %v\n", path, status)
|
||||||
|
ck.errorCount++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Release()
|
||||||
|
buf := make([]byte, fuse.MAX_KERNEL_WRITE)
|
||||||
|
var off int64
|
||||||
|
for {
|
||||||
|
result, status := f.Read(buf, off)
|
||||||
|
if !status.Ok() {
|
||||||
|
fmt.Printf("fsck: error reading file %q at offset %d: %v\n", path, off, status)
|
||||||
|
ck.errorCount++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// EOF
|
||||||
|
if result.Size() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
off += int64(result.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fsck(args *argContainer) {
|
||||||
|
if args.reverse {
|
||||||
|
tlog.Fatal.Printf("Running -fsck with -reverse is not supported")
|
||||||
|
os.Exit(exitcodes.Usage)
|
||||||
|
}
|
||||||
|
args.allow_other = false
|
||||||
|
pfs, wipeKeys := initFuseFrontend(args)
|
||||||
|
defer wipeKeys()
|
||||||
|
fs := pfs.(*fusefrontend.FS)
|
||||||
|
ck := fsckObj{
|
||||||
|
fs: fs,
|
||||||
|
}
|
||||||
|
ck.dir("")
|
||||||
|
fmt.Printf("fsck: found %d problems\n", ck.errorCount)
|
||||||
|
if ck.errorCount != 0 {
|
||||||
|
os.Exit(exitcodes.FsckErrors)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,8 @@ const (
|
|||||||
// Profiler - error occoured when trying to write cpu or memory profile or
|
// Profiler - error occoured when trying to write cpu or memory profile or
|
||||||
// execution trace
|
// execution trace
|
||||||
Profiler = 25
|
Profiler = 25
|
||||||
|
// FsckErrors - the filesystem check found errors
|
||||||
|
FsckErrors = 26
|
||||||
)
|
)
|
||||||
|
|
||||||
// Err wraps an error with an associated numeric exit code
|
// Err wraps an error with an associated numeric exit code
|
||||||
|
1
main.go
1
main.go
@ -287,5 +287,6 @@ func main() {
|
|||||||
// "-fsck"
|
// "-fsck"
|
||||||
if args.fsck {
|
if args.fsck {
|
||||||
fsck(&args)
|
fsck(&args)
|
||||||
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,18 +18,6 @@ import (
|
|||||||
|
|
||||||
var testPw = []byte("test")
|
var testPw = []byte("test")
|
||||||
|
|
||||||
// Extract the exit code from an error value that was returned from
|
|
||||||
// exec.Run()
|
|
||||||
func extractExitCode(err error) int {
|
|
||||||
if err == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
// OMG this is convoluted
|
|
||||||
err2 := err.(*exec.ExitError)
|
|
||||||
code := err2.Sys().(syscall.WaitStatus).ExitStatus()
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
test_helpers.ResetTmpDir(false)
|
test_helpers.ResetTmpDir(false)
|
||||||
r := m.Run()
|
r := m.Run()
|
||||||
@ -344,7 +332,7 @@ func TestMountPasswordIncorrect(t *testing.T) {
|
|||||||
cDir := test_helpers.InitFS(t) // Create filesystem with password "test"
|
cDir := test_helpers.InitFS(t) // Create filesystem with password "test"
|
||||||
pDir := cDir + ".mnt"
|
pDir := cDir + ".mnt"
|
||||||
err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo WRONG", "-wpanic=false")
|
err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo WRONG", "-wpanic=false")
|
||||||
exitCode := extractExitCode(err)
|
exitCode := test_helpers.ExtractCmdExitCode(err)
|
||||||
if exitCode != exitcodes.PasswordIncorrect {
|
if exitCode != exitcodes.PasswordIncorrect {
|
||||||
t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode)
|
t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode)
|
||||||
}
|
}
|
||||||
@ -373,7 +361,7 @@ func TestPasswdPasswordIncorrect(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = cmd.Wait()
|
err = cmd.Wait()
|
||||||
exitCode := extractExitCode(err)
|
exitCode := test_helpers.ExtractCmdExitCode(err)
|
||||||
if exitCode != exitcodes.PasswordIncorrect {
|
if exitCode != exitcodes.PasswordIncorrect {
|
||||||
t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode)
|
t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode)
|
||||||
}
|
}
|
||||||
@ -444,7 +432,7 @@ func TestMultipleOperationFlags(t *testing.T) {
|
|||||||
//t.Logf("testing %v", args)
|
//t.Logf("testing %v", args)
|
||||||
cmd := exec.Command(test_helpers.GocryptfsBinary, args...)
|
cmd := exec.Command(test_helpers.GocryptfsBinary, args...)
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
exitCode := extractExitCode(err)
|
exitCode := test_helpers.ExtractCmdExitCode(err)
|
||||||
if exitCode != exitcodes.Usage {
|
if exitCode != exitcodes.Usage {
|
||||||
t.Fatalf("this should have failed with code %d, but returned %d",
|
t.Fatalf("this should have failed with code %d, but returned %d",
|
||||||
exitcodes.Usage, exitCode)
|
exitcodes.Usage, exitCode)
|
||||||
|
1
tests/fsck/broken_fs_v1.4/GUvJFSfy7S1AXUdy4pDRLw
Symbolic link
1
tests/fsck/broken_fs_v1.4/GUvJFSfy7S1AXUdy4pDRLw
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
RFPnVN8r1HjIrFVJ8PffC7ObzAIeBx3DQh8FbgvmbT8Ho8mU
|
BIN
tests/fsck/broken_fs_v1.4/OtrNpznB8aMTKPi6bopM2g
Normal file
BIN
tests/fsck/broken_fs_v1.4/OtrNpznB8aMTKPi6bopM2g
Normal file
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
f—`Κα
|
||||||
|
{g*@wΉ6£
|
20
tests/fsck/broken_fs_v1.4/gocryptfs.conf
Normal file
20
tests/fsck/broken_fs_v1.4/gocryptfs.conf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"Creator": "gocryptfs v1.4.4-13-ga4f3a7d-dirty",
|
||||||
|
"EncryptedKey": "yfnIx9uKv2ZX80KXOlfb4fWws3RNqvcjsx/Ajr0x4pRfg8NLqhWRpEUWGk8NSdVFXKWVDgdhSoYkbfVnFXl07g==",
|
||||||
|
"ScryptObject": {
|
||||||
|
"Salt": "R78m123zJxxO6uU1bg6/0azppry1FoGdH1/Op1xFq+4=",
|
||||||
|
"N": 65536,
|
||||||
|
"R": 8,
|
||||||
|
"P": 1,
|
||||||
|
"KeyLen": 32
|
||||||
|
},
|
||||||
|
"Version": 2,
|
||||||
|
"FeatureFlags": [
|
||||||
|
"GCMIV128",
|
||||||
|
"HKDF",
|
||||||
|
"DirIV",
|
||||||
|
"EMENames",
|
||||||
|
"LongNames",
|
||||||
|
"Raw64"
|
||||||
|
]
|
||||||
|
}
|
2
tests/fsck/broken_fs_v1.4/gocryptfs.diriv
Normal file
2
tests/fsck/broken_fs_v1.4/gocryptfs.diriv
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ε8]ΓjM
|
||||||
|
βPg‘ηDση
|
0
tests/fsck/broken_fs_v1.4/invalid_file_name.3
Normal file
0
tests/fsck/broken_fs_v1.4/invalid_file_name.3
Normal file
0
tests/fsck/broken_fs_v1.4/invalid_file_name_2
Normal file
0
tests/fsck/broken_fs_v1.4/invalid_file_name_2
Normal file
0
tests/fsck/broken_fs_v1.4/invalid_file_name____1
Normal file
0
tests/fsck/broken_fs_v1.4/invalid_file_name____1
Normal file
1
tests/fsck/broken_fs_v1.4/qOA8a4yuvgbMFpz7277R8A
Normal file
1
tests/fsck/broken_fs_v1.4/qOA8a4yuvgbMFpz7277R8A
Normal file
@ -0,0 +1 @@
|
|||||||
|
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
1
tests/fsck/broken_fs_v1.4/s-P7PcQDUcVkoeMDnC3EYA
Symbolic link
1
tests/fsck/broken_fs_v1.4/s-P7PcQDUcVkoeMDnC3EYA
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
Qso5-4WJ2iAxF674mUarvuNbIMTLSJLqfEh3Chq3I_Rm2sY2
|
BIN
tests/fsck/broken_fs_v1.4/vDKs8a7UtM3PmEKk9wlPcA
Normal file
BIN
tests/fsck/broken_fs_v1.4/vDKs8a7UtM3PmEKk9wlPcA
Normal file
Binary file not shown.
26
tests/fsck/fsck_test.go
Normal file
26
tests/fsck/fsck_test.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package fsck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/exitcodes"
|
||||||
|
"github.com/rfjakob/gocryptfs/tests/test_helpers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBrokenFsV14(t *testing.T) {
|
||||||
|
cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", "broken_fs_v1.4")
|
||||||
|
outBin, err := cmd.CombinedOutput()
|
||||||
|
out := string(outBin)
|
||||||
|
t.Log(out)
|
||||||
|
code := test_helpers.ExtractCmdExitCode(err)
|
||||||
|
if code != exitcodes.FsckErrors {
|
||||||
|
t.Errorf("wrong exit code, have=%d want=%d", code, exitcodes.FsckErrors)
|
||||||
|
}
|
||||||
|
lines := strings.Split(out, "\n")
|
||||||
|
summaryLine := lines[len(lines)-2]
|
||||||
|
if summaryLine != "fsck: found 5 problems" {
|
||||||
|
t.Errorf("wrong summary line: %q", summaryLine)
|
||||||
|
}
|
||||||
|
}
|
2
tests/fsck/run_fsck.bash
Executable file
2
tests/fsck/run_fsck.bash
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
exec ../../gocryptfs -fsck -extpass "echo test" broken_fs_v1.4
|
@ -400,3 +400,15 @@ func QueryCtlSock(t *testing.T, socketPath string, req ctlsock.RequestStruct) (r
|
|||||||
json.Unmarshal(buf, &response)
|
json.Unmarshal(buf, &response)
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract the exit code from an error value that was returned from
|
||||||
|
// exec / cmd.Run()
|
||||||
|
func ExtractCmdExitCode(err error) int {
|
||||||
|
if err == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// OMG this is convoluted
|
||||||
|
err2 := err.(*exec.ExitError)
|
||||||
|
code := err2.Sys().(syscall.WaitStatus).ExitStatus()
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user