From f69a39cff50c9774d4f5283c8fe3c9b2c0649055 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sat, 14 Mar 2015 11:56:45 +0100 Subject: [PATCH] Add ID to repository This allows identifying a repository regardless if it's accessed over SFTP or locally. Introduced for having a per-repository cache. --- backend/interface.go | 5 +++ backend/local.go | 82 +++++++++++++++++++++++++++++++++++++------- backend/sftp.go | 81 ++++++++++++++++++++++++++++++++++++------- server.go | 4 +++ 4 files changed, 146 insertions(+), 26 deletions(-) diff --git a/backend/interface.go b/backend/interface.go index bd2cb401c..7b40d6bb3 100644 --- a/backend/interface.go +++ b/backend/interface.go @@ -62,6 +62,10 @@ type Locationer interface { Location() string } +type IDer interface { + ID() ID +} + type Backend interface { Lister Getter @@ -69,4 +73,5 @@ type Backend interface { Tester Remover Closer + IDer } diff --git a/backend/local.go b/backend/local.go index c5368a69b..fc76a1363 100644 --- a/backend/local.go +++ b/backend/local.go @@ -1,13 +1,14 @@ package backend import ( + "crypto/rand" + "crypto/sha256" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" - "strconv" "strings" ) @@ -20,6 +21,7 @@ const ( keyPath = "keys" tempPath = "tmp" versionFileName = "version" + idFileName = "id" ) var ErrWrongData = errors.New("wrong data returned by backend, checksum does not match") @@ -27,6 +29,7 @@ var ErrWrongData = errors.New("wrong data returned by backend, checksum does not type Local struct { p string ver uint + id ID } // OpenLocal opens the local backend at dir. @@ -54,8 +57,33 @@ func OpenLocal(dir string) (*Local, error) { return nil, fmt.Errorf("unable to read version file: %v\n", err) } - buf := make([]byte, 100) - n, err := f.Read(buf) + var version uint + n, err := fmt.Fscanf(f, "%d", &version) + if err != nil { + return nil, err + } + + if n != 1 { + return nil, errors.New("could not read version from file") + } + + err = f.Close() + if err != nil { + return nil, err + } + + // check version + if version != BackendVersion { + return nil, fmt.Errorf("wrong version %d", version) + } + + // read ID + f, err = os.Open(filepath.Join(dir, idFileName)) + if err != nil { + return nil, err + } + + buf, err := ioutil.ReadAll(f) if err != nil { return nil, err } @@ -65,23 +93,19 @@ func OpenLocal(dir string) (*Local, error) { return nil, err } - version, err := strconv.Atoi(strings.TrimSpace(string(buf[:n]))) + id, err := ParseID(strings.TrimSpace(string(buf))) if err != nil { - return nil, fmt.Errorf("unable to convert version to integer: %v\n", err) + return nil, err } - // check version - if version != BackendVersion { - return nil, fmt.Errorf("wrong version %d", version) - } - - return &Local{p: dir, ver: uint(version)}, nil + return &Local{p: dir, ver: version, id: id}, nil } // CreateLocal creates all the necessary files and directories for a new local // backend at dir. func CreateLocal(dir string) (*Local, error) { versionFile := filepath.Join(dir, versionFileName) + idFile := filepath.Join(dir, idFileName) dirs := []string{ dir, filepath.Join(dir, dataPath), @@ -92,12 +116,17 @@ func CreateLocal(dir string) (*Local, error) { filepath.Join(dir, tempPath), } - // test if version file already exists + // test if files already exist _, err := os.Lstat(versionFile) if err == nil { return nil, errors.New("version file already exists") } + _, err = os.Lstat(idFile) + if err == nil { + return nil, errors.New("id file already exists") + } + // test if directories already exist for _, d := range dirs[1:] { if _, err := os.Stat(d); err == nil { @@ -119,7 +148,29 @@ func CreateLocal(dir string) (*Local, error) { return nil, err } - _, err = f.Write([]byte(fmt.Sprintf("%d\n", BackendVersion))) + _, err = fmt.Fprintf(f, "%d\n", BackendVersion) + if err != nil { + return nil, err + } + + err = f.Close() + if err != nil { + return nil, err + } + + // create ID file + id := make([]byte, sha256.Size) + _, err = rand.Read(id) + if err != nil { + return nil, err + } + + f, err = os.Create(idFile) + if err != nil { + return nil, err + } + + _, err = fmt.Fprintf(f, "%s\n", ID(id).String()) if err != nil { return nil, err } @@ -378,6 +429,11 @@ func (b *Local) Version() uint { return b.ver } +// ID returns the ID of this local backend. +func (b *Local) ID() ID { + return b.id +} + // Close closes the backend func (b *Local) Close() error { return nil diff --git a/backend/sftp.go b/backend/sftp.go index 712cfc82b..f722f664b 100644 --- a/backend/sftp.go +++ b/backend/sftp.go @@ -2,6 +2,7 @@ package backend import ( "crypto/rand" + "crypto/sha256" "encoding/hex" "errors" "fmt" @@ -11,7 +12,6 @@ import ( "os" "os/exec" "path/filepath" - "strconv" "strings" "github.com/pkg/sftp" @@ -25,6 +25,7 @@ type SFTP struct { c *sftp.Client p string ver uint + id ID cmd *exec.Cmd } @@ -92,9 +93,34 @@ func OpenSFTP(dir string, program string, args ...string) (*SFTP, error) { return nil, fmt.Errorf("unable to read version file: %v\n", err) } - buf := make([]byte, 100) - n, err := f.Read(buf) - if err != nil && err != io.EOF { + var version uint + n, err := fmt.Fscanf(f, "%d", &version) + if err != nil { + return nil, err + } + + if n != 1 { + return nil, errors.New("could not read version from file") + } + + err = f.Close() + if err != nil { + return nil, err + } + + // check version + if version != BackendVersion { + return nil, fmt.Errorf("wrong version %d", version) + } + + // read ID + f, err = sftp.c.Open(filepath.Join(dir, idFileName)) + if err != nil { + return nil, err + } + + buf, err := ioutil.ReadAll(f) + if err != nil { return nil, err } @@ -103,16 +129,12 @@ func OpenSFTP(dir string, program string, args ...string) (*SFTP, error) { return nil, err } - version, err := strconv.Atoi(strings.TrimSpace(string(buf[:n]))) + id, err := ParseID(strings.TrimSpace(string(buf))) if err != nil { - return nil, fmt.Errorf("unable to convert version to integer: %v\n", err) - } - - // check version - if version != BackendVersion { - return nil, fmt.Errorf("wrong version %d", version) + return nil, err } + sftp.id = id sftp.p = dir return sftp, nil @@ -127,6 +149,7 @@ func CreateSFTP(dir string, program string, args ...string) (*SFTP, error) { } versionFile := filepath.Join(dir, versionFileName) + idFile := filepath.Join(dir, idFileName) dirs := []string{ dir, filepath.Join(dir, dataPath), @@ -137,12 +160,17 @@ func CreateSFTP(dir string, program string, args ...string) (*SFTP, error) { filepath.Join(dir, tempPath), } - // test if version file already exists + // test if files already exist _, err = sftp.c.Lstat(versionFile) if err == nil { return nil, errors.New("version file already exists") } + _, err = sftp.c.Lstat(idFile) + if err == nil { + return nil, errors.New("id file already exists") + } + // test if directories already exist for _, d := range dirs[1:] { if _, err := sftp.c.Lstat(d); err == nil { @@ -164,7 +192,29 @@ func CreateSFTP(dir string, program string, args ...string) (*SFTP, error) { return nil, err } - _, err = f.Write([]byte(strconv.Itoa(BackendVersion))) + _, err = fmt.Fprintf(f, "%d\n", BackendVersion) + if err != nil { + return nil, err + } + + err = f.Close() + if err != nil { + return nil, err + } + + // create ID file + id := make([]byte, sha256.Size) + _, err = rand.Read(id) + if err != nil { + return nil, err + } + + f, err = sftp.c.Create(idFile) + if err != nil { + return nil, err + } + + _, err = fmt.Fprintf(f, "%s\n", ID(id).String()) if err != nil { return nil, err } @@ -504,6 +554,11 @@ func (r *SFTP) Version() uint { return r.ver } +// ID returns the ID of this local backend. +func (r *SFTP) ID() ID { + return r.id +} + // Close closes the sftp connection and terminates the underlying command. func (s *SFTP) Close() error { s.c.Close() diff --git a/server.go b/server.go index 3ea573141..0e3220cd5 100644 --- a/server.go +++ b/server.go @@ -414,3 +414,7 @@ func (s Server) Delete() error { return errors.New("Delete() called for backend that does not implement this method") } + +func (s Server) ID() backend.ID { + return s.be.ID() +}