Skip to content

Commit 019430c

Browse files
GustedOtto
authored andcommitted
[v12.0/forgejo] fix: do better parsing of file modes (#9171)
**Backport: forgejo/forgejo#9161** - No longer hardcode the file modes we expect, parse them as numbers and do bitmask tricks that Git does so we allow a more variety of _weird_ file modes that can happen in the wild. - Ref: https://codeberg.org/forgejo/forgejo/pulls/8900#issuecomment-6429175 - Resolves Codeberg/Community#2111 (cherry picked from commit 1cfd5e0b98) Conflict resolution: trivial, choose the new code. Confliction arised because v12 doesn't contain forgejo/forgejo#8900 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9171 Reviewed-by: Earl Warren <[email protected]> Reviewed-by: Otto <[email protected]> Co-authored-by: Gusted <[email protected]> Co-committed-by: Gusted <[email protected]>
1 parent ea5371e commit 019430c

File tree

2 files changed

+69
-31
lines changed

2 files changed

+69
-31
lines changed

modules/git/parse.go

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import (
1010
"io"
1111
"strconv"
1212
"strings"
13-
14-
"forgejo.org/modules/log"
1513
)
1614

1715
// ParseTreeEntries parses the output of a `git ls-tree -l` command.
@@ -55,19 +53,9 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
5553
entry.sized = true
5654
}
5755

58-
switch string(entryMode) {
59-
case "100644":
60-
entry.entryMode = EntryModeBlob
61-
case "100755":
62-
entry.entryMode = EntryModeExec
63-
case "120000":
64-
entry.entryMode = EntryModeSymlink
65-
case "160000":
66-
entry.entryMode = EntryModeCommit
67-
case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
68-
entry.entryMode = EntryModeTree
69-
default:
70-
return nil, fmt.Errorf("unknown type: %v", string(entryMode))
56+
entry.entryMode, err = parseMode(string(entryMode))
57+
if err != nil {
58+
return nil, err
7159
}
7260

7361
entry.ID, err = NewIDFromString(string(entryObjectID))
@@ -108,23 +96,10 @@ loop:
10896
sz -= int64(count)
10997
entry := new(TreeEntry)
11098
entry.ptree = ptree
111-
112-
switch string(mode) {
113-
case "100644":
114-
entry.entryMode = EntryModeBlob
115-
case "100755":
116-
entry.entryMode = EntryModeExec
117-
case "120000":
118-
entry.entryMode = EntryModeSymlink
119-
case "160000":
120-
entry.entryMode = EntryModeCommit
121-
case "40000", "40755": // git uses 40000 for tree object, but some users may get 40755 for unknown reasons
122-
entry.entryMode = EntryModeTree
123-
default:
124-
log.Debug("Unknown mode: %v", string(mode))
125-
return nil, fmt.Errorf("unknown mode: %v", string(mode))
99+
entry.entryMode, err = parseMode(string(mode))
100+
if err != nil {
101+
return nil, err
126102
}
127-
128103
entry.ID = objectFormat.MustID(sha)
129104
entry.name = string(fname)
130105
entries = append(entries, entry)
@@ -135,3 +110,31 @@ loop:
135110

136111
return entries, nil
137112
}
113+
114+
// Parse the file mode, we cannot hardcode the modes that we expect for
115+
// a variety of reasons (that is not known to us) the permissions bits
116+
// of files can vary, usually the result because of tooling that uses Git in
117+
// a funny way. So we have to parse the mode as a integer and do bit tricks.
118+
func parseMode(modeStr string) (EntryMode, error) {
119+
mode, err := strconv.ParseUint(modeStr, 8, 64)
120+
if err != nil {
121+
return 0, fmt.Errorf("cannot parse mode: %v", err)
122+
}
123+
124+
switch mode & 0o170000 {
125+
case 0o040000:
126+
return EntryModeTree, nil
127+
case 0o120000:
128+
return EntryModeSymlink, nil
129+
case 0o160000:
130+
return EntryModeCommit, nil
131+
case 0o100000:
132+
// Check for the permission bit on the owner.
133+
if mode&0o100 == 0o100 {
134+
return EntryModeExec, nil
135+
}
136+
return EntryModeBlob, nil
137+
}
138+
139+
return 0, fmt.Errorf("unknown mode: %o", mode)
140+
}

modules/git/parse_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,38 @@ func TestParseTreeEntriesInvalid(t *testing.T) {
101101
require.Error(t, err)
102102
assert.Empty(t, entries)
103103
}
104+
105+
func TestParseMode(t *testing.T) {
106+
ok := func(t *testing.T, mode string, entry EntryMode) {
107+
t.Helper()
108+
actualEntry, err := parseMode(mode)
109+
require.NoError(t, err)
110+
assert.Equal(t, entry, actualEntry)
111+
}
112+
113+
fail := func(t *testing.T, mode string) {
114+
t.Helper()
115+
entry, err := parseMode(mode)
116+
require.Error(t, err)
117+
assert.Zero(t, entry)
118+
}
119+
120+
ok(t, "100644", EntryModeBlob)
121+
ok(t, "100755", EntryModeExec)
122+
ok(t, "100754", EntryModeExec)
123+
ok(t, "100700", EntryModeExec)
124+
ok(t, "100744", EntryModeExec)
125+
ok(t, "120000", EntryModeSymlink)
126+
ok(t, "120644", EntryModeSymlink)
127+
ok(t, "160000", EntryModeCommit)
128+
ok(t, "160644", EntryModeCommit)
129+
ok(t, "040000", EntryModeTree)
130+
ok(t, "040755", EntryModeTree)
131+
ok(t, "040775", EntryModeTree)
132+
ok(t, "040754", EntryModeTree)
133+
134+
fail(t, "not-a-number")
135+
fail(t, "000000")
136+
fail(t, "400000")
137+
fail(t, "111111")
138+
}

0 commit comments

Comments
 (0)