@@ -30,7 +30,6 @@ import (
3030 "github.com/aws/aws-sdk-go-v2/service/s3/types"
3131 "github.com/pkg/xattr"
3232 "github.com/versity/versitygw/backend"
33- "github.com/versity/versitygw/backend/meta"
3433 "github.com/versity/versitygw/backend/posix"
3534 "github.com/versity/versitygw/s3err"
3635 "github.com/versity/versitygw/s3response"
@@ -50,9 +49,6 @@ type ScoutFS struct {
5049 rootfd * os.File
5150 rootdir string
5251
53- // bucket/object metadata storage facility
54- meta meta.MetadataStorer
55-
5652 // glaciermode enables the following behavior:
5753 // GET object: if file offline, return invalid object state
5854 // HEAD object: if file offline, set obj storage class to GLACIER
@@ -75,8 +71,6 @@ var _ backend.Backend = &ScoutFS{}
7571const (
7672 metaTmpDir = ".sgwtmp"
7773 metaTmpMultipartDir = metaTmpDir + "/multipart"
78- etagkey = "etag"
79- checksumsKey = "checksums"
8074)
8175
8276var (
@@ -245,173 +239,25 @@ func (s *ScoutFS) GetObject(ctx context.Context, input *s3.GetObjectInput) (*s3.
245239}
246240
247241func (s * ScoutFS ) ListObjects (ctx context.Context , input * s3.ListObjectsInput ) (s3response.ListObjectsResult , error ) {
248- bucket := * input .Bucket
249- prefix := ""
250- if input .Prefix != nil {
251- prefix = * input .Prefix
252- }
253- marker := ""
254- if input .Marker != nil {
255- marker = * input .Marker
256- }
257- delim := ""
258- if input .Delimiter != nil {
259- delim = * input .Delimiter
260- }
261- maxkeys := int32 (0 )
262- if input .MaxKeys != nil {
263- maxkeys = * input .MaxKeys
264- }
265-
266- _ , err := os .Stat (bucket )
267- if errors .Is (err , fs .ErrNotExist ) {
268- return s3response.ListObjectsResult {}, s3err .GetAPIError (s3err .ErrNoSuchBucket )
269- }
270- if err != nil {
271- return s3response.ListObjectsResult {}, fmt .Errorf ("stat bucket: %w" , err )
272- }
273-
274- fileSystem := os .DirFS (bucket )
275- results , err := backend .Walk (ctx , fileSystem , prefix , delim , marker , maxkeys ,
276- s .fileToObj (bucket ), []string {metaTmpDir })
277- if err != nil {
278- return s3response.ListObjectsResult {}, fmt .Errorf ("walk %v: %w" , bucket , err )
279- }
280-
281- return s3response.ListObjectsResult {
282- CommonPrefixes : results .CommonPrefixes ,
283- Contents : results .Objects ,
284- Delimiter : backend .GetPtrFromString (delim ),
285- Marker : backend .GetPtrFromString (marker ),
286- NextMarker : backend .GetPtrFromString (results .NextMarker ),
287- Prefix : backend .GetPtrFromString (prefix ),
288- IsTruncated : & results .Truncated ,
289- MaxKeys : & maxkeys ,
290- Name : & bucket ,
291- }, nil
242+ return s .Posix .ListObjectsParametrized (ctx , input , s .fileToObj )
292243}
293244
294245func (s * ScoutFS ) ListObjectsV2 (ctx context.Context , input * s3.ListObjectsV2Input ) (s3response.ListObjectsV2Result , error ) {
295- bucket := * input .Bucket
296- prefix := ""
297- if input .Prefix != nil {
298- prefix = * input .Prefix
299- }
300- marker := ""
301- if input .ContinuationToken != nil {
302- if input .StartAfter != nil {
303- marker = max (* input .StartAfter , * input .ContinuationToken )
304- } else {
305- marker = * input .ContinuationToken
306- }
307- }
308- delim := ""
309- if input .Delimiter != nil {
310- delim = * input .Delimiter
311- }
312- maxkeys := int32 (0 )
313- if input .MaxKeys != nil {
314- maxkeys = * input .MaxKeys
315- }
316-
317- _ , err := os .Stat (bucket )
318- if errors .Is (err , fs .ErrNotExist ) {
319- return s3response.ListObjectsV2Result {}, s3err .GetAPIError (s3err .ErrNoSuchBucket )
320- }
321- if err != nil {
322- return s3response.ListObjectsV2Result {}, fmt .Errorf ("stat bucket: %w" , err )
323- }
324-
325- fileSystem := os .DirFS (bucket )
326- results , err := backend .Walk (ctx , fileSystem , prefix , delim , marker , int32 (maxkeys ),
327- s .fileToObj (bucket ), []string {metaTmpDir })
328- if err != nil {
329- return s3response.ListObjectsV2Result {}, fmt .Errorf ("walk %v: %w" , bucket , err )
330- }
331-
332- count := int32 (len (results .Objects ))
333-
334- return s3response.ListObjectsV2Result {
335- CommonPrefixes : results .CommonPrefixes ,
336- Contents : results .Objects ,
337- IsTruncated : & results .Truncated ,
338- MaxKeys : & maxkeys ,
339- Name : & bucket ,
340- KeyCount : & count ,
341- Delimiter : backend .GetPtrFromString (delim ),
342- ContinuationToken : backend .GetPtrFromString (marker ),
343- NextContinuationToken : backend .GetPtrFromString (results .NextMarker ),
344- Prefix : backend .GetPtrFromString (prefix ),
345- StartAfter : backend .GetPtrFromString (* input .StartAfter ),
346- }, nil
246+ return s .Posix .ListObjectsV2Parametrized (ctx , input , s .fileToObj )
347247}
348248
349- func (s * ScoutFS ) fileToObj (bucket string ) backend.GetObjFunc {
350- return func (path string , d fs.DirEntry ) (s3response.Object , error ) {
351- objPath := filepath .Join (bucket , path )
352- if d .IsDir () {
353- // directory object only happens if directory empty
354- // check to see if this is a directory object by checking etag
355- etagBytes , err := s .meta .RetrieveAttribute (nil , bucket , path , etagkey )
356- if errors .Is (err , meta .ErrNoSuchKey ) || errors .Is (err , fs .ErrNotExist ) {
357- return s3response.Object {}, backend .ErrSkipObj
358- }
359- if err != nil {
360- return s3response.Object {}, fmt .Errorf ("get etag: %w" , err )
361- }
362- etag := string (etagBytes )
363-
364- fi , err := d .Info ()
365- if errors .Is (err , fs .ErrNotExist ) {
366- return s3response.Object {}, backend .ErrSkipObj
367- }
368- if err != nil {
369- return s3response.Object {}, fmt .Errorf ("get fileinfo: %w" , err )
370- }
371-
372- size := int64 (0 )
373- mtime := fi .ModTime ()
374-
375- return s3response.Object {
376- ETag : & etag ,
377- Key : & path ,
378- LastModified : & mtime ,
379- Size : & size ,
380- StorageClass : types .ObjectStorageClassStandard ,
381- }, nil
382- }
383-
384- // Retreive the object checksum algorithm
385- checksums , err := s .retrieveChecksums (nil , bucket , path )
386- if err != nil && ! errors .Is (err , meta .ErrNoSuchKey ) {
387- return s3response.Object {}, backend .ErrSkipObj
388- }
249+ func (s * ScoutFS ) fileToObj (bucket string , fetchOwner bool ) backend.GetObjFunc {
250+ posixFileToObj := s .Posix .FileToObj (bucket , fetchOwner )
389251
390- // file object, get object info and fill out object data
391- b , err := s .meta .RetrieveAttribute (nil , bucket , path , etagkey )
392- if errors .Is (err , fs .ErrNotExist ) {
393- return s3response.Object {}, backend .ErrSkipObj
394- }
395- if err != nil && ! errors .Is (err , meta .ErrNoSuchKey ) {
396- return s3response.Object {}, fmt .Errorf ("get etag: %w" , err )
397- }
398- // note: meta.ErrNoSuchKey will return etagBytes = []byte{}
399- // so this will just set etag to "" if its not already set
400-
401- etag := string (b )
402-
403- fi , err := d .Info ()
404- if errors .Is (err , fs .ErrNotExist ) {
405- return s3response.Object {}, backend .ErrSkipObj
406- }
407- if err != nil {
408- return s3response.Object {}, fmt .Errorf ("get fileinfo: %w" , err )
252+ return func (path string , d fs.DirEntry ) (s3response.Object , error ) {
253+ res , err := posixFileToObj (path , d )
254+ if err != nil || d .IsDir () {
255+ return res , err
409256 }
410-
411- sc := types .ObjectStorageClassStandard
257+ objPath := filepath .Join (bucket , path )
412258 if s .glaciermode {
413259 // Check if there are any offline exents associated with this file.
414- // If so, we will return the InvalidObjectState error.
260+ // If so, we will return the Glacier storage class
415261 st , err := statMore (objPath )
416262 if errors .Is (err , fs .ErrNotExist ) {
417263 return s3response.Object {}, backend .ErrSkipObj
@@ -420,33 +266,11 @@ func (s *ScoutFS) fileToObj(bucket string) backend.GetObjFunc {
420266 return s3response.Object {}, fmt .Errorf ("stat more: %w" , err )
421267 }
422268 if st .Offline_blocks != 0 {
423- sc = types .ObjectStorageClassGlacier
269+ res . StorageClass = types .ObjectStorageClassGlacier
424270 }
425271 }
426-
427- size := fi .Size ()
428- mtime := fi .ModTime ()
429-
430- return s3response.Object {
431- ETag : & etag ,
432- Key : & path ,
433- LastModified : & mtime ,
434- Size : & size ,
435- StorageClass : sc ,
436- ChecksumAlgorithm : []types.ChecksumAlgorithm {checksums .Algorithm },
437- ChecksumType : checksums .Type ,
438- }, nil
439- }
440- }
441-
442- func (s * ScoutFS ) retrieveChecksums (f * os.File , bucket , object string ) (checksums s3response.Checksum , err error ) {
443- checksumsAtr , err := s .meta .RetrieveAttribute (f , bucket , object , checksumsKey )
444- if err != nil {
445- return checksums , err
272+ return res , nil
446273 }
447-
448- err = json .Unmarshal (checksumsAtr , & checksums )
449- return checksums , err
450274}
451275
452276// RestoreObject will set stage request on file if offline and do nothing if
0 commit comments