X Tutup
Skip to content

BACKENDS: FS: Use stat() fallback for all unexpected dirent d_type values#6505

Merged
sev- merged 2 commits intoscummvm:masterfrom
dwatteau:fix/backends-fs-posix-d_type-stat-fallback
Mar 26, 2025
Merged

BACKENDS: FS: Use stat() fallback for all unexpected dirent d_type values#6505
sev- merged 2 commits intoscummvm:masterfrom
dwatteau:fix/backends-fs-posix-d_type-stat-fallback

Conversation

@dwatteau
Copy link
Contributor

@dwatteau dwatteau commented Mar 24, 2025

Problem

On OSX Tiger, playing Indy3 FM-TOWNS from its original CD (which has both an ISO-9660 layer and a Redbook layer) produced no music.

Explanation

It looks like that's because we rely on dirent's d_type (on systems where it's available). Thing is, this field is not guaranteed to be meaningful for all filesystems (even on Linux; as seen in its readdir(3) manual page).

In OSX Tiger in particular, dirent returns completely bogus d_type values for the files being part of the CDDAFS volumes (used by MacOSXAudioCDManager). I'd get DT_CHR for the AIFF files, and 3 (which has no DT_* value name!) for the TOC file. (It looks like this is because their cddafs module, up until cddafs-240.0.9 in 2008, uses improper constants for d_type. This was fixed in cddafs-242.0.1, apparently part of macOS 10.6.2.).

The posix-fs.cpp code would then flag all the .aiff files as invalid, and then MacOSXAudioCDManager had nothing left to play.

If you stat() those files, though, you do get proper file type values. But the current posix-fs.cpp code only falls back to stat() for DT_UNKNOWN values.

Suggested fix

Fall back to stat() for all unexpected values (that is, anything different from: DT_DIR, DT_REG, DT_LNK), and not just DT_UNKNOWN.

This makes our d_type usage a bit more robust on more limited implementations. It also effectively unbreaks Indy3 TOWNS on OSX Tiger.

Still, this code is shared by many things, so we may need a careful review of the possible side effects of this change.

EDIT: FreeBSD also documents this d_type behavior ([1], [2]) for some filesystems.

…lumes

getChildren() may have succeeded but found no children at all, for some
reason. Properly print a warning in this case as well.
@dwatteau
Copy link
Contributor Author

by the way, it looks like we do this in current stat() fallback code:

void POSIXFilesystemNode::setFlags() {
struct stat st;
_isValid = (0 == stat(_path.c_str(), &st));
_isDirectory = _isValid ? S_ISDIR(st.st_mode) : false;
}

i.e. a file is marked as valid only if stat() succeeds.

but when we rely on the d_type trick, a file is only marked as valid iff it's a regular file, symlink or directory.

#if defined(SYSTEM_NOT_SUPPORTING_D_TYPE)
/* TODO: d_type is not part of POSIX, so it might not be supported
* on some of our targets. For those systems where it isn't supported,
* add this #elif case, which tries to use stat() instead.
*
* The d_type method is used to avoid costly recurrent stat() calls in big
* directories.
*/
entry.setFlags();
#else
if (dp->d_type == DT_UNKNOWN) {
// Fall back to stat()
entry.setFlags();
} else {
entry._isValid = (dp->d_type == DT_DIR) || (dp->d_type == DT_REG) || (dp->d_type == DT_LNK);

I can't say whether this discrepancy is intended. It looks like it's been this way for years.

@dwatteau dwatteau force-pushed the fix/backends-fs-posix-d_type-stat-fallback branch from 2bc31fc to 4937f17 Compare March 24, 2025 00:28
…lues

On systems where dirent provides a `d_type` field, we currently fall
back to stat() only for DT_UNKNOWN values.

Do so for all unexpected `d_type` values instead (that is, anything
different from DT_DIR, DT_REG and DT_LNK).

This is because there's no guarantee that `d_type` will be meaningful
for all OSes and filesystems. One such example is macOS Tiger, where
`d_type` will hold bogus values for the .aiff files of cddafs mount
points (as triggered by MacOSXAudioCDManager). (This bug appears to
have been fixed in cddafs-242.0.1, around the Snow Leopard area.)

When using stat() over the same files, the proper file type is
returned, though. Hence the need for the stat() fallback to be
triggered in more cases than DT_UNKNOWN.

This fixes Indy3 FM-TOWNS music being silent on macOS Tiger, when
playing from the original CD.
@dwatteau dwatteau force-pushed the fix/backends-fs-posix-d_type-stat-fallback branch from 4937f17 to 49d5e88 Compare March 24, 2025 16:14
@sev-
Copy link
Member

sev- commented Mar 26, 2025

Thank you, makes sense.

@sev- sev- merged commit 858847e into scummvm:master Mar 26, 2025
8 checks passed
@dwatteau dwatteau deleted the fix/backends-fs-posix-d_type-stat-fallback branch March 26, 2025 14:55
dwatteau added a commit that referenced this pull request Apr 29, 2025
…< 10.6.2

This fixes Indy3 FM-TOWNS music being silent on macOS Tiger, when
playing from the original CD.

A more comprehensive fix was added in GH-6505 PR for the 2.10.0 release,
but for bugfix 2.9.1 I've reworked it so that a more minimal fix is
used, only targeting the impacted old macOS releases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

X Tutup