github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/lfs/pointer_scanner_nogogit.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 //go:build !gogit 7 8 package lfs 9 10 import ( 11 "bufio" 12 "context" 13 "io" 14 "strconv" 15 "strings" 16 "sync" 17 18 "github.com/gitbundle/modules/git" 19 "github.com/gitbundle/modules/git/pipeline" 20 ) 21 22 // SearchPointerBlobs scans the whole repository for LFS pointer files 23 func SearchPointerBlobs(ctx context.Context, repo *git.Repository, pointerChan chan<- PointerBlob, errChan chan<- error) { 24 basePath := repo.Path 25 26 catFileCheckReader, catFileCheckWriter := io.Pipe() 27 shasToBatchReader, shasToBatchWriter := io.Pipe() 28 catFileBatchReader, catFileBatchWriter := io.Pipe() 29 30 wg := sync.WaitGroup{} 31 wg.Add(4) 32 33 // Create the go-routines in reverse order. 34 35 // 4. Take the output of cat-file --batch and check if each file in turn 36 // to see if they're pointers to files in the LFS store 37 go createPointerResultsFromCatFileBatch(ctx, catFileBatchReader, &wg, pointerChan) 38 39 // 3. Take the shas of the blobs and batch read them 40 go pipeline.CatFileBatch(ctx, shasToBatchReader, catFileBatchWriter, &wg, basePath) 41 42 // 2. From the provided objects restrict to blobs <=1k 43 go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg) 44 45 // 1. Run batch-check on all objects in the repository 46 if git.CheckGitVersionAtLeast("2.6.0") != nil { 47 revListReader, revListWriter := io.Pipe() 48 shasToCheckReader, shasToCheckWriter := io.Pipe() 49 wg.Add(2) 50 go pipeline.CatFileBatchCheck(ctx, shasToCheckReader, catFileCheckWriter, &wg, basePath) 51 go pipeline.BlobsFromRevListObjects(revListReader, shasToCheckWriter, &wg) 52 go pipeline.RevListAllObjects(ctx, revListWriter, &wg, basePath, errChan) 53 } else { 54 go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan) 55 } 56 wg.Wait() 57 58 close(pointerChan) 59 close(errChan) 60 } 61 62 func createPointerResultsFromCatFileBatch(ctx context.Context, catFileBatchReader *io.PipeReader, wg *sync.WaitGroup, pointerChan chan<- PointerBlob) { 63 defer wg.Done() 64 defer catFileBatchReader.Close() 65 66 bufferedReader := bufio.NewReader(catFileBatchReader) 67 buf := make([]byte, 1025) 68 69 loop: 70 for { 71 select { 72 case <-ctx.Done(): 73 break loop 74 default: 75 } 76 77 // File descriptor line: sha 78 sha, err := bufferedReader.ReadString(' ') 79 if err != nil { 80 _ = catFileBatchReader.CloseWithError(err) 81 break 82 } 83 sha = strings.TrimSpace(sha) 84 // Throw away the blob 85 if _, err := bufferedReader.ReadString(' '); err != nil { 86 _ = catFileBatchReader.CloseWithError(err) 87 break 88 } 89 sizeStr, err := bufferedReader.ReadString('\n') 90 if err != nil { 91 _ = catFileBatchReader.CloseWithError(err) 92 break 93 } 94 size, err := strconv.Atoi(sizeStr[:len(sizeStr)-1]) 95 if err != nil { 96 _ = catFileBatchReader.CloseWithError(err) 97 break 98 } 99 pointerBuf := buf[:size+1] 100 if _, err := io.ReadFull(bufferedReader, pointerBuf); err != nil { 101 _ = catFileBatchReader.CloseWithError(err) 102 break 103 } 104 pointerBuf = pointerBuf[:size] 105 // Now we need to check if the pointerBuf is an LFS pointer 106 pointer, _ := ReadPointerFromBuffer(pointerBuf) 107 if !pointer.IsValid() { 108 continue 109 } 110 111 pointerChan <- PointerBlob{Hash: sha, Pointer: pointer} 112 } 113 }