osutil: fix Windows use old Camlistore-named APPDATA directory

Fixes #1150

Change-Id: Ia2843437abbaba96e936c1c0f46bf8b171a2e28e
This commit is contained in:
Brad Fitzpatrick 2018-05-07 19:58:36 -07:00
parent 19079950b0
commit 245bb63fff
4 changed files with 172 additions and 39 deletions

View File

@ -90,22 +90,56 @@ func makeCacheDir() {
}
}
func CamliVarDir() string {
func upperFirst(s string) string {
return strings.ToUpper(s[:1]) + s[1:]
}
func CamliVarDir() (string, error) {
oldName := camliVarDirOf("camlistore")
newName := camliVarDirOf("perkeep")
if fi, err := os.Lstat(oldName); err == nil && fi.IsDir() && oldName != newName {
n := numRegularFilesUnder(oldName)
if n == 0 {
log.Printf("removing old, empty var directory %s", oldName)
os.RemoveAll(oldName)
} else {
return "", fmt.Errorf("Now that Perkeep has been renamed from Camlistore, you need to rename your data directory from %s to %s", oldName, newName)
}
}
return newName, nil
}
func numRegularFilesUnder(dir string) (n int) {
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if fi != nil && fi.Mode().IsRegular() {
n++
}
return nil
})
return
}
func camliVarDirOf(name string) string {
if d := os.Getenv("CAMLI_VAR_DIR"); d != "" {
return d
}
failInTests()
switch runtime.GOOS {
case "windows":
return filepath.Join(os.Getenv("APPDATA"), "Camlistore")
return filepath.Join(os.Getenv("APPDATA"), upperFirst(name))
case "darwin":
return filepath.Join(HomeDir(), "Library", "Camlistore")
return filepath.Join(HomeDir(), "Library", upperFirst(name))
}
return filepath.Join(HomeDir(), "var", "camlistore")
return filepath.Join(HomeDir(), "var", name)
}
func CamliBlobRoot() string {
return filepath.Join(CamliVarDir(), "blobs")
func CamliBlobRoot() (string, error) {
varDir, err := CamliVarDir()
if err != nil {
return "", err
}
return filepath.Join(varDir, "blobs"), nil
}
// RegisterConfigDirFunc registers a func f to return the Perkeep configuration directory.
@ -127,36 +161,41 @@ func CamliConfigDir() string {
}
failInTests()
return camliConfigDir()
}
func camliConfigDir() string {
if fi, err := os.Lstat(oldCamliConfigDir()); err == nil && fi.IsDir() {
fmt.Fprintf(os.Stderr, "Error: old configuration directory detected. Not running until it's moved.\nRename %s to %s\n",
oldCamliConfigDir(), perkeepConfigDir())
os.Exit(1)
dir, err := perkeepConfigDir()
if err != nil {
log.Fatalf("PerkeepConfigDir: %v", err)
}
return perkeepConfigDir()
return dir
}
func perkeepConfigDir() string {
func perkeepConfigDir() (string, error) {
oldName := configDirNamed("camlistore")
newName := configDirNamed("perkeep")
if fi, err := os.Lstat(oldName); err == nil && fi.IsDir() && oldName != newName {
n := numRegularFilesUnder(oldName)
if n == 0 {
log.Printf("removing old, empty config dir %s", oldName)
os.RemoveAll(oldName)
} else {
return "", fmt.Errorf("Error: old configuration directory detected. Not running until it's moved.\nRename %s to %s\n", oldName, newName)
}
}
return newName, nil
}
var configDirNamedTestHook func(string) string
func configDirNamed(name string) string {
if h := configDirNamedTestHook; h != nil {
return h(name)
}
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "Perkeep")
return filepath.Join(os.Getenv("APPDATA"), upperFirst(name))
}
if xdg := os.Getenv("XDG_CONFIG_HOME"); xdg != "" {
return filepath.Join(xdg, "perkeep")
return filepath.Join(xdg, name)
}
return filepath.Join(HomeDir(), ".config", "perkeep")
}
func oldCamliConfigDir() string {
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "Camlistore")
}
if xdg := os.Getenv("XDG_CONFIG_HOME"); xdg != "" {
return filepath.Join(xdg, "camlistore")
}
return filepath.Join(HomeDir(), ".config", "camlistore")
return filepath.Join(HomeDir(), ".config", name)
}
func UserServerConfigPath() string {
@ -207,13 +246,21 @@ func ExplicitSecretRingFile() (string, bool) {
// DefaultSecretRingFile returns the path to the default GPG secret
// keyring. It is not influenced by any flag or CAMLI* env var.
func DefaultSecretRingFile() string {
return filepath.Join(camliConfigDir(), "identity-secring.gpg")
dir, err := perkeepConfigDir()
if err != nil {
log.Fatalf("couldn't compute DefaultSecretRingFile: %v", err)
}
return filepath.Join(dir, "identity-secring.gpg")
}
// identitySecretRing returns the path to the default GPG
// secret keyring. It is still affected by CAMLI_CONFIG_DIR.
func identitySecretRing() string {
return filepath.Join(CamliConfigDir(), "identity-secring.gpg")
dir, err := perkeepConfigDir()
if err != nil {
log.Fatalf("couldn't compute DefaultSecretRingFile: %v", err)
}
return filepath.Join(dir, "identity-secring.gpg")
}
// SecretRingFile returns the path to the user's GPG secret ring file.

View File

@ -19,8 +19,10 @@ package osutil
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"testing"
)
@ -140,3 +142,75 @@ func TestOpenCamliIncludePath(t *testing.T) {
os.Setenv("CAMLI_INCLUDE_PATH", "/not/a/camli/config/dir"+sep+td+sep+"/another/fake/camli/dir")
checkOpen(t, name)
}
func TestCamPkConfigMigration(t *testing.T) {
oldFuncs := configDirFuncs
defer func() {
configDirFuncs = oldFuncs
configDirNamedTestHook = nil
log.SetOutput(os.Stderr)
}()
log.SetOutput(ioutil.Discard)
td, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(td)
configDirNamedTestHook = func(name string) string {
return filepath.Join(td, name)
}
oldDir := filepath.Join(td, "camlistore")
newDir := filepath.Join(td, "perkeep")
if err := os.MkdirAll(filepath.Join(oldDir, "blobs", "foo", "sub"), 0755); err != nil {
t.Fatal(err)
}
calls := 0
RegisterConfigDirFunc(func() string {
calls++
log.Printf("call %d", calls)
switch calls {
case 1:
return oldDir
case 2:
return newDir
}
t.Fatalf("unexpected %d calls to get config dir", calls)
return ""
})
got, err := perkeepConfigDir()
if err != nil {
t.Fatal(err)
}
if got != newDir {
t.Errorf("first call = %v; want %v", got, newDir)
}
if fi, err := os.Lstat(oldDir); !os.IsNotExist(err) {
t.Errorf("Lstat = %v, %v; want IsNotExist error", fi, err)
}
// Now try with some regular file in the old dir.
if err := os.MkdirAll(filepath.Join(oldDir, "blobs"), 0755); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(filepath.Join(oldDir, "blobs/x.dat"), []byte("hi"), 0644); err != nil {
t.Fatal(err)
}
_, err = perkeepConfigDir()
if err == nil {
t.Error("unexpected success looking up config dir after the old one had a file in it")
} else if !strings.Contains(err.Error(), "old configuration directory detected") {
t.Errorf("expected migration error; got: %v", err)
}
if fi, err := os.Lstat(oldDir); err != nil || !fi.IsDir() {
t.Errorf("error looking up old directory; want valid directory. Got: %v, %v", fi, err)
}
}

View File

@ -1240,16 +1240,24 @@ var defaultBaseConfig = serverconfig.Config{
// leveldb. If filePath already exists, it is overwritten.
func WriteDefaultConfigFile(filePath string, useSQLite bool) error {
conf := defaultBaseConfig
blobDir := osutil.CamliBlobRoot()
blobDir, err := osutil.CamliBlobRoot()
if err != nil {
return err
}
varDir, err := osutil.CamliVarDir()
if err != nil {
return err
}
if err := wkfs.MkdirAll(blobDir, 0700); err != nil {
return fmt.Errorf("Could not create default blobs directory: %v", err)
}
conf.BlobPath = blobDir
conf.PackRelated = true
if useSQLite {
conf.SQLite = filepath.Join(osutil.CamliVarDir(), "index.sqlite")
conf.SQLite = filepath.Join(varDir, "index.sqlite")
} else {
conf.LevelDB = filepath.Join(osutil.CamliVarDir(), "index.leveldb")
conf.LevelDB = filepath.Join(varDir, "index.leveldb")
}
keyID, secretRing, err := getOrMakeKeyring()

View File

@ -45,11 +45,15 @@ func TestStarts(t *testing.T) {
if _, err := os.Stat(osutil.CamliConfigDir()); !os.IsNotExist(err) {
t.Fatalf("expected conf dir %q to not exist", osutil.CamliConfigDir())
}
if !strings.Contains(osutil.CamliBlobRoot(), td) {
t.Fatalf("blob root %q should contain the temp dir %q", osutil.CamliBlobRoot(), td)
blobRoot, err := osutil.CamliBlobRoot()
if err != nil {
t.Fatal(err)
}
if _, err := os.Stat(osutil.CamliBlobRoot()); !os.IsNotExist(err) {
t.Fatalf("expected blobroot dir %q to not exist", osutil.CamliBlobRoot())
if !strings.Contains(blobRoot, td) {
t.Fatalf("blob root %q should contain the temp dir %q", blobRoot, td)
}
if _, err := os.Stat(blobRoot); !os.IsNotExist(err) {
t.Fatalf("expected blobroot dir %q to not exist", blobRoot)
}
if fi, err := os.Stat(osutil.UserServerConfigPath()); !os.IsNotExist(err) {
t.Errorf("expected no server config file; got %v, %v", fi, err)
@ -57,7 +61,7 @@ func TestStarts(t *testing.T) {
mkdir(t, confDir)
*flagOpenBrowser = false
*flagListen = ":0"
*flagListen = "localhost:0"
up := make(chan struct{})
down := make(chan struct{})