github.com/mckael/restic@v0.8.3/internal/archiver/archive_reader.go (about)

     1  package archiver
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"time"
     7  
     8  	"github.com/restic/restic/internal/debug"
     9  	"github.com/restic/restic/internal/restic"
    10  
    11  	"github.com/restic/restic/internal/errors"
    12  
    13  	"github.com/restic/chunker"
    14  )
    15  
    16  // Reader allows saving a stream of data to the repository.
    17  type Reader struct {
    18  	restic.Repository
    19  
    20  	Tags     []string
    21  	Hostname string
    22  }
    23  
    24  // Archive reads data from the reader and saves it to the repo.
    25  func (r *Reader) Archive(ctx context.Context, name string, rd io.Reader, p *restic.Progress) (*restic.Snapshot, restic.ID, error) {
    26  	if name == "" {
    27  		return nil, restic.ID{}, errors.New("no filename given")
    28  	}
    29  
    30  	debug.Log("start archiving %s", name)
    31  	sn, err := restic.NewSnapshot([]string{name}, r.Tags, r.Hostname, time.Now())
    32  	if err != nil {
    33  		return nil, restic.ID{}, err
    34  	}
    35  
    36  	p.Start()
    37  	defer p.Done()
    38  
    39  	repo := r.Repository
    40  	chnker := chunker.New(rd, repo.Config().ChunkerPolynomial)
    41  
    42  	ids := restic.IDs{}
    43  	var fileSize uint64
    44  
    45  	for {
    46  		chunk, err := chnker.Next(getBuf())
    47  		if errors.Cause(err) == io.EOF {
    48  			break
    49  		}
    50  
    51  		if err != nil {
    52  			return nil, restic.ID{}, errors.Wrap(err, "chunker.Next()")
    53  		}
    54  
    55  		id := restic.Hash(chunk.Data)
    56  
    57  		if !repo.Index().Has(id, restic.DataBlob) {
    58  			_, err := repo.SaveBlob(ctx, restic.DataBlob, chunk.Data, id)
    59  			if err != nil {
    60  				return nil, restic.ID{}, err
    61  			}
    62  			debug.Log("saved blob %v (%d bytes)\n", id, chunk.Length)
    63  		} else {
    64  			debug.Log("blob %v already saved in the repo\n", id)
    65  		}
    66  
    67  		freeBuf(chunk.Data)
    68  
    69  		ids = append(ids, id)
    70  
    71  		p.Report(restic.Stat{Bytes: uint64(chunk.Length)})
    72  		fileSize += uint64(chunk.Length)
    73  	}
    74  
    75  	tree := &restic.Tree{
    76  		Nodes: []*restic.Node{
    77  			{
    78  				Name:       name,
    79  				AccessTime: time.Now(),
    80  				ModTime:    time.Now(),
    81  				Type:       "file",
    82  				Mode:       0644,
    83  				Size:       fileSize,
    84  				UID:        sn.UID,
    85  				GID:        sn.GID,
    86  				User:       sn.Username,
    87  				Content:    ids,
    88  			},
    89  		},
    90  	}
    91  
    92  	treeID, err := repo.SaveTree(ctx, tree)
    93  	if err != nil {
    94  		return nil, restic.ID{}, err
    95  	}
    96  	sn.Tree = &treeID
    97  	debug.Log("tree saved as %v", treeID)
    98  
    99  	id, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, sn)
   100  	if err != nil {
   101  		return nil, restic.ID{}, err
   102  	}
   103  
   104  	debug.Log("snapshot saved as %v", id)
   105  
   106  	err = repo.Flush(ctx)
   107  	if err != nil {
   108  		return nil, restic.ID{}, err
   109  	}
   110  
   111  	err = repo.SaveIndex(ctx)
   112  	if err != nil {
   113  		return nil, restic.ID{}, err
   114  	}
   115  
   116  	return sn, id, nil
   117  }