github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/data/dir_data.go (about) 1 // Copyright 2018 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package data 6 7 import ( 8 "github.com/keybase/client/go/kbfs/idutil" 9 "github.com/keybase/client/go/kbfs/kbfsblock" 10 "github.com/keybase/client/go/kbfs/libkey" 11 "github.com/keybase/client/go/kbfs/tlf" 12 "github.com/keybase/client/go/libkb" 13 "github.com/keybase/client/go/logger" 14 "github.com/keybase/client/go/protocol/keybase1" 15 "golang.org/x/net/context" 16 ) 17 18 // dirBlockGetter is a function that gets a block suitable for 19 // reading or writing, and also returns whether the block was already 20 // dirty. It may be called from new goroutines, and must handle any 21 // required locks accordingly. 22 type dirBlockGetter func(context.Context, libkey.KeyMetadata, BlockPointer, 23 Path, BlockReqType) (dblock *DirBlock, wasDirty bool, err error) 24 25 // DirData is a helper struct for accessing and manipulating data 26 // within a directory. It's meant for use within a single scope, not 27 // for long-term storage. The caller must ensure goroutine-safety. 28 type DirData struct { 29 getter dirBlockGetter 30 tree *blockTree 31 } 32 33 // NewDirData creates a new DirData instance. 34 func NewDirData( 35 dir Path, chargedTo keybase1.UserOrTeamID, bsplit BlockSplitter, 36 kmd libkey.KeyMetadata, getter dirBlockGetter, cacher dirtyBlockCacher, 37 log logger.Logger, vlog *libkb.VDebugLog) *DirData { 38 dd := &DirData{ 39 getter: getter, 40 } 41 dd.tree = &blockTree{ 42 file: dir, 43 chargedTo: chargedTo, 44 kmd: kmd, 45 bsplit: bsplit, 46 getter: dd.blockGetter, 47 cacher: cacher, 48 log: log, 49 vlog: vlog, 50 } 51 return dd 52 } 53 54 func (dd *DirData) rootBlockPointer() BlockPointer { 55 return dd.tree.file.TailPointer() 56 } 57 58 func (dd *DirData) blockGetter( 59 ctx context.Context, kmd libkey.KeyMetadata, ptr BlockPointer, 60 dir Path, rtype BlockReqType) ( 61 block BlockWithPtrs, wasDirty bool, err error) { 62 return dd.getter(ctx, kmd, ptr, dir, rtype) 63 } 64 65 var hiddenEntries = map[string]bool{ 66 ".kbfs_git": true, 67 ".kbfs_autogit": true, 68 ".kbfs_deleted_repos": true, 69 } 70 71 // GetTopBlock returns the top-most block in this directory block tree. 72 func (dd *DirData) GetTopBlock(ctx context.Context, rtype BlockReqType) ( 73 *DirBlock, error) { 74 topBlock, _, err := dd.getter( 75 ctx, dd.tree.kmd, dd.rootBlockPointer(), dd.tree.file, rtype) 76 if err != nil { 77 return nil, err 78 } 79 return topBlock, nil 80 } 81 82 func (dd *DirData) obfuscator() Obfuscator { 83 return dd.tree.file.Obfuscator() 84 } 85 86 // GetChildren returns a map of all the child EntryInfos in this 87 // directory. 88 func (dd *DirData) GetChildren(ctx context.Context) ( 89 children map[PathPartString]EntryInfo, err error) { 90 topBlock, err := dd.GetTopBlock(ctx, BlockRead) 91 if err != nil { 92 return nil, err 93 } 94 95 _, blocks, _, err := dd.tree.getBlocksForOffsetRange( 96 ctx, dd.rootBlockPointer(), topBlock, topBlock.FirstOffset(), nil, 97 false, true) 98 if err != nil { 99 return nil, err 100 } 101 102 numEntries := 0 103 for _, b := range blocks { 104 numEntries += len(b.(*DirBlock).Children) 105 } 106 children = make(map[PathPartString]EntryInfo, numEntries) 107 for _, b := range blocks { 108 for k, de := range b.(*DirBlock).Children { 109 if hiddenEntries[k] { 110 continue 111 } 112 children[NewPathPartString(k, dd.obfuscator())] = de.EntryInfo 113 } 114 } 115 return children, nil 116 } 117 118 // GetEntries returns a map of all the child DirEntrys in this 119 // directory. 120 func (dd *DirData) GetEntries(ctx context.Context) ( 121 children map[PathPartString]DirEntry, err error) { 122 topBlock, err := dd.GetTopBlock(ctx, BlockRead) 123 if err != nil { 124 return nil, err 125 } 126 127 _, blocks, _, err := dd.tree.getBlocksForOffsetRange( 128 ctx, dd.rootBlockPointer(), topBlock, topBlock.FirstOffset(), nil, 129 false, true) 130 if err != nil { 131 return nil, err 132 } 133 134 numEntries := 0 135 for _, b := range blocks { 136 numEntries += len(b.(*DirBlock).Children) 137 } 138 children = make(map[PathPartString]DirEntry, numEntries) 139 for _, b := range blocks { 140 for k, de := range b.(*DirBlock).Children { 141 children[NewPathPartString(k, dd.obfuscator())] = de 142 } 143 } 144 return children, nil 145 } 146 147 // Lookup returns the DirEntry for the given entry named by `name` in 148 // this directory. 149 func (dd *DirData) Lookup(ctx context.Context, name PathPartString) ( 150 DirEntry, error) { 151 topBlock, err := dd.GetTopBlock(ctx, BlockRead) 152 if err != nil { 153 return DirEntry{}, err 154 } 155 156 namePlain := name.Plaintext() 157 off := StringOffset(namePlain) 158 _, _, block, _, _, _, err := dd.tree.getBlockAtOffset( 159 ctx, topBlock, &off, BlockLookup) 160 if err != nil { 161 return DirEntry{}, err 162 } 163 164 de, ok := block.(*DirBlock).Children[namePlain] 165 if !ok { 166 return DirEntry{}, idutil.NoSuchNameError{Name: name.String()} 167 } 168 return de, nil 169 } 170 171 // createIndirectBlock creates a new indirect block and pick a new id 172 // for the existing block, and use the existing block's ID for the new 173 // indirect block that becomes the parent. 174 func (dd *DirData) createIndirectBlock(ctx context.Context, dver Ver) ( 175 BlockWithPtrs, error) { 176 newID, err := kbfsblock.MakeTemporaryID() 177 if err != nil { 178 return nil, err 179 } 180 dblock := &DirBlock{ 181 CommonBlock: CommonBlock{ 182 IsInd: true, 183 }, 184 IPtrs: []IndirectDirPtr{ 185 { 186 BlockInfo: BlockInfo{ 187 BlockPointer: BlockPointer{ 188 ID: newID, 189 KeyGen: dd.tree.kmd.LatestKeyGeneration(), 190 DataVer: dver, 191 Context: kbfsblock.MakeFirstContext( 192 dd.tree.chargedTo, 193 dd.rootBlockPointer().GetBlockType()), 194 DirectType: dd.rootBlockPointer().DirectType, 195 }, 196 EncodedSize: 0, 197 }, 198 Off: "", 199 }, 200 }, 201 } 202 203 dd.tree.vlog.CLogf( 204 ctx, libkb.VLog1, "Creating new level of indirection for dir %v, "+ 205 "new block id for old top level is %v", 206 dd.rootBlockPointer(), newID) 207 208 err = dd.tree.cacher(ctx, dd.rootBlockPointer(), dblock) 209 if err != nil { 210 return nil, err 211 } 212 213 return dblock, nil 214 } 215 216 func (dd *DirData) processModifiedBlock( 217 ctx context.Context, ptr BlockPointer, 218 parentBlocks []ParentBlockAndChildIndex, block *DirBlock) ( 219 unrefs []BlockInfo, err error) { 220 newBlocks, newOffset := dd.tree.bsplit.SplitDirIfNeeded(block) 221 222 err = dd.tree.cacher(ctx, ptr, block) 223 if err != nil { 224 return nil, err 225 } 226 227 _, newUnrefs, err := dd.tree.markParentsDirty(ctx, parentBlocks) 228 if err != nil { 229 return nil, err 230 } 231 unrefs = append(unrefs, newUnrefs...) 232 233 if len(newBlocks) > 1 { 234 dd.tree.vlog.CLogf( 235 ctx, libkb.VLog1, "Making new right block for %v", 236 dd.rootBlockPointer()) 237 238 rightParents, _, err := dd.tree.newRightBlock( 239 ctx, parentBlocks, newOffset, FirstValidVer, 240 NewDirBlockWithPtrs, dd.createIndirectBlock) 241 if err != nil { 242 return nil, err 243 } 244 245 if len(parentBlocks) == 0 { 246 // We just created the first level of indirection. In that 247 // case `newRightBlock` doesn't cache the old top block, 248 // so we should do it here. 249 err = dd.tree.cacher( 250 ctx, rightParents[0].pblock.(*DirBlock).IPtrs[0].BlockPointer, 251 newBlocks[0]) 252 if err != nil { 253 return nil, err 254 } 255 } 256 257 // Cache the split block in place of the blank one made by 258 // `newRightBlock`. 259 pb := rightParents[len(rightParents)-1] 260 err = dd.tree.cacher(ctx, pb.childBlockPtr(), newBlocks[1]) 261 if err != nil { 262 return nil, err 263 } 264 265 // Shift it over if needed. 266 _, newUnrefs, _, err := dd.tree.shiftBlocksToFillHole(ctx, rightParents) 267 if err != nil { 268 return nil, err 269 } 270 unrefs = append(unrefs, newUnrefs...) 271 } 272 273 return unrefs, nil 274 } 275 276 func (dd *DirData) addEntryHelper( 277 ctx context.Context, name PathPartString, newDe DirEntry, 278 errorIfExists, errorIfNoMatch bool) ( 279 unrefs []BlockInfo, err error) { 280 topBlock, err := dd.GetTopBlock(ctx, BlockWrite) 281 if err != nil { 282 return nil, err 283 } 284 285 namePlain := name.Plaintext() 286 off := StringOffset(namePlain) 287 ptr, parentBlocks, block, _, _, _, err := dd.tree.getBlockAtOffset( 288 ctx, topBlock, &off, BlockWrite) 289 if err != nil { 290 return nil, err 291 } 292 dblock := block.(*DirBlock) 293 294 de, exists := dblock.Children[namePlain] 295 if errorIfExists && exists { 296 return nil, NameExistsError{name.String()} 297 } else if errorIfNoMatch && 298 (!exists || de.BlockPointer != newDe.BlockPointer) { 299 return nil, idutil.NoSuchNameError{Name: name.String()} 300 } 301 dblock.Children[namePlain] = newDe 302 303 return dd.processModifiedBlock(ctx, ptr, parentBlocks, dblock) 304 } 305 306 // AddEntry adds a new entry to this directory. 307 func (dd *DirData) AddEntry( 308 ctx context.Context, newName PathPartString, newDe DirEntry) ( 309 unrefs []BlockInfo, err error) { 310 return dd.addEntryHelper(ctx, newName, newDe, true, false) 311 } 312 313 // UpdateEntry updates an existing entry to this directory. 314 func (dd *DirData) UpdateEntry( 315 ctx context.Context, name PathPartString, newDe DirEntry) ( 316 unrefs []BlockInfo, err error) { 317 return dd.addEntryHelper(ctx, name, newDe, false, true) 318 } 319 320 // SetEntry set an entry to this directory, whether it is new or existing. 321 func (dd *DirData) SetEntry( 322 ctx context.Context, name PathPartString, newDe DirEntry) ( 323 unrefs []BlockInfo, err error) { 324 return dd.addEntryHelper(ctx, name, newDe, false, false) 325 } 326 327 // RemoveEntry removes an entry from this directory. 328 func (dd *DirData) RemoveEntry(ctx context.Context, name PathPartString) ( 329 unrefs []BlockInfo, err error) { 330 topBlock, err := dd.GetTopBlock(ctx, BlockWrite) 331 if err != nil { 332 return nil, err 333 } 334 335 namePlain := name.Plaintext() 336 off := StringOffset(namePlain) 337 ptr, parentBlocks, block, _, _, _, err := dd.tree.getBlockAtOffset( 338 ctx, topBlock, &off, BlockWrite) 339 if err != nil { 340 return nil, err 341 } 342 dblock := block.(*DirBlock) 343 344 if _, exists := dblock.Children[namePlain]; !exists { 345 // Nothing to do. 346 return nil, nil 347 } 348 delete(dblock.Children, namePlain) 349 350 // For now, just leave the block empty, at its current place in 351 // the tree. TODO: remove empty blocks all the way up the tree 352 // and shift parent pointers around as needed. 353 return dd.processModifiedBlock(ctx, ptr, parentBlocks, dblock) 354 } 355 356 // Ready readies all the dirty child blocks for a directory tree with 357 // an indirect top-block, and updates their block IDs in their parent 358 // block's list of indirect pointers. It returns a map pointing from 359 // the new block info from any readied block to its corresponding old 360 // block pointer. 361 func (dd *DirData) Ready(ctx context.Context, id tlf.ID, 362 bcache BlockCache, dirtyBcache IsDirtyProvider, 363 rp ReadyProvider, bps BlockPutState, 364 topBlock *DirBlock, hashBehavior BlockCacheHashBehavior) ( 365 map[BlockInfo]BlockPointer, error) { 366 return dd.tree.ready( 367 ctx, id, bcache, dirtyBcache, rp, bps, topBlock, nil, hashBehavior) 368 } 369 370 // GetDirtyChildPtrs returns a set of dirty child pointers (not the 371 // root pointer) for the directory. 372 func (dd *DirData) GetDirtyChildPtrs( 373 ctx context.Context, dirtyBcache IsDirtyProvider) ( 374 ptrs map[BlockPointer]bool, err error) { 375 topBlock, err := dd.GetTopBlock(ctx, BlockRead) 376 if err != nil { 377 return nil, err 378 } 379 380 if !topBlock.IsIndirect() { 381 return nil, nil 382 } 383 384 ptrs = make(map[BlockPointer]bool) 385 386 // Gather all the paths to all dirty leaf blocks first. 387 off := topBlock.FirstOffset() 388 for off != nil { 389 _, parentBlocks, block, nextBlockOff, _, err := 390 dd.tree.getNextDirtyBlockAtOffset( 391 ctx, topBlock, off, BlockWrite, dirtyBcache) 392 if err != nil { 393 return nil, err 394 } 395 396 if block == nil { 397 // No more dirty blocks. 398 break 399 } 400 off = nextBlockOff // Will be `nil` if there are no more blocks. 401 402 for _, pb := range parentBlocks { 403 ptrs[pb.childBlockPtr()] = true 404 } 405 } 406 return ptrs, nil 407 } 408 409 // GetIndirectDirBlockInfos returns all of the BlockInfos for blocks 410 // pointed to by indirect blocks within this directory tree. 411 func (dd *DirData) GetIndirectDirBlockInfos(ctx context.Context) ( 412 []BlockInfo, error) { 413 return dd.tree.getIndirectBlockInfos(ctx) 414 }