Report only symlinks to regular files as extra files in check (closes #192) #212
@@ -289,9 +289,12 @@ are now tracked only in the [issues](https://git.eeqj.de/sneak/mfer/issues).
|
|||||||
else the directory holding the manifest, or the current directory for a
|
else the directory holding the manifest, or the current directory for a
|
||||||
manifest given by URL. So `mfer check /media/drive` checks a drive against
|
manifest given by URL. So `mfer check /media/drive` checks a drive against
|
||||||
the `index.mf` at its root, from any directory
|
the `index.mf` at its root, from any directory
|
||||||
- warns about each file under the base directory that the manifest does not
|
- warns about each regular file under the base directory that the manifest
|
||||||
list, hidden files included; with `--no-extra-files` each one is a failure
|
does not list, hidden files included, and each symlink there to a regular
|
||||||
instead
|
file, which gen lists only with `--follow-symlinks`; with
|
||||||
|
`--no-extra-files` each one is a failure instead. A named pipe, socket or
|
||||||
|
device, a symlink to a directory and a broken symlink are never reported,
|
||||||
|
since gen cannot list them
|
||||||
- `mfer freshen` / `mfer freshen .`
|
- `mfer freshen` / `mfer freshen .`
|
||||||
- rewrites `index.mf` to list the files now under the directory holding it,
|
- rewrites `index.mf` to list the files now under the directory holding it,
|
||||||
or under the one `--base` names, hashing only the files that are new or
|
or under the one `--base` names, hashing only the files that are new or
|
||||||
|
|||||||
@@ -960,6 +960,53 @@ func TestCheckWarnsAboutUnlistedFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCheckReportsUnlistedSymlinkToFileOnly makes a tree holding a symlink
|
||||||
|
// to a directory, one to a file and a broken one. gen lists the symlink to
|
||||||
|
// the file only with --follow-symlinks, and never lists the other two, so
|
||||||
|
// check --no-extra-files passes on the manifest gen --follow-symlinks writes
|
||||||
|
// and fails only on the symlink to the file on the one plain gen writes.
|
||||||
|
func TestCheckReportsUnlistedSymlinkToFileOnly(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
genFlags []string
|
||||||
|
exitCode int
|
||||||
|
extras []string // what check reports as not in the manifest
|
||||||
|
}{
|
||||||
|
"gen --follow-symlinks": {[]string{"--follow-symlinks"}, 0, nil},
|
||||||
|
"plain gen": {nil, 1, []string{"filelink"}},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
dir := t.TempDir()
|
||||||
|
fs := afero.NewOsFs()
|
||||||
|
writeTestTree(t, fs, dir)
|
||||||
|
require.NoError(t, os.Symlink(testSubdir, filepath.Join(dir, "dirlink")))
|
||||||
|
require.NoError(t, os.Symlink(testFileTxt, filepath.Join(dir, "filelink")))
|
||||||
|
require.NoError(t,
|
||||||
|
os.Symlink("nowhere", filepath.Join(dir, "brokenlink")))
|
||||||
|
|
||||||
|
opts := testOpts(slices.Concat(
|
||||||
|
[]string{testApp, cmdGenerate, "-q"}, tc.genFlags, []string{dir},
|
||||||
|
), fs)
|
||||||
|
require.Equal(t, 0, runCLI(opts), "stderr: %s", testStderr(t, opts))
|
||||||
|
|
||||||
|
opts = testOpts([]string{testApp, cmdCheck, testFlagNoExtra, dir}, fs)
|
||||||
|
exitCode := runCLI(opts)
|
||||||
|
stderr := testStderr(t, opts)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.exitCode, exitCode, "stderr: %s", stderr)
|
||||||
|
assert.Equal(t, len(tc.extras),
|
||||||
|
strings.Count(stderr, "not in manifest"), stderr)
|
||||||
|
|
||||||
|
for _, extra := range tc.extras {
|
||||||
|
assert.Contains(t, stderr, "EXTRA: "+extra)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestCheckNeverReportsManifest keeps the manifest inside the checked tree,
|
// TestCheckNeverReportsManifest keeps the manifest inside the checked tree,
|
||||||
// under several names and path spellings, and names the tree both directly
|
// under several names and path spellings, and names the tree both directly
|
||||||
// and through a symlink: the manifest is never reported, even under
|
// and through a symlink: the manifest is never reported, even under
|
||||||
|
|||||||
+13
-10
@@ -264,11 +264,12 @@ func (c *Checker) Check(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindExtraFiles walks the filesystem and reports files not in the manifest,
|
// FindExtraFiles walks the filesystem and reports the regular files, and
|
||||||
// hidden files and directories included. The manifest file itself is not
|
// symlinks to them, that are not in the manifest, hidden files and
|
||||||
// reported. Anything the search cannot read, such as a directory that cannot
|
// directories included. The manifest file itself is not reported. Anything
|
||||||
// be listed, is reported with StatusError and the search goes on. Results are
|
// the search cannot read, such as a directory that cannot be listed, is
|
||||||
// sent to the results channel. The channel is closed when done.
|
// reported with StatusError and the search goes on. Results are sent to the
|
||||||
|
// results channel. The channel is closed when done.
|
||||||
func (c *Checker) FindExtraFiles(ctx context.Context, results chan<- Result) error {
|
func (c *Checker) FindExtraFiles(ctx context.Context, results chan<- Result) error {
|
||||||
if results != nil {
|
if results != nil {
|
||||||
defer close(results)
|
defer close(results)
|
||||||
@@ -318,11 +319,6 @@ func (c *Checker) FindExtraFiles(ctx context.Context, results chan<- Result) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// gen never lists a named pipe, a socket or a device.
|
|
||||||
if info.Mode()&(os.ModeNamedPipe|os.ModeSocket|os.ModeDevice) != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A symlink is compared by what it points to, so a manifest reached
|
// A symlink is compared by what it points to, so a manifest reached
|
||||||
// through one is not reported either.
|
// through one is not reported either.
|
||||||
if info.Mode()&os.ModeSymlink != 0 {
|
if info.Mode()&os.ModeSymlink != 0 {
|
||||||
@@ -332,6 +328,13 @@ func (c *Checker) FindExtraFiles(ctx context.Context, results chan<- Result) err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gen lists regular files and symlinks to them only, so a named
|
||||||
|
// pipe, a socket, a device, a symlink to a directory and a broken
|
||||||
|
// symlink are never reported.
|
||||||
|
if !info.Mode().IsRegular() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Skip the manifest file itself, however its path is spelled
|
// Skip the manifest file itself, however its path is spelled
|
||||||
if os.SameFile(info, c.manifestInfo) {
|
if os.SameFile(info, c.manifestInfo) {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user