From 3156098a5bf47e402043f4c3a8c4dcd0ecb17ba7 Mon Sep 17 00:00:00 2001 From: Pawel Rozlach Date: Tue, 4 Oct 2016 15:33:54 +0200 Subject: [PATCH] Fix file backend so that it properly removes nested secrets. This patch makes file backend properly remove nested secrets, without leaving empty directory artifacts, no matter how nested directories were. --- physical/file.go | 65 ++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/physical/file.go b/physical/file.go index 335c75e992667..3037c06689546 100644 --- a/physical/file.go +++ b/physical/file.go @@ -6,6 +6,7 @@ import ( "io" "os" "path/filepath" + "strings" "sync" log "github.com/mgutz/logxi/v1" @@ -39,48 +40,52 @@ func newFileBackend(conf map[string]string, logger log.Logger) (Backend, error) }, nil } -func (b *FileBackend) Delete(k string) error { +func (b *FileBackend) Delete(path string) error { b.l.Lock() defer b.l.Unlock() - path, key := b.path(k) - fullPath := filepath.Join(path, key) + basePath, key := b.path(path) + fullPath := filepath.Join(basePath, key) - // If the path doesn't exist return success; this is in line with Vault's - // expected behavior and we don't want to check for an empty directory if - // we couldn't even find the path in the first place. err := os.Remove(fullPath) - if err != nil { - if os.IsNotExist(err) { - return nil - } else { - return err - } + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("Failed to remove `%s`: %v", fullPath, err) } - // Check for the directory being empty and remove if so, with another - // additional guard for the path not existing - dir, err := os.Open(path) - if err != nil { - if os.IsNotExist(err) { - return nil - } else { - return err - } - } + err = b.cleanupLogicalPath(path) - list, err := dir.Readdir(1) - dir.Close() - if err != nil && err != io.EOF { - return err - } + return err +} - // If we have no entries, it's an empty directory; remove it - if err == io.EOF || list == nil || len(list) == 0 { - err = os.Remove(path) +// cleanupLogicalPath is used to remove all empty nodes, begining with deepest +// one, aborting on first non-empty one, up to top-level node. +func (b *FileBackend) cleanupLogicalPath(path string) error { + nodes := strings.Split(path, "/") + for i := len(nodes) - 1; i > 0; i-- { + fullPath := b.Path + "/" + strings.Join(nodes[:i], "/") + + dir, err := os.Open(fullPath) if err != nil { + if os.IsNotExist(err) { + return nil + } else { + return err + } + } + + list, err := dir.Readdir(1) + dir.Close() + if err != nil && err != io.EOF { return err } + + // If we have no entries, it's an empty directory; remove it + if err == io.EOF || list == nil || len(list) == 0 { + err = os.Remove(fullPath) + if err != nil { + return err + } + } } return nil