github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/xact/xs/wi_lso.go (about)

     1  // Package xs contains most of the supported eXtended actions (xactions) with some
     2  // exceptions that include certain storage services (mirror, EC) and extensions (downloader, lru).
     3  /*
     4   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     5   */
     6  package xs
     7  
     8  import (
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/NVIDIA/aistore/api/apc"
    13  	"github.com/NVIDIA/aistore/cmn"
    14  	"github.com/NVIDIA/aistore/cmn/cos"
    15  	"github.com/NVIDIA/aistore/cmn/debug"
    16  	"github.com/NVIDIA/aistore/core"
    17  	"github.com/NVIDIA/aistore/core/meta"
    18  	"github.com/NVIDIA/aistore/fs"
    19  )
    20  
    21  // common context and helper methods for object listing
    22  
    23  type (
    24  	lomVisitedCb func(lom *core.LOM)
    25  
    26  	// context used to `list` objects in local filesystems
    27  	walkInfo struct {
    28  		smap         *meta.Smap
    29  		msg          *apc.LsoMsg
    30  		lomVisitedCb lomVisitedCb
    31  		markerDir    string
    32  		wanted       cos.BitFlags
    33  	}
    34  )
    35  
    36  func noopCb(*core.LOM) {}
    37  
    38  func isOK(status uint16) bool { return status == apc.LocOK }
    39  
    40  // TODO: `msg.StartAfter`
    41  func newWalkInfo(msg *apc.LsoMsg, lomVisitedCb lomVisitedCb) (wi *walkInfo) {
    42  	wi = &walkInfo{
    43  		smap:         core.T.Sowner().Get(),
    44  		lomVisitedCb: lomVisitedCb,
    45  		msg:          msg,
    46  		wanted:       wanted(msg),
    47  	}
    48  	if msg.ContinuationToken != "" { // marker is always a filename
    49  		wi.markerDir = filepath.Dir(msg.ContinuationToken)
    50  		if wi.markerDir == "." {
    51  			wi.markerDir = ""
    52  		}
    53  	}
    54  	return
    55  }
    56  
    57  func (wi *walkInfo) lsmsg() *apc.LsoMsg { return wi.msg }
    58  
    59  func (wi *walkInfo) processDir(fqn string) error {
    60  	ct, err := core.NewCTFromFQN(fqn, nil)
    61  	if err != nil {
    62  		return nil
    63  	}
    64  
    65  	if !cmn.DirHasOrIsPrefix(ct.ObjectName(), wi.msg.Prefix) {
    66  		return filepath.SkipDir
    67  	}
    68  
    69  	// e.g., when `markerDir` "b/c/d/" we skip directories "a/", "b/a/",
    70  	// "b/b/" etc. but do not skip entire "b/" and "b/c/" since it is our
    71  	// parent that we need to traverse ("b/" < "b/c/d/").
    72  	if wi.markerDir != "" && ct.ObjectName() < wi.markerDir && !strings.HasPrefix(wi.markerDir, ct.ObjectName()) {
    73  		return filepath.SkipDir
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  func (wi *walkInfo) match(objName string) bool {
    80  	if !cmn.ObjHasPrefix(objName, wi.msg.Prefix) {
    81  		return false
    82  	}
    83  	return wi.msg.ContinuationToken == "" || !cmn.TokenGreaterEQ(wi.msg.ContinuationToken, objName)
    84  }
    85  
    86  // new entry to be added to the listed page (note: slow path)
    87  func (wi *walkInfo) ls(lom *core.LOM, status uint16) (e *cmn.LsoEnt) {
    88  	e = &cmn.LsoEnt{Name: lom.ObjName, Flags: status | apc.EntryIsCached}
    89  	if wi.msg.IsFlagSet(apc.LsVerChanged) {
    90  		checkRemoteMD(lom, e)
    91  	}
    92  	if wi.msg.IsFlagSet(apc.LsNameOnly) {
    93  		return
    94  	}
    95  	wi.setWanted(e, lom)
    96  	wi.lomVisitedCb(lom)
    97  	return
    98  }
    99  
   100  // NOTE: slow path
   101  func checkRemoteMD(lom *core.LOM, e *cmn.LsoEnt) {
   102  	if !lom.Bucket().HasVersioningMD() {
   103  		debug.Assert(false, lom.Cname())
   104  		return
   105  	}
   106  	res := lom.CheckRemoteMD(false /*locked*/, false /*sync*/, nil /*origReq*/)
   107  	switch {
   108  	case res.Eq:
   109  		debug.AssertNoErr(res.Err)
   110  	case cos.IsNotExist(res.Err, res.ErrCode):
   111  		e.SetVerRemoved()
   112  	default:
   113  		e.SetVerChanged()
   114  	}
   115  }
   116  
   117  // Performs a number of syscalls to load object metadata.
   118  func (wi *walkInfo) callback(fqn string, de fs.DirEntry) (entry *cmn.LsoEnt, err error) {
   119  	if de.IsDir() {
   120  		return
   121  	}
   122  
   123  	lom := core.AllocLOM("")
   124  	entry, err = wi._cb(lom, fqn)
   125  	core.FreeLOM(lom)
   126  	return
   127  }
   128  
   129  func (wi *walkInfo) _cb(lom *core.LOM, fqn string) (*cmn.LsoEnt, error) {
   130  	if err := lom.PreInit(fqn); err != nil {
   131  		return nil, err
   132  	}
   133  	if !wi.match(lom.ObjName) {
   134  		return nil, nil
   135  	}
   136  	if err := lom.PostInit(); err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	_, local, err := lom.HrwTarget(wi.smap)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	status := uint16(apc.LocOK)
   146  	if !local {
   147  		status = apc.LocMisplacedNode
   148  	} else if !lom.IsHRW() {
   149  		// preliminary
   150  		status = apc.LocMisplacedMountpath
   151  	}
   152  
   153  	// shortcut #1: name-only optimizes-out loading md (NOTE: won't show misplaced and copies)
   154  	if wi.msg.IsFlagSet(apc.LsNameOnly) {
   155  		if !isOK(status) {
   156  			return nil, nil
   157  		}
   158  		return wi.ls(lom, status), nil
   159  	}
   160  	// load
   161  	if err := lom.Load(isOK(status) /*cache it*/, false /*locked*/); err != nil {
   162  		if cmn.IsErrObjNought(err) || !isOK(status) {
   163  			return nil, nil
   164  		}
   165  		return nil, err
   166  	}
   167  	if local && lom.IsCopy() {
   168  		// still may change below
   169  		status = apc.LocIsCopy
   170  	}
   171  	if isOK(status) {
   172  		return wi.ls(lom, status), nil
   173  	}
   174  
   175  	if !wi.msg.IsFlagSet(apc.LsMissing) {
   176  		return nil, nil
   177  	}
   178  	if local {
   179  		// check hrw mountpath location
   180  		hlom := &core.LOM{}
   181  		if err := hlom.InitFQN(lom.HrwFQN, lom.Bucket()); err != nil {
   182  			return nil, err
   183  		}
   184  		if err := hlom.Load(true /*cache it*/, false /*locked*/); err != nil {
   185  			mirror := lom.MirrorConf()
   186  			if mirror.Enabled && mirror.Copies > 1 {
   187  				status = apc.LocIsCopyMissingObj
   188  			}
   189  		}
   190  	}
   191  	return wi.ls(lom, status), nil
   192  }