dwatch/scheme.md

3 KiB
Raw Blame History

sequenceDiagram
%%{init: { 'theme': 'default', 'themeVariables': {
    'primaryColor': '#ff0000',
    'nodeTextColor': '#ffffff',
    'edgeLabelBackground': '#f0f0f0'
}}}%%
    autonumber
    participant Watcher as Демон-наблюдатель
    participant Kernel as Ядро (fanotify)
    participant Editor as Процесс-редактор (PID X)
    participant FS as Файловая система (целевой каталог/файл)
    participant Ep as epoll
    participant Pfd as pidfd(PID X)
    participant Map as Карта соответствий (в памяти)
    participant ChLog as Журнал изменений

    Note over Watcher: Инициализация
    Watcher->>Kernel: fanotify_init(PRE_CONTENT|NONBLOCK|CLOEXEC, O_RDONLY|O_CLOEXEC|O_LARGEFILE)
    Watcher->>Kernel: fanotify_mark(ADD|ONLYDIR, EVENT_ON_CHILD | OPEN[_EXEC]_PERM | OPEN | CLOSE_WRITE, watchDir)
    Watcher->>Ep: add(fanotify_fd, tag=FAN)

    Note over Editor: Запрос доступа к файлу
    Editor->>FS: open(target, O_RDONLY/O_RDWR/EXEC)
    FS-->>Kernel: системный вызов доступа
    Kernel-->>Watcher: PERM-событие (+fd, mask, pid)

    Note over Watcher: Сбор контекста
    Watcher->>FS: fstat(fd) → (inode, dev)
    FS-->>Watcher: inode, dev
    Watcher->>Watcher: readlink(/proc/self/fd/FD) → file_path_open
    Watcher->>Watcher: read /proc/PID/comm → proc_name
    Watcher->>Watcher: read /proc/PID/status(Uid:) → RUID
    Watcher->>Watcher: read /proc/PID/loginuid → UID(loginuid)

    %% Кладём связку
    Watcher->>Map: PUT key=(PID,inode,dev), val={proc_name, RUID, UID, file_path_open, changed=false}
    Note over Map: Добавлена связка key=(PID,inode,dev)

    %% pidfd и epoll
    Watcher->>Pfd: pidfd_open(PID, flags=0)
    Pfd-->>Watcher: pidfd
    Watcher->>Ep: add(pidfd, tag=PID, events=IN|HUP|RDHUP|ERR)

    %% Разрешаем доступ
    Watcher->>Kernel: fanotify_response{fd, FAN_ALLOW}
    Watcher->>FS: close(fd)

    Note over Editor: Работа с файлом
    Editor->>FS: write(...)
    Editor->>FS: close()
    Kernel-->>Watcher: CLOSE_WRITE (+fd)

    Note over Watcher: Фиксация изменения
    Watcher->>FS: fstat(fd) → (inode, dev)
    FS-->>Watcher: inode, dev
    Watcher->>Map: GET key=(PID,inode,dev)
    Map-->>Watcher: {…}
    Watcher->>Map: SET changed=true
    Watcher->>FS: close(fd)

    Note over Editor: Завершение процесса
    Editor->>Editor: exit()
    Pfd-->>Ep: EPOLLIN/HUP (процесс завершён)
    Ep-->>Watcher: событие(tag=PID)

    %% Пишем журнал и чистим
    Watcher->>Map: GET_ALL where pid=PID
    Map-->>Watcher: список записей
    Watcher->>ChLog: APPEND для changed=true → {proc=proc_name, file=file_path_open, uid=UID(loginuid), ruid=RUID}
    Watcher->>Ep: remove(pidfd); Watcher->>Pfd: close(pidfd)
    Watcher->>Map: DELETE_ALL where pid=PID
    Note over Map: Удалены все связки для PID