canonicalPathFor static method
How two spellings of one directory are recognised as one.
Without this, foo/bar and foo/./bar would take separate instances
over the same files and diverge — the exact failure this class exists to
prevent, reintroduced by a string comparison.
Lexical canonicalisation alone is NOT enough, and believing it was would
leave the same hole open under a different spelling. p.canonicalize is
pure string work: it does not follow symlinks, so a link and its target
canonicalise to two different strings and would take two instances over
one directory. That is not exotic — /data -> /mnt/data is an ordinary
deployment, and on macOS Directory.systemTemp is /var/folders/...
while its real path is /private/var/folders/....
So the directory is resolved on the filesystem, which needs it to exist: it is created first (idempotent, and Hive would create it moments later at box open anyway). Were it left to come into existence on its own, a caller arriving before it did would key on the unresolved spelling and a caller arriving after would key on the resolved one — two instances again.
create is false only on the close path, where a teardown verb must not
write. Nothing is filed under a path that does not exist, so there the
lexical form is enough to miss cleanly.
Falls back to the lexical form only if the filesystem refuses (a permission error, a path that cannot be a directory). That fallback restores the symlink hole for that path, so it logs rather than passing silently.
Implementation
static String canonicalPathFor(String storagePath, {bool create = true}) {
// An empty path is not a location, and every step below would launder it
// into one: createSync('') throws PathNotFoundException, which is a
// FileSystemException, so the fallback catches it and p.canonicalize('')
// hands back the PROCESS WORKING DIRECTORY. The server would then start
// normally, rooted in its own image layer instead of the mounted volume,
// and - because the secret file cannot be written there either, and that
// failure is swallowed - with an UNENCRYPTED keystore. An unset
// `${STORAGE_PATH}` in a compose or k8s manifest arrives here as '',
// because Platform.environment reports an unset-but-interpolated variable
// as present with an empty value.
if (storagePath.trim().isEmpty) {
throw ArgumentError.value(
storagePath, 'storagePath', 'must not be empty or blank');
}
try {
final dir = Directory(storagePath);
if (!dir.existsSync()) {
if (!create) {
// Closing must not write. Nothing is filed under a path that does
// not exist, so the lexical form is enough to miss cleanly.
return p.canonicalize(storagePath);
}
dir.createSync(recursive: true);
}
return p.canonicalize(dir.resolveSymbolicLinksSync());
} on FileSystemException catch (e) {
_logger.warning('Could not resolve "$storagePath" on the filesystem'
' ($e); falling back to a lexical path. Two spellings of this'
' directory that differ by a symlink would now take separate Hive'
' instances over the same files.');
return p.canonicalize(storagePath);
}
}