From 2749e623a0ce6df9b73054460a215c4f09ee9a9d Mon Sep 17 00:00:00 2001 From: Son Roy Almerol Date: Tue, 11 Feb 2025 10:42:15 -0500 Subject: [PATCH 1/2] just straight up do xxhash on path + use relative path on handles --- internal/agent/nfs/vssfs/fileid_helpers.go | 59 ++-------------------- internal/agent/nfs/vssfs/handler.go | 43 +++++++--------- 2 files changed, 22 insertions(+), 80 deletions(-) diff --git a/internal/agent/nfs/vssfs/fileid_helpers.go b/internal/agent/nfs/vssfs/fileid_helpers.go index 90565f5d..0437cbe9 100644 --- a/internal/agent/nfs/vssfs/fileid_helpers.go +++ b/internal/agent/nfs/vssfs/fileid_helpers.go @@ -1,67 +1,14 @@ package vssfs import ( - "path/filepath" - "strings" - "github.com/zeebo/xxh3" ) func generateFullPathID(path string) uint64 { - hash := xxh3.HashString(path) - depth := uint64(getPathDepth(path)) - length := uint64(len(path)) - ext := getExtensionHash(path) - - return (depth&0x3f)<<58 | // 6 bits depth (max 63) - (length&0x3ff)<<48 | // 10 bits length (max 1023) - (uint64(ext)&0xff)<<40 | // 8 bits extension - (hash>>24)&0xffffffffff // 40 bits hash (upper 40 of 64) + return xxh3.HashString(path) } +// quickMatch recomputes the hash and compares it. func quickMatch(id uint64, path string) bool { - // Check length first (cheapest operation) - pathLen := uint64(len(path)) - if (id >> 48 & 0x3ff) != pathLen { - return false - } - - // Then check depth - depth := uint64(getPathDepth(path)) - if (id >> 58) != depth { - return false - } - - // Finally check partial hash (adjust bit positions to match generateFullPathID) - hash := xxh3.HashString(path) - return (id & 0xffffffffff) == ((hash >> 24) & 0xffffffffff) -} - -func getPathDepth(path string) int { - // Count both types of separators for cross-platform support - return strings.Count(path, "/") + strings.Count(path, "\\") -} - -func getParentPath(path string) string { - parent := filepath.Dir(path) - if parent == "." || parent == path { - return "" - } - return parent -} - -func getFileName(path string) string { - return filepath.Base(path) -} - -func getExtensionHash(path string) uint8 { - ext := filepath.Ext(path) - if ext == "" { - return 0 - } - if ext[0] == '.' { - ext = ext[1:] // Remove leading dot - } - h := xxh3.HashString(ext) - return uint8(h>>56) ^ uint8(h>>48) ^ uint8(h>>40) ^ uint8(h>>32) + return generateFullPathID(path) == id } diff --git a/internal/agent/nfs/vssfs/handler.go b/internal/agent/nfs/vssfs/handler.go index d31a0966..c97c33bb 100644 --- a/internal/agent/nfs/vssfs/handler.go +++ b/internal/agent/nfs/vssfs/handler.go @@ -42,11 +42,11 @@ func (h *VSSIDHandler) getHandle(key uint64) (string, bool) { return handle, ok } -func (h *VSSIDHandler) storeHandle(key uint64, path string) { +func (h *VSSIDHandler) storeHandle(key uint64, relativePath string) { h.activeHandlesMu.Lock() defer h.activeHandlesMu.Unlock() - h.activeHandles[key] = path + h.activeHandles[key] = relativePath } func (h *VSSIDHandler) ToHandle(f billy.Filesystem, path []string) []byte { @@ -55,32 +55,32 @@ func (h *VSSIDHandler) ToHandle(f billy.Filesystem, path []string) []byte { return nil } - // Handle root directory specially + // Handle root directory specially. + // For the root, we store an empty relative path. if len(path) == 0 || (len(path) == 1 && path[0] == "") { - return h.createHandle(RootHandleID, vssFS.Root()) + return h.createHandle(RootHandleID, "") } - // Convert NFS path to Windows format - winPath := filepath.Join(path...) - fullPath := filepath.Join(vssFS.Root(), winPath) + // Compute the relative path from the filesystem's root. + relativePath := filepath.Join(path...) - // Get or create stable ID - info, err := vssFS.Stat(winPath) + // Get the stable file ID using the relative path. + info, err := vssFS.Stat(relativePath) if err != nil { return nil } fileID := info.(*VSSFileInfo).stableID - return h.createHandle(fileID, fullPath) + return h.createHandle(fileID, relativePath) } -func (h *VSSIDHandler) createHandle(fileID uint64, fullPath string) []byte { - // Add to cache if not exists +func (h *VSSIDHandler) createHandle(fileID uint64, relativePath string) []byte { + // Add to cache if not already present. if _, exists := h.getHandle(fileID); !exists { - h.storeHandle(fileID, fullPath) + h.storeHandle(fileID, relativePath) } - // Convert ID to 8-byte handle + // Convert fileID to an 8-byte handle. handle := make([]byte, 8) binary.BigEndian.PutUint64(handle, fileID) return handle @@ -96,21 +96,16 @@ func (h *VSSIDHandler) FromHandle(handle []byte) (billy.Filesystem, []string, er return h.vssFS, []string{}, nil } - // Retrieve cached path - fullPath, exists := h.getHandle(fileID) + // Retrieve the stored relative path. + relativePath, exists := h.getHandle(fileID) if !exists { return nil, nil, &nfs.NFSStatusError{NFSStatus: nfs.NFSStatusStale} } - // Convert Windows path to NFS components - relativePath := strings.TrimPrefix( - filepath.ToSlash(fullPath), - filepath.ToSlash(h.vssFS.Root())+"/", - ) - var parts []string if relativePath != "" { - parts = strings.Split(relativePath, "/") + // Convert to slash format and split into components. + parts = strings.Split(filepath.ToSlash(relativePath), "/") } return h.vssFS, parts, nil } @@ -120,7 +115,7 @@ func (h *VSSIDHandler) HandleLimit() int { } func (h *VSSIDHandler) InvalidateHandle(fs billy.Filesystem, handle []byte) error { - // No-op for read-only filesystem + // No-op for read-only filesystem. return nil } From 1497556d354e54a58e227e2a0b8d0e1de7a8f1e8 Mon Sep 17 00:00:00 2001 From: Son Roy Almerol Date: Tue, 11 Feb 2025 10:49:19 -0500 Subject: [PATCH 2/2] fix vssfs full path requirements --- internal/agent/nfs/vssfs/handler.go | 48 +++++++++++++++++++---------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/internal/agent/nfs/vssfs/handler.go b/internal/agent/nfs/vssfs/handler.go index c97c33bb..a3b70c1c 100644 --- a/internal/agent/nfs/vssfs/handler.go +++ b/internal/agent/nfs/vssfs/handler.go @@ -18,15 +18,21 @@ const ( RootHandleID = uint64(0) // Reserved ID for root directory ) -// VSSIDHandler uses VSSFS's stable file IDs for handle management +// VSSIDHandler uses VSSFS's stable file IDs for handle management. +// It caches relative paths (from the filesystem root) and recreates the full +// path (by joining vssFS.Root()) when needed. type VSSIDHandler struct { nfs.Handler - vssFS *VSSFS + vssFS *VSSFS + activeHandlesMu sync.RWMutex - activeHandles map[uint64]string + // Maps fileID to the file's relative path. + activeHandles map[uint64]string } -func NewVSSIDHandler(vssFS *VSSFS, underlyingHandler nfs.Handler) (*VSSIDHandler, error) { +func NewVSSIDHandler(vssFS *VSSFS, underlyingHandler nfs.Handler) ( + *VSSIDHandler, error, +) { return &VSSIDHandler{ Handler: underlyingHandler, vssFS: vssFS, @@ -38,8 +44,8 @@ func (h *VSSIDHandler) getHandle(key uint64) (string, bool) { h.activeHandlesMu.RLock() defer h.activeHandlesMu.RUnlock() - handle, ok := h.activeHandles[key] - return handle, ok + path, ok := h.activeHandles[key] + return path, ok } func (h *VSSIDHandler) storeHandle(key uint64, relativePath string) { @@ -55,32 +61,33 @@ func (h *VSSIDHandler) ToHandle(f billy.Filesystem, path []string) []byte { return nil } - // Handle root directory specially. - // For the root, we store an empty relative path. + // Special-case the root directory: store an empty relative path. if len(path) == 0 || (len(path) == 1 && path[0] == "") { return h.createHandle(RootHandleID, "") } - // Compute the relative path from the filesystem's root. + // Compute the relative path from the provided NFS path components. relativePath := filepath.Join(path...) + // Build the full path by joining the filesystem root with the relative path. + fullPath := filepath.Join(vssFS.Root(), relativePath) - // Get the stable file ID using the relative path. - info, err := vssFS.Stat(relativePath) + // vssFS methods require the full path. + info, err := vssFS.Stat(fullPath) if err != nil { return nil } fileID := info.(*VSSFileInfo).stableID + // Only store the relative path in the cache. return h.createHandle(fileID, relativePath) } func (h *VSSIDHandler) createHandle(fileID uint64, relativePath string) []byte { - // Add to cache if not already present. if _, exists := h.getHandle(fileID); !exists { h.storeHandle(fileID, relativePath) } - // Convert fileID to an 8-byte handle. + // Convert the 64-bit fileID to an 8-byte handle. handle := make([]byte, 8) binary.BigEndian.PutUint64(handle, fileID) return handle @@ -96,16 +103,23 @@ func (h *VSSIDHandler) FromHandle(handle []byte) (billy.Filesystem, []string, er return h.vssFS, []string{}, nil } - // Retrieve the stored relative path. relativePath, exists := h.getHandle(fileID) if !exists { return nil, nil, &nfs.NFSStatusError{NFSStatus: nfs.NFSStatusStale} } + // Recreate the full path when needed. + fullPath := filepath.Join(h.vssFS.Root(), relativePath) + + // Strip the filesystem root from the full path to get the NFS components. + nfsRelative := strings.TrimPrefix( + filepath.ToSlash(fullPath), + filepath.ToSlash(h.vssFS.Root())+"/", + ) + var parts []string - if relativePath != "" { - // Convert to slash format and split into components. - parts = strings.Split(filepath.ToSlash(relativePath), "/") + if nfsRelative != "" { + parts = strings.Split(nfsRelative, "/") } return h.vssFS, parts, nil }