> On 2023-02-10, at 8:58 PM, Pádraig Brady
wrote: > > On 10/02/2023 17:24, George Valkov wrote: >>> On 2023-02-10, at 4:02 PM, Pádraig Brady
wrote: >>> >>> On 10/02/2023 12:13, George Valkov wrote: >>>>> On 2023-02-10, at 11:18 AM, Pádraig Brady
wrote:
>>>>>
>>>>> I'll apply the simple patch later I think.
>>>> I have an interesting idea: If I copy a large file, say the 16 GB disk image
>>>> where I compiled OpenWRT. So I copy this on the same filesystem, and check
>>>> disk usage before an after: no change. Also the copy is instant. That’s because
>>>> APFS supports copy-on-write. Initially both files share the same sectors on disk,
>>>> any changes made after that are copied to new sectors.
>>>> This is the most efficient way to copy files on APFS, and should produce no corruption.
>>>> Let’s implement it. I would assume there is a system cal that does the entire copy,
>>>> And we don’t need to read and write any data.
>>>> Does the trace contain any interesting calls that might be related to that?
>>>
>>> When you say "I copy a large file", is that with gcp or something else?
>> Finder: option + drag in the same directory
>>> Since coreutils 9.1 we try the CoW by default with fclonefileat()
>>> which is available since macOS 10.12 (2016).
>> I can confirms fclonefileat works on macOS 12.6.3 and solves the issue.
>> File attributes are also preserved. To clarify: these observations are from
>> the sample below. I don’t have internal experience with cloreutils, but I can
>> test it if you provide some description.
>>> Note that works only when src and dest are within the same file system,
>>> but that is the case for you if I'm reading your original report correctly.
>> Correct. fclonefileat only works on the same file system.
>> If we attempt to clone to another volume, fclonefileat fails 18 Cross-device link.
>> When building OpenWRT everything works on the same file system, so
>> fclonefileat is applicable.
>>> When I mentioned that earlier I thought your macOS version was too old to support that,
>>> but in fact your 12.6.3 should support fclonefileat() fine.
>> Thankfully it is supported. The latest security update came in January 2023.
>>> So that's another variable. Is that call failing completely for you?
>>> You might be quicker to add a couple of printfs around the
>>> fclonefileat() calls in the coreutils from latest git you compliled.
>>> Note there is new error handling related to that call in the latest git,
>>> compared to what was released in coreutils 9.1.
>> Coreutils tests on master
>> git clone git://git.savannah.gnu.org/coreutils.git
>> mv coreutils coreutils-clone
>> cd coreutils-clone
>> git submodule foreach git pull origin master
>> ./bootstrap
>> ./configure
>> make -j 16
>> git log
>> commit d374d32ccf12f8cf33c8f823d573498b7c8b27a4 (HEAD -> clone, origin/master, origin/HEAD, master)
>> cd ..
>> ./coreutils-clone/src/cp cc1 cc1-test
>> printf("HAVE_FCLONEFILEAT %u USE_XATTR %u\n", HAVE_FCLONEFILEAT, USE_XATTR);
>> HAVE_FCLONEFILEAT 1 USE_XATTR 0
>> int fc_flags = x->preserve_ownership ? 0 : CLONE_NOOWNERCOPY;
>> printf(
>> "data_copy_required %u x->reflink_mode %u\n"
>> "x->preserve_mode %u x->preserve_timestamps %u\n"
>> "x->preserve_ownership %u CLONE_NOOWNERCOPY %u\n",
>> data_copy_required, x->reflink_mode,
>> x->preserve_mode, x->preserve_timestamps,
>> x->preserve_ownership, CLONE_NOOWNERCOPY
>> );
>> if (data_copy_required && x->reflink_mode
>> && x->preserve_mode && x->preserve_timestamps
>> && (x->preserve_ownership || CLONE_NOOWNERCOPY))
>> {
>> int a = fclonefileat (source_desc, dst_dirfd, dst_relname, fc_flags);
>> int e = errno;
>> printf("fclonefileat %i %i\n", a, e);
>> if (a == 0)
>> goto close_src_desc;
>> data_copy_required 1 x->reflink_mode 1
>> x->preserve_mode 0 x->preserve_timestamps 0
>> x->preserve_ownership 0 CLONE_NOOWNERCOPY 2
>> fclonefileat is not used because x->preserve_mode = 0, x->preserve_timestamps = 0
>> That design seems wrong. The fact than no one requested to preserve that data,
>> doesn’t mean preserving it via fclonefileat is a bad thing. I think we should
>> always call fclonefileat and proceed to other means if it fails. It is more efficient, since
>> it requires no additional space or data to be copied. And always produces a valid copy.
>>> Note also I don't see any fclonefileat() syscalls in your Finder logs at least.
>> Great news: fclonefileat works on the same file system, and if we
>> need to copy outside, then just disable sparse copy according to my patch.
>> One option is to start blindly with fclonefileat, and if that fails, fall back to
>> normal copy. If you can use the trace to find what Finder is doing, we can try it.
>> It might bring some improvements. Else, disable sparse and do a regular copy.
>> It was easier for me to write a small sample:
>> // clone.c
>> #include