Как заставить rsync применить права/владельцев к файлам, которые «не изменились»
Ситуация: резервная копия хранится под учётной записью без sudo, потом изменили настройку — rsync на удалённой стороне запускается с sudo, чтобы сохранять исходные владельцев/права. Но поскольку большинство файлов не менялось, rsync их пропускает и не приводит метаданные в соответствие. Как сделать так, чтобы rsync «считал» файлы изменёнными и применил права/владельцев, при этом не передавая заново все данные?
Коротко о том, как rsync решает — копировать файл или нет
- По умолчанию rsync считает файл изменённым, если отличаются размер или время модификации (mtime).
- Если файл признаётся «тем же» (size и mtime совпадают), rsync обычно не пересылает ни содержимое, ни обновляет файл на приёмнике; метаданные тоже могут оставаться прежними.
- Опция —checksum заставляет сравнивать контрольные суммы содержимого, но это дорого по I/O/CPU.
- Есть способы обновить только метаданные без передачи данных — при условии, что rsync может быть уверен, что данные идентичны.
Вариант 1 — самый прямой: «затронуть» временные метки на приёмнике (touch)
Идея: сделать mtime у файлов на приёмнике старее, чем у источника, тогда rsync посчитает источник новее и попытается «обновить» файлы. Поскольку содержимое на самом деле не меняется, rsync при корректных флагах не станет передавать всё заново, а только применит права/владельцев/времена.
Пример команды, которая устанавливает давнюю дату для всех файлов в каталоге приёмника (выполнять только на приёмнике):
find . -name '*' -execdir touch -d '2000-01-01' -h '{}' ';'
Плюсы:
- Работает без изменения команды rsync.
- Простая и надёжная (в том числе при большом количестве мелких файлов).
Минусы и предостережения:
- Затрагивает все файлы — проход по дереву и изменение времён займёт время.
- Важно не делать это на источнике, иначе вы потеряете полезные исходные временные метки.
- Если каталог содержательный (много больших файлов), find/touch всё равно придётся обойти все файлы — это I/O-затраты.
Вариант 2 — использовать возможности rsync, чтобы обновить только метаданные
Если можно гарантировать (или с высокой вероятностью считать), что для всех файлов одинаковый размер означает одинаковое содержимое, есть несколько опций rsync, которые помогут обновить метаданные без передачи данных:
- —size-only — rsync считает файлы одинаковыми, если у них одинаковый размер; это позволяет обновлять права/владельцев для файлов одинакового размера без полной пересылки. Опасно, если файлы могли измениться по содержимому, но не по размеру.
- —checksum — rsync сравнит контрольные суммы и, при совпадении, не будет передавать данные; но это дорого по чтению всех файлов на обеих сторонах.
- —times вместе с другими опциями часто позволяет rsync обновлять временные метки и метаданные, не передавая содержимое через сеть (если rsync решает, что копировать содержимое не нужно).
- —super / —fake-super / -M—fake-super — про обеспечение восстановления владельцев/прав при отсутствии прав root на одной из сторон (подробнее ниже).
Важно: при правильно выбранных флагах rsync будет читать файлы (и на источнике, и на приёмнике) для сравнения, но это не значит, что он будет передавать или записывать данные, если они одинаковы. Поэтому чтение большого объёма данных всё равно может занять время.
Оптимизация набора опций
Можно упростить и привести команду к более понятному набору опций (многие из них входят в —archive). Пример «очищённого» набора опций:
localArgs=(
--archive
--include-from="${DIR_SCRIPT}/${BAK_TOP}.rs_fltr"
--delete-during
--hard-links
--mkpath
--numeric-ids
--one-file-system
--progress
--relative
--sparse
-M--fake-super
--verbose
--stats
--log-file="$log_file"
)
Пример команды для отправки (push) набора данных на удалённую машину:
localHost=$(hostname) # This host
remoteUser=… # Remote user account (optional)
remotehost=… # Remote host
rsync "${localArgs[@]}" /data/ "${remoteUser:+$remoteUser@}$remoteHost:/backups/$localHost"
Пояснения к важным моментам:
- —fake-super / -M—fake-super — полезно, когда приёмная сторона работает под непривилегированным пользователем: rsync сохранит владельцев/группы/права в расширенных атрибутах. При этом файловая система приёмника должна поддерживать xattr.
- —super требует запуска rsync с привилегиями (root) для полноценного восстановления владельцев/прав.
- Не сочетайте
--inplace
и--hard-links
— это конфликтующие режимы. - —archive (-a) включает много распространённых опций и часто заменяет множество отдельных флагов.
- —size-only как одноразовое решение хорошо работает, когда можно гарантировать, что одинаковый размер == одинаковое содержимое. В противном случае риск потери изменений.
Рекомендация по безопасности: удобнее и безопаснее запускать rsync на сервере резервного копирования в режиме pull (сервер тянет данные с продакшн-хостов). Тогда если продакшн скомпрометирован, злоумышленник не получает прямого пути к бэкапам.
Подробный демонстрационный пример
Пример показывает, что rsync не будет передавать содержимое заново, если метаданные можно обновить; для демонстрации создаётся большой файл и затем проверяется поведение:
# Create the dataset
mkdir -p /tmp/1921309/{src,dst}
date >/tmp/1921309/src/date
ps -ef >/tmp/1921309/src/ps-ef
dd bs=1M count=512 if=/dev/urandom iflag=fullblock | pv >/tmp/1921309/src/urandom
(Если нет pv — dd записать прямо в файл.)
# Copy the files without metadata to the destination
rsync -rv --progress /tmp/1921309/src/ localhost:/tmp/1921309/dst
Теперь данные есть на приёмнике, но метаданные — нет. Обновим метаданные безопасно, медленно:
# Adjust metadata and copy it to the destination safely but slowly
touch /tmp/1921309/src/*
rsync -rv --progress /tmp/1921309/src/ localhost:/tmp/1921309/dst
И вариант с —size-only (быстро, но потенциально опасно):
# Adjust the metadata and copy it quicky to the destination **POTENTIALLY DANGEROUS**
touch /tmp/1921309/src/*
rsync -av --progress --size-only /tmp/1921309/src/ localhost:/tmp/1921309/dst
Замечание: в примере видно, что скорость для больших файлов будет намного выше, чем при полной передаче — это объясняет, что передача данных не происходит, если rsync считает её ненужной.
Какой вариант выбрать?
- Если можно поднять права на приёмнике (root) — использовать нормальный режим с правами (—super) или запускать rsync на приёмнике (pull). Это наиболее корректно для восстановления владельцев/прав.
- Если приёмник не root и файловая система поддерживает xattr — использовать -M—fake-super (или —fake-super при запуске на приёмнике), чтобы rsync сохранял метаданные в xattr.
- Если нужно быстро и вы уверены, что одинаковый размер = одинаковое содержимое — однократно использовать —size-only, чтобы обновить только метаданные.
- Если нельзя менять rsync-команду или права, и важно гарантированно применить права ко всем файлам — выполнить touch времён на приёмнике (вариант с find) и потом запустить обычный rsync.
Итоговые рекомендации
- Перед экспериментами протестируйте команду с —dry-run и —itemize-changes, чтобы увидеть какие файлы будут затронуты.
- Не выполняйте массовые изменения времён на источнике — только на приёмнике, если используете подход с touch.
- Для регулярных резервных копий рекомендуется запускать rsync с правами на стороне бэкапа (pull) или использовать —fake-super при отсутствии root-прав, чтобы корректно сохранять владельцев/прав.
- —size-only — удобный трюк для одноразового обновления метаданных, но применять его стоит только если вы уверены в однозначности размера ↔ содержимое.
- Избавьтесь от лишних или конфликтующих флагов (например, —inplace vs —hard-links) — используйте —archive там, где это уместно.
Если нужно, могу помочь проверить вашу текущую команду rsync и предложить минимально необходимый набор опций для безопасного применения владельцев/прав без повторной передачи данных.