Repository: rclone/rclone
File: backend/local/clone_darwin.go (Go)

Code before:
	}
	srcObj, ok := src.(*Object)
	if !ok {
		fs.Debugf(src, "Can't clone - not same remote type")
		return nil, fs.ErrorCantCopy
	}
	if f.opt.TranslateSymlinks && srcObj.translatedLink { // in --links mode, use cloning only for regular files
		return nil, fs.ErrorCantCopy
	}

	// Fetch metadata if --metadata is in use
	meta, err := fs.GetMetadataOptions(ctx, f, src, fs.MetadataAsOpenOptions(ctx))
	if err != nil {
		return nil, fmt.Errorf("copy: failed to read metadata: %w", err)
	}

	// Create destination
	dstObj := f.newObject(remote)
	err = dstObj.mkdirAll()
	if err != nil {
		return nil, err
	}

	srcPath := srcObj.path
	if f.opt.FollowSymlinks { // in --copy-links mode, find the real file being pointed to and pass that in instead
		linkPath, err := os.Readlink(srcPath)
		if err == nil { // if it's a link
			srcPath = linkPath
			if !filepath.IsAbs(linkPath) { // handle relative links
				srcPath = path.Join(path.Dir(srcObj.path), linkPath)
			}
		}
	}

	err = Clone(srcPath, f.localPath(remote))
	if err != nil {
		return nil, err
	}

	// Set metadata if --metadata is in use
	if meta != nil {
		err = dstObj.writeMetadata(meta)
		if err != nil {
			return nil, fmt.Errorf("copy: failed to set metadata: %w", err)
		}
	}

	return f.NewObject(ctx, remote)
}

// Clone uses APFS cloning if possible, otherwise falls back to copying (with full metadata preservation)

Review comment (suggestion):
This won't work if the symlink points to another symlink...

Resolving symlinks is a pain as you have to detect loops also, so we should try to get the kernel to do it if we can.

I would have thought we want to run the Clone without `CLONE_NOFOLLOW`/`COPYFILE_NOFOLLOW` or something like that? Though you don't appear to be using those flags so there is a gap in my understanding!

Actually I found [filepath.EvalSymlinks](https://pkg.go.dev/path/filepath?utm_source=godoc#EvalSymlinks) which looks like it would do the job if we can't get Clone to do it

Here is [the source for the meat of filpath.EvalSymlinks](https://cs.opensource.google/go/go/+/refs/tags/go1.23.1:src/path/filepath/symlink.go;l=16) - as you see it is quite non trivial resolving symlinks!



Code after:
	if !ok {
		fs.Debugf(src, "Can't clone - not same remote type")
		return nil, fs.ErrorCantCopy
	}
	if f.opt.TranslateSymlinks && srcObj.translatedLink { // in --links mode, use cloning only for regular files
		return nil, fs.ErrorCantCopy
	}

	// Fetch metadata if --metadata is in use
	meta, err := fs.GetMetadataOptions(ctx, f, src, fs.MetadataAsOpenOptions(ctx))
	if err != nil {
		return nil, fmt.Errorf("copy: failed to read metadata: %w", err)
	}

	// Create destination
	dstObj := f.newObject(remote)
	err = dstObj.mkdirAll()
	if err != nil {
		return nil, err
	}

	srcPath := srcObj.path
	if f.opt.FollowSymlinks { // in --copy-links mode, find the real file being pointed to and pass that in instead
		srcPath, err = filepath.EvalSymlinks(srcPath)
		if err != nil {
			return nil, err
		}
	}

	err = Clone(srcPath, f.localPath(remote))
	if err != nil {
		return nil, err
	}

	// Set metadata if --metadata is in use
	if meta != nil {
		err = dstObj.writeMetadata(meta)
		if err != nil {
			return nil, fmt.Errorf("copy: failed to set metadata: %w", err)
		}
	}

	return f.NewObject(ctx, remote)
}

// Clone uses APFS cloning if possible, otherwise falls back to copying (with full metadata preservation)
// note that this is closely related to unix.Clonefile(src, dst, unix.CLONE_NOFOLLOW) but not 100% identical
// https://opensource.apple.com/source/copyfile/copyfile-173.40.2/copyfile.c.auto.html
func Clone(src, dst string) error {
	state := apfs.CopyFileStateAlloc()
	defer func() {