Как стать автором
Обновить

Динамическое расширение ядра Linux — добавляем функцию «удалить в корзину»

Время на прочтение4 мин
Количество просмотров20K
Многим пользователям Linux, особенно тем, кто по тем или иным причинам перешёл на эту ОС с Windows, не хватает возможности удаления файлов «в корзину». Кроме того, наверняка, каждый, кто пользовался Linux'ом и по ошибке удалял какой-либо файл, испытывал смешанные чувства от отсутствия простой возможности восстановить утраченные данные.

В продолжение предшествующего материала, посвящённого перехвату функций ядра Linux, представляю способ использования разработанного ранее фреймворка для создания модуля ядра Linux, реализующего возможность удаления файлов «в корзину» (just for fun).



Постановка задачи



Будем стремиться создать возможность удаления файлов таким образом, чтобы они попадали в «корзину». При этом, в роли корзины будет выступать специальный каталог, находящийся в корне файловой системы (.fstrash). Суть перемещения в корзину будет сводиться к тому, чтобы в момент удаления файла создавать для него «жёсткую ссылку» в этом каталоге. В этом случае, операция удаления, следующая за операцией создания жёсткой ссылки, удалит лишь «старое имя» файла, но не его содержимое.

Итак, для того, чтобы иметь возможность удаления в корзину, необхдимо:

  • использовать ФС с поддержкой жёсткких ссылок (например, ext4)
  • перехватить функцию драйвера соответствующей ФС, реализующую удаление (например, ext4_unlink)
  • в момент удаления дать файлу «второе имя», создав для него жёсткую ссылку


Реализация



Начнём с того, что рассмотрим функцию fstrash_unlink, реализующую логику работы с корзиной:

static struct qstr fstrash = {
        .len = 8, .name = ".fstrash",
};

static int fstrash_unlink(struct inode * inode, struct dentry * dentry)
{
        int result = -EINVAL;

        /* handle real deletes only */
        if (dentry->d_inode->i_nlink == 1) {
                struct dentry * trash = NULL;

                trash = d_lookup(inode->i_sb->s_root, &fstrash);
                if (trash) {
                        /* don't loop while deleting from the trash itself */
                        if (trash->d_inode && (trash->d_inode != inode))
                                result = move_to_trash(trash, dentry);
                        dput(trash);
                }
        }

        return result;
}


В первую очередь, данная функция проверяет счётчик ссылок (i_nlink) на элементе dentry, описывающем файл. Сравнение с единицей необходимо для того, чтобы убедиться в том, что запрашиваемая операция (удаление) ведёт именно к удалению файла, т.к. счётчик i_nlink содержит количество элементов каталогов, ссылающихся на данный файл (число жёстких ссылок). Очевидно, что значение «1» соответствует единственной (последней) ссылке.

Далее, осуществляется поиск «корзины» и, в случае, если она обнаружена и запрашиваемая операция удаления не является операцией удаления из самой корзины (проверка trash->d_inode != inode), вызывается функция создания жёсткой ссылки (перемещения файла в корзину) — move_to_trash:

static int move_to_trash(struct dentry * trash, struct dentry * object)
{
        int result;
        char name[64];
        struct dentry * de;

        snprintf(name, sizeof(name), "XXX-%lu-%s", \
                object->d_inode->i_ino, object->d_name.name);

        de = d_alloc_name(trash, name);
        if (!de)
                return -ENOMEM;

        trash->d_inode->i_op->lookup(trash->d_inode, de, 0);

        inc_nlink(object->d_inode);

        mutex_lock(&trash->d_inode->i_mutex);
        result = trash->d_inode->i_op->link(object, trash->d_inode, de);
        mutex_unlock(&trash->d_inode->i_mutex);

        drop_nlink(object->d_inode);
        if (!result)
                mark_inode_dirty(object->d_inode);

        dput(de);

        return result;
}


Как видно, в этой функции происходит:
  • формирование имени файла по шаблону (XXX-<номер_инода>-<имя_файла>)
  • создание нового имени файла посредством операции i_op->lookup для нового имени файла
  • создание жёсткой ссылки — операция i_op->link


В случае успешного выполнения перечисленных операций, объкт отмечается как «грязный» при помощи функции mark_inode_dirty, что сигнализирует системе о необходимости синхронизации метаданных этого файла.

Таким образом, в соответствии с рассматриваемым ранее способом встраивания в ядро, перехват ключевой функции удаления для ФС ext4 ext4_unlink будет выглядеть следующим образом:

DECLARE_KHOOK(ext4_unlink);
int khook_ext4_unlink(struct inode * inode, struct dentry * dentry)
{
        int result;

        KHOOK_USAGE_INC(ext4_unlink);

        fstrash_unlink(inode, dentry);
        result = KHOOK_ORIGIN(ext4_unlink, inode, dentry);

        KHOOK_USAGE_DEC(ext4_unlink);

        return result;
}


Стоит отметить также, что перехватываемая функция ext4_unlink вызывается ядром из функции vfs_unlink, которая в свою очередь осуществляет необходимую синхронизацию, предотвращая конкурентный доступ к информации на время выполнения операции:

3398 int vfs_unlink(struct inode *dir, struct dentry *dentry)
3399 {
...
3408         mutex_lock(&dentry->d_inode->i_mutex);
3409         if (d_mountpoint(dentry))
3410                 error = -EBUSY;
3411         else {
3412                 error = security_inode_unlink(dir, dentry);
3413                 if (!error) {
3414                         error = dir->i_op->unlink(dir, dentry);
3415                         if (!error)
3416                                 dont_mount(dentry);
3417                 }
3418         }
3419         mutex_unlock(&dentry->d_inode->i_mutex);
...
3428 }


Таким образом, операция i_op->unlink(...) защищена соответствующим примитивом синхронизации (d_inode->i_mutex).

Использование



Что касается использования, то один из возможных вариантов — это создание cron-задачи, выполняющейся с определённой периодичностью (например, 5 минут) и подчищающей, как вариант, всё, что лежит в корзине по маске /.fstrash/XXX_*. Время каждый может подобрать для себя сам как и то, по каким критериям чистить :)

Код данного проекта доступен github.
Теги:
Хабы:
Всего голосов 70: ↑61 и ↓9+52
Комментарии57

Публикации

Истории

Работа

Программист С
32 вакансии

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань