github.com/ethersphere/bee/v2@v2.2.0/pkg/sharky/recovery.go (about)

     1  // Copyright 2021 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package sharky
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"io/fs"
    12  	"os"
    13  	"path"
    14  	"sync"
    15  
    16  	"github.com/hashicorp/go-multierror"
    17  )
    18  
    19  // Recovery allows disaster recovery.
    20  type Recovery struct {
    21  	mtx        sync.Mutex
    22  	shards     []*slots
    23  	shardFiles []*os.File
    24  	datasize   int
    25  }
    26  
    27  var ErrShardNotFound = errors.New("shard not found")
    28  
    29  func NewRecovery(dir string, shardCnt int, datasize int) (*Recovery, error) {
    30  	shards := make([]*slots, shardCnt)
    31  	shardFiles := make([]*os.File, shardCnt)
    32  
    33  	for i := 0; i < shardCnt; i++ {
    34  		file, err := os.OpenFile(path.Join(dir, fmt.Sprintf("shard_%03d", i)), os.O_RDWR, 0666)
    35  		if errors.Is(err, fs.ErrNotExist) {
    36  			return nil, fmt.Errorf("index %d: %w", i, ErrShardNotFound)
    37  		}
    38  		if err != nil {
    39  			return nil, err
    40  		}
    41  		fi, err := file.Stat()
    42  		if err != nil {
    43  			return nil, err
    44  		}
    45  		size := uint32(fi.Size() / int64(datasize))
    46  		ffile, err := os.OpenFile(path.Join(dir, fmt.Sprintf("free_%03d", i)), os.O_RDWR|os.O_CREATE, 0666)
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  		sl := newSlots(ffile, nil)
    51  		sl.data = make([]byte, size/8)
    52  		shards[i] = sl
    53  		shardFiles[i] = file
    54  	}
    55  	return &Recovery{shards: shards, shardFiles: shardFiles, datasize: datasize}, nil
    56  }
    57  
    58  // Add marks a location as used (not free).
    59  func (r *Recovery) Add(loc Location) error {
    60  	r.mtx.Lock()
    61  	defer r.mtx.Unlock()
    62  
    63  	sh := r.shards[loc.Shard]
    64  	l := len(sh.data)
    65  	if diff := int(loc.Slot/8) - l; diff >= 0 {
    66  		sh.extend(diff + 1)
    67  		for i := 0; i <= diff; i++ {
    68  			sh.data[l+i] = 0x0
    69  		}
    70  	}
    71  	sh.push(loc.Slot)
    72  	return nil
    73  }
    74  
    75  func (r *Recovery) Read(ctx context.Context, loc Location, buf []byte) error {
    76  	r.mtx.Lock()
    77  	defer r.mtx.Unlock()
    78  	_, err := r.shardFiles[loc.Shard].ReadAt(buf, int64(loc.Slot)*int64(r.datasize))
    79  	return err
    80  }
    81  
    82  func (r *Recovery) Move(ctx context.Context, from Location, to Location) error {
    83  	r.mtx.Lock()
    84  	defer r.mtx.Unlock()
    85  
    86  	chData := make([]byte, from.Length)
    87  	_, err := r.shardFiles[from.Shard].ReadAt(chData, int64(from.Slot)*int64(r.datasize))
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	_, err = r.shardFiles[to.Shard].WriteAt(chData, int64(to.Slot)*int64(r.datasize))
    93  	return err
    94  }
    95  
    96  func (r *Recovery) TruncateAt(ctx context.Context, shard uint8, slot uint32) error {
    97  	r.mtx.Lock()
    98  	defer r.mtx.Unlock()
    99  
   100  	return r.shardFiles[shard].Truncate(int64(slot) * int64(r.datasize))
   101  }
   102  
   103  // Save saves all free slots files of the recovery (without closing).
   104  func (r *Recovery) Save() error {
   105  	r.mtx.Lock()
   106  	defer r.mtx.Unlock()
   107  
   108  	err := new(multierror.Error)
   109  	for _, sh := range r.shards {
   110  		for i := range sh.data {
   111  			sh.data[i] ^= 0xff
   112  		}
   113  		err = multierror.Append(err, sh.save())
   114  	}
   115  	return err.ErrorOrNil()
   116  }
   117  
   118  // Close closes data and free slots files of the recovery (without saving).
   119  func (r *Recovery) Close() error {
   120  	r.mtx.Lock()
   121  	defer r.mtx.Unlock()
   122  
   123  	err := new(multierror.Error)
   124  	for idx, sh := range r.shards {
   125  		err = multierror.Append(err, sh.file.Close(), r.shardFiles[idx].Close())
   126  	}
   127  	return err.ErrorOrNil()
   128  }