v2api: make fsck compile again

Horribly broken, but it compiles.

.../tests/fsck$ ./run_fsck.bash
Reading password from extpass program "echo", arguments: ["test"]
Decrypting master key
OpenDir ".": invalid entry "invalid_file_name.3": illegal base64 data at input byte 17
OpenDir ".": invalid entry "invalid_file_name_2": bad message
fsck: corrupt entry in dir "": "invalid_file_name.3"
fsck: corrupt entry in dir "": "invalid_file_name_2"
OpenDir ".": invalid entry "invalid_file_name____1": bad message
fsck: corrupt entry in dir "": "invalid_file_name____1"
fsck: error stating file ".go-fuse.5577006791947779410/deleted": no such file or directory
fsck: error listing xattrs on ".go-fuse.13260572831089785859/deleted": no such file or directory
fsck: error opening dir "i10488239 (dir): ": no such file or directory
fsck: error reading symlink ".go-fuse.10667007354186551956/deleted": no such file or directory
fsck: error listing xattrs on ".go-fuse.11998794077335055257/deleted": no such file or directory
[...]
This commit is contained in:
Jakob Unterwurzacher 2020-07-18 23:41:27 +02:00
parent 6b7ff09373
commit 751f237993
3 changed files with 74 additions and 57 deletions

127
fsck.go
View File

