Обнаружили, что файл журнала был удален или усечен в системах POSIX?

Предположим, что длительный процесс записывает в файл журнала. Предположим, что файл журнала остается открытым бесконечно. Предположим, что нерадивый системный администратор удаляет этот файл журнала. Может ли программа обнаружить, что это произошло?

Можно ли предположить, что fstat() сообщит о нулевом счетчике ссылок для удаленного файла?

Усечение, как мне кажется, немного сложнее. Частично это зависит от того, работает ли файловый дескриптор в режиме O_APPEND. Если файл журнала не запущен с O_APPEND, то текущая позиция записи дескриптора журнала программы не изменяется, и при усечении удаляются ведущие байты, но программа продолжает запись в «конце», оставляя пробел в воображаемом нулевые байты (они читаются как нули, но не обязательно занимают место на диске).

Если программа работает с O_APPEND, она будет писать в конец файла в том виде, в котором он существует в данный момент. Единственный способ наблюдать за усечением - это заметить, что позиция файла не там, где ее ожидала программа, что, в свою очередь, означает явное отслеживание этой позиции.

В целом, усечение меня не так волнует, как удаление, но любые мысли приветствуются.

Как насчет блокировки вместо обнаружения удаления?

Eduard Wirch 20.01.2009 20:02

@snot: Я не понимаю, как это могло бы сильно помочь. Консультативная блокировка будет проигнорирована; принудительная блокировка необычна - возможна, но я бы предпочел не делать этого. Кроме того, администраторам должно быть разрешено манипулировать журналами в разумных пределах. Возможно, программе нужен более совершенный механизм «файла журнала изменений».

Jonathan Leffler 20.01.2009 20:59

Имена файлов - это просто указатели на файлы, когда ничто не указывает на файл, он удаляется. Поскольку вы всегда можете добавить ссылку, а затем удалить другую, счетчик ссылок может не помочь, лучшее, что вы, вероятно, можете сделать, - это проверить, существует ли имя файла или ваш процесс единственный, у которого есть ссылка на файл.

Robert Gamble 20.01.2009 21:28

@ Роберт: не совсем; перед освобождением файл должен быть закрыт всеми процессами. Если демон удерживает файл открытым, то дисковое пространство остается выделенным для файла, а индексный дескриптор не используется повторно, даже если нет имени. Вы не можете повторно связать файл без имени, по крайней мере, в классическом Unix.

Jonathan Leffler 21.01.2009 10:18

Ничто из того, что вы сказали, не противоречит тому, что я сказал.

Robert Gamble 21.01.2009 15:18

@Robert: хорошо, тогда я обозначу свое непонимание того, что вы сказали, как проблему, накладываемую 300-символьными ограничениями на комментарии.

Jonathan Leffler 21.01.2009 18:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
6
1 844
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Проверка того, что fstat() возвращает нулевое количество ссылок, завершится ошибкой, если файл был жестко связан или переименован. Вместо этого я бы, вероятно, периодически сравнивал номер inode stat() с номером fstat().

Я не уверен насчет усечения.

tail -F проверяет удаление и, возможно, усечение, поэтому я бы проверил его источник, чтобы увидеть, как он это реализует.

Хороший момент в том, что количество ссылок не равно нулю, если файл был жестко связан. Для моих целей переименование не проблема. Мне нужно подумать, является ли связывание проблемой - данные по-прежнему доступны через какое-то имя, что является ключевым моментом.

Jonathan Leffler 20.01.2009 20:22

Вы можете использовать inotify для просмотра файла журнала, отслеживая его на предмет событий файловой системы.

Интересное программное обеспечение, но я смотрю на него с точки зрения программы, пишущей файл журнала, а не с точки зрения внешнего «потребителя» файла журнала.

Jonathan Leffler 20.01.2009 20:58
inotify может помочь вам с любой задачей, но он специфичен для Linux.
R.. GitHub STOP HELPING ICE 08.03.2011 09:49

Предположим, неосторожный системный администратор убивает процесс. Вы действительно хотите защитить себя от случайных действий администратора? Я думаю, вы просто ищете способ время от времени создавать новый файл журнала, например, используя logrotate. Здесь достаточно предоставить возможность вручную разрешить программе повторно открыть файл журнала. Стандартный способ сделать это - прослушать HUP-сигнал в программе и повторно открыть файл журнала, если он поступит:

#include <signal.h>

volatile int f_sighup;

void sighup_handler() {
  f_sighup = 1;
}

void trap_sighup() {
  struct sigaction sa;
  int rv;

  memset(&sa, 0, sizeof(struct sigaction));
  sa.sa_handler = &sighup_handler;
  rv = sigaction(SIGHUP, &sa, NULL);
  if (-1 == rv) {
    fprintf(stderr, "warning: setting SIGHUP signal handler failed");
  }
}

int main() {
  f_sighup = 0;
  trap_sighup();
  ...
}

Затем регулярно проверяйте флаг f_sighup в основной программе, чтобы увидеть, нужно ли повторно открывать файл журнала. Это хорошо работает с такими инструментами, как logrotate, которые могут переименовывать старый файл журнала и затем вызывать kill -s HUP $PID. А неосторожный сисадмин может сделать это вручную, удалив (а лучше переименовав) старый файл журнала.

Это должно быть «sig_atomic_t volatile f_sighup = 0;»?

Jonathan Leffler 21.01.2009 03:10

Наверное, не повредит, хотя я бы предположил, что int всегда атомарен. volatile может быть хорошей идеей в любом случае.

sth 21.01.2009 04:59

Когда файл закрывается, время модификации изменяется. Поэтому периодически проверяйте mtime, используя stat ().

Если посмотреть на спецификацию POSIX close(), можно утверждать, что close() не влияет на время модификации файлов. Напротив, спецификация POSIX для write() действительно требует изменения времени модификации. Итак, по крайней мере теоретически, если вы пишете в файл, скажем, в 09:00:00 и оставляете файл открытым без дальнейших изменений до 15:00:00, время последнего обновления останется 09:00:00.

Jonathan Leffler 31.12.2016 02:09

В ответ на отвечать от Søren-Holm

When a file is closed the modification time is changed.

это не кажется правильным:

import os
from time import sleep

TMPF = '/tmp/f'

def print_stats():
    print("%s, %s" % (os.stat(TMPF).st_mtime, os.stat(TMPF).st_ctime))
    sleep(1.1)

print("Opening...")
with open(TMPF, 'w') as f:
    print_stats()
    print("Writing...")
    os.write(f.fileno(), 'apple')
    print_stats()
    print("Flushing...")
    f.flush()
    print_stats()
    print("Closing...")

print_stats()

Производит:

Opening...
1483052647.08, 1483052647.08
Writing...
1483052648.18, 1483052648.18
Flushing...
1483052648.18, 1483052648.18
Closing...
1483052648.18, 1483052648.18

По общему признанию, здесь творится немного волшебства Python; что write() не имеет достаточных гарантий для автоматической очистки, но суть в том, что mtime обновляется при изменении файла, а не при его закрытии. Поведение ctime будет зависеть от вашей файловой системы и ее параметров монтирования.

Если посмотреть на спецификацию POSIX close(), можно утверждать, что close() не влияет на время модификации файлов. Напротив, спецификация POSIX для write() действительно требует изменения времени модификации. Итак, по крайней мере теоретически, если вы пишете в файл, скажем, в 09:00:00 и оставляете файл открытым без дальнейших изменений до 15:00:00, время последнего обновления останется 09:00:00.

Jonathan Leffler 31.12.2016 02:09

Другие вопросы по теме