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 }