@ -1,6 +1,3 @@
// +build ignore
// TODO
package main package main
import ( import (
@ -8,13 +5,12 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"sort"
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse" "github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/fuse/nodefs"
"github.com/rfjakob/gocryptfs/internal/exitcodes" "github.com/rfjakob/gocryptfs/internal/exitcodes"
"github.com/rfjakob/gocryptfs/internal/fusefrontend" "github.com/rfjakob/gocryptfs/internal/fusefrontend"
@ -22,7 +18,7 @@ import (
) )
type fsckObj struct { type fsckObj struct {
fs *fusefrontend.FS rootNode *fusefrontend.RootNode
// List of corrupt files // List of corrupt files
corruptList []string corruptList []string
// List of skipped files // List of skipped files
@ -55,7 +51,7 @@ func (ck *fsckObj) markSkipped(path string) {
func (ck *fsckObj) watchMitigatedCorruptionsOpenDir(path string) { func (ck *fsckObj) watchMitigatedCorruptionsOpenDir(path string) {
for { for {
select { select {
case item := <-ck.fs.MitigatedCorruptions: case item := <-ck.rootNode.MitigatedCorruptions:
fmt.Printf("fsck: corrupt entry in dir %q: %q\n", path, item) fmt.Printf("fsck: corrupt entry in dir %q: %q\n", path, item)
ck.markCorrupt(filepath.Join(path, item)) ck.markCorrupt(filepath.Join(path, item))
case <-ck.watchDone: case <-ck.watchDone:
@ -65,30 +61,39 @@ func (ck *fsckObj) watchMitigatedCorruptionsOpenDir(path string) {
} }
// Recursively check dir for corruption // Recursively check dir for corruption
func (ck *fsckObj) dir(path string) { func (ck *fsckObj) dir(n *fusefrontend.Node) {
tlog.Debug.Printf("ck.dir %q\n", path) path := n.Path()
ck.xattrs(path) tlog.Debug.Printf("ck.dir %q\n")
ck.xattrs(n)
// Run OpenDir and catch transparently mitigated corruptions // Run OpenDir and catch transparently mitigated corruptions
go ck.watchMitigatedCorruptionsOpenDir(path) go ck.watchMitigatedCorruptionsOpenDir(path)
entries, status := ck.fs.OpenDir(path, nil) entries, errno := n.Readdir(nil)
ck.watchDone <- struct{}{} ck.watchDone <- struct{}{}
// Also catch non-mitigated corruptions // Also catch non-mitigated corruptions
if !status.Ok() { if errno != 0 {
fmt.Printf("fsck: error opening dir %q: %v\n", path, status) fmt.Printf("fsck: error opening dir %q: %v\n", n, errno)
if status == fuse.EACCES && !runsAsRoot() { if errno == syscall.EACCES && !runsAsRoot() {
ck.markSkipped(path) ck.markSkipped(path)
} else { } else {
ck.markCorrupt(path) ck.markCorrupt(path)
} }
return return
} }
// Sort alphabetically for entries.HasNext() {
sort.Sort(sortableDirEntries(entries)) entry, errno := entries.Next()
for _, entry := range entries { if errno != 0 {
fmt.Printf("fsck: dirstream error: %v\n", errno)
break
}
if entry.Name == "." || entry.Name == ".." { if entry.Name == "." || entry.Name == ".." {
continue continue
} }
nextPath := filepath.Join(path, entry.Name) tmp, errno := n.Lookup(nil, entry.Name, &fuse.EntryOut{})
if errno != 0 {
ck.markCorrupt(filepath.Join(path, entry.Name))
continue
}
nextPath := tmp.Operations().(*fusefrontend.Node)
filetype := entry.Mode & syscall.S_IFMT filetype := entry.Mode & syscall.S_IFMT
//fmt.Printf(" %q %x\n", entry.Name, entry.Mode) //fmt.Printf(" %q %x\n", entry.Name, entry.Mode)
switch filetype { switch filetype {
@ -106,11 +111,12 @@ func (ck *fsckObj) dir(path string) {
} }
} }
func (ck *fsckObj) symlink(path string) { func (ck *fsckObj) symlink(n *fusefrontend.Node) {
_, status := ck.fs.Readlink(path, nil) _, errno := n.Readlink(nil)
if !status.Ok() { if errno != 0 {
path := n.Path()
ck.markCorrupt(path) ck.markCorrupt(path)
fmt.Printf("fsck: error reading symlink %q: %v\n", path, status) fmt.Printf("fsck: error reading symlink %q: %v\n", path, errno)
} }
} }
@ -118,7 +124,7 @@ func (ck *fsckObj) symlink(path string) {
func (ck *fsckObj) watchMitigatedCorruptionsRead(path string) { func (ck *fsckObj) watchMitigatedCorruptionsRead(path string) {
for { for {
select { select {
case item := <-ck.fs.MitigatedCorruptions: case item := <-ck.rootNode.MitigatedCorruptions:
fmt.Printf("fsck: corrupt file %q (inode %s)\n", path, item) fmt.Printf("fsck: corrupt file %q (inode %s)\n", path, item)
ck.markCorrupt(path) ck.markCorrupt(path)
case <-ck.watchDone: case <-ck.watchDone:
@ -128,12 +134,14 @@ func (ck *fsckObj) watchMitigatedCorruptionsRead(path string) {
} }
// Check file for corruption // Check file for corruption
func (ck *fsckObj) file(path string) { func (ck *fsckObj) file(n *fusefrontend.Node) {
path := n.Path()
tlog.Debug.Printf("ck.file %q\n", path) tlog.Debug.Printf("ck.file %q\n", path)
attr, status := ck.fs.GetAttr(path, nil) var attr fuse.AttrOut
if !status.Ok() { errno := n.Getattr(nil, nil, &attr)
if errno != 0 {
ck.markCorrupt(path) ck.markCorrupt(path)
fmt.Printf("fsck: error stating file %q: %v\n", path, status) fmt.Printf("fsck: error stating file %q: %v\n", path, errno)
return return
} }
if attr.Nlink > 1 { if attr.Nlink > 1 {
@ -144,18 +152,19 @@ func (ck *fsckObj) file(path string) {
} }
ck.seenInodes[attr.Ino] = struct{}{} ck.seenInodes[attr.Ino] = struct{}{}
} }
ck.xattrs(path) ck.xattrs(n)
f, status := ck.fs.Open(path, syscall.O_RDONLY, nil) tmp, _, errno := n.Open(nil, syscall.O_RDONLY)
if !status.Ok() { if errno != 0 {
fmt.Printf("fsck: error opening file %q: %v\n", path, status) fmt.Printf("fsck: error opening file %q: %v\n", path, errno)
if status == fuse.EACCES && !runsAsRoot() { if errno == syscall.EACCES && !runsAsRoot() {
ck.markSkipped(path) ck.markSkipped(path)
} else { } else {
ck.markCorrupt(path) ck.markCorrupt(path)
} }
return return
} }
defer f.Release() f := tmp.(*fusefrontend.File2)
defer f.Release(nil)
// 128 kiB of zeros // 128 kiB of zeros
allZero := make([]byte, fuse.MAX_KERNEL_WRITE) allZero := make([]byte, fuse.MAX_KERNEL_WRITE)
buf := make([]byte, fuse.MAX_KERNEL_WRITE) buf := make([]byte, fuse.MAX_KERNEL_WRITE)
@ -165,10 +174,10 @@ func (ck *fsckObj) file(path string) {
defer func() { ck.watchDone <- struct{}{} }() defer func() { ck.watchDone <- struct{}{} }()
for { for {
tlog.Debug.Printf("ck.file: read %d bytes from offset %d\n", len(buf), off) tlog.Debug.Printf("ck.file: read %d bytes from offset %d\n", len(buf), off)
result, status := f.Read(buf, off) result, errno := f.Read(nil, buf, off)
if !status.Ok() { if errno != 0 {
ck.markCorrupt(path) ck.markCorrupt(path)
fmt.Printf("fsck: error reading file %q (inum %d): %v\n", path, inum(f), status) fmt.Printf("fsck: error reading file %q (inum %d): %v\n", path, inum(f), errno)
return return
} }
n := result.Size() n := result.Size()
@ -182,8 +191,7 @@ func (ck *fsckObj) file(path string) {
data := buf[:n] data := buf[:n]
if bytes.Equal(data, allZero) { if bytes.Equal(data, allZero) {
tlog.Debug.Printf("ck.file: trying to skip file hole\n") tlog.Debug.Printf("ck.file: trying to skip file hole\n")
f2 := f.(*fusefrontend.File) nextOff, err := f.SeekData(off)
nextOff, err := f2.SeekData(off)
if err == nil { if err == nil {
off = nextOff off = nextOff
} }
@ -195,7 +203,7 @@ func (ck *fsckObj) file(path string) {
func (ck *fsckObj) watchMitigatedCorruptionsListXAttr(path string) { func (ck *fsckObj) watchMitigatedCorruptionsListXAttr(path string) {
for { for {
select { select {
case item := <-ck.fs.MitigatedCorruptions: case item := <-ck.rootNode.MitigatedCorruptions:
fmt.Printf("fsck: corrupt xattr name on file %q: %q\n", path, item) fmt.Printf("fsck: corrupt xattr name on file %q: %q\n", path, item)
ck.markCorrupt(path + " xattr:" + item) ck.markCorrupt(path + " xattr:" + item)
case <-ck.watchDone: case <-ck.watchDone:
@ -205,22 +213,32 @@ func (ck *fsckObj) watchMitigatedCorruptionsListXAttr(path string) {
} }
// Check xattrs on file/dir at path // Check xattrs on file/dir at path
func (ck *fsckObj) xattrs(path string) { func (ck *fsckObj) xattrs(n *fusefrontend.Node) {
// Run ListXAttr() and catch transparently mitigated corruptions // Run ListXAttr() and catch transparently mitigated corruptions
path := n.Path()
go ck.watchMitigatedCorruptionsListXAttr(path) go ck.watchMitigatedCorruptionsListXAttr(path)
attrs, status := ck.fs.ListXAttr(path, nil) listBuf := make([]byte, 1024*1024)
cnt, errno := n.Listxattr(nil, listBuf)
ck.watchDone <- struct{}{} ck.watchDone <- struct{}{}
// Also catch non-mitigated corruptions // Also catch non-mitigated corruptions
if !status.Ok() { if errno != 0 {
fmt.Printf("fsck: error listing xattrs on %q: %v\n", path, status) fmt.Printf("fsck: error listing xattrs on %q: %v\n", path, errno)
ck.markCorrupt(path) ck.markCorrupt(path)
return return
} }
if cnt == 0 {
return
}
// Drop final trailing NULL byte
cnt--
listBuf = listBuf[:cnt]
attrs := bytes.Split(listBuf, []byte{0})
for _, a := range attrs { for _, a := range attrs {
_, status := ck.fs.GetXAttr(path, a, nil) getBuf := make([]byte, 1024*1024)
if !status.Ok() { _, errno := n.Getxattr(nil, string(a), getBuf)
fmt.Printf("fsck: error reading xattr %q from %q: %v\n", a, path, status) if errno != 0 {
if status == fuse.EACCES && !runsAsRoot() { fmt.Printf("fsck: error reading xattr %q from %q: %v\n", a, path, errno)
if errno == syscall.EACCES && !runsAsRoot() {
ck.markSkipped(path) ck.markSkipped(path)
} else { } else {
ck.markCorrupt(path) ck.markCorrupt(path)
@ -236,14 +254,15 @@ func fsck(args *argContainer) {
} }
args.allow_other = false args.allow_other = false
pfs, wipeKeys := initFuseFrontend(args) pfs, wipeKeys := initFuseFrontend(args)
fs := pfs.(*fusefrontend.FS) fs.NewNodeFS(pfs, &fs.Options{})
fs.MitigatedCorruptions = make(chan string) rn := pfs.(*fusefrontend.RootNode)
rn.MitigatedCorruptions = make(chan string)
ck := fsckObj{ ck := fsckObj{
fs: fs, rootNode: rn,
watchDone: make(chan struct{}), watchDone: make(chan struct{}),
seenInodes: make(map[uint64]struct{}), seenInodes: make(map[uint64]struct{}),
} }
ck.dir("") ck.dir(&rn.Node)
wipeKeys() wipeKeys()
if len(ck.corruptList) == 0 && len(ck.skippedList) == 0 { if len(ck.corruptList) == 0 && len(ck.skippedList) == 0 {
tlog.Info.Printf("fsck summary: no problems found\n") tlog.Info.Printf("fsck summary: no problems found\n")
@ -270,8 +289,8 @@ func (s sortableDirEntries) Less(i, j int) bool {
return strings.Compare(s[i].Name, s[j].Name) < 0 return strings.Compare(s[i].Name, s[j].Name) < 0
} }
func inum(f nodefs.File) uint64 { func inum(f *fusefrontend.File2) uint64 {
var a fuse.Attr var a fuse.AttrOut
f.GetAttr(&a) f.Getattr(nil, &a)
return a.Ino return a.Ino
} }

View File

@ -313,8 +313,7 @@ func main() {
} }
// "-fsck" // "-fsck"
if args.fsck { if args.fsck {
// TODO fsck(&args)
//fsck(&args)
os.Exit(0) os.Exit(0)
} }
} }

View File

@ -316,7 +316,6 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
log.Panic("reverse mode must use AES-SIV, everything else is insecure") log.Panic("reverse mode must use AES-SIV, everything else is insecure")
} }
rootNode = fusefrontend_reverse.NewRootNode(frontendArgs, cEnc, nameTransform) rootNode = fusefrontend_reverse.NewRootNode(frontendArgs, cEnc, nameTransform)
} else { } else {
rootNode = fusefrontend.NewRootNode(frontendArgs, cEnc, nameTransform) rootNode = fusefrontend.NewRootNode(frontendArgs, cEnc, nameTransform)
} }