diff --git a/modules/git/parse.go b/modules/git/parse.go index 69649460b4..c7b84d7198 100644 --- a/modules/git/parse.go +++ b/modules/git/parse.go @@ -10,8 +10,6 @@ import ( "io" "strconv" "strings" - - "forgejo.org/modules/log" ) // ParseTreeEntries parses the output of a `git ls-tree -l` command. @@ -55,19 +53,9 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) { entry.sized = true } - switch string(entryMode) { - case "100644": - entry.entryMode = EntryModeBlob - case "100755": - entry.entryMode = EntryModeExec - case "120000": - entry.entryMode = EntryModeSymlink - case "160000": - entry.entryMode = EntryModeCommit - case "040000", "040755", "040775": // git uses 040000 for tree object, but some users may get 040755 or 040775 for unknown reasons - entry.entryMode = EntryModeTree - default: - return nil, fmt.Errorf("unknown type: %v", string(entryMode)) + entry.entryMode, err = parseMode(string(entryMode)) + if err != nil { + return nil, err } entry.ID, err = NewIDFromString(string(entryObjectID)) @@ -108,23 +96,10 @@ loop: sz -= int64(count) entry := new(TreeEntry) entry.ptree = ptree - - switch string(mode) { - case "100644": - entry.entryMode = EntryModeBlob - case "100755": - entry.entryMode = EntryModeExec - case "120000": - entry.entryMode = EntryModeSymlink - case "160000": - entry.entryMode = EntryModeCommit - case "40000", "40755", "40775": // git uses 40000 for tree object, but some users may get 40755 or 40775 for unknown reasons - entry.entryMode = EntryModeTree - default: - log.Debug("Unknown mode: %v", string(mode)) - return nil, fmt.Errorf("unknown mode: %v", string(mode)) + entry.entryMode, err = parseMode(string(mode)) + if err != nil { + return nil, err } - entry.ID = objectFormat.MustID(sha) entry.name = string(fname) entries = append(entries, entry) @@ -135,3 +110,31 @@ loop: return entries, nil } + +// Parse the file mode, we cannot hardcode the modes that we expect for +// a variety of reasons (that is not known to us) the permissions bits +// of files can vary, usually the result because of tooling that uses Git in +// a funny way. So we have to parse the mode as a integer and do bit tricks. +func parseMode(modeStr string) (EntryMode, error) { + mode, err := strconv.ParseUint(modeStr, 8, 64) + if err != nil { + return 0, fmt.Errorf("cannot parse mode: %v", err) + } + + switch mode & 0o170000 { + case 0o040000: + return EntryModeTree, nil + case 0o120000: + return EntryModeSymlink, nil + case 0o160000: + return EntryModeCommit, nil + case 0o100000: + // Check for the permission bit on the owner. + if mode&0o100 == 0o100 { + return EntryModeExec, nil + } + return EntryModeBlob, nil + } + + return 0, fmt.Errorf("unknown mode: %o", mode) +} diff --git a/modules/git/parse_test.go b/modules/git/parse_test.go index 03f359f6c1..502adab4da 100644 --- a/modules/git/parse_test.go +++ b/modules/git/parse_test.go @@ -101,3 +101,38 @@ func TestParseTreeEntriesInvalid(t *testing.T) { require.Error(t, err) assert.Empty(t, entries) } + +func TestParseMode(t *testing.T) { + ok := func(t *testing.T, mode string, entry EntryMode) { + t.Helper() + actualEntry, err := parseMode(mode) + require.NoError(t, err) + assert.Equal(t, entry, actualEntry) + } + + fail := func(t *testing.T, mode string) { + t.Helper() + entry, err := parseMode(mode) + require.Error(t, err) + assert.Zero(t, entry) + } + + ok(t, "100644", EntryModeBlob) + ok(t, "100755", EntryModeExec) + ok(t, "100754", EntryModeExec) + ok(t, "100700", EntryModeExec) + ok(t, "100744", EntryModeExec) + ok(t, "120000", EntryModeSymlink) + ok(t, "120644", EntryModeSymlink) + ok(t, "160000", EntryModeCommit) + ok(t, "160644", EntryModeCommit) + ok(t, "040000", EntryModeTree) + ok(t, "040755", EntryModeTree) + ok(t, "040775", EntryModeTree) + ok(t, "040754", EntryModeTree) + + fail(t, "not-a-number") + fail(t, "000000") + fail(t, "400000") + fail(t, "111111") +}