From 78aaad1f5c3c6e91f32dc3f7cf5b18f5b9f6b9f3 Mon Sep 17 00:00:00 2001 From: Dominik Schulz Date: Mon, 18 Jan 2021 22:17:31 +0100 Subject: [PATCH] Add windows support to the self updater (#1724) Fixes #1722 RELEASE_NOTES=[ENHANCEMENT] Add windows support to the self updater Signed-off-by: Dominik Schulz --- go.sum | 6 +++ internal/updater/extract.go | 88 +++++++++++++++++++++++++++---------- 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/go.sum b/go.sum index 2c4a9b289c..d7360c2f1c 100644 --- a/go.sum +++ b/go.sum @@ -104,11 +104,14 @@ github.com/xrash/smetrics v0.0.0-20200730060457-89a2a8a1fb0b h1:tnWgqoOBmInkt5pb github.com/xrash/smetrics v0.0.0-20200730060457-89a2a8a1fb0b/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200930160638-afb6bcd081ae h1:duLSQW+DZ5MsXKX7kc4rXlq6/mmxz4G6ewJuBPlhRe0= golang.org/x/crypto v0.0.0-20200930160638-afb6bcd081ae/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -116,10 +119,13 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5 h1:iCaAy5bMeEvwANu3YnJfWwI0kWAGkEa2RXPdweI/ysk= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4 h1:1mMox4TgefDwqluYCv677yNXwlfTkija4owZve/jr78= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= diff --git a/internal/updater/extract.go b/internal/updater/extract.go index 4d8233ffa9..5761009541 100644 --- a/internal/updater/extract.go +++ b/internal/updater/extract.go @@ -2,6 +2,7 @@ package updater import ( "archive/tar" + "archive/zip" "bytes" "compress/bzip2" "compress/gzip" @@ -23,6 +24,20 @@ func extractFile(buf []byte, filename, dest string) error { mode = fi.Mode() } + if err := os.Remove(dest); err != nil { + if !os.IsNotExist(err) { + return fmt.Errorf("unable to remove destination file: %q", err) + } + } + + dfh, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_EXCL, mode) + if err != nil { + return errors.Wrapf(err, "Failed to open file: %s", dest) + } + defer func() { + _ = dfh.Close() + }() + var rd io.Reader = bytes.NewReader(buf) switch filepath.Ext(filename) { case ".gz": @@ -30,27 +45,47 @@ func extractFile(buf []byte, filename, dest string) error { if err != nil { return err } - rd = gzr + return extractTar(gzr, dfh, dest) case ".bz2": - rd = bzip2.NewReader(rd) + return extractTar(bzip2.NewReader(rd), dfh, dest) case ".zip": - return fmt.Errorf("zip archives are not supported, yet") + return extractZip(buf, dfh, dest) + default: + return fmt.Errorf("unsupported") } +} - if err := os.Remove(dest); err != nil { - if !os.IsNotExist(err) { - return fmt.Errorf("unable to remove destination file: %q", err) - } +func extractZip(buf []byte, dfh io.WriteCloser, dest string) error { + zrd, err := zip.NewReader(bytes.NewReader(buf), int64(len(buf))) + if err != nil { + return err } - dfh, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_EXCL, mode) - if err != nil { - return errors.Wrapf(err, "Failed to open file: %s", dest) + for i := 0; i < len(zrd.File); i++ { + if zrd.File[i].Name != "gopass.exe" { + continue + } + + file, err := zrd.File[i].Open() + if err != nil { + return errors.Wrapf(err, "failed to read from zip file") + } + + n, err := io.Copy(dfh, file) + if err != nil { + dfh.Close() + os.Remove(dest) + return errors.Wrapf(err, "failed to read gopass.exe from zip file") + } + // success + debug.Log("wrote %d bytes to %v", n, dest) + return nil } - defer func() { - _ = dfh.Close() - }() + return errors.Errorf("file not found in archive") +} + +func extractTar(rd io.Reader, dfh io.WriteCloser, dest string) error { tarReader := tar.NewReader(rd) for { header, err := tarReader.Next() @@ -58,20 +93,25 @@ func extractFile(buf []byte, filename, dest string) error { break } if err != nil { - return errors.Wrapf(err, "Failed to read from tar file") + return errors.Wrapf(err, "failed to read from tar file") } name := filepath.Base(header.Name) - if header.Typeflag == tar.TypeReg && name == "gopass" { - n, err := io.Copy(dfh, tarReader) - if err != nil { - dfh.Close() - os.Remove(dest) - return errors.Wrapf(err, "Failed to read gopass from tar file") - } - // success - debug.Log("wrote %d bytes to %v", n, dest) - return nil + if header.Typeflag != tar.TypeReg { + continue + } + if name != "gopass" { + continue + } + + n, err := io.Copy(dfh, tarReader) + if err != nil { + dfh.Close() + os.Remove(dest) + return errors.Wrapf(err, "Failed to read gopass from tar file") } + // success + debug.Log("wrote %d bytes to %v", n, dest) + return nil } return errors.Errorf("file not found in archive") }