@@ -3,28 +3,22 @@ package util
3
3
import (
4
4
"errors"
5
5
"fmt"
6
- "io"
7
6
"strconv"
8
7
9
8
"github.com/nspcc-dev/neo-go/cli/cmdargs"
10
9
"github.com/nspcc-dev/neo-go/cli/options"
11
- "github.com/nspcc-dev/neo-go/pkg/services/helpers/neofs"
12
10
"github.com/nspcc-dev/neo-go/pkg/wallet"
13
11
"github.com/nspcc-dev/neofs-sdk-go/client"
14
12
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
15
13
"github.com/nspcc-dev/neofs-sdk-go/object"
16
14
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
17
- "github.com/nspcc-dev/neofs-sdk-go/pool"
18
- "github.com/nspcc-dev/neofs-sdk-go/user"
19
15
"github.com/urfave/cli/v2"
20
16
)
21
17
22
18
func auditBin (ctx * cli.Context ) error {
23
19
if err := cmdargs .EnsureNone (ctx ); err != nil {
24
20
return err
25
21
}
26
- indexAttrKey := ctx .String ("index-attribute" )
27
- indexFileSize := ctx .Uint ("index-file-size" )
28
22
retries := ctx .Uint ("retries" )
29
23
cnrID := ctx .String ("container" )
30
24
debug := ctx .Bool ("debug" )
@@ -58,179 +52,74 @@ func auditBin(ctx *cli.Context) error {
58
52
}
59
53
60
54
if skip > 0 {
61
- fmt .Fprintf (ctx .App .Writer , "Skipping %d index files \n " , skip )
55
+ fmt .Fprintf (ctx .App .Writer , "Skipping first %d blocks \n " , skip )
62
56
}
63
- filters := object . NewSearchFilters ()
64
- filters . AddFilter ( indexAttrKey , fmt . Sprintf ( "%d" , skip ), object .MatchNumGE )
65
- results , errs := neofs . ObjectSearch ( ctx . Context , neoFSPool , acc . PrivateKey ( ), containerID , filters , [] string { indexAttrKey } )
57
+
58
+ f := object .NewSearchFilters ( )
59
+ f . AddFilter ( blockAttr , strconv . Itoa ( skip ), object . MatchNumGE )
66
60
67
61
var (
62
+ cursor string
68
63
originalID uint64
69
64
originalOID oid.ID
70
65
)
71
- loop:
72
- for {
73
- select {
74
- case <- ctx .Done ():
75
- return cli .Exit ("context cancelled" , 1 )
76
- case err , ok := <- errs :
77
- if ! ok {
78
- break loop
79
- }
80
- if err != nil {
81
- return cli .Exit (fmt .Sprintf ("search index files: %v" , err ), 1 )
82
- }
83
- case itm , ok := <- results :
84
- if ! ok {
85
- break loop
86
- }
87
- duplicateID , err := strconv .ParseUint (itm .Attributes [0 ], 10 , 32 )
88
- if err != nil {
89
- return cli .Exit (fmt .Errorf ("failed to parse index file ID (%s): %w" , itm .ID , err ), 1 )
90
- }
91
-
92
- if ! originalOID .IsZero () && duplicateID == originalID {
93
- if dryRun {
94
- fmt .Fprintf (ctx .App .Writer , "[dry-run] index file duplicate %s / %s (%d)\n " , itm .ID , originalOID , originalID )
95
- } else {
96
- _ , err := neoFSPool .ObjectDelete (ctx .Context , containerID , itm .ID , signer , client.PrmObjectDelete {})
97
- if err != nil {
98
- return cli .Exit (fmt .Errorf ("failed to remove index file duplicate %s / %s (%d): %w" , itm .ID , originalOID , originalID , err ), 1 )
99
- }
100
- fmt .Fprintf (ctx .App .Writer , "Index file duplicate %s / %s (%d) is removed\n " , itm .ID , originalOID , originalID )
101
- }
102
- continue
103
- }
104
- originalID = duplicateID
105
- originalOID = itm .ID
106
- fmt .Fprintf (ctx .App .Writer , "Processing index file %d (%s)\n " , originalID , originalOID )
107
-
108
- originalOIDs , err := getBlockIDs (ctx , neoFSPool , containerID , originalOID , indexFileSize , signer , retries , debug )
109
- if err != nil {
110
- return cli .Exit (fmt .Errorf ("failed to retrieve block OIDs for index file %d (%s): %w" , originalID , originalOID , err ), 1 )
111
- }
112
-
113
- startHeight := uint32 (duplicateID ) * uint32 (indexFileSize )
114
- endHeight := startHeight + uint32 (indexFileSize )
115
- err = deleteOrphans (ctx , neoFSPool , signer , containerID , blockAttr , originalOIDs ,
116
- int (startHeight ), int (endHeight ), int (retries ), debug , dryRun )
117
- if err != nil {
118
- return cli .Exit (fmt .Errorf ("failed to remove block duplicates: %w" , err ), 1 )
119
- }
120
- }
121
- }
122
- fmt .Fprintln (ctx .App .Writer , "Audit is completed." )
123
- return nil
124
- }
125
-
126
- func getBlockIDs (ctx * cli.Context , p * pool.Pool , containerID cid.ID , indexFileID oid.ID , indexFileSize uint , signer user.Signer , maxRetries uint , debug bool ) ([]oid.ID , error ) {
127
- var rc io.ReadCloser
128
-
129
- err := retry (func () error {
130
- var e error
131
- _ , rc , e = p .ObjectGetInit (ctx .Context , containerID , indexFileID , signer , client.PrmObjectGet {})
132
- return e
133
- }, maxRetries , debug )
134
- if err != nil {
135
- return nil , fmt .Errorf ("failed to get index file %s: %w" , indexFileID , err )
136
- }
137
- defer rc .Close ()
138
-
139
- raw , err := io .ReadAll (rc )
140
- if err != nil {
141
- return nil , err
142
- }
143
- if len (raw ) != int (indexFileSize )* oid .Size {
144
- return nil , fmt .Errorf ("index file %s: size mismatch: expected %d bytes, got %d" , indexFileID , int (indexFileSize )* oid .Size , len (raw ))
145
- }
146
-
147
- out := make ([]oid.ID , 0 , indexFileSize )
148
- for i := range indexFileSize {
149
- out = append (out , oid .ID (raw [i * oid .Size :(i + 1 )* oid .Size ]))
150
- }
151
- return out , nil
152
- }
153
-
154
- // deleteOrphans removes every block object those OID differs from the one
155
- // specified in the index file for the given height. It prints a WARN if the
156
- // expected object is missing. If dryRun is enabled, it prints duplicate OIDs
157
- // instead of removing them.
158
- func deleteOrphans (ctx * cli.Context , p * pool.Pool , signer user.Signer , containerID cid.ID , blockAttr string , originalOIDs []oid.ID , start , end , maxRetries int , debug , dryRun bool ) error {
159
- var (
160
- cursor string
161
- oidIndex int
162
- )
163
-
164
- // Search for block objects with height matching the expected one.
165
- f := object .NewSearchFilters ()
166
- f .AddFilter (blockAttr , strconv .Itoa (start ), object .MatchNumGE )
167
- f .AddFilter (blockAttr , strconv .Itoa (end ), object .MatchNumLT )
168
66
169
67
for {
170
68
var (
171
- err error
172
- nextCursor string
173
69
page []client.SearchResultItem
70
+ nextCursor string
174
71
)
175
72
176
73
err = retry (func () error {
177
- page , nextCursor , err = p .SearchObjects (ctx .Context , containerID , f , []string {blockAttr }, cursor , signer , client.SearchObjectsOptions {})
178
- if err != nil {
179
- return fmt .Errorf ("failed to search objects: %w" , err )
74
+ var e error
75
+ page , nextCursor , e = neoFSPool .SearchObjects (ctx .Context , containerID , f , []string {blockAttr }, cursor , signer , client.SearchObjectsOptions {})
76
+ if e != nil {
77
+ return fmt .Errorf ("failed to search objects: %w" , e )
180
78
}
181
79
return nil
182
- }, uint ( maxRetries ) , debug )
80
+ }, retries , debug )
183
81
if err != nil {
184
- return err
82
+ return cli . Exit ( fmt . Sprintf ( "search block objects: %v" , err ), 1 )
185
83
}
84
+
186
85
for _ , itm := range page {
187
86
select {
188
87
case <- ctx .Done ():
189
- return nil
88
+ return cli . Exit ( "context cancelled" , 1 )
190
89
default :
191
90
}
192
- foundID := itm . ID
193
- foundIndex , err := strconv .Atoi (itm .Attributes [0 ])
91
+
92
+ duplicateID , err := strconv .ParseUint (itm .Attributes [0 ], 10 , 64 )
194
93
if err != nil {
195
- return fmt .Errorf ("incorrect index in result: %q" , itm .Attributes [0 ])
196
- }
197
- if debug {
198
- fmt .Fprintf (ctx .App .Writer , "found block %d (%s), expected %d (%s)\n " , foundIndex , foundID , oidIndex , originalOIDs [oidIndex ])
199
- }
200
- if foundIndex == start + oidIndex && foundID == originalOIDs [oidIndex ] {
201
- oidIndex ++
202
- continue
94
+ return cli .Exit (fmt .Errorf ("failed to parse block ID (%s): %w" , itm .ID , err ), 1 )
203
95
}
204
96
205
- if foundIndex > start + oidIndex {
206
- for start + oidIndex < foundIndex {
207
- fmt .Fprintf (ctx .App .Writer , "WARN: block %d (%s) is listed in the index file but missing from the storage\n " , start + oidIndex , originalOIDs [oidIndex ])
208
- oidIndex ++
209
- }
210
- if foundID == originalOIDs [oidIndex ] {
211
- continue
212
- }
213
- }
214
- if dryRun {
215
- fmt .Fprintf (ctx .App .Writer , "[dry-run] block duplicate %s / %s (%d)\n " , foundID , originalOIDs [oidIndex ], foundIndex )
216
- } else {
217
- err := retry (func () error {
218
- _ , errDelete := p .ObjectDelete (ctx .Context , containerID , foundID , signer , client.PrmObjectDelete {})
219
- return errDelete
220
- }, uint (maxRetries ), debug )
221
-
222
- if err != nil {
223
- fmt .Fprintf (ctx .App .Writer , "WARN: failed to remove block %s / %s (%d): %s\n " , foundID , originalOIDs [foundIndex - start ], foundIndex , err )
224
- } else if debug {
225
- fmt .Fprintf (ctx .App .Writer , "Block duplicate %s / %s (%d) is removed\n " , foundID , originalOIDs [foundIndex - start ], foundIndex )
97
+ if ! originalOID .IsZero () && duplicateID == originalID {
98
+ if dryRun {
99
+ fmt .Fprintf (ctx .App .Writer , "[dry-run] block duplicate %s / %s (%d)\n " , itm .ID , originalOID , originalID )
100
+ } else {
101
+ err = retry (func () error {
102
+ _ , e := neoFSPool .ObjectDelete (ctx .Context , containerID , itm .ID , signer , client.PrmObjectDelete {})
103
+ return e
104
+ }, retries , debug )
105
+ if err != nil {
106
+ return cli .Exit (fmt .Errorf ("failed to remove block duplicate %s / %s (%d): %w" , itm .ID , originalOID , originalID , err ), 1 )
107
+ }
108
+ fmt .Fprintf (ctx .App .Writer , "block duplicate %s / %s (%d) is removed\n " , itm .ID , originalOID , originalID )
226
109
}
110
+ continue
227
111
}
112
+
113
+ originalID = duplicateID
114
+ originalOID = itm .ID
228
115
}
116
+
229
117
if nextCursor == "" {
230
118
break
231
119
}
232
120
cursor = nextCursor
233
121
}
234
122
123
+ fmt .Fprintln (ctx .App .Writer , "Audit is completed." )
235
124
return nil
236
125
}
0 commit comments