Skip to content

Commit ed1cada

Browse files
committed
Added No VSS flag, closes #31
Signed-off-by: Tiziano Bacocco <tizbac2@gmail.com>
1 parent dccc66c commit ed1cada

4 files changed

Lines changed: 176 additions & 86 deletions

File tree

directorybackup/config.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Config struct {
3939
BackupStreamName string `json:"backupstreamname"`
4040
PxarOut string `json:"pxarout"`
4141
SMTP *SMTPConfig `json:"smtp"`
42+
UseVSS bool `json:"usevss"`
4243
}
4344

4445
func (c *Config) valid() bool {
@@ -73,6 +74,7 @@ func loadConfig() *Config {
7374
backupSourceDirFlag := flag.String("backupdir", "", "Backup source directory, must not be symlink")
7475
backupStreamNameFlag := flag.String("backupstream", "", "Filename for stream backup")
7576
pxarOutFlag := flag.String("pxarout", "", "Output PXAR archive for debug purposes (optional)")
77+
noVSSFlag := flag.Bool("novss", false, "Disable VSS ( For filesystems that don't support it, for example veracrypt )")
7678

7779
mailHostFlag := flag.String("mail-host", "", "mail notification system: mail server host(optional)")
7880
mailPortFlag := flag.String("mail-port", "", "mail notification system: mail server port(optional)")
@@ -89,7 +91,9 @@ func loadConfig() *Config {
8991
// Parse command line flags
9092
flag.Parse()
9193

92-
config := &Config{}
94+
config := &Config{
95+
UseVSS: true,
96+
}
9397
if *configFile != "" {
9498
file, err := os.ReadFile(*configFile)
9599
if err != nil {
@@ -134,6 +138,9 @@ func loadConfig() *Config {
134138
if *pxarOutFlag != "" {
135139
config.PxarOut = *pxarOutFlag
136140
}
141+
if *noVSSFlag {
142+
config.UseVSS = false
143+
}
137144

138145
initSmtpConfigIfNeeded := func() {
139146
if config.SMTP == nil {

directorybackup/main.go

Lines changed: 92 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func main() {
218218

219219
begin := time.Now()
220220
if cfg.BackupSourceDir != "" {
221-
err = backup(client, newchunk, reusechunk, cfg.PxarOut, cfg.BackupSourceDir)
221+
err = backup(client, newchunk, reusechunk, cfg.PxarOut, cfg.BackupSourceDir, cfg.UseVSS)
222222
} else if cfg.BackupStreamName != "" {
223223
sn := cfg.BackupStreamName
224224
if !strings.HasSuffix(sn, ".didx") {
@@ -359,113 +359,122 @@ func backup_stream(client *pbscommon.PBSClient, newchunk, reusechunk *atomic.Uin
359359
return client.Finish()
360360
}
361361

362-
func backup(client *pbscommon.PBSClient, newchunk, reusechunk *atomic.Uint64, pxarOut string, backupdir string) error {
362+
func backup_real(client *pbscommon.PBSClient, newchunk, reusechunk *atomic.Uint64, pxarOut string, backupdir string) error {
363+
client.Connect(false)
363364
knownChunks := hashmap.New[string, bool]()
364365

365-
fmt.Printf("Starting backup of %s\n", backupdir)
366-
367-
err := snapshot.CreateVSSSnapshot(([]string{backupdir}), func(snaps map[string]snapshot.SnapShot) error {
368-
k := maps.Keys(snaps)
369-
k2 := slices.Collect(k)
370-
SNAP := snaps[k2[0]]
371-
backupdir = SNAP.FullPath
372-
//Remove VSS snapshot on windows, on linux for now NOP
373-
374-
client.Connect(false)
375-
376-
archive := &pbscommon.PXARArchive{}
377-
archive.ArchiveName = "backup.pxar.didx"
366+
archive := &pbscommon.PXARArchive{}
367+
archive.ArchiveName = "backup.pxar.didx"
378368

379-
previousDidx, err := client.DownloadPreviousToBytes(archive.ArchiveName)
380-
if err != nil {
381-
return err
382-
}
369+
previousDidx, err := client.DownloadPreviousToBytes(archive.ArchiveName)
370+
if err != nil {
371+
return err
372+
}
383373

384-
fmt.Printf("Downloaded previous DIDX: %d bytes\n", len(previousDidx))
374+
fmt.Printf("Downloaded previous DIDX: %d bytes\n", len(previousDidx))
385375

386-
/*f2, _ := os.Create("test.didx")
387-
defer f2.Close()
376+
/*f2, _ := os.Create("test.didx")
377+
defer f2.Close()
388378
389-
f2.Write(previous_didx)*/
379+
f2.Write(previous_didx)*/
390380

391-
/*
392-
Here we download the previous dynamic index to figure out which chunks are the same of what
393-
we are going to upload to avoid unnecessary traffic and compression cpu usage
394-
*/
381+
/*
382+
Here we download the previous dynamic index to figure out which chunks are the same of what
383+
we are going to upload to avoid unnecessary traffic and compression cpu usage
384+
*/
395385

396-
if !bytes.HasPrefix(previousDidx, didxMagic) {
397-
fmt.Printf("Previous index has wrong magic (%s)!\n", previousDidx[:8])
386+
if !bytes.HasPrefix(previousDidx, didxMagic) {
387+
fmt.Printf("Previous index has wrong magic (%s)!\n", previousDidx[:8])
398388

399-
} else {
400-
//Header as per proxmox documentation is fixed size of 4096 bytes,
401-
//then offset of type uint64 and sha256 digests follow , so 40 byte each record until EOF
402-
previousDidx = previousDidx[4096:]
403-
for i := 0; i*40 < len(previousDidx); i += 1 {
404-
e := DidxEntry{}
405-
e.offset = binary.LittleEndian.Uint64(previousDidx[i*40 : i*40+8])
406-
e.digest = previousDidx[i*40+8 : i*40+40]
407-
shahash := hex.EncodeToString(e.digest)
408-
fmt.Printf("Previous: %s\n", shahash)
409-
knownChunks.Set(shahash, true)
410-
}
389+
} else {
390+
//Header as per proxmox documentation is fixed size of 4096 bytes,
391+
//then offset of type uint64 and sha256 digests follow , so 40 byte each record until EOF
392+
previousDidx = previousDidx[4096:]
393+
for i := 0; i*40 < len(previousDidx); i += 1 {
394+
e := DidxEntry{}
395+
e.offset = binary.LittleEndian.Uint64(previousDidx[i*40 : i*40+8])
396+
e.digest = previousDidx[i*40+8 : i*40+40]
397+
shahash := hex.EncodeToString(e.digest)
398+
fmt.Printf("Previous: %s\n", shahash)
399+
knownChunks.Set(shahash, true)
411400
}
401+
}
412402

413-
fmt.Printf("Known chunks: %d!\n", knownChunks.Len())
414-
f := &os.File{}
415-
if pxarOut != "" {
416-
f, err = os.Create(pxarOut)
417-
if err != nil {
418-
return err
419-
}
420-
defer f.Close()
403+
fmt.Printf("Known chunks: %d!\n", knownChunks.Len())
404+
f := &os.File{}
405+
if pxarOut != "" {
406+
f, err = os.Create(pxarOut)
407+
if err != nil {
408+
return err
421409
}
422-
/**/
410+
defer f.Close()
411+
}
412+
/**/
423413

424-
pxarChunk := ChunkState{}
425-
pxarChunk.Init(newchunk, reusechunk, knownChunks)
414+
pxarChunk := ChunkState{}
415+
pxarChunk.Init(newchunk, reusechunk, knownChunks)
426416

427-
pcat1Chunk := ChunkState{}
428-
pcat1Chunk.Init(newchunk, reusechunk, knownChunks)
417+
pcat1Chunk := ChunkState{}
418+
pcat1Chunk.Init(newchunk, reusechunk, knownChunks)
429419

430-
pxarChunk.wrid, err = client.CreateDynamicIndex(archive.ArchiveName)
431-
if err != nil {
432-
return err
433-
}
434-
pcat1Chunk.wrid, err = client.CreateDynamicIndex("catalog.pcat1.didx")
435-
if err != nil {
436-
return err
420+
pxarChunk.wrid, err = client.CreateDynamicIndex(archive.ArchiveName)
421+
if err != nil {
422+
return err
423+
}
424+
pcat1Chunk.wrid, err = client.CreateDynamicIndex("catalog.pcat1.didx")
425+
if err != nil {
426+
return err
427+
}
428+
429+
archive.WriteCB = func(b []byte) {
430+
431+
if pxarOut != "" {
432+
// TODO: error handling inside callback
433+
f.Write(b)
437434
}
438435

439-
archive.WriteCB = func(b []byte) {
436+
pxarChunk.HandleData(b, client)
440437

441-
if pxarOut != "" {
442-
// TODO: error handling inside callback
443-
f.Write(b)
444-
}
438+
//
439+
}
445440

446-
pxarChunk.HandleData(b, client)
441+
archive.CatalogWriteCB = func(b []byte) {
442+
pcat1Chunk.HandleData(b, client)
443+
}
447444

448-
//
449-
}
445+
//This is the entry point of backup job which will start streaming with the PCAT and PXAR write callback
446+
//Data to be hashed and eventuall uploaded
450447

451-
archive.CatalogWriteCB = func(b []byte) {
452-
pcat1Chunk.HandleData(b, client)
453-
}
448+
archive.WriteDir(backupdir, "", true)
454449

455-
//This is the entry point of backup job which will start streaming with the PCAT and PXAR write callback
456-
//Data to be hashed and eventuall uploaded
450+
pxarChunk.Eof(client)
451+
pcat1Chunk.Eof(client)
457452

458-
archive.WriteDir(backupdir, "", true)
453+
err = client.UploadManifest()
454+
if err != nil {
455+
return err
456+
}
457+
return nil
458+
}
459459

460-
pxarChunk.Eof(client)
461-
pcat1Chunk.Eof(client)
460+
func backup(client *pbscommon.PBSClient, newchunk, reusechunk *atomic.Uint64, pxarOut string, backupdir string, usevss bool) error {
461+
462462

463-
err = client.UploadManifest()
464-
if err != nil {
465-
return err
466-
}
467-
return nil
468-
})
463+
fmt.Printf("Starting backup of %s\n", backupdir)
464+
var err error
465+
if usevss {
466+
err = snapshot.CreateVSSSnapshot(([]string{backupdir}), func(snaps map[string]snapshot.SnapShot) error {
467+
k := maps.Keys(snaps)
468+
k2 := slices.Collect(k)
469+
SNAP := snaps[k2[0]]
470+
backupdir = SNAP.FullPath
471+
//Remove VSS snapshot on windows, on linux for now NOP
472+
return backup_real(client, newchunk, reusechunk, pxarOut, backupdir)
473+
474+
})
475+
}else{
476+
err = backup_real(client, newchunk, reusechunk, pxarOut, backupdir)
477+
}
469478

470479
if err != nil {
471480
return err

go.work

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ use ./clientcommon
44
use ./pbscommon
55
use ./directorybackup
66
use ./snapshot
7-
use ./machinebackup
7+
use ./machinebackup
8+
use ./nbd

pbscommon/pbsapi.go

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,52 @@ type PBSClient struct {
103103
var blobCompressedMagic = []byte{49, 185, 88, 66, 111, 182, 163, 127}
104104
var blobUncompressedMagic = []byte{66, 171, 56, 7, 190, 131, 112, 161}
105105

106+
type SnapshotsResp struct {
107+
Data []BackupManifest `json:"data"`
108+
}
109+
110+
func (pbs *PBSClient) ListSnapshots() ([]BackupManifest, error) {
111+
client := &http.Client{
112+
Timeout: 10 * time.Second,
113+
}
114+
if pbs.Insecure {
115+
tr := &http.Transport{
116+
TLSClientConfig: &tls.Config{
117+
InsecureSkipVerify: true,
118+
},
119+
120+
}
121+
client.Transport = tr
122+
}
123+
124+
ret := make([]BackupManifest, 0)
125+
var r SnapshotsResp
126+
params := url.Values{}
127+
params.Add("ns", pbs.Namespace)
128+
fullURL := fmt.Sprintf("%s/api2/json/admin/datastore/%s/snapshots?%s", pbs.BaseURL, pbs.Datastore, params.Encode())
129+
130+
req, err := http.NewRequest(http.MethodGet, fullURL, nil)
131+
if err != nil {
132+
return ret, err
133+
}
134+
req.Header.Set("Accept", "application/json")
135+
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.AuthID, pbs.Secret))
136+
resp, err := client.Do(req)
137+
if err != nil {
138+
return ret, err
139+
}
140+
defer resp.Body.Close()
141+
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
142+
body, _ := io.ReadAll(resp.Body)
143+
return ret, fmt.Errorf("HTTP error: %d - %s", resp.StatusCode, string(body))
144+
}
145+
if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
146+
return ret, err
147+
}
148+
return r.Data, nil
149+
150+
}
151+
106152
func (pbs *PBSClient) CreateFixedIndex(fic FixedIndexCreateReq) (uint64, error) {
107153
jd, err := json.Marshal(fic)
108154
if err != nil {
@@ -460,7 +506,7 @@ func (pbs *PBSClient) Connect(reader bool) {
460506
calculatedFingerprint := sha256.Sum256(peerCert.Raw)
461507

462508
// Compare the calculated fingerprint with the expected one
463-
if hex.EncodeToString(calculatedFingerprint[:]) != expectedFingerprint {
509+
if hex.EncodeToString(calculatedFingerprint[:]) != expectedFingerprint && !pbs.Insecure {
464510
return fmt.Errorf("certificate fingerprint does not match (%s,%s)", expectedFingerprint, hex.EncodeToString(calculatedFingerprint[:]))
465511
}
466512

@@ -576,6 +622,33 @@ func (pbs *PBSClient) DownloadPreviousToBytes(archivename string) ([]byte, error
576622

577623
}
578624

625+
func (pbs *PBSClient) DownloadToBytes(archivename string) ([]byte, error) { //In the future also download to tmp if index is extremely big...
626+
q := &url.Values{}
627+
628+
q.Add("file-name", archivename)
629+
630+
req, err := http.NewRequest("GET", pbs.BaseURL+"/download?"+q.Encode(), nil)
631+
req.Header.Add("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", pbs.AuthID, pbs.Secret))
632+
if err != nil {
633+
return nil, err
634+
}
635+
resp2, err := pbs.Client.Do(req)
636+
if err != nil {
637+
fmt.Println("Error making request:", err)
638+
return nil, err
639+
}
640+
defer resp2.Body.Close()
641+
642+
ret, err := io.ReadAll(resp2.Body)
643+
644+
if err != nil {
645+
return nil, err
646+
}
647+
648+
return ret, nil
649+
650+
}
651+
579652
func (pbs *PBSClient) GetKnownSha265FromFIDX(archivename string) (*hashmap.Map[string, bool], error) {
580653
data, err := pbs.DownloadPreviousToBytes(archivename)
581654
if err != nil {

0 commit comments

Comments
 (0)