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-03-23 10:52:51 +00:00
"github.com/golang/protobuf/ptypes"
2019-04-25 16:43:26 +01:00
"github.com/zeebo/errs"
2018-10-05 14:34:15 +01:00
"storj.io/storj/pkg/eestream"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/storage/meta"
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"
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 ) {
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-04-25 09:46:32 +01:00
pointerItems , more , err = cursor . metainfo . List ( "" , 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-04-25 16:43:26 +01:00
pointer , path , err := cursor . getRandomValidPointer ( 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-03-18 10:55:06 +00:00
index , err := getRandomStripe ( pointer )
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-03-18 10:55:06 +00:00
func getRandomStripe ( pointer * pb . Pointer ) ( index int64 , err error ) {
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
func ( cursor * Cursor ) getRandomValidPointer ( pointerItems [ ] * pb . ListResponse_Item ) ( pointer * pb . Pointer , path storj . Path , err error ) {
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
pointer , err := cursor . metainfo . Get ( path )
if err != nil {
errGroup . Add ( err )
continue
}
//delete expired items rather than auditing them
if expiration := pointer . GetExpirationDate ( ) ; expiration != nil {
t , err := ptypes . Timestamp ( expiration )
if err != nil {
errGroup . Add ( err )
continue
}
if t . Before ( time . Now ( ) ) {
err := cursor . metainfo . Delete ( path )
if err != nil {
errGroup . Add ( err )
}
continue
}
}
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
}