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  }