Підтримуйте (або відновлюйте) дозволи на файли під час заміни файлу


11

У мене є команда, яка приймає файл як аргумент, модифікує файл, а потім записує його до імені файлу, зазначеного у другому аргументі. Я назву цю програму modifyfile.

Я хотів, щоб він працював "на місці", тому я написав скрипт оболонки (bash), який модифікує його до тимчасового файлу, а потім переміщує його назад:

TMP=`mktemp`
modifyfile "$original" "$TMP"
mv -v "$TMP" "$original"

Це призводить до прикрого побічного ефекту: знищення дозволів на цей файл. Файл буде створений заново за допомогою стандартних дозволів.

Чи є спосіб сказати mvкоманді перезаписати призначення, не змінюючи його дозволів? Або по черзі є спосіб зберегти користувача, групу та права доступу від оригіналу та відновити їх?

Відповіді:


10

Замість використання mvпросто перенаправляйте cat. Наприклад:

TMP=$(mktemp)
modifyfile "$original" "$TMP"
cat "$TMP" > "$original"

Це перезаписується $originalіз вмістом $TMP, не торкаючись нічого на рівні файлу.


Не може це бути проблематично, якщо якась програма має відкритий файл обробки файлу?
— Мартін фон Віттіч

2
Тоді я повинен також робити rm "$TMP", але, здається, роблю саме те, що хочу.
— Стівен Остерміллер

@MartinvonWittich, мабуть, це буде проблемою, якщо ви використовували mvзамість цього. Я не бачу способу вирішити цю проблему.
— strugee

2
@MartinvonWittich Так. Create-new-then-move дає атомну зміну і не впливає на програми, у яких відкритий файл, але оскільки він створює новий файл, права власності на файл та його права втрачаються. Обрізання-існуюче, а потім записує, зберігає дозволи та права власності, але втрачає дані у випадку аварії та провела килим під ноги програм, у яких відкритий файл. Ви не можете поєднати хороші частини обох.
— Жил "ТАК - перестань бути злим"

1
@MartinvonWittich chownпрацює лише як root. chmodі chgrpможе працювати або не працювати залежно від дозволів користувача. Ні копії інших атрибутів, таких як ACL або розширені атрибути, характерні для файлової системи.
— Жил "ТАК - перестань бути злим"

10

Існує дві стратегії заміни файлу новою версією:

  1. Створіть тимчасовий файл з новою версією, а потім перемістіть його на місце.

    • Перевага: якщо програма відкриє цей файл, вона буде читати старий вміст або новий вміст, залежно від того, відкрив він файл до або після переміщення. Немає мішання.
    • Перевага: у разі аварії старий вміст зберігається.
    • Знизу: оскільки створюється новий файл, його атрибути (право власності, дозвіл тощо) не зберігаються.
  2. Перезапишіть старий файл на місці.

    • Перевага: атрибути файлу зберігаються.
    • Нижня сторона: у випадку аварії файл може бути залишеним наполовину записаним.
    • Знизу: якщо програма відкриває файл під час оновлення, ця програма може прочитати суперечливі дані.

Якщо можете, скористайтеся методом 1, але спочатку скопіюйте атрибути вихідного файлу за допомогою cp -p --attributes-only. Для цього потрібні GNU coreutils (тобто невбудований Linux або достатньо Linux-подібні середовища). Якщо у вас cpнемає --attributes-only, пропустіть цю опцію: вона буде працювати, але також буде копіювати дані.

tmp=$(mktemp)
cp -p --attributes-only "$original" "$tmp"
modifyfile "$original" "$tmp"
mv -f "$tmp" "$original"

Якщо ви не можете копіювати атрибути наявного файлу, наприклад, тому що у вас є дозволи на запис, але ви не володієте ним і хочете зберегти власника, можливий лише метод 2. Щоб мінімізувати ризик втрати даних:

  • Зробіть вікно, протягом якого файл буде максимально неповним. Підготуйте дані спочатку у тимчасовий файл, а потім скопіюйте їх на місце.
  • Спершу зробіть резервну копію старого файлу.

tmp=$(mktemp)
backup="${original}~"
modifyfile "$original" "$tmp"
cp -p "$original" "$backup"
cp -f "$tmp" "$original"

Гарна відповідь! Сьогодні я б запропонував використовувати аргумент --attributes-only з командою cp в Методі 1 . Таким чином, cp -p --attributes-only "$original" "$tmp"заповіт не буде використовувати ресурси для копіювання вмісту файлу. Я не зміг знайти, з якої версії додано цей аргумент.
— Марсело Баррос

@MarceloBarros Додано в GNU coreutils 8.6, випущений 2010-10-15, тому в ці дні, якщо у вас є GNU coreutils, ви повинні мати його. З іншими cpреалізаціями досі такого немає .
— перестань бути злим"

5

Після нашого обговорення першої відповіді пропоную іншу відповідь:

TMP="$(mktemp "$original".XXXXXXXXXX)"
modifyfile "$original" "$TMP"
chmod --reference="$original" "$TMP"
chown --reference="$original" "$TMP"
mv -f "$TMP" "$original"

Зауваження:

  • Я використовую $originalв mktempшаблоні для того, щоб тимчасовий файл не розміщувався /tmpв тій же папці, що і $original. Я вважаю, що якщо /tmpвстановити іншу файлову систему, операція більше не буде атомною.
  • Зараз результат mktempцитується, якщо він містить пробіл.
  • Я використовую $()замість ``, тому що вважаю це чистішим.
  • ch{mod,own} --referenceвикористовуються для передачі дозволів $originalна $TMP. Якщо хтось має додаткові ідеї, які метадані можна і потрібно передавати, то будь ласка, відредагуйте мою публікацію та додайте її.
  • Ну добре, для цього потрібні кореневі дозволи, як вказував Жилл. Ну, я не збираюся відкидати це зараз, коли я це написав: P
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.