2019-01-24 20:15:10 +00:00
// Copyright (C) 2019 Storj Labs, Inc.
2018-10-05 14:34:15 +01:00
// See LICENSE for copying information.
package audit
import (
"context"
2019-04-25 16:43:26 +01:00
crand "crypto/rand"
"encoding/binary"
"math/rand"
2018-10-05 14:34:15 +01:00
"sync"
2019-03-23 10:52:51 +00:00
"time"
2018-10-05 14:34:15 +01:00
2019-04-25 16:43:26 +01:00
"github.com/zeebo/errs"
2018-10-05 14:34:15 +01:00
"storj.io/storj/pkg/pb"
2018-10-25 21:28:16 +01:00
"storj.io/storj/pkg/storj"
2019-04-25 09:46:32 +01:00
"storj.io/storj/satellite/metainfo"
2019-07-28 06:55:36 +01:00
"storj.io/storj/uplink/eestream"
"storj.io/storj/uplink/storage/meta"
2018-10-05 14:34:15 +01:00
)
2018-10-10 19:25:46 +01:00
// Stripe keeps track of a stripe's index and its parent segment
type Stripe struct {
2019-03-18 10:55:06 +00:00
Index int64
Segment * pb . Pointer
2019-03-28 20:09:23 +00:00
SegmentPath storj . Path
2018-10-10 19:25:46 +01:00
}
// Cursor keeps track of audit location in pointer db
type Cursor struct {
2019-04-25 09:46:32 +01:00
metainfo * metainfo . Service
lastPath storj . Path
mutex sync . Mutex
2018-10-05 14:34:15 +01:00
}
2018-10-10 19:25:46 +01:00
// NewCursor creates a Cursor which iterates over pointer db
2019-04-25 09:46:32 +01:00
func NewCursor ( metainfo * metainfo . Service ) * Cursor {
2019-03-18 10:55:06 +00:00
return & Cursor {
2019-04-25 09:46:32 +01:00
metainfo : metainfo ,
2019-03-18 10:55:06 +00:00
}
2018-10-05 14:34:15 +01:00
}
2019-05-01 19:59:30 +01:00
// NextStripe returns a random stripe to be audited. "more" is true except when we have completed iterating over metainfo. It can be disregarded if there is an error or stripe returned
func ( cursor * Cursor ) NextStripe ( ctx context . Context ) ( stripe * Stripe , more bool , err error ) {
2019-06-04 12:36:27 +01:00
defer mon . Task ( ) ( & ctx ) ( & err )
2018-10-10 19:25:46 +01:00
cursor . mutex . Lock ( )
defer cursor . mutex . Unlock ( )
2018-10-05 14:34:15 +01:00
2019-01-10 16:35:18 +00:00
var pointerItems [ ] * pb . ListResponse_Item
2018-10-25 21:28:16 +01:00
var path storj . Path
2018-10-05 14:34:15 +01:00
2019-06-05 15:23:10 +01:00
pointerItems , more , err = cursor . metainfo . List ( ctx , "" , cursor . lastPath , "" , true , 0 , meta . None )
2018-10-05 14:34:15 +01:00
if err != nil {
2019-05-01 19:59:30 +01:00
return nil , more , err
2018-10-05 14:34:15 +01:00
}
// keep track of last path listed
if ! more {
2018-10-25 21:28:16 +01:00
cursor . lastPath = ""
2018-10-05 14:34:15 +01:00
} else {
2018-10-25 21:28:16 +01:00
cursor . lastPath = pointerItems [ len ( pointerItems ) - 1 ] . Path
2018-10-05 14:34:15 +01:00
}
2019-07-18 19:08:15 +01:00
if len ( pointerItems ) == 0 {
return nil , more , nil
}
2019-06-04 12:36:27 +01:00
pointer , path , err := cursor . getRandomValidPointer ( ctx , pointerItems )
2019-01-19 18:58:53 +00:00
if err != nil {
2019-05-01 19:59:30 +01:00
return nil , more , err
}
if pointer == nil {
return nil , more , nil
2019-01-19 18:58:53 +00:00
}
2019-06-04 12:36:27 +01:00
index , err := getRandomStripe ( ctx , pointer )
2019-03-18 10:55:06 +00:00
if err != nil {
2019-05-01 19:59:30 +01:00
return nil , more , err
2019-03-18 10:55:06 +00:00
}
2019-01-10 16:35:18 +00:00
return & Stripe {
2019-03-18 10:55:06 +00:00
Index : index ,
Segment : pointer ,
2019-03-28 20:09:23 +00:00
SegmentPath : path ,
2019-05-01 19:59:30 +01:00
} , more , nil
2018-10-05 14:34:15 +01:00
}
2019-06-04 12:36:27 +01:00
func getRandomStripe ( ctx context . Context , pointer * pb . Pointer ) ( index int64 , err error ) {
defer mon . Task ( ) ( & ctx ) ( & err )
2019-03-18 10:55:06 +00:00
redundancy , err := eestream . NewRedundancyStrategyFromProto ( pointer . GetRemote ( ) . GetRedundancy ( ) )
2018-10-05 14:34:15 +01:00
if err != nil {
2019-03-18 10:55:06 +00:00
return 0 , err
2018-10-05 14:34:15 +01:00
}
2018-11-07 21:23:05 +00:00
// the last segment could be smaller than stripe size
2019-03-18 10:55:06 +00:00
if pointer . GetSegmentSize ( ) < int64 ( redundancy . StripeSize ( ) ) {
2018-11-07 21:23:05 +00:00
return 0 , nil
}
2019-04-25 16:43:26 +01:00
var src cryptoSource
rnd := rand . New ( src )
numStripes := pointer . GetSegmentSize ( ) / int64 ( redundancy . StripeSize ( ) )
randomStripeIndex := rnd . Int63n ( numStripes )
return randomStripeIndex , nil
}
// getRandomValidPointer attempts to get a random remote pointer from a list. If it sees expired pointers in the process of looking, deletes them
2019-06-04 12:36:27 +01:00
func ( cursor * Cursor ) getRandomValidPointer ( ctx context . Context , pointerItems [ ] * pb . ListResponse_Item ) ( pointer * pb . Pointer , path storj . Path , err error ) {
defer mon . Task ( ) ( & ctx ) ( & err )
2019-04-25 16:43:26 +01:00
var src cryptoSource
rnd := rand . New ( src )
errGroup := new ( errs . Group )
randomNums := rnd . Perm ( len ( pointerItems ) )
for _ , randomIndex := range randomNums {
pointerItem := pointerItems [ randomIndex ]
path := pointerItem . Path
// get pointer info
2019-06-05 15:23:10 +01:00
pointer , err := cursor . metainfo . Get ( ctx , path )
2019-04-25 16:43:26 +01:00
if err != nil {
errGroup . Add ( err )
continue
}
//delete expired items rather than auditing them
2019-07-09 22:54:00 +01:00
if ! pointer . ExpirationDate . IsZero ( ) && pointer . ExpirationDate . Before ( time . Now ( ) ) {
err := cursor . metainfo . Delete ( ctx , path )
2019-04-25 16:43:26 +01:00
if err != nil {
errGroup . Add ( err )
}
2019-07-09 22:54:00 +01:00
continue
2019-04-25 16:43:26 +01:00
}
if pointer . GetType ( ) != pb . Pointer_REMOTE || pointer . GetSegmentSize ( ) == 0 {
continue
}
return pointer , path , nil
2018-10-05 14:34:15 +01:00
}
2019-03-18 10:55:06 +00:00
2019-04-25 16:43:26 +01:00
return nil , "" , errGroup . Err ( )
}
// cryptoSource implements the math/rand Source interface using crypto/rand
type cryptoSource struct { }
func ( s cryptoSource ) Seed ( seed int64 ) { }
func ( s cryptoSource ) Int63 ( ) int64 {
return int64 ( s . Uint64 ( ) & ^ uint64 ( 1 << 63 ) )
2018-10-05 14:34:15 +01:00
}
2019-04-25 16:43:26 +01:00
func ( s cryptoSource ) Uint64 ( ) ( v uint64 ) {
err := binary . Read ( crand . Reader , binary . BigEndian , & v )
2018-10-05 14:34:15 +01:00
if err != nil {
2019-04-25 16:43:26 +01:00
panic ( err )
2018-10-05 14:34:15 +01:00
}
2019-04-25 16:43:26 +01:00
return v
2019-03-18 10:55:06 +00:00
}