github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/commands/command_fsck.go (about) 1 package commands 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "io" 7 "os" 8 "path/filepath" 9 10 "github.com/git-lfs/git-lfs/filepathfilter" 11 "github.com/git-lfs/git-lfs/git" 12 "github.com/git-lfs/git-lfs/lfs" 13 "github.com/spf13/cobra" 14 ) 15 16 var ( 17 fsckDryRun bool 18 ) 19 20 // TODO(zeroshirts): 'git fsck' reports status (percentage, current#/total) as 21 // it checks... we should do the same, as we are rehashing potentially gigs and 22 // gigs of content. 23 // 24 // NOTE(zeroshirts): Ideally git would have hooks for fsck such that we could 25 // chain a lfs-fsck, but I don't think it does. 26 func fsckCommand(cmd *cobra.Command, args []string) { 27 installHooks(false) 28 requireInRepo() 29 30 ref, err := git.CurrentRef() 31 if err != nil { 32 ExitWithError(err) 33 } 34 35 var corruptOids []string 36 gitscanner := lfs.NewGitScanner(func(p *lfs.WrappedPointer, err error) { 37 if err == nil { 38 var pointerOk bool 39 pointerOk, err = fsckPointer(p.Name, p.Oid) 40 if !pointerOk { 41 corruptOids = append(corruptOids, p.Oid) 42 } 43 } 44 45 if err != nil { 46 Panic(err, "Error checking Git LFS files") 47 } 48 }) 49 50 // If 'lfs.fetchexclude' is set and 'git lfs fsck' is run after the 51 // initial fetch (i.e., has elected to fetch a subset of Git LFS 52 // objects), the "missing" ones will fail the fsck. 53 // 54 // Attach a filepathfilter to avoid _only_ the excluded paths. 55 gitscanner.Filter = filepathfilter.New(nil, cfg.FetchExcludePaths()) 56 57 if err := gitscanner.ScanRef(ref.Sha, nil); err != nil { 58 ExitWithError(err) 59 } 60 61 if err := gitscanner.ScanIndex("HEAD", nil); err != nil { 62 ExitWithError(err) 63 } 64 65 gitscanner.Close() 66 67 if len(corruptOids) == 0 { 68 Print("Git LFS fsck OK") 69 return 70 } 71 72 if fsckDryRun { 73 return 74 } 75 76 badDir := filepath.Join(cfg.LFSStorageDir(), "bad") 77 Print("Moving corrupt objects to %s", badDir) 78 79 if err := os.MkdirAll(badDir, 0755); err != nil { 80 ExitWithError(err) 81 } 82 83 for _, oid := range corruptOids { 84 badFile := filepath.Join(badDir, oid) 85 if err := os.Rename(cfg.Filesystem().ObjectPathname(oid), badFile); err != nil { 86 ExitWithError(err) 87 } 88 } 89 } 90 91 func fsckPointer(name, oid string) (bool, error) { 92 path := cfg.Filesystem().ObjectPathname(oid) 93 94 Debug("Examining %v (%v)", name, path) 95 96 f, err := os.Open(path) 97 if pErr, pOk := err.(*os.PathError); pOk { 98 Print("Object %s (%s) could not be checked: %s", name, oid, pErr.Err) 99 return false, nil 100 } 101 102 if err != nil { 103 return false, err 104 } 105 106 oidHash := sha256.New() 107 _, err = io.Copy(oidHash, f) 108 f.Close() 109 if err != nil { 110 return false, err 111 } 112 113 recalculatedOid := hex.EncodeToString(oidHash.Sum(nil)) 114 if recalculatedOid == oid { 115 return true, nil 116 } 117 118 Print("Object %s (%s) is corrupt", name, oid) 119 return false, nil 120 } 121 122 func init() { 123 RegisterCommand("fsck", fsckCommand, func(cmd *cobra.Command) { 124 cmd.Flags().BoolVarP(&fsckDryRun, "dry-run", "d", false, "List corrupt objects without deleting them.") 125 }) 126 }