<commit_before>/*
 * Copyright 2020, Cossack Labs Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package backend

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/cossacklabs/acra/keystore/v2/keystore/filesystem/backend/api"
	log "github.com/sirupsen/logrus"
)

const serviceName = "keystore"
const directorySubsystemName = "directory-backend"

// Permissions used to create new files and directories.
// They are not enforced for existing storage since we verify integrity by other means.
const (
	keyDirPerm  = os.FileMode(0700)
	keyFilePerm = os.FileMode(0600)
	versionPerm = os.FileMode(0644)
)

// Errors returned by DirectoryBackend:
var (
	ErrNotDirectory       = errors.New("root key directory is not a directory")
	ErrInvalidPermissions = errors.New("invalid key directory access permissions")
	ErrInvalidVersion     = errors.New("invalid key store version file content")
)

// DirectoryBackend keeps data in filesystem directory hierarchy.
type DirectoryBackend struct {
	root string
	log  *log.Entry
	lock *os.File
}

const (
	lockFile      = ".lock"
	versionFile   = "version"
	versionString = "Acra Key Store v2"
)

// CreateDirectoryBackend opens a directory backend at given root path.
// The root directory will be created if it does not exist.
func CreateDirectoryBackend(root string) (*DirectoryBackend, error) {
	newLog := log.WithFields(log.Fields{
		"service":   serviceName,
		"subsystem": directorySubsystemName,
	})
	errLog := newLog.WithField("path", root)
	// Make sure the root directory stays the same even if somene changes current directory.
	root, err := filepath.Abs(root)
	if err != nil {
		errLog.WithError(err).Debug("failed to get absolute root key directory")
		return nil, err
	}
	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 := os.Create(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
}

// OpenDirectoryBackend opens an existing directory backend at given root path.
func OpenDirectoryBackend(root string) (*DirectoryBackend, error) {
	newLog := log.WithFields(log.Fields{
		"service":   serviceName,
		"subsystem": directorySubsystemName,
	})
	errLog := newLog.WithField("path", root)
	// Make sure the root directory stays the same even if somene changes current directory.
	root, err := filepath.Abs(root)
	if err != nil {
		errLog.WithError(err).Debug("failed to get absolute root key directory")
		return nil, err
	}
	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 key store")
		return nil, err
	}
	lock, err := os.Create(filepath.Join(root, lockFile))
	if err != nil {<commit_msg>Don't make key store paths absolute (#368)<commit_after>/*
 * Copyright 2020, Cossack Labs Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package backend

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/cossacklabs/acra/keystore/v2/keystore/filesystem/backend/api"
	log "github.com/sirupsen/logrus"
)

const serviceName = "keystore"
const directorySubsystemName = "directory-backend"

// Permissions used to create new files and directories.
// They are not enforced for existing storage since we verify integrity by other means.
const (
	keyDirPerm  = os.FileMode(0700)
	keyFilePerm = os.FileMode(0600)
	versionPerm = os.FileMode(0644)
)

// Errors returned by DirectoryBackend:
var (
	ErrNotDirectory       = errors.New("root key directory is not a directory")
	ErrInvalidPermissions = errors.New("invalid key directory access permissions")
	ErrInvalidVersion     = errors.New("invalid key store version file content")
)

// DirectoryBackend keeps data in filesystem directory hierarchy.
type DirectoryBackend struct {
	root string
	log  *log.Entry
	lock *os.File
}

const (
	lockFile      = ".lock"
	versionFile   = "version"
	versionString = "Acra Key Store v2"
)

// CreateDirectoryBackend opens a directory backend at given root path.
// The root directory will be created if it does not exist.
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 := os.Create(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
}

// OpenDirectoryBackend opens an existing directory backend at given root path.
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 key store")
		return nil, err
	}
	lock, err := os.Create(filepath.Join(root, lockFile))
	if err != nil {
{'fraction_non_alphanumeric': 0.11210711448995801, 'fraction_numerical': 0.005333030750028367, 'mean_word_length': 3.9433538979248457, 'pattern_counts': {'":': 16, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3829418', 'n_tokens_mistral': 2940, 'n_tokens_neox': 2764, 'n_words': 886}