materializeFile function
Materializes source at destination, replacing any existing file.
keepSourcePermissions keeps the source's mode on the materialized copy
(clonefile does this inherently); the default sets 0644 — skipped for
hardlinks, whose mode is shared with the source inode.
Implementation
Future<FileMaterialization> materializeFile({
required String source,
required String destination,
bool keepSourcePermissions = false,
List<_LinkAttempt>? linkAttempts,
}) async {
final src = File(source);
final size = await src.length();
final destFile = File(destination);
// Replace semantics: a stale destination (hardlinked to a previous blob,
// read-only, whatever) must never make the new materialization fail.
if (await destFile.exists()) {
try {
await destFile.delete();
} on FileSystemException {
// Fall through — strategies below recreate the path their own way.
}
}
await destFile.parent.create(recursive: true);
final attempts = linkAttempts ?? defaultLinkAttempts();
for (final attempt in attempts) {
final strategy = attempt(source, destination);
if (strategy != null && await destFile.exists()) {
if (!keepSourcePermissions && strategy != MaterializationStrategy.hardlink) {
_chmod(destination, 420);
}
return FileMaterialization(
strategy: strategy,
destination: destination,
sizeBytes: size,
);
}
}
await File(destination).parent.create(recursive: true);
await src.copy(destination);
if (!keepSourcePermissions) _chmod(destination, 420);
return FileMaterialization(
strategy: MaterializationStrategy.copy,
destination: destination,
sizeBytes: size,
);
}