gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/skynetbackup.go (about) 1 package skymodules 2 3 // The Skynet Backup subsystem handles persistence for creating and reading 4 // skynet backup data. These backups contain all the information needed to 5 // restore a Skyfile with the original Skylink. 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 12 "gitlab.com/NebulousLabs/encoding" 13 "gitlab.com/NebulousLabs/errors" 14 "go.sia.tech/siad/modules" 15 "go.sia.tech/siad/persist" 16 ) 17 18 const ( 19 // backupHeaderSize defines the size of the encoded backup header. 20 // 21 // This is the size of the encoded persist.Metadata and a skylink so the size 22 // should be constant. 23 backupHeaderSize = 92 24 25 // MetadataHeader defines the header for the backup 26 MetadataHeader = "Skyfile Backup\n" 27 28 // MetadataVersion defines the version for the backup 29 MetadataVersion = "v1.5.5\n" 30 ) 31 32 var ( 33 // errWrongHeader is returned if the wrong header is found in a backup 34 errWrongHeader = errors.New("wrong header") 35 36 // errWrongVersion is returned if the wrong version is found in a backup 37 errWrongVersion = errors.New("wrong version") 38 ) 39 40 // SkyfileBackupHeader defines the data that goes at the head of the backup 41 type SkyfileBackupHeader struct { 42 // Metadata contains the persist Metadata identifying the type and version of 43 // the backup 44 persist.Metadata 45 46 // Skylink is the skylink for the backed up skyfile 47 Skylink string 48 } 49 50 // BackupSkylink backs up a skylink by writing skylink and baseSector to 51 // a header and then writing the header and the reader data to the writer. 52 func BackupSkylink(skylink string, baseSector []byte, reader io.Reader, writer io.Writer) error { 53 // Write the header 54 err := writeBackupHeader(writer, skylink) 55 if err != nil { 56 return errors.AddContext(err, "unable to write header") 57 } 58 59 // Write the baseSector 60 err = writeBaseSector(writer, baseSector) 61 if err != nil { 62 return errors.AddContext(err, "unable to write baseSector") 63 } 64 65 // If no reader was provided then it means that it was a small file backup so 66 // the baseSector is all that is needed. 67 if reader == nil { 68 return nil 69 } 70 71 // Write the body of the skyfile 72 err = writeBackupBody(writer, reader) 73 return errors.AddContext(err, "unable to write skyfile data to the writer") 74 } 75 76 // RestoreSkylink restores a skylink by returning the Skylink and the baseSector 77 // from the reader. 78 func RestoreSkylink(r io.Reader) (string, []byte, error) { 79 // Read the header 80 skylink, err := readBackupHeader(r) 81 if err != nil { 82 return "", nil, errors.AddContext(err, "unable to read header") 83 } 84 85 // Read the baseSector 86 baseSector, err := readBaseSector(r) 87 if err != nil { 88 return "", nil, errors.AddContext(err, "unable to read baseSector") 89 } 90 91 // Return information needs to restore the Skyfile by re-uploading 92 return skylink, baseSector, nil 93 } 94 95 // readBackupHeader reads the header from the backup and returns the skylink 96 func readBackupHeader(r io.Reader) (string, error) { 97 // Read the header 98 headerBytes := make([]byte, backupHeaderSize) 99 _, err := io.ReadFull(r, headerBytes) 100 if err != nil { 101 return "", errors.AddContext(err, "header read error") 102 } 103 104 // Unmarshal the Header 105 var sbh SkyfileBackupHeader 106 err = encoding.Unmarshal(headerBytes, &sbh) 107 if err != nil { 108 return "", errors.AddContext(err, "unable to unmarshal header") 109 } 110 111 // Header and Version Check 112 if sbh.Header != MetadataHeader { 113 return "", errWrongHeader 114 } 115 if sbh.Version != MetadataVersion { 116 return "", errWrongVersion 117 } 118 119 return sbh.Skylink, nil 120 } 121 122 // readBaseSector reads the baseSector from the backup 123 func readBaseSector(r io.Reader) ([]byte, error) { 124 // Read the header 125 baseSector := make([]byte, modules.SectorSize) 126 _, err := io.ReadFull(r, baseSector) 127 if err != nil && !(errors.Contains(err, io.EOF) || errors.Contains(err, io.ErrUnexpectedEOF)) { 128 return nil, errors.AddContext(err, "unable to read baseSector") 129 } 130 return baseSector, nil 131 } 132 133 // writeBackupBody writes the contents of the reader to the backup 134 func writeBackupBody(w io.Writer, reader io.Reader) error { 135 _, err := io.Copy(w, reader) 136 return errors.AddContext(err, "unable to copy data from reader to the backup") 137 } 138 139 // writeBackupHeader writes the header of the backup reader 140 func writeBackupHeader(w io.Writer, skylink string) error { 141 // Encoding the header information 142 encodedHeader := encoding.Marshal(SkyfileBackupHeader{ 143 Metadata: persist.Metadata{ 144 Header: MetadataHeader, 145 Version: MetadataVersion, 146 }, 147 Skylink: skylink, 148 }) 149 if len(encodedHeader) > backupHeaderSize { 150 return fmt.Errorf("encoded header has length of %v; max length is %v", len(encodedHeader), backupHeaderSize) 151 } 152 153 // Create a reader for the encoded Header and copy it to the writer 154 headerReader := bytes.NewReader(encodedHeader) 155 _, err := io.Copy(w, headerReader) 156 return errors.AddContext(err, "unable to copy header data to the backup") 157 } 158 159 // writeBaseSector writes the contents of the baseSector to the backup 160 func writeBaseSector(w io.Writer, baseSector []byte) error { 161 baseSectorReader := bytes.NewReader(baseSector) 162 _, err := io.Copy(w, baseSectorReader) 163 return errors.AddContext(err, "unable to copy baseSector to backup") 164 }