APFS-клоны: исследование¶
Дата: 2026-10. Проверено на macOS 27.0.1 (APFS, clonefile).
Проблема¶
Клон APFS делит блоки с оригиналом. DiskScope, как и du, учитывает каждый
файл отдельно, поэтому две полные копии по 10 ГБ могут давать «20 ГБ», хотя
физически занято около 10 ГБ. Удаление одной копии освобождает 0 байт.
Публичный API¶
В sys/attr.h есть расширенные атрибуты (FSOPT_ATTR_CMN_EXTENDED):
| Атрибут | Тип | Смысл |
|---|---|---|
ATTR_CMNEXT_CLONEID |
u_int64_t |
Идентификатор data stream; у полных клонов совпадает |
ATTR_CMNEXT_CLONE_REFCNT |
u_int32_t |
Число полных клонов |
ATTR_CMNEXT_PRIVATESIZE |
off_t |
Байты, освобождаемые при удалении файла (не входят в клоны/снимки) |
ATTR_CMNEXT_EXT_FLAGS |
u_int64_t |
EF_MAY_SHARE_BLOCKS (0x1), EF_SHARES_ALL_BLOCKS (0x40) |
Поддержка тома — VOL_CAP_FMT_CLONE_MAPPING (0x04000000).
getattrlist/getattrlistbulk возвращают длину, затем attribute_set_t;
fork-атрибуты идут после common/file в порядке битов. ATTR_CMN_RETURNED_ATTRS
позволяет не парсить отсутствующие поля.
Прототип¶
| Файл | cloneID | refcnt | private size | flags | du |
|---|---|---|---|---|---|
| обычный | уникальный | 1 | ≈ размер | 0 | 9,7 МБ |
| source (полный клон) | X | 2 | 0 | 0x41 | 9,7 МБ |
| clone (полный клон) | X | 2 | 0 | 0x41 | 9,7 МБ |
| source после append в clone | X | 1 | 8 КБ | 0x1 | 9,7 МБ |
| clone после append | Y | 1 | ≈1 МБ | 0x1 | 10,7 МБ |
Выводы:
- Полные клоны надёжно группируются по
CLONEID+REFCNT > 1. PRIVATESIZEточно отвечает на вопрос «сколько освободится при удалении этого файла».- После частичного изменения
CLONEIDрасходится,REFCNTстановится 1, остаётся толькоEF_MAY_SHARE_BLOCKS. Публичного API с картой частичных extents нет. - Сумма
PRIVATESIZEне равна физическому уникальному объёму: общие 10 ГБ не входят ни в один private size, пока живы оба файла.
Решение¶
Полный учёт клонов отложен. Реализовывать приблизительный учёт опасно: он покажет неверное «освободится» и может подтолкнуть удалить клон, который ничего не освободит.
Сделано:
- в «Диске» добавлено предупреждение, что клоны считаются отдельными файлами;
- ограничение описано в limitations;
- API зафиксировано здесь для будущей реализации.
Что нужно для полного учёта¶
cloneID/refcnt/privateSizeвScanNodeчерез расширенныйgetattrlistbulk(нужен отдельный парсер fork-атрибутов).- Группировка полных клонов и вычитание дублей из «занято», но с сохранением
PRIVATESIZEдля сценария удаления. - Для частичных extents — приватный
F_LOG2PHYS_EXT/extent map илиfs_snapshot-метаданные; публичного стабильного API не найдено. - Проверка на macOS 14/15/26 и на томах без
VOL_CAP_FMT_CLONE_MAPPING. - Тесты:
cp -c, частичная модификация, удаление одного клона,du.
Ссылки¶
sys/attr.h:ATTR_CMNEXT_CLONEID,ATTR_CMNEXT_CLONE_REFCNT,ATTR_CMNEXT_PRIVATESIZE,VOL_CAP_FMT_CLONE_MAPPING.sys/stat.h:EF_MAY_SHARE_BLOCKS,EF_SHARES_ALL_BLOCKS.