From 9ecf2d1a3f69e3d995012073afe3fc664bd928f2 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Thu, 25 May 2017 21:33:16 +0200 Subject: [PATCH] fusefrontend_reverse: store derived values for hard-linked files With hard links, the path to a file is not unique. This means that the ciphertext data depends on the path that is used to access the files. Fix that by storing the derived values when we encounter a hard-linked file. This means that the first path wins. --- internal/fusefrontend_reverse/rfile.go | 46 ++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/internal/fusefrontend_reverse/rfile.go b/internal/fusefrontend_reverse/rfile.go index 71d35a1..9a680bb 100644 --- a/internal/fusefrontend_reverse/rfile.go +++ b/internal/fusefrontend_reverse/rfile.go @@ -5,6 +5,10 @@ import ( "encoding/binary" "io" "os" + "syscall" + + // In newer Go versions, this has moved to just "sync/syncmap". + "golang.org/x/sync/syncmap" "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" @@ -26,6 +30,13 @@ type reverseFile struct { contentEnc *contentenc.ContentEnc } +var inodeTable syncmap.Map + +type derivedIVContainer struct { + id []byte + block0IV []byte +} + func (rfs *ReverseFS) newFile(relPath string, flags uint32) (nodefs.File, fuse.Status) { absPath, err := rfs.abs(rfs.decryptPath(relPath)) if err != nil { @@ -35,16 +46,45 @@ func (rfs *ReverseFS) newFile(relPath string, flags uint32) (nodefs.File, fuse.S if err != nil { return nil, fuse.ToStatus(err) } - id := derivePathIV(relPath, ivPurposeFileID) + var st syscall.Stat_t + err = syscall.Fstat(int(fd.Fd()), &st) + if err != nil { + tlog.Warn.Printf("newFile: Fstat error: %v", err) + return nil, fuse.ToStatus(err) + } + // See if we have that inode number already in the table + // (even if Nlink has dropped to 1) + var derivedIVs derivedIVContainer + v, found := inodeTable.Load(st.Ino) + if found { + tlog.Debug.Printf("ino%d: newFile: found in the inode table", st.Ino) + derivedIVs = v.(derivedIVContainer) + } else { + derivedIVs.id = derivePathIV(relPath, ivPurposeFileID) + derivedIVs.block0IV = derivePathIV(relPath, ivPurposeBlock0IV) + // Nlink > 1 means there is more than one path to this file. + // Store the derived values so we always return the same data, + // regardless of the path that is used to access the file. + // This means that the first path wins. + if st.Nlink > 1 { + v, found = inodeTable.LoadOrStore(st.Ino, derivedIVs) + if found { + // Another thread has stored a different value before we could. + derivedIVs = v.(derivedIVContainer) + } else { + tlog.Debug.Printf("ino%d: newFile: Nlink=%d, stored in the inode table", st.Ino, st.Nlink) + } + } + } header := contentenc.FileHeader{ Version: contentenc.CurrentVersion, - ID: id, + ID: derivedIVs.id, } return &reverseFile{ File: nodefs.NewDefaultFile(), fd: fd, header: header, - block0IV: derivePathIV(relPath, ivPurposeBlock0IV), + block0IV: derivedIVs.block0IV, contentEnc: rfs.contentEnc, }, fuse.OK }