From fe84cbdc9df4bc8513da3f6f700c650fe93b3daa Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 6 Mar 2025 12:00:08 +0000 Subject: [PATCH] serve nfs: change the format of --nfs-cache-type symlink file handles This is an backwards incompatible change which will invalidate the current handles. This change adds a 4 byte big endian length prefix to the handles so we can in future suffix extra info on the handles. This needed to be 4 bytes as Linux does not like File handles which aren't multiples of 4 bytes long. --- cmd/serve/nfs/symlink_cache_linux.go | 35 +++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/cmd/serve/nfs/symlink_cache_linux.go b/cmd/serve/nfs/symlink_cache_linux.go index 0dbe42152..8002824ff 100644 --- a/cmd/serve/nfs/symlink_cache_linux.go +++ b/cmd/serve/nfs/symlink_cache_linux.go @@ -27,6 +27,7 @@ package nfs import ( "bytes" + "encoding/binary" "errors" "fmt" "os" @@ -85,6 +86,31 @@ func (dh *diskHandler) makeSymlinkCache() error { return nil } +// Prefixes a []byte with its length as a 4-byte big-endian integer. +func addLengthPrefix(data []byte) []byte { + length := uint32(len(data)) + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.BigEndian, length) + if err != nil { + // This should never fail + panic(err) + } + buf.Write(data) + return buf.Bytes() +} + +// Removes the 4-byte big-endian length prefix from a []byte. +func removeLengthPrefix(data []byte) ([]byte, error) { + if len(data) < 4 { + return nil, errors.New("file handle too short") + } + length := binary.BigEndian.Uint32(data[:4]) + if int(length) != len(data)-4 { + return nil, errors.New("file handle invalid length") + } + return data[4 : 4+length], nil +} + // Write the fullPath into cachePath returning the possibly updated fh // // This writes the fullPath into the file with the cachePath given and @@ -115,7 +141,8 @@ func (dh *diskHandler) symlinkCacheWrite(fh []byte, cachePath string, fullPath s dh.handleType = handle.Type() } - return handle.Bytes(), nil + // Adjust the raw handle so it has a length prefix + return addLengthPrefix(handle.Bytes()), nil } // Read the contents of (fh, cachePath) @@ -128,6 +155,12 @@ func (dh *diskHandler) symlinkCacheWrite(fh []byte, cachePath string, fullPath s func (dh *diskHandler) symlinkCacheRead(fh []byte, cachePath string) (fullPath []byte, err error) { //defer log.Trace(nil, "fh=%x, cachePath=%q", fh, cachePath)("fullPath=%q, err=%v", &fullPath, &err) + // First check and remove the file handle prefix length + fh, err = removeLengthPrefix(fh) + if err != nil { + return nil, fmt.Errorf("symlink cache open by handle at: %w", err) + } + // Find the file with the handle passed in handle := unix.NewFileHandle(dh.handleType, fh) fd, err := unix.OpenByHandleAt(unix.AT_FDCWD, handle, unix.O_RDONLY|unix.O_PATH|unix.O_NOFOLLOW) // needs O_PATH for symlinks