git-lfs/commands/command_fsck.go
brian m. carlson cc815f59e6
Use proper repo permissions when creating directories
Honor the repository permssions set with the umask or
core.sharedRepository when creating directories by using tools.MkdirAll.
Pass an appropriate configuration or filesystem object through as needed
to ensure that we can query the proper permissions. Add a test that
these code paths work by cloning a new repository and then performing
some operations on it.

Note that in the test, we match patterns with either the setgid bit
clear or set, since it may be set or not depending on whether the system
has SysV or BSD group semantics.
2018-12-13 17:51:02 +00:00

128 lines
2.8 KiB
Go

package commands
import (
"crypto/sha256"
"encoding/hex"
"io"
"os"
"path/filepath"
"github.com/git-lfs/git-lfs/filepathfilter"
"github.com/git-lfs/git-lfs/git"
"github.com/git-lfs/git-lfs/lfs"
"github.com/git-lfs/git-lfs/tools"
"github.com/spf13/cobra"
)
var (
fsckDryRun bool
)
// 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) {
installHooks(false)
requireInRepo()
ref, err := git.CurrentRef()
if err != nil {
ExitWithError(err)
}
var corruptOids []string
gitscanner := lfs.NewGitScanner(func(p *lfs.WrappedPointer, err error) {
if err == nil {
var pointerOk bool
pointerOk, err = fsckPointer(p.Name, p.Oid)
if !pointerOk {
corruptOids = append(corruptOids, p.Oid)
}
}
if err != nil {
Panic(err, "Error checking Git LFS files")
}
})
// 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())
if err := gitscanner.ScanRef(ref.Sha, nil); err != nil {
ExitWithError(err)
}
if err := gitscanner.ScanIndex("HEAD", nil); err != nil {
ExitWithError(err)
}
gitscanner.Close()
if len(corruptOids) == 0 {
Print("Git LFS fsck OK")
return
}
if fsckDryRun {
return
}
badDir := filepath.Join(cfg.LFSStorageDir(), "bad")
Print("Moving corrupt objects to %s", badDir)
if err := tools.MkdirAll(badDir, cfg); err != nil {
ExitWithError(err)
}
for _, oid := range corruptOids {
badFile := filepath.Join(badDir, oid)
if err := os.Rename(cfg.Filesystem().ObjectPathname(oid), badFile); err != nil {
ExitWithError(err)
}
}
}
func fsckPointer(name, oid string) (bool, error) {
path := cfg.Filesystem().ObjectPathname(oid)
Debug("Examining %v (%v)", name, path)
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
}
if err != nil {
return false, err
}
oidHash := sha256.New()
_, err = io.Copy(oidHash, f)
f.Close()
if err != nil {
return false, err
}
recalculatedOid := hex.EncodeToString(oidHash.Sum(nil))
if recalculatedOid == oid {
return true, nil
}
Print("Object %s (%s) is corrupt", name, oid)
return false, nil
}
func init() {
RegisterCommand("fsck", fsckCommand, func(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&fsckDryRun, "dry-run", "d", false, "List corrupt objects without deleting them.")
})
}