materializeFile function

Future<FileMaterialization> materializeFile({
  1. required String source,
  2. required String destination,
  3. bool keepSourcePermissions = false,
  4. List<_LinkAttempt>? linkAttempts,
})

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,
  );
}