Repository navigation
libfatx: fix intermediate path component matching - #99
Conversation
|
Confirming this one on a real drive. It'd be great to see this merged; it's an easy one to hit when copying a game collection. |
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues were identified for the change preventing intermediate path components from matching longer sibling names.
No open issues 🎉
What changed in this PR
Fixes intermediate directory matching in fatx_open_dir() so a path component does not resolve to a longer sibling with the same prefix.
Changes:
- Preserves the original component length while scanning sibling entries.
- Requires an exact filename length for slash-terminated components before comparing their contents.
| File | Description |
|---|---|
libfatx/fatx_dir.c |
Uses a separate comparison length and checks intermediate component lengths to prevent prefix matches. |
Review effort: Balanced
|
Could you please add some test cases for this while we are at it? |
|
@Akashijk here are two test cases for this, written for @with_formatted_disk
def test_path_component_does_not_match_longer_sibling(self, path):
fs = Fatx(path)
# '/Game 2' is created first, so a lookup of the intermediate
# component 'Game/' that matched by prefix would resolve to it.
fs.mkdir('/Game 2')
fs.mkdir('/Game 2/video')
fs.write('/Game 2/video/a.txt', b'two')
fs.mkdir('/Game')
fs.mkdir('/Game/video')
fs.write('/Game/video/b.txt', b'one')
assert [a.filename for a in fs.listdir('/Game 2/video')] == ['a.txt']
assert [a.filename for a in fs.listdir('/Game/video')] == ['b.txt']
assert fs.read('/Game 2/video/a.txt') == b'two'
assert fs.read('/Game/video/b.txt') == b'one'
@with_formatted_disk
def test_path_component_prefix_is_not_found(self, path):
fs = Fatx(path)
fs.mkdir('/top')
fs.mkdir('/top/wolf')
fs.mkdir('/top/wolf/inner')
fs.write('/top/wolf/inner/x', b'x')
# Prefixes of an existing directory name, used as intermediate
# components, are not that directory
for missing in ('/top/w/inner/x', '/top/wol/inner/x'):
found = False
try:
fs.get_attr(missing)
found = True
except AssertionError:
pass
assert not found, missing
assert fs.read('/top/wolf/inner/x') == b'x'
|
|
@xmutantson Thanks for the tests. I'll merge this PR now, would you then please submit a PR with the tests you created in a follow up? |
Covers the lookup fixed in 4fd8e81 (#99): an intermediate path component must match a directory name exactly, not by prefix. - test_path_component_does_not_match_longer_sibling: '/Game 2' is created before '/Game', as rsync orders directories, and each directory's files must stay in that directory. - test_path_component_prefix_is_not_found: '/top/w/inner/x' and '/top/wol/inner/x' must not resolve into '/top/wolf/'. The prefix has to sit in the middle of the path, because the last component of a parent path is already compared exactly. Both fail before 4fd8e81 and pass with it.
Summary
Fix
fatx_open_dir()incorrectly resolving an intermediate directory component as a longer sibling that shares the same prefix.For example, with sibling directories: