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


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.