cp method

Future<void> cp(
  1. String src,
  2. String dst, {
  3. bool force = false,
  4. bool parents = false,
})

Copies a file or directory from src to dst.

src may be an MFS path (/dir/file) or an IPFS path (/ipfs/<cid>[/sub/path]), matching Kubo files cp. When dst ends with a trailing slash the source is copied inside that directory under its basename (ipfs files cp /src /dir/).

parents creates missing destination directories. force overwrites an existing file at the destination; it refuses to overwrite a directory, matching Kubo's unlinkNodeIfExists. Without force, an existing destination of any kind is an error.

Implementation

Future<void> cp(
  String src,
  String dst, {
  bool force = false,
  bool parents = false,
}) async {
  final denylist = _denylistService;
  if (denylist != null && _pathLooksBlocked(src)) {
    throw DenylistBlockedException(src);
  }

  final srcCid = await _resolveAny(src);
  if (srcCid == null) {
    throw Exception('Source path not found: $src');
  }

  // Kubo only treats the destination as a container when it ends with a
  // trailing slash; an existing directory named without a slash is an
  // "already exists" error there, not an implicit container.
  var dstPath = dst;
  if (dst.endsWith('/')) {
    dstPath = '$dst${_basename(src)}';
  }
  final destParts = _splitPath(dstPath);
  if (destParts.isEmpty) {
    throw Exception('Cannot overwrite MFS root: $dst');
  }

  await _mutationLock.synchronized(() async {
    await _modifyPath(destParts, (currentCid) async {
      if (currentCid != null) {
        if (!force) {
          throw Exception('file already exists: $dst');
        }
        final existingType = await _unixfsType(currentCid);
        if (existingType == Data_DataType.Directory ||
            existingType == Data_DataType.HAMTShard) {
          throw Exception(
            'cp: cannot overwrite directory with --force: $dst',
          );
        }
      }
      return srcCid;
    }, recursive: parents);
  });
}