null

Эксперементы с incrond

Демон incrond отслеживает события файловой системы и позволяет запускать обработчики этих событий в виде скрипта или программы. Например, можно написать достаточно простое правило в incrontab для автоматической распаковки архивов:

/tmp/arc   IN_CLOSE_WRITE,IN_MOVED_TO   cd /tmp/extracted && dtrx -rn --one=here $#

Теперь каждый файл, который копируется или перемещается в директорию /tmp/arc, будет обработан скриптом dtrx. Если файл является архивом, dtrx распакует его и все вложенные архивы в отдельную директорию /tmp/extracted. Имя файла подставляется вместо $#.
Если распаковывать архивы в ту же директорию /tmp/arc, dtrx будет рекурсивно вызываться для каждого нового файла, что нам совсем не нужно - dtrx сам прекрасно справляется с рекурсией.

Почему именно IN_CLOSE_WRITE и IN_MOVED_TO?

  • В общем случае, копирование файла происходит примерно так: новый файл создаётся и открывается на запись, копируется содержимое. По завершению копирования, файл закрывается, вызывая событие IN_CLOSE_WRITE. В нашем случае это означает, что с файлом уже можно работать.
  • Перемещение файла между различными файловыми системами выполняется примерно так же, но с удалением оригинала в конце.
  • Перемещение файла в пределах одной файловой системы обычно приводит к его переименованию, на что генерируется два события: IN_MOVED_FROM со старым именем и IN_MOVED_TO с новым именем. Нас интересует только второе.

Итак, архивы автоматически распаковываются. Казалось бы, можно с удовольствием скачивать браузером архивы прямо в эту директорию, но тут надо вспомнить как браузеры скачивают файлы...

Например, Mozilla Firefox может скачать файл в указанную директорию, свою временную директорию, либо спросить юзера. В последнем случае, не дожидаясь ответа, скачка начинается в фоновом режиме во временную директорию. Когда пользователь определится с выбором, файл перемещается в нужную директорию и докачивается. При этом Firefox создаёт два файла: пустой с правильным именем и временный с расширением .part, куда сохраняет всё во время скачивания. В конце файл .part переименовывается (заменяя собой пустой файл).

Всё это безобразие приводит к последовательности событий, которые рассмотрены далее с пояснениями. Для отлова в incrontab использовались следующие строки:

/home/user/Downloads   IN_ATTRIB,IN_CLOSE_WRITE,IN_CLOSE_NOWRITE,IN_CREATE,IN_DELETE,IN_MOVED_FROM,IN_MOVED_TO,IN_OPEN   :
/tmp/mozilla_user0   IN_ATTRIB,IN_CLOSE_WRITE,IN_CLOSE_NOWRITE,IN_CREATE,IN_DELETE,IN_MOVED_FROM,IN_MOVED_TO,IN_OPEN   :

В обеих строках перечислены все события, которые могут произойти с файлами в директории, кроме IN_ACCESS и IN_MODIFY, ибо их генерируется слишком много при скачивании/копировании. Команда : не делает ничего, мы просто ловим события в journalctl.
И вот, что получилось:

Jun 08 12:39:41 localhost incrond[887]: PATH (/tmp/mozilla_user0) FILE (7ZQ91II9.zip.part) EVENT (IN_CREATE)
Jun 08 12:39:41 localhost incrond[887]: PATH (/tmp/mozilla_user0) FILE (7ZQ91II9.zip.part) EVENT (IN_OPEN)
Jun 08 12:39:41 localhost incrond[887]: PATH (/tmp/mozilla_user0) FILE (7ZQ91II9.zip.part) EVENT (IN_CLOSE_WRITE)
Jun 08 12:39:41 localhost incrond[887]: PATH (/tmp/mozilla_user0) FILE (7ZQ91II9.zip.part) EVENT (IN_OPEN)
Jun 08 12:39:53 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip) EVENT (IN_CREATE)
Jun 08 12:39:53 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip) EVENT (IN_OPEN)
Jun 08 12:39:53 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip) EVENT (IN_CLOSE_WRITE)
Jun 08 12:39:53 localhost incrond[887]: PATH (/tmp/mozilla_user0) FILE (7ZQ91II9.zip.part) EVENT (IN_CLOSE_WRITE)
Jun 08 12:39:53 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip.part) EVENT (IN_CREATE)
Jun 08 12:39:53 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip.part) EVENT (IN_OPEN)
Jun 08 12:39:53 localhost incrond[887]: PATH (/tmp/mozilla_user0) FILE (7ZQ91II9.zip.part) EVENT (IN_OPEN)
Jun 08 12:39:54 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip.part) EVENT (IN_CLOSE_WRITE)
Jun 08 12:39:54 localhost incrond[887]: PATH (/tmp/mozilla_user0) FILE (7ZQ91II9.zip.part) EVENT (IN_CLOSE_NOWRITE)
Jun 08 12:39:54 localhost incrond[887]: PATH (/tmp/mozilla_user0) FILE (7ZQ91II9.zip.part) EVENT (IN_DELETE)
Jun 08 12:39:54 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip.part) EVENT (IN_OPEN)
Jun 08 12:39:59 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip.part) EVENT (IN_CLOSE_WRITE)
Jun 08 12:40:00 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip.part) EVENT (IN_MOVED_FROM)
Jun 08 12:40:00 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip) EVENT (IN_MOVED_TO)
Jun 08 12:40:00 localhost incrond[887]: PATH (/home/user/Downloads) FILE (best_naked_cheerleaders.zip) EVENT (IN_ATTRIB)

В строках 1-3 создаётся файл .part во временной директории.
В строке 4 этот файл открывается и в него записываются данные.
В строках 5-7 создаётся целевой файл в директории Downloads. Это реакция на выбор пользователя.
В строке 8 запись в файл .part останавливается.
В строках 9-10 создаётся и открывается на запись новый файл .part, уже в директории Downloads.
В строке 11 открывается на чтение старый файл .part. Далее происходит чтение старого файла (IN_ACCESS) и запись в новый (IN_MODIFY). Соответствующие события отфильтрованы и не показаны.
В строках 12-13 оба файла закрываются.
В строке 14 старый файл .part удаляется. На этом обработка пользовательского действия заканчивается. Файл .part перенесен в директорию Downloads.
В строке 15 новый файл .part открывается, в него производится докачка.
В строке 16 скачивание данных завершено, файл .part закрывается.
В строках 17-18 происходит как бы переименование файла .part, а по факту замещение пустого файла на файл с нужным содержимым.
В строке 19 браузер правит аттрибуты файла.
На этом все манипуляции со скачаным файлом завершены.

Важные замечания:

  • Событие IN_CLOSE_WRITE вообще не является показателем того, что браузер закончил работать с файлом. Он может оказаться пустым или неполным.
  • Событие IN_MOVED_TO тоже не является уникальным. В строках 9-14 файл .part перемещается между файловыми системами из /tmp (tmpfs) в ~/Downloads. Однако, если целевая директория проживает в той же ФС, что и временная, то вместо указанной последовательности произойдёт переименование (IN_MOVED_FROM + IN_MOVED_TO).
  • Событие IN_ATTRIB можно считать уникальным, но только для Firefox. Например, Google Chrome правит аттрибуты и временного файла тоже, что приводит к ложным срабатываниям.

Как итог

Использовать incrond легко, но нужно понимать как софт работает с файлами, чтобы учесть все варианты и отловить правильные события.