Как получить номер поколения индексного дескриптора в Linux?

Резюме: я хочу получить номер поколения (i_generation) файла в Linux (или хотя бы ext4) из пользовательского пространства. Или, альтернативно, «время рождения» (время создания файла).

Я пытаюсь написать программу двунаправленной синхронизации файлов (она же Unison), но без центральной базы данных (просто, просто храните данные в корнях синхронизации) и сохраняя перемещение файлов (очень, очень сложно сделать правильно, если вы хотите поддерживать все странные случаи, такие как перемещение файла из каталога, который впоследствии удаляется, например ).

Наличие способа уникальной идентификации файлов ОЧЕНЬ облегчило бы задачу. Я знаю, как получить индекс и номер устройства (через stat), но поскольку индексы можно использовать повторно (я сам это видел), я хочу использовать более стабильную уникальную идентификацию.

Я читал о «номере поколения», который используется NFS для уникальной идентификации файлов. В NFS комбинация (st_ino, i_generation) используется для уникальной идентификации дескрипторов файлов при перезагрузках и сбоях сервера (или, по крайней мере, для предотвращения повторного использования одного и того же дескриптора файла, что может привести к повреждению данных).

Но мне так и не удалось получить номер. Эта документация по ext4 предполагает, что число может быть получено из ext4 файловая система, но я не могу получить ее из пользовательского пространства (я не собираюсь запускать эту простую программу в пространстве ядра). См. EXT4_IOC_GETVERSION. Я не могу найти соответствующий заголовочный файл в своей системе (тестирование LMDE, Debian). Есть два варианта, которые я мог найти:

  1. Попробуйте использовать тот, что в ядре - не получается сказать, что его нельзя использовать из пользовательского пространства.
  2. Попробуйте использовать EXT2_IOC_GETVERSION в <linux/ext2_fs.h> — получится EBADF (errno 9). Может быть, потому что я пытаюсь получить информацию EXT2 из файловой системы EXT4? А может я что-то не так делаю с ioctl, первый раз пытаюсь им пользоваться. Файл открывается корректно, так как вызов open в моем случае возвращает 3 (что должно быть корректно).

код:

#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <linux/ext2_fs.h>
#include <stdio.h>
#include <errno.h>

int main () {
    int fileno = open("generation.c", O_RDONLY);
    printf("fileno: %d\n", fileno);
    int generation = 0;
    if (ioctl(EXT2_IOC_GETVERSION, fileno, &generation)) {
        printf("errno: %d\n", errno);
    }
    printf("generation: %d\n", generation);
}

Этот код выдает ошибку (errno=9) в Ubuntu 12.04, в LMDE (Debian Testing) выдает ошибку компилятора (EXT2_IOC_GETVERSION не найден).

В качестве альтернативы можно было бы получить время создания (crtime или btime / время рождения) файла, но все, что я нашел в Google, это то, что это невозможно получить из пользовательского пространства в Linux (IIRC FreeBSD имеет системный вызов для Это). Я бы предпочел crtime, потому что crtime может быть более переносимым (в Windows), чем номер поколения.

Я знаю, что это будет зависеть от системы. Я хочу иметь запасной вариант, когда не может быть найден уникальный индекс (или вообще нет индекса, в случае FAT, который распространен на USB-накопителях).

inotify не вариант. Это программа для синхронизации файлов после их изменения, как это делает rsync одним из способов. В отличие от того, как Dropbox остается в фоновом режиме и следит за изменениями файлов.

Если нет возможности получить эти числа, я также приму это как ответ (конечно, с надлежащей документацией) :)


person ayke    schedule 18.11.2013    source источник


Ответы (1)


Я нашел решение. Я поменял местами вызов fileno и ioctl (очевидно, C делает там какое-то автоматическое преобразование типов).

Рабочий код выглядит так:

#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <linux/fs.h>
#include <stdio.h>
#include <errno.h>

int main () {
    int fileno = open("generation.c", O_RDONLY);
    printf("fileno: %d\n", fileno);
    int generation = 0;
    if (ioctl(fileno, FS_IOC_GETVERSION, &generation)) {
        printf("errno: %d\n", errno);
    }
    printf("generation: %d\n", generation);
}

Использование FS_IOC_GETVERSION позволяет использовать вызов для большинства распространенных файловых систем в Linux. Это исключает vfat и ntfs (по крайней мере так выглядит в исходниках ядра, там не указаны ни vfat, ни fuse), так как они, вероятно, не поддерживают номера поколений.

Этот фрагмент из coreutils (src/copy.c в архиве) заставил меня задуматься:

/* Perform the O(1) btrfs clone operation, if possible.
   Upon success, return 0.  Otherwise, return -1 and set errno.  */
static inline int
clone_file (int dest_fd, int src_fd)
{
#ifdef __linux__
# undef BTRFS_IOCTL_MAGIC
# define BTRFS_IOCTL_MAGIC 0x94
# undef BTRFS_IOC_CLONE
# define BTRFS_IOC_CLONE _IOW (BTRFS_IOCTL_MAGIC, 9, int)
  return ioctl (dest_fd, BTRFS_IOC_CLONE, src_fd);
#else
  (void) dest_fd;
  (void) src_fd;
  errno = ENOTSUP;
  return -1;
#endif
}
person ayke    schedule 18.01.2015