func (b *DirectoryBackend) ListAll() ([]string, error) {
	paths := make([]string, 0)
	err := filepath.Walk(b.root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		path = strings.TrimPrefix(path, b.root+string(os.PathSeparator))
		if path == lockFile || path == versionFile {
			return nil
		}
		if info.IsDir() {
			return nil
		}
		paths = append(paths, path)
		return nil
	})
	if err != nil {
		return nil, err
	}
	return paths, nil
}
ListAll enumerates all paths currently stored.
The paths are returned in lexicographical order.
package backend
import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"github.com/cossacklabs/acra/keystore/v2/keystore/filesystem/backend/api"
	log "github.com/sirupsen/logrus"
)
const serviceName = "keystore"
const directorySubsystemName = "directory-backend"
const (
	keyDirPerm  = os.FileMode(0700)
	keyFilePerm = os.FileMode(0600)
	versionPerm = os.FileMode(0644)
)
var (
	ErrNotDirectory       = errors.New("root key directory is not a directory")
	ErrInvalidPermissions = errors.New("invalid key directory access permissions")
	ErrInvalidVersion     = errors.New("invalid keystore version file content")
)
type DirectoryBackend struct {
	root string
	log  *log.Entry
	lock *fileLock
}
const (
	lockFile      = ".lock"
	versionFile   = "version"
	versionString = "Acra Keystore v2"
)
func CreateDirectoryBackend(root string) (*DirectoryBackend, error) {
	newLog := log.WithFields(log.Fields{
		"service":   serviceName,
		"subsystem": directorySubsystemName,
	})
	errLog := newLog.WithField("path", root)
	fi, err := os.Stat(root)
	if err == nil {
		if !fi.IsDir() {
			errLog.Debug("root key directory is not a directory")
			return nil, ErrNotDirectory
		}
		if fi.Mode().Perm() != keyDirPerm {
			errLog.WithFields(log.Fields{
				"actual-perm":   fi.Mode().Perm(),
				"expected-perm": keyDirPerm,
			}).
				Debugf("invalid access permissions on root key directory")
			return nil, ErrInvalidPermissions
		}
	} else {
		if os.IsNotExist(err) {
			err = os.MkdirAll(root, keyDirPerm)
			if err != nil {
				errLog.WithError(err).Debug("failed to create root key directory")
				return nil, err
			}
		} else {
			errLog.WithError(err).Debug("failed to stat root key directory")
			return nil, err
		}
	}
	err = checkVersionFile(root)
	if err != nil {
		errLog.WithError(err).Debug("failed to create version file")
		return nil, err
	}
	lock, err := newFileLock(filepath.Join(root, lockFile))
	if err != nil {
		errLog.WithError(err).Debug("failed to create lock file")
		return nil, err
	}
	return &DirectoryBackend{
		root: root,
		log:  newLog,
		lock: lock,
	}, nil
}
func OpenDirectoryBackend(root string) (*DirectoryBackend, error) {
	newLog := log.WithFields(log.Fields{
		"service":   serviceName,
		"subsystem": directorySubsystemName,
	})
	errLog := newLog.WithField("path", root)
	fi, err := os.Stat(root)
	if err != nil {
		errLog.WithError(err).Debug("failed to stat root key directory")
		if os.IsNotExist(err) {
			err = api.ErrNotExist
		}
		return nil, err
	}
	if !fi.IsDir() {
		errLog.Debug("root key directory is not a directory")
		return nil, ErrNotDirectory
	}
	if fi.Mode().Perm() != keyDirPerm {
		errLog.WithFields(log.Fields{
			"actual-perm":   fi.Mode().Perm(),
			"expected-perm": keyDirPerm,
		}).
			Debugf("invalid access permissions on root key directory")
		return nil, ErrInvalidPermissions
	}
	err = CheckDirectoryVersion(root)
	if err != nil {
		errLog.WithError(err).Debug("not a keystore")
		return nil, err
	}
	lock, err := newFileLock(filepath.Join(root, lockFile))
	if err != nil {
		errLog.WithError(err).Debug("failed to create lock file")
		return nil, err
	}
	return &DirectoryBackend{
		root: root,
		log:  newLog,
		lock: lock,
	}, nil
}
func versionFilePath(rootDir string) string {
	return filepath.Join(rootDir, versionFile)
}
func CheckDirectoryVersion(rootDir string) error {
	content, err := ioutil.ReadFile(versionFilePath(rootDir))
	if err != nil {
		return err
	}
	if string(content) != versionString {
		return ErrInvalidVersion
	}
	return nil
}
func checkVersionFile(rootDir string) error {
	err := CheckDirectoryVersion(rootDir)
	if err == nil {
		return nil
	}
	if !os.IsNotExist(err) {
		return err
	}
	return createVersionFile(rootDir)
}
func createVersionFile(rootDir string) (err error) {
	path := versionFilePath(rootDir)
	file, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, versionPerm)
	if err != nil {
		return err
	}
	defer func() {
		err2 := file.Close()
		if err == nil {
			err = err2
		}
	}()
	_, err = file.WriteString(versionString)
	if err != nil {
		return err
	}
	err = file.Sync()
	if err != nil {
		return err
	}
	return nil
}
func (b *DirectoryBackend) Close() error {
	err := b.lock.Close()
	if err != nil {
		b.log.WithError(err).Warn("failed to close lock file")
	}
	return nil
}
var pathSeparators = strings.NewReplacer(
	"/", string(os.PathSeparator),
	"\\", string(os.PathSeparator),
)
func (b *DirectoryBackend) osPath(path string) (string, error) {
	fullPath := filepath.Join(b.root, pathSeparators.Replace(path))
	if fullPath != filepath.Clean(fullPath) {
		b.log.WithField("path", path).Warn("invalid key path used")
		return "", api.ErrInvalidPath
	}
	return fullPath, nil
}
func (b *DirectoryBackend) Lock() error {
	return b.lock.Lock()
}
func (b *DirectoryBackend) Unlock() error {
	return b.lock.Unlock()
}
func (b *DirectoryBackend) RLock() error {
	return b.lock.RLock()
}
func (b *DirectoryBackend) RUnlock() error {
	return b.lock.RUnlock()
}
func (b *DirectoryBackend) Get(path string) ([]byte, error) {
	fullPath, err := b.osPath(path)
	if err != nil {
		return nil, err
	}
	data, err := ioutil.ReadFile(fullPath)
	if err != nil {
		b.log.WithError(err).WithField("path", fullPath).Debug("failed to read key data")
		if os.IsNotExist(err) {
			err = api.ErrNotExist
		}
		return nil, err
	}
	return data, nil
}
func (b *DirectoryBackend) Put(path string, data []byte) error {
	fullPath, err := b.osPath(path)
	if err != nil {
		return err
	}
	directory := filepath.Dir(fullPath)
	err = os.MkdirAll(directory, keyDirPerm)
	if err != nil {
		b.log.WithError(err).WithField("path", directory).Debug("failed to create key directory")
		if os.IsNotExist(err) {
			err = api.ErrNotExist
		}
		return err
	}
	log := b.log.WithField("path", fullPath)
	file, err := os.OpenFile(fullPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, keyFilePerm)
	if err != nil {
		log.WithError(err).Debug("failed to create key file")
		if os.IsExist(err) {
			err = api.ErrExist
		}
		return err
	}
	defer func() {
		if file != nil {
			err := file.Close()
			if err != nil {
				log.WithError(err).Debug("failed to close key file")
			}
			err = os.Remove(fullPath)
			if err != nil {
				log.WithError(err).Warn("failed to remove temporary key file")
			}
		}
	}()
	_, err = file.Write(data)
	if err != nil {
		log.WithError(err).Debug("failed to write key data")
		return err
	}
	err = file.Sync()
	if err != nil {
		log.WithError(err).Debug("failed to sync key data")
		return err
	}
	err = file.Close()
	if err != nil {
		log.WithError(err).Debug("failed to close key file")
		return err
	}
	file = nil
	return nil
}