Refactor into modules

Signed-off-by: Tiziano Bacocco <tizbac2@gmail.com>
This commit is contained in:
Tiziano Bacocco 2025-10-11 16:36:17 +02:00
parent 4561e095aa
commit c92b59b66f
22 changed files with 306 additions and 179 deletions

View File

@ -1,3 +1,4 @@
set CGO_ENABLED=1
set GOOS=windows
go build -o proxmoxbackupgo_cli.exe
set GOEXPERIMENT=nodwarf5
go build -o proxmoxbackupgo_cli.exe ./directorybackup

5
clientcommon/go.mod Normal file
View File

@ -0,0 +1,5 @@
module clientcommon
go 1.25.2
require github.com/rodolfoag/gow32 v0.0.0-20230512144032-1e896a3c51aa

2
clientcommon/go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/rodolfoag/gow32 v0.0.0-20230512144032-1e896a3c51aa h1:cd9mmDEXO4YxGYTbrhbfEt7btgUlcXFedTMoZ9fA4Ns=
github.com/rodolfoag/gow32 v0.0.0-20230512144032-1e896a3c51aa/go.mod h1:w/ebPUfAcyZMYjstwPIWTEGSahChHx5R3Y+xElrvxDc=

View File

@ -1,4 +1,4 @@
package main
package clientcommon
var ICON = []byte{
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x30, 0x30, 0x10, 0x00, 0x01, 0x00,

View File

@ -1,4 +1,4 @@
package main
package clientcommon
import (
"crypto/tls"
@ -20,7 +20,7 @@ func (a unencryptedAuth) Start(server *smtp.ServerInfo) (string, []byte, error)
return a.Auth.Start(&s)
}
func setupClient(host, port, username, password string, allowInsecure bool) (*smtp.Client, error) {
func SetupMailClient(host, port, username, password string, allowInsecure bool) (*smtp.Client, error) {
var auth smtp.Auth
auth = smtp.PlainAuth("", username, password, host)
if port == "25" {
@ -75,7 +75,7 @@ func setupClient(host, port, username, password string, allowInsecure bool) (*sm
return c, nil
}
func sendMail(from, to, subject, body string, c *smtp.Client) error {
func SendMail(from, to, subject, body string, c *smtp.Client) error {
// Setup headers
headers := make(map[string]string)
headers["From"] = from
@ -122,7 +122,7 @@ func sendMail(from, to, subject, body string, c *smtp.Client) error {
return nil
}
type mailCtx struct {
type MailCtx struct {
NewChunks uint64
ReusedChunks uint64
Datastore string
@ -132,33 +132,33 @@ type mailCtx struct {
EndTime time.Time
}
func (m *mailCtx) Duration() time.Duration {
func (m *MailCtx) Duration() time.Duration {
return m.EndTime.Sub(m.StartTime)
}
func (m *mailCtx) FromattedDuration() string {
func (m *MailCtx) FromattedDuration() string {
return m.Duration().String()
}
func (m *mailCtx) ErrorStr() string {
func (m *MailCtx) ErrorStr() string {
if m.Error != nil {
return m.Error.Error()
}
return ""
}
func (m *mailCtx) Success() bool {
func (m *MailCtx) Success() bool {
return m.Error == nil
}
func (m *mailCtx) Status() string {
func (m *MailCtx) Status() string {
if m.Success() {
return "Success"
}
return "Failed"
}
func (m *mailCtx) buildStr(txt string) (string, error) {
func (m *MailCtx) BuildStr(txt string) (string, error) {
tmpl, err := template.New("mail").Parse(txt)
if err != nil {
return "", err

View File

@ -1,7 +1,7 @@
//go:build linux || darwin || freebsd || openbsd
// +build linux darwin freebsd openbsd
package main
package clientcommon
type Locking struct {
mutexid uintptr

View File

@ -1,22 +1,25 @@
//go:build windows
// +build windows
package main
import "github.com/rodolfoag/gow32"
import "syscall"
package clientcommon
import (
"syscall"
"github.com/rodolfoag/gow32"
)
const MutexName = "proxmoxbackupclient_go"
type Locking struct {
mutexid uintptr
}
func (l *Locking) AcquireProcessLock() bool {
mutexid , err := gow32.CreateMutex(MutexName)
mutexid, err := gow32.CreateMutex(MutexName)
if err != nil {
if exitcode := int(err.(syscall.Errno)); exitcode == gow32.ERROR_ALREADY_EXISTS {
return false
return false
}
panic(err)
}
@ -24,6 +27,6 @@ func (l *Locking) AcquireProcessLock() bool {
return true
}
func (l * Locking) ReleaseProcessLock() {
func (l *Locking) ReleaseProcessLock() {
gow32.ReleaseMutex(l.mutexid)
}
}

View File

@ -36,13 +36,13 @@ type Config struct {
Namespace string `json:"namespace"`
BackupID string `json:"backup-id"`
BackupSourceDir string `json:"backupdir"`
BackupStreamName string `json:"backupstreamname"`
BackupStreamName string `json:"backupstreamname"`
PxarOut string `json:"pxarout"`
SMTP *SMTPConfig `json:"smtp"`
}
func (c *Config) valid() bool {
baseValid := c.BaseURL != "" && c.AuthID != "" && c.Secret != "" && c.Datastore != "" && ( c.BackupSourceDir != "" || c.BackupStreamName != "" )
baseValid := c.BaseURL != "" && c.AuthID != "" && c.Secret != "" && c.Datastore != "" && (c.BackupSourceDir != "" || c.BackupStreamName != "")
if !baseValid {
return baseValid
}

View File

@ -1,35 +1,37 @@
module proxmoxbackupgo
module directorybackup
go 1.19
go 1.25.2
require (
github.com/cornelk/hashmap v1.0.8
github.com/dchest/siphash v1.2.3
github.com/gen2brain/beeep v0.0.0-20230907135156-1a38885a97fc
github.com/getlantern/systray v1.2.2
github.com/jeromehadorn/vss v0.1.0
github.com/klauspost/compress v1.17.4
github.com/rodolfoag/gow32 v0.0.0-20230512144032-1e896a3c51aa
github.com/tawesoft/golib/v2 v2.10.0
golang.org/x/net v0.19.0
github.com/gen2brain/beeep v0.11.1
)
require (
github.com/alessio/shellescape v1.4.1 // indirect
github.com/alessio/shellescape v1.4.2 // indirect
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/text v0.15.0 // indirect
)
require (
git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
github.com/esiqveland/notify v0.13.3 // indirect
github.com/getlantern/systray v1.2.2
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/jackmordaunt/icns/v3 v3.0.1 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/sergeymakinen/go-bmp v1.0.0 // indirect
github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
github.com/tawesoft/golib/v2 v2.16.0
golang.org/x/sys v0.30.0 // indirect
)

69
directorybackup/go.sum Normal file
View File

@ -0,0 +1,69 @@
git.sr.ht/~jackmordaunt/go-toast v1.1.2 h1:/yrfI55LRt1M7H1vkaw+NaH1+L1CDxrqDltwm5euVuE=
git.sr.ht/~jackmordaunt/go-toast v1.1.2/go.mod h1:jA4OqHKTQ4AFBdwrSnwnskUIIS3HYzlJSgdzCKqfavo=
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/cornelk/hashmap v1.0.8 h1:nv0AWgw02n+iDcawr5It4CjQIAcdMMKRrs10HOJYlrc=
github.com/cornelk/hashmap v1.0.8/go.mod h1:RfZb7JO3RviW/rT6emczVuC/oxpdz4UsSB2LJSclR1k=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/esiqveland/notify v0.13.3 h1:QCMw6o1n+6rl+oLUfg8P1IIDSFsDEb2WlXvVvIJbI/o=
github.com/esiqveland/notify v0.13.3/go.mod h1:hesw/IRYTO0x99u1JPweAl4+5mwXJibQVUcP0Iu5ORE=
github.com/gen2brain/beeep v0.11.1 h1:EbSIhrQZFDj1K2fzlMpAYlFOzV8YuNe721A58XcCTYI=
github.com/gen2brain/beeep v0.11.1/go.mod h1:jQVvuwnLuwOcdctHn/uyh8horSBNJ8uGb9Cn2W4tvoc=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So=
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 h1:guBYzEaLz0Vfc/jv0czrr2z7qyzTOGC9hiQ0VC+hKjk=
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc=
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0=
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc=
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/getlantern/systray v1.2.2 h1:dCEHtfmvkJG7HZ8lS/sLklTH4RKUcIsKrAD9sThoEBE=
github.com/getlantern/systray v1.2.2/go.mod h1:pXFOI1wwqwYXEhLPm9ZGjS2u/vVELeIgNMY5HvhHhcE=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/jackmordaunt/icns/v3 v3.0.1 h1:xxot6aNuGrU+lNgxz5I5H0qSeCjNKp8uTXB1j8D4S3o=
github.com/jackmordaunt/icns/v3 v3.0.1/go.mod h1:5sHL59nqTd2ynTnowxB/MDQFhKNqkK8X687uKNygaSQ=
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M=
github.com/sergeymakinen/go-bmp v1.0.0/go.mod h1:/mxlAQZRLxSvJFNIEGGLBE/m40f3ZnUifpgVDlcUIEY=
github.com/sergeymakinen/go-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3K6MFJw4GfZvQ=
github.com/sergeymakinen/go-ico v1.0.0-beta.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
github.com/tawesoft/golib/v2 v2.16.0 h1:QJPqTFPVz++45fTVyP66o8IfCfz8MYgTrn6nypVLv2o=
github.com/tawesoft/golib/v2 v2.16.0/go.mod h1:S+cpYdLd1NwKQmWnycfIJqJegOek/Zz+JY9FH7EJTWs=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -2,6 +2,7 @@ package main
import (
"bytes"
"clientcommon"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
@ -10,7 +11,9 @@ import (
"hash"
"io"
"os"
"pbscommon"
"runtime"
"snapshot"
"strings"
"sync/atomic"
"time"
@ -21,8 +24,6 @@ import (
"github.com/tawesoft/golib/v2/dialog"
)
var defaultMailSubjectTemplate = "Backup {{.Status}}"
var defaultMailBodyTemplate = `{{if .Success}}Backup complete ({{.FromattedDuration}})
Chunks New {{.NewChunks}}, Reused {{.ReusedChunks}}.{{else}}Error occurred while working, backup may be not completed.
@ -38,10 +39,10 @@ type ChunkState struct {
chunkcount uint64
chunkdigests hash.Hash
current_chunk []byte
C Chunker
newchunk *atomic.Uint64
reusechunk *atomic.Uint64
knownChunks *hashmap.Map[string, bool]
C pbscommon.Chunker
newchunk *atomic.Uint64
reusechunk *atomic.Uint64
knownChunks *hashmap.Map[string, bool]
}
type DidxEntry struct {
@ -49,25 +50,25 @@ type DidxEntry struct {
digest []byte
}
func (c *ChunkState) Init(newchunk *atomic.Uint64 , reusechunk *atomic.Uint64, knownChunks *hashmap.Map[string, bool] ) {
func (c *ChunkState) Init(newchunk *atomic.Uint64, reusechunk *atomic.Uint64, knownChunks *hashmap.Map[string, bool]) {
c.assignments = make([]string, 0)
c.assignments_offset = make([]uint64, 0)
c.pos = 0
c.chunkcount = 0
c.chunkdigests = sha256.New()
c.current_chunk = make([]byte, 0)
c.C = Chunker{}
c.C = pbscommon.Chunker{}
c.C.New(1024 * 1024 * 4)
c.reusechunk = reusechunk
c.newchunk = newchunk
c.knownChunks = knownChunks
}
func (c *ChunkState) HandleData(b []byte, client *PBSClient){
func (c *ChunkState) HandleData(b []byte, client *pbscommon.PBSClient) {
chunkpos := c.C.Scan(b)
if chunkpos == 0 {
//No break happened, just append data
//No break happened, just append data
c.current_chunk = append(c.current_chunk, b...)
} else {
@ -102,9 +103,9 @@ func (c *ChunkState) HandleData(b []byte, client *PBSClient){
c.chunkcount += 1
c.current_chunk = make([]byte, 0)
b = b[chunkpos:] //Take remainder of data
b = b[chunkpos:] //Take remainder of data
chunkpos = c.C.Scan(b)
}
//No further break happened, append remaining data
@ -112,9 +113,9 @@ func (c *ChunkState) HandleData(b []byte, client *PBSClient){
}
}
func (c *ChunkState) Eof(client *PBSClient) {
func (c *ChunkState) Eof(client *pbscommon.PBSClient) {
//Here we write the remainder of data for which cyclic hash did not trigger
if len(c.current_chunk) > 0 {
h := sha256.New()
_, err := h.Write(c.current_chunk)
@ -152,8 +153,6 @@ func (c *ChunkState) Eof(client *PBSClient) {
client.CloseDynamicIndex(c.wrid, hex.EncodeToString(c.chunkdigests.Sum(nil)), c.pos, c.chunkcount)
}
func main() {
var newchunk *atomic.Uint64 = new(atomic.Uint64)
var reusechunk *atomic.Uint64 = new(atomic.Uint64)
@ -175,19 +174,18 @@ func main() {
os.Exit(1)
}
L := Locking{}
L := clientcommon.Locking{}
lock_ok := L.AcquireProcessLock()
if !lock_ok {
dialog.Error("Backup jobs need to run exclusively, please wait until the previous job has finished")
os.Exit(2)
}
defer L.ReleaseProcessLock()
if runtime.GOOS == "windows" {
go systray.Run(func() {
systray.SetIcon(ICON)
systray.SetIcon(clientcommon.ICON)
systray.SetTooltip("PBSGO Backup running")
beeep.Notify("Proxmox Backup Go", "Backup started", "")
},
@ -195,19 +193,18 @@ func main() {
})
}
insecure := cfg.CertFingerprint != ""
client := &PBSClient{
baseurl: cfg.BaseURL,
certfingerprint: cfg.CertFingerprint, //"ea:7d:06:f9:87:73:a4:72:d0:e8:05:a4:b3:3d:95:d7:0a:26:dd:6d:5c:ca:e6:99:83:e4:11:3b:5f:10:f4:4b",
authid: cfg.AuthID,
secret: cfg.Secret,
datastore: cfg.Datastore,
namespace: cfg.Namespace,
insecure: insecure,
manifest: BackupManifest{
client := &pbscommon.PBSClient{
BaseURL: cfg.BaseURL,
CertFingerPrint: cfg.CertFingerprint, //"ea:7d:06:f9:87:73:a4:72:d0:e8:05:a4:b3:3d:95:d7:0a:26:dd:6d:5c:ca:e6:99:83:e4:11:3b:5f:10:f4:4b",
AuthID: cfg.AuthID,
Secret: cfg.Secret,
Datastore: cfg.Datastore,
Namespace: cfg.Namespace,
Insecure: insecure,
Manifest: pbscommon.BackupManifest{
BackupID: cfg.BackupID,
},
}
@ -222,20 +219,19 @@ func main() {
err = backup(client, newchunk, reusechunk, cfg.PxarOut, cfg.BackupSourceDir)
} else if cfg.BackupStreamName != "" {
sn := cfg.BackupStreamName
if ! strings.HasSuffix(sn, ".didx" ) {
if !strings.HasSuffix(sn, ".didx") {
sn += ".didx"
}
fmt.Printf("Backing up from STDIN to %s", sn)
err = backup_stream(client, newchunk, reusechunk, sn, os.Stdin )
err = backup_stream(client, newchunk, reusechunk, sn, os.Stdin)
}else{
} else {
panic("No backup dir or stream name specified, exiting")
}
end := time.Now()
mailCtx := mailCtx{
mailCtx := clientcommon.MailCtx{
NewChunks: newchunk.Load(),
ReusedChunks: reusechunk.Load(),
Error: err,
@ -252,10 +248,10 @@ func main() {
fmt.Printf("New %d, Reused %d, backup took %s.\n", newchunk.Load(), reusechunk.Load(), end.Sub(begin))
var msg string
msg, err = mailCtx.buildStr(mailBodyTemplate)
msg, err = mailCtx.BuildStr(mailBodyTemplate)
if err != nil {
fmt.Println("Cannot use custom mail body: " + err.Error())
msg, err = mailCtx.buildStr(defaultMailBodyTemplate)
msg, err = mailCtx.BuildStr(defaultMailBodyTemplate)
if err != nil {
// this should never happen
panic(err)
@ -273,23 +269,23 @@ func main() {
mailSubjectTemplate = cfg.SMTP.Template.Subject
}
subject, err = mailCtx.buildStr(mailSubjectTemplate)
subject, err = mailCtx.BuildStr(mailSubjectTemplate)
if err != nil {
fmt.Println("Cannot use custom mail subject: " + err.Error())
msg, err = mailCtx.buildStr(defaultMailSubjectTemplate)
msg, err = mailCtx.BuildStr(defaultMailSubjectTemplate)
if err != nil {
// this should never happen
panic(err)
}
}
client, err := setupClient(cfg.SMTP.Host, cfg.SMTP.Port, cfg.SMTP.Username, cfg.SMTP.Password, cfg.SMTP.Insecure)
client, err := clientcommon.SetupMailClient(cfg.SMTP.Host, cfg.SMTP.Port, cfg.SMTP.Username, cfg.SMTP.Password, cfg.SMTP.Insecure)
if err != nil {
fmt.Println("Cannot connect to mail server: " + err.Error())
os.Exit(1)
}
defer client.Quit()
for _, ccc := range cfg.SMTP.Mails {
err = sendMail(ccc.From, ccc.To, subject, msg, client)
err = clientcommon.SendMail(ccc.From, ccc.To, subject, msg, client)
if err != nil {
fmt.Println("Cannot send email: " + err.Error())
os.Exit(1)
@ -299,7 +295,7 @@ func main() {
}
func backup_stream(client *PBSClient, newchunk, reusechunk *atomic.Uint64, filename string, stream io.Reader ) error {
func backup_stream(client *pbscommon.PBSClient, newchunk, reusechunk *atomic.Uint64, filename string, stream io.Reader) error {
knownChunks := hashmap.New[string, bool]()
client.Connect(false)
previousDidx, err := client.DownloadPreviousToBytes(filename)
@ -337,11 +333,11 @@ func backup_stream(client *PBSClient, newchunk, reusechunk *atomic.Uint64, filen
}
B := make([]byte, 65536)
for {
n, err := stream.Read(B)
b := B[:n]
streamChunk.HandleData(b, client)
if err == io.EOF {
@ -361,21 +357,22 @@ func backup_stream(client *PBSClient, newchunk, reusechunk *atomic.Uint64, filen
return client.Finish()
}
func backup(client *PBSClient, newchunk, reusechunk *atomic.Uint64, pxarOut string, backupdir string) error {
func backup(client *pbscommon.PBSClient, newchunk, reusechunk *atomic.Uint64, pxarOut string, backupdir string) error {
knownChunks := hashmap.New[string, bool]()
fmt.Printf("Starting backup of %s\n", backupdir)
backupdir = createVSSSnapshot(backupdir)
SNAP := snapshot.CreateVSSSnapshot(backupdir)
backupdir = SNAP.FullPath
//Remove VSS snapshot on windows, on linux for now NOP
defer VSSCleanup()
defer snapshot.VSSCleanup()
client.Connect(false)
archive := &PXARArchive{}
archive.archivename = "backup.pxar.didx"
archive := &pbscommon.PXARArchive{}
archive.ArchiveName = "backup.pxar.didx"
previousDidx, err := client.DownloadPreviousToBytes(archive.archivename)
previousDidx, err := client.DownloadPreviousToBytes(archive.ArchiveName)
if err != nil {
return err
}
@ -426,7 +423,7 @@ func backup(client *PBSClient, newchunk, reusechunk *atomic.Uint64, pxarOut stri
pcat1Chunk := ChunkState{}
pcat1Chunk.Init(newchunk, reusechunk, knownChunks)
pxarChunk.wrid, err = client.CreateDynamicIndex(archive.archivename)
pxarChunk.wrid, err = client.CreateDynamicIndex(archive.ArchiveName)
if err != nil {
return err
}
@ -435,8 +432,7 @@ func backup(client *PBSClient, newchunk, reusechunk *atomic.Uint64, pxarOut stri
return err
}
archive.writeCB = func(b []byte) {
archive.WriteCB = func(b []byte) {
if pxarOut != "" {
// TODO: error handling inside callback
@ -448,7 +444,7 @@ func backup(client *PBSClient, newchunk, reusechunk *atomic.Uint64, pxarOut stri
//
}
archive.catalogWriteCB = func(b []byte) {
archive.CatalogWriteCB = func(b []byte) {
pcat1Chunk.HandleData(b, client)
}
@ -457,12 +453,9 @@ func backup(client *PBSClient, newchunk, reusechunk *atomic.Uint64, pxarOut stri
archive.WriteDir(backupdir, "", true)
pxarChunk.Eof(client)
pcat1Chunk.Eof(client)
err = client.UploadManifest()
if err != nil {
return err

6
go.work Normal file
View File

@ -0,0 +1,6 @@
go 1.25.2
use ./clientcommon
use ./pbscommon
use ./directorybackup
use ./snapshot

View File

@ -1,4 +1,4 @@
package main
package pbscommon
import (
"fmt"

13
pbscommon/go.mod Normal file
View File

@ -0,0 +1,13 @@
module pbscommon
go 1.25.2
require (
github.com/klauspost/compress v1.18.0
golang.org/x/net v0.46.0
)
require (
github.com/dchest/siphash v1.2.3
golang.org/x/text v0.30.0 // indirect
)

8
pbscommon/go.sum Normal file
View File

@ -0,0 +1,8 @@
github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=

View File

@ -1,4 +1,4 @@
package main
package pbscommon
import (
"bytes"
@ -75,22 +75,22 @@ func (e *AuthErr) Error() string {
}
type PBSClient struct {
baseurl string
certfingerprint string
apitoken string
secret string
authid string
BaseURL string
CertFingerPrint string
APIToken string
Secret string
AuthID string
datastore string
namespace string
manifest BackupManifest
Datastore string
Namespace string
Manifest BackupManifest
insecure bool
Insecure bool
client http.Client
tlsConfig tls.Config
Client http.Client
TLSConfig tls.Config
writersManifest map[uint64]int
WritersManifest map[uint64]int
}
var blobCompressedMagic = []byte{49, 185, 88, 66, 111, 182, 163, 127}
@ -98,15 +98,15 @@ var blobUncompressedMagic = []byte{66, 171, 56, 7, 190, 131, 112, 161}
func (pbs *PBSClient) CreateDynamicIndex(name string) (uint64, error) {
req, err := http.NewRequest("POST", pbs.baseurl+"/dynamic_index", bytes.NewBuffer([]byte(fmt.Sprintf("{\"archive-name\": \"%s\"}", name))))
req, err := http.NewRequest("POST", pbs.BaseURL+"/dynamic_index", bytes.NewBuffer([]byte(fmt.Sprintf("{\"archive-name\": \"%s\"}", name))))
if err != nil {
return 0, err
}
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.authid, pbs.secret))
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.AuthID, pbs.Secret))
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
resp2, err := pbs.client.Do(req)
resp2, err := pbs.Client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return 0, err
@ -133,8 +133,8 @@ func (pbs *PBSClient) CreateDynamicIndex(name string) (uint64, error) {
Filename: name,
Size: 0,
}
pbs.manifest.Files = append(pbs.manifest.Files, f)
pbs.writersManifest[uint64(R.WriterID)] = len(pbs.manifest.Files) - 1
pbs.Manifest.Files = append(pbs.Manifest.Files, f)
pbs.WritersManifest[uint64(R.WriterID)] = len(pbs.Manifest.Files) - 1
return uint64(R.WriterID), nil
}
@ -151,12 +151,12 @@ func (pbs *PBSClient) UploadUncompressedChunk(writerid uint64, digest string, ch
q.Add("size", fmt.Sprintf("%d", len(chunkdata)))
q.Add("wid", fmt.Sprintf("%d", writerid))
req, err := http.NewRequest("POST", pbs.baseurl+"/dynamic_chunk?"+q.Encode(), bytes.NewBuffer(outBuffer))
req, err := http.NewRequest("POST", pbs.BaseURL+"/dynamic_chunk?"+q.Encode(), bytes.NewBuffer(outBuffer))
if err != nil {
return err
}
resp2, err := pbs.client.Do(req)
resp2, err := pbs.Client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return err
@ -198,9 +198,9 @@ func (pbs *PBSClient) UploadCompressedChunk(writerid uint64, digest string, chun
q.Add("size", fmt.Sprintf("%d", len(chunkdata)))
q.Add("wid", fmt.Sprintf("%d", writerid))
req, err := http.NewRequest("POST", pbs.baseurl+"/dynamic_chunk?"+q.Encode(), bytes.NewBuffer(outBuffer))
req, err := http.NewRequest("POST", pbs.BaseURL+"/dynamic_chunk?"+q.Encode(), bytes.NewBuffer(outBuffer))
resp2, err := pbs.client.Do(req)
resp2, err := pbs.Client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return err
@ -227,12 +227,12 @@ func (pbs *PBSClient) AssignChunks(writerid uint64, digests []string, offsets []
return err
}
req, err := http.NewRequest("PUT", pbs.baseurl+"/dynamic_index", bytes.NewBuffer(jsondata))
req, err := http.NewRequest("PUT", pbs.BaseURL+"/dynamic_index", bytes.NewBuffer(jsondata))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
resp2, err := pbs.client.Do(req)
resp2, err := pbs.Client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return err
@ -252,20 +252,20 @@ func (pbs *PBSClient) CloseDynamicIndex(writerid uint64, checksum string, totals
if err != nil {
return err
}
req, err := http.NewRequest("POST", pbs.baseurl+"/dynamic_close", bytes.NewBuffer(jsonpayload))
req, err := http.NewRequest("POST", pbs.BaseURL+"/dynamic_close", bytes.NewBuffer(jsonpayload))
if err != nil {
return err
}
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.authid, pbs.secret))
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.AuthID, pbs.Secret))
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
resp2, err := pbs.client.Do(req)
resp2, err := pbs.Client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return err
}
f := &pbs.manifest.Files[pbs.writersManifest[writerid]]
f := &pbs.Manifest.Files[pbs.WritersManifest[writerid]]
f.Csum = checksum
f.Size = int64(totalsize)
@ -286,9 +286,9 @@ func (pbs *PBSClient) UploadBlob(name string, data []byte) error {
q.Add("encoded-size", fmt.Sprintf("%d", len(out)))
q.Add("file-name", name)
req, _ := http.NewRequest("POST", pbs.baseurl+"/blob?"+q.Encode(), bytes.NewBuffer(out))
req, _ := http.NewRequest("POST", pbs.BaseURL+"/blob?"+q.Encode(), bytes.NewBuffer(out))
resp2, err := pbs.client.Do(req)
resp2, err := pbs.Client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return err
@ -304,7 +304,7 @@ func (pbs *PBSClient) UploadBlob(name string, data []byte) error {
}
func (pbs *PBSClient) UploadManifest() error {
manifestBin, err := json.Marshal(pbs.manifest)
manifestBin, err := json.Marshal(pbs.Manifest)
if err != nil {
return err
}
@ -312,12 +312,12 @@ func (pbs *PBSClient) UploadManifest() error {
}
func (pbs *PBSClient) Finish() error {
req, err := http.NewRequest("POST", pbs.baseurl+"/finish", nil)
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.authid, pbs.secret))
req, err := http.NewRequest("POST", pbs.BaseURL+"/finish", nil)
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.AuthID, pbs.Secret))
if err != nil {
return err
}
resp2, err := pbs.client.Do(req)
resp2, err := pbs.Client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
if err != nil {
@ -329,12 +329,12 @@ func (pbs *PBSClient) Finish() error {
}
func (pbs *PBSClient) Connect(reader bool) {
pbs.writersManifest = make(map[uint64]int)
pbs.tlsConfig = tls.Config{
InsecureSkipVerify: pbs.insecure,
pbs.WritersManifest = make(map[uint64]int)
pbs.TLSConfig = tls.Config{
InsecureSkipVerify: pbs.Insecure,
}
if pbs.insecure {
pbs.tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
if pbs.Insecure {
pbs.TLSConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// Extract the peer certificate
if len(rawCerts) == 0 {
return fmt.Errorf("no certificates presented by the peer")
@ -345,7 +345,7 @@ func (pbs *PBSClient) Connect(reader bool) {
}
// Calculate the SHA-256 fingerprint of the certificate
expectedFingerprint := strings.ReplaceAll(pbs.certfingerprint, ":", "")
expectedFingerprint := strings.ReplaceAll(pbs.CertFingerPrint, ":", "")
calculatedFingerprint := sha256.Sum256(peerCert.Raw)
// Compare the calculated fingerprint with the expected one
@ -358,13 +358,13 @@ func (pbs *PBSClient) Connect(reader bool) {
}
}
pbs.manifest.BackupTime = time.Now().Unix()
pbs.manifest.BackupType = "host"
if pbs.manifest.BackupID == "" {
pbs.Manifest.BackupTime = time.Now().Unix()
pbs.Manifest.BackupType = "host"
if pbs.Manifest.BackupID == "" {
hostname, _ := os.Hostname()
pbs.manifest.BackupID = hostname
pbs.Manifest.BackupID = hostname
}
pbs.client = http.Client{
pbs.Client = http.Client{
Transport: &http2.Transport{
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
@ -373,22 +373,22 @@ func (pbs *PBSClient) Connect(reader bool) {
//So to achieve that the function to create SSL socket has been hijacked here
//Here an http 1.1 request to authenticate, start the backup and require upgrade to HTTP2 is done then the socket is passed to
// http2.Transport handler
conn, err := tls.Dial(network, addr, &pbs.tlsConfig)
conn, err := tls.Dial(network, addr, &pbs.TLSConfig)
if err != nil {
return nil, err
}
q := &url.Values{}
q.Add("backup-time", fmt.Sprintf("%d", pbs.manifest.BackupTime))
q.Add("backup-type", pbs.manifest.BackupType)
q.Add("store", pbs.datastore)
if pbs.namespace != "" {
q.Add("ns", pbs.namespace)
q.Add("backup-time", fmt.Sprintf("%d", pbs.Manifest.BackupTime))
q.Add("backup-type", pbs.Manifest.BackupType)
q.Add("store", pbs.Datastore)
if pbs.Namespace != "" {
q.Add("ns", pbs.Namespace)
}
q.Add("backup-id", pbs.manifest.BackupID)
q.Add("backup-id", pbs.Manifest.BackupID)
q.Add("debug", "1")
conn.Write([]byte("GET /api2/json/backup?" + q.Encode() + " HTTP/1.1\r\n"))
conn.Write([]byte("Authorization: " + fmt.Sprintf("PBSAPIToken=%s:%s", pbs.authid, pbs.secret) + "\r\n"))
conn.Write([]byte("Authorization: " + fmt.Sprintf("PBSAPIToken=%s:%s", pbs.AuthID, pbs.Secret) + "\r\n"))
if !reader {
conn.Write([]byte("Upgrade: proxmox-backup-protocol-v1\r\n"))
} else {
@ -433,12 +433,12 @@ func (pbs *PBSClient) DownloadPreviousToBytes(archivename string) ([]byte, error
q.Add("archive-name", archivename)
req, err := http.NewRequest("GET", pbs.baseurl+"/previous?"+q.Encode(), nil)
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.authid, pbs.secret))
req, err := http.NewRequest("GET", pbs.BaseURL+"/previous?"+q.Encode(), nil)
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.AuthID, pbs.Secret))
if err != nil {
return nil, err
}
resp2, err := pbs.client.Do(req)
resp2, err := pbs.Client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return nil, err

View File

@ -1,4 +1,4 @@
package main
package pbscommon
import (
"bytes"
@ -141,14 +141,14 @@ func ca_make_bst(input []GoodByeItem, output *[]GoodByeItem) {
type PXAROutCB func([]byte)
type PXARArchive struct {
//Create(filename string, writeCB PXAROutCB)
//Create(filename string, WriteCB PXAROutCB)
//AddFile(filename string)
//AddDirectory(dirname string)
writeCB PXAROutCB
catalogWriteCB PXAROutCB
WriteCB PXAROutCB
CatalogWriteCB PXAROutCB
buffer bytes.Buffer
pos uint64
archivename string
ArchiveName string
catalog_pos uint64
}
@ -165,7 +165,7 @@ func (a *PXARArchive) Flush() {
if count <= 0 {
break
}
a.writeCB(b[:count])
a.WriteCB(b[:count])
a.pos = a.pos + uint64(count)
}
//fmt.Printf("Flush %d bytes\n", count)
@ -260,8 +260,8 @@ func (a *PXARArchive) WriteDir(path string, dirname string, toplevel bool) Catal
a.buffer.WriteString(dirname)
a.buffer.WriteByte(0x00)
} else {
if a.catalogWriteCB != nil {
a.catalogWriteCB(catalog_magic)
if a.CatalogWriteCB != nil {
a.CatalogWriteCB(catalog_magic)
a.catalog_pos = 8
}
}
@ -338,8 +338,8 @@ func (a *PXARArchive) WriteDir(path string, dirname string, toplevel bool) Catal
catalog_outdata = append_u64_7bit(catalog_outdata, uint64(len(tabledata)))
catalog_outdata = append(catalog_outdata, tabledata...)
if a.catalogWriteCB != nil {
a.catalogWriteCB(catalog_outdata)
if a.CatalogWriteCB != nil {
a.CatalogWriteCB(catalog_outdata)
}
@ -389,17 +389,17 @@ func (a *PXARArchive) WriteDir(path string, dirname string, toplevel bool) Catal
tabledata := make([]byte, 0)
tabledata = append_u64_7bit(tabledata, uint64(1))
tabledata = append(tabledata, 'd')
tabledata = append_u64_7bit(tabledata, uint64(len(a.archivename)))
tabledata = append(tabledata, []byte(a.archivename)...)
tabledata = append_u64_7bit(tabledata, uint64(len(a.ArchiveName)))
tabledata = append(tabledata, []byte(a.ArchiveName)...)
tabledata = append_u64_7bit(tabledata, a.catalog_pos-oldpos)
catalog_outdata := make([]byte, 0)
catalog_outdata = append_u64_7bit(catalog_outdata, uint64(len(tabledata)))
catalog_outdata = append(catalog_outdata, tabledata...)
ptr := make([]byte, 0)
ptr = binary.LittleEndian.AppendUint64(ptr, a.catalog_pos)
if a.catalogWriteCB != nil {
a.catalogWriteCB(catalog_outdata)
a.catalogWriteCB(ptr)
if a.CatalogWriteCB != nil {
a.CatalogWriteCB(catalog_outdata)
a.CatalogWriteCB(ptr)
}
}

10
snapshot/go.mod Normal file
View File

@ -0,0 +1,10 @@
module snapshot
go 1.25.2
require github.com/jeromehadorn/vss v0.1.0
require (
github.com/go-ole/go-ole v1.2.6 // indirect
golang.org/x/sys v0.1.0 // indirect
)

7
snapshot/go.sum Normal file
View File

@ -0,0 +1,7 @@
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/jeromehadorn/vss v0.1.0 h1:0EWv9lG/jv1Wzbt5WhwIUMLxDZ5uSh21LkPHUO34w2Y=
github.com/jeromehadorn/vss v0.1.0/go.mod h1:wHwqd/OMHe4Eu0rS/QX4/jKj2sJPtjRf6YrxBhj/EfY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -1,10 +1,10 @@
//go:build linux || darwin || freebsd || openbsd
// +build linux darwin freebsd openbsd
package main
package snapshot
func createVSSSnapshot(path string) string {
return path
func CreateVSSSnapshot(path string) SnapShot {
return SnapShot{FullPath: path, Valid: false}
}
func VSSCleanup() {

View File

@ -0,0 +1,8 @@
package snapshot
type SnapShot struct {
FullPath string
Id string
ObjectPath string
Valid bool
}

View File

@ -1,7 +1,7 @@
//go:build windows
// +build windows
package main
package snapshot
import (
"fmt"
@ -52,7 +52,7 @@ func getAppDataFolder() (string, error) {
return appDataFolder, nil
}
func createVSSSnapshot(path string) string {
func CreateVSSSnapshot(path string) SnapShot {
path, _ = filepath.Abs(path)
volName := filepath.VolumeName(path)
@ -62,7 +62,7 @@ func createVSSSnapshot(path string) string {
appDataFolder, err := getAppDataFolder()
if err != nil {
fmt.Println("Error:", err)
return path
return SnapShot{FullPath: path, Valid: false}
}
sn := vss.Snapshotter{}
@ -101,7 +101,7 @@ func createVSSSnapshot(path string) string {
panic(err)
}
return filepath.Join(appDataFolder, "VSS", snapshot.Id, subPath)
return SnapShot{FullPath: filepath.Join(appDataFolder, "VSS", snapshot.Id, subPath), Id: snapshot.Id, ObjectPath: snapshot.DeviceObjectPath, Valid: true}
}