Record file mode in the manifest, 0000 unless asked (closes #161)
check / check (push) Failing after 4s

MFFilePath gains mode (field 304): a file's permission bits, 0777 at
most, or 0000, meaning none recorded. gen and freshen record real modes
only with --include-permissions (ScannerOptions.IncludePermissions); the
builder keeps only mode.Perm(), so setuid, setgid and sticky are never
written. list -l and export show the mode in octal. check reports
MODE_MISMATCH for a recorded mode other than 0000 the file lacks. fetch
refuses a manifest with a mode above 0777 before requesting any file,
sets only the permission bits of each recorded mode on the files it
writes, and downloads again a present file whose mode differs. The decoding-cost bound counts a file
entry at 176 bytes, up from 160.

Model: opus-5-5
This commit was merged in pull request #163.
This commit is contained in:
2026-10-06 11:43:18 +02:00
parent ce66f7c1c1
commit 2a270b40c5
26 changed files with 652 additions and 106 deletions
+41
View File
@@ -4,6 +4,7 @@ package mfer
import (
"bytes"
"context"
"os"
"testing"
"time"
@@ -350,6 +351,46 @@ func TestScannerFileEntryFields(t *testing.T) {
assert.WithinDuration(t, now, time.Time(entry.Mtime), 2*time.Second)
}
// A manifest records every mode as 0000 unless the creator asks for
// permissions; then it records each file's permission bits and never the
// setuid, setgid or sticky bits.
func TestScannerRecordsModeOnlyWhenAsked(t *testing.T) {
t.Parallel()
fs := afero.NewMemMapFs()
require.NoError(t, afero.WriteFile(fs, "/"+testFile1, []byte("a"), 0o640))
require.NoError(t, afero.WriteFile(fs, "/run.sh", []byte("b"), 0o755))
require.NoError(t, afero.WriteFile(fs, "/su", []byte("c"), 0o755))
require.NoError(t, fs.Chmod("/su", 0o755|os.ModeSetuid|os.ModeSetgid|os.ModeSticky))
for _, tc := range []struct {
includePermissions bool
want map[string]uint32
}{
{false, map[string]uint32{testFile1: 0, "run.sh": 0, "su": 0}},
{true, map[string]uint32{testFile1: 0o640, "run.sh": 0o755, "su": 0o755}},
} {
s := NewScannerWithOptions(&ScannerOptions{
Fs: fs,
IncludePermissions: tc.includePermissions,
})
require.NoError(t, s.EnumerateFS(fs, "/", nil))
var buf bytes.Buffer
require.NoError(t, s.ToManifest(context.Background(), &buf, nil))
m, err := NewManifestFromReader(&buf)
require.NoError(t, err)
got := map[string]uint32{}
for _, f := range m.Files() {
got[f.GetPath()] = f.GetMode()
}
assert.Equal(t, tc.want, got, "IncludePermissions: %v", tc.includePermissions)
}
}
func TestScannerLargeFileEnumeration(t *testing.T) {
t.Parallel()