2015-05-04 05:36:34 +00:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/hex"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
2018-02-01 02:16:43 +00:00
|
|
|
"github.com/git-lfs/git-lfs/filepathfilter"
|
2016-11-15 17:01:18 +00:00
|
|
|
"github.com/git-lfs/git-lfs/git"
|
|
|
|
"github.com/git-lfs/git-lfs/lfs"
|
2018-12-05 16:15:52 +00:00
|
|
|
"github.com/git-lfs/git-lfs/tools"
|
2016-05-23 18:02:27 +00:00
|
|
|
"github.com/spf13/cobra"
|
2015-05-04 05:36:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2015-05-08 16:35:19 +00:00
|
|
|
fsckDryRun bool
|
2015-05-04 05:36:34 +00:00
|
|
|
)
|
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
// TODO(zeroshirts): 'git fsck' reports status (percentage, current#/total) as
|
|
|
|
// it checks... we should do the same, as we are rehashing potentially gigs and
|
|
|
|
// gigs of content.
|
|
|
|
//
|
|
|
|
// NOTE(zeroshirts): Ideally git would have hooks for fsck such that we could
|
|
|
|
// chain a lfs-fsck, but I don't think it does.
|
|
|
|
func fsckCommand(cmd *cobra.Command, args []string) {
|
2017-10-18 21:42:00 +00:00
|
|
|
installHooks(false)
|
2015-09-08 16:23:27 +00:00
|
|
|
requireInRepo()
|
2015-09-08 15:29:53 +00:00
|
|
|
|
2015-05-04 05:36:34 +00:00
|
|
|
ref, err := git.CurrentRef()
|
|
|
|
if err != nil {
|
2016-11-29 20:56:47 +00:00
|
|
|
ExitWithError(err)
|
2015-05-04 05:36:34 +00:00
|
|
|
}
|
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
var corruptOids []string
|
2019-08-09 15:25:23 +00:00
|
|
|
gitscanner := lfs.NewGitScanner(cfg, func(p *lfs.WrappedPointer, err error) {
|
2016-11-29 20:56:47 +00:00
|
|
|
if err == nil {
|
|
|
|
var pointerOk bool
|
|
|
|
pointerOk, err = fsckPointer(p.Name, p.Oid)
|
|
|
|
if !pointerOk {
|
|
|
|
corruptOids = append(corruptOids, p.Oid)
|
|
|
|
}
|
|
|
|
}
|
2015-05-04 05:36:34 +00:00
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
if err != nil {
|
|
|
|
Panic(err, "Error checking Git LFS files")
|
|
|
|
}
|
|
|
|
})
|
2015-05-08 16:20:02 +00:00
|
|
|
|
2018-02-01 02:16:43 +00:00
|
|
|
// If 'lfs.fetchexclude' is set and 'git lfs fsck' is run after the
|
|
|
|
// initial fetch (i.e., has elected to fetch a subset of Git LFS
|
|
|
|
// objects), the "missing" ones will fail the fsck.
|
|
|
|
//
|
|
|
|
// Attach a filepathfilter to avoid _only_ the excluded paths.
|
|
|
|
gitscanner.Filter = filepathfilter.New(nil, cfg.FetchExcludePaths())
|
|
|
|
|
2017-03-09 23:23:43 +00:00
|
|
|
if err := gitscanner.ScanRef(ref.Sha, nil); err != nil {
|
2016-11-29 20:56:47 +00:00
|
|
|
ExitWithError(err)
|
|
|
|
}
|
2015-05-04 05:36:34 +00:00
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
if err := gitscanner.ScanIndex("HEAD", nil); err != nil {
|
|
|
|
ExitWithError(err)
|
|
|
|
}
|
2015-05-04 05:36:34 +00:00
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
gitscanner.Close()
|
2015-05-04 05:36:34 +00:00
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
if len(corruptOids) == 0 {
|
|
|
|
Print("Git LFS fsck OK")
|
|
|
|
return
|
|
|
|
}
|
2015-05-04 05:36:34 +00:00
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
if fsckDryRun {
|
|
|
|
return
|
|
|
|
}
|
2015-06-07 14:51:51 +00:00
|
|
|
|
2017-10-24 21:58:42 +00:00
|
|
|
badDir := filepath.Join(cfg.LFSStorageDir(), "bad")
|
2016-11-29 20:56:47 +00:00
|
|
|
Print("Moving corrupt objects to %s", badDir)
|
2015-06-07 14:51:51 +00:00
|
|
|
|
2018-12-05 16:15:52 +00:00
|
|
|
if err := tools.MkdirAll(badDir, cfg); err != nil {
|
2016-11-29 20:56:47 +00:00
|
|
|
ExitWithError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, oid := range corruptOids {
|
|
|
|
badFile := filepath.Join(badDir, oid)
|
2017-10-25 17:33:04 +00:00
|
|
|
if err := os.Rename(cfg.Filesystem().ObjectPathname(oid), badFile); err != nil {
|
2016-11-29 20:56:47 +00:00
|
|
|
ExitWithError(err)
|
2015-05-04 05:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
func fsckPointer(name, oid string) (bool, error) {
|
2017-10-25 17:33:04 +00:00
|
|
|
path := cfg.Filesystem().ObjectPathname(oid)
|
2016-11-29 17:02:21 +00:00
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
Debug("Examining %v (%v)", name, path)
|
2016-11-29 17:02:21 +00:00
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
f, err := os.Open(path)
|
|
|
|
if pErr, pOk := err.(*os.PathError); pOk {
|
|
|
|
Print("Object %s (%s) could not be checked: %s", name, oid, pErr.Err)
|
|
|
|
return false, nil
|
2016-11-29 17:02:21 +00:00
|
|
|
}
|
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
2016-11-29 17:02:21 +00:00
|
|
|
}
|
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
oidHash := sha256.New()
|
|
|
|
_, err = io.Copy(oidHash, f)
|
|
|
|
f.Close()
|
2015-05-04 05:36:34 +00:00
|
|
|
if err != nil {
|
2016-11-29 20:56:47 +00:00
|
|
|
return false, err
|
2015-05-08 16:20:02 +00:00
|
|
|
}
|
|
|
|
|
2016-11-29 20:56:47 +00:00
|
|
|
recalculatedOid := hex.EncodeToString(oidHash.Sum(nil))
|
|
|
|
if recalculatedOid == oid {
|
|
|
|
return true, nil
|
2015-05-04 05:36:34 +00:00
|
|
|
}
|
2016-11-29 20:56:47 +00:00
|
|
|
|
|
|
|
Print("Object %s (%s) is corrupt", name, oid)
|
|
|
|
return false, nil
|
2015-05-04 05:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2016-09-01 16:09:38 +00:00
|
|
|
RegisterCommand("fsck", fsckCommand, func(cmd *cobra.Command) {
|
2016-08-10 15:33:25 +00:00
|
|
|
cmd.Flags().BoolVarP(&fsckDryRun, "dry-run", "d", false, "List corrupt objects without deleting them.")
|
|
|
|
})
|
2015-05-04 05:36:34 +00:00
|
|
|
}
|