Разработка собственной файловой системы с помощью FUSE
Сумит Сингх
С помощью Filesystem in Userspace (FUSE) вы можете разработать файловую систему в пространстве пользователя без знания внутреннего устройства файловой системы или изучения программирования модулей ядра. Прочтите это простое, пошаговое руководство по установке, настройке и разрешению FUSE и AFS, для того чтобы вы могли создать вашу собственную полностью функциональную файловую систему в пользовательской области на Linux
Файловая система представляет собой способ хранения и организации компьютерных файлов и каталогов, а также данных, которые в них содержатся, облегчающий поиск и доступ к этим данным. Если вы используете компьютер, вероятнее всего, вы работаете с файловыми системами более чем одного типа. Файловая система может предоставлять расширенные возможности. Она может быть написана как надстройка над существующей файловой системой для управления ее данными и предоставлять расширенную, функционально полную файловую систему (например, cvsfs-fuse, которая обеспечивает интерфейс файловой системы для CVS, или файловая система Wayback, которая обеспечивает механизм резервного копирования для хранения старых копий данных).
До появления файловых систем в пространстве пользователя их разработка была прерогативой разработчиков ядра. Создание файловой системы требовало знаний программирования ядра и технологий ядра (например, vfs), а процесс отладки требовал опыта работы с C и C++. Но и другие разработчики нуждались в управлении файловой системой для добавления персональных особенностей (таких как история или предупреждающее кэширование) и расширений.
FUSE позволяет разрабатывать полностью функциональную файловую систему, которая имеет простую библиотеку API, может использоваться непривилегированными пользователями и обеспечивает защищенную реализацию. И вдобавок FUSE обладает доказанной стабильностью.
При помощи FUSE вы можете разработать файловую систему в виде исполняемых двоичных файлов, связанных с библиотеками FUSE; другими словами, эта интегрированная файловая система не требует от вас изучения внутреннего устройства файловой системы или программирования модулей ядра.
Что касается файловых систем, то файловая система в пространстве пользователя не является чем-то новым. Приведем несколько примеров коммерческих и академических реализаций таких систем:
- LUFS - гибридная файловая система в пространстве пользователя, поддерживающая неопределенное число файловых систем прозрачно для любого приложения. Она состоит из модуля ядра и демона, работающего в пространстве пользователя. По существу, он делегирует большинство VFS-вызовов специализированному демону, который их обрабатывает.
- UserFS позволяет пользовательским процессам быть смонтированными как нормальные файловые системы. Этот экспериментальный прототип предоставляет программу ftpfs, которая организует анонимный FTP с интерфейсом файловой системы.
- Ufo Project - глобальная файловая система для Solaris, которая позволяет пользователям работать с удаленными файлами так, как будто они являются локальными.
- OpenAFS - это версия с открытыми исходными кодами файловой системы Andrew FileSystem.
- CIFS - Common Internet FileSystem.
В отличие от этих коммерческих и академических примеров FUSE переносит возможности этих проектов файловых систем в Linux. Поскольку FUSE использует исполняемые файлы (вместо, например, разделяемых объектов, используемых в LUFS), облегчается отладка и разработка. FUSE работает с обеими версиями ядра (2.4.x и 2.6.x) и теперь поддерживает Java™-связывание, т.е. вы не ограничены программированием файловой системы в C и C++.
Для создания файловой системы в FUSE, вы должны установить модуль ядра FUSE, а затем использовать библиотеку FUSE и набор API.
Для разработки файловой системы загрузите, прежде всего, исходный код FUSE (см. "Ссылки по теме") и разархивируйте пакет: tar -zxvf fuse-2.2.tar.gz . При этом создастся каталог FUSE с исходным кодом. Содержимое каталога fuse-2.2:
- ./doc содержит относящуюся к FUSE документацию. В настоящее время здесь находится только один файл how-fuse-works.
- ./kernel содержит исходный код модуля ядра FUSE (который, естественно, вы не должны знать для разработки файловой системы с помощью FUSE).
- ./include содержит заголовочные файлы FUSE API, необходимые для создания файловой системы. Сейчас вы должны знать только fuse.h.
- ./lib содержит исходный код для создания библиотек FUSE, которые вы будете связывать с вашими двоичными файлами для создания файловой системы.
- ./util содержит исходный код для служебной библиотеки FUSE.
- ./example, естественно, содержит примеры: файловые системы fusexmp.null и hello.
- Выполните сценарий configure из каталога fuse-2.2:
./configure . При этом будут созданы необходимые файлы makefile и др.
- Выполните сценарий
./make для компоновки библиотек, двоичных файлов и модуля ядра. Проверьте каталог kernel на наличие файла ./kernel/fuse.ko - это файл модуля ядра. Также проверьте каталог lib - в нем должны находиться файлы fuse.o, mount.o и helper.o.
- Выполните сценарий
./make install для завершения установки FUSE.
Альтернативный вариант: пропустите этот шаг, если желаете установить модуль в ядре самостоятельно при помощи insmod . Например: /usr/local/sbin/insmod ./kernel/fuse.ko или /sbin/insmod ./kernel/fuse.ko . Для установки необходимого модуля нужно иметь привилегии пользователя root.
Если хотите, вы можете выполнить описанные выше шаги за одну команду - в каталоге fuse-2.2 выполните ./configure; make; make install; .
Важно: При компоновке FUSE вы должны иметь доступ к заголовочным файлам ядра или исходному коду. Проще говоря, убедитесь в наличии исходного кода ядра в каталоге /usr/src/.
Теперь давайте создадим файловую систему, для того чтобы вы могли обращаться к AFS-разделу последней версии Linux, используя старое ядро Linux. У вас будут работать два процесса: серверный процесс на старом ядре Linux и FUSE-клиент на новейшем ядре Linux. При поступлении запроса к FUSE-клиенту, он связывается с удаленным серверным процессом. Для взаимодействия эта файловая система использует RX RPC-код, являющийся частью AFS, поэтому вы должны скомпоновать OpenAFS (обзор этой файловой системы AFS представлен на рисунке 1).
Рис. 1. Обзор файловой системы AFS-FUSE
- Загрузите исходные файлы OpenAFS для Linux и разархивируйте их.
В каталоге с распакованными исходными файлами выполните команду ./make ./configure --enable-transarc-paths . Если ./configure не может понять при компоновке sysname , используйте параметр --with-afs-sysname с соответствующим sysname .
Для компоновки на ядре Linux 2.4 используйте следующую команду: ./configure --enable transarc-paths --with-afs-sysname=i386_linux24 .
- Выполните команду
./make и затем ./make dest . Следите за всеми ошибками во время компоновки.
Если компоновка прошла успешно, ваше дерево исходных файлов AFS готово к работе. На этом этапе вы должны подготовить каталог для разработки с названием afsfuse. В этом каталоге создайте еще два каталога:
- Каталог include, который будет содержать включаемые заголовочные файлы из OpenAFS и FUSE.
- Каталог lib, который будет содержать библиотеки из OpenAFS и FUSE.
- Скопируйте заголовочные файлы и библиотеки.
Сначала скопируйте заголовочные файлы AFS из каталога OpenAFS, выполнив копирование всех каталогов и файлов из dest\i386_linux24\include в каталог include. Затем скопируйте в этот каталог заголовочные файлы FUSE из каталогов fuse-2.2. Повторите эти действия для библиотек в каталоге lib.
- Создайте структуру приложения.
Вам необходимы два набора файлов для двух наборов процессов. Назовите файлы клиентского процесса, используя схему afsfuse_client.*; назовите файлы серверного процесса afsfuse_server.*.
Таким образом, вы будете иметь файл afsfuse_client.c, который будет содержать код процесса FUSE, файл afsfuse_server.c, который будет содержать серверный код для процесса, работающего на удаленной системе, файл makefile и файл rxgen для создания RPC-заголовка (например, afsfuse.xg).
Файл afsfuse_client.c будет создавать код процесса afsfuse_client, который будет вызываться файловой системой FUSE для создания вашей файловой системы (используйте пример fuse-2.2/example/fusexmp.c для создания этого файла).
Для создания файловой системы с FUSE, вы должны объявить переменную структуры с типом fuse_operations и передать ее в функцию fuse_main . Структура fuse_operations передает указатель на функции, которые будут вызываться при необходимости выполнить соответствующее действие. В листинге 1 приведена структура fuse_operations .
Листинг 1. Необходимые функции в структуре fuse_operation
struct fuse_operations {
int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t);
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*symlink) (const char *, const char *);
int (*rename) (const char *, const char *);
int (*link) (const char *, const char *);
int (*chmod) (const char *, mode_t);
int (*chown) (const char *, uid_t, gid_t);
int (*truncate) (const char *, off_t);
int (*utime) (const char *, struct utimbuf *);
int (*open) (const char *, struct fuse_file_info *);
int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
int (*write) (const char *, const char *, size_t, off_t,struct fuse_file_info *);
int (*statfs) (const char *, struct statfs *);
int (*flush) (const char *, struct fuse_file_info *);
int (*release) (const char *, struct fuse_file_info *);
int (*fsync) (const char *, int, struct fuse_file_info *);
int (*setxattr) (const char *, const char *, const char *, size_t, int);
int (*getxattr) (const char *, const char *, char *, size_t);
int (*listxattr) (const char *, char *, size_t);
int (*removexattr) (const char *, const char *);
};
|
Ни одна из этих операций не является абсолютно необходимой, но многие нужны для корректной работы файловой системы. Вы можете реализовать полнофункциональную файловую систему с методами специального назначения .flush , .release или .fsync (в этой статье не рассматривается ни одна из функций xattr ). В листинге 1 показаны следующие функции:
getattr: int (*getattr) (const char *, struct stat *); Аналогична функции stat() . Поля st_dev и st_blksize игнорируются. Поле st_ino игнорируется, если не предоставляется параметр монтирования use_ino .
readlink: int (*readlink) (const char *, char *, size_t); Читает объект символической ссылки. Буфер должен быть заполнен строкой, заканчивающейся нулем. Аргумент размер буфера включает место для символа null. Если linkname не помещается в буфер, он должен быть обрезан. При успешном выполнении функции возвращаемое значение равно "0".
getdir: int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t); Читает содержимое каталога. Эта операция выполняет последовательно функции opendir() , readdir() , ..., closedir() в одном вызове. Для каждого элемента каталога должна быть выполнена функция filldir() .
mknod: int (*mknod) (const char *, mode_t, dev_t); Создает узел file. Нет операции create() ; mknod() будет вызываться для создания узлов, отличных от каталога и символических ссылок.
mkdir: int (*mkdir) (const char *, mode_t);
rmdir: int (*rmdir) (const char *); Создают и удаляют каталог соответственно.
unlink: int (*unlink) (const char *);
rename: int (*rename) (const char *, const char *); Удаляют и переименовывают файл соответственно.
symlink: int (*symlink) (const char *, const char *); Создает символическую ссылку.
link: int (*link) (const char *, const char *); Создает жесткую ссылку на файл.
chmod: int (*chmod) (const char *, mode_t);
chown: int (*chown) (const char *, uid_t, gid_t);
truncate: int (*truncate) (const char *, off_t);
utime: int (*utime) (const char *, struct utimbuf *); Изменение битов полномочий, пользователя и группы, размера и времени доступа/модификации файла соответственно.
open: int (*open) (const char *, struct fuse_file_info *); Операция открытия файла. В open() не передаются флаги создания и усечения (O_CREAT , O_EXCL , O_TRUNC ). Проверяет, разрешена ли операция для данных флагов. open() может также (необязательно) возвратить произвольный filehandle в структуре fuse_file_info , который будет передан всем файловым операций.
read: int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *); Чтение данных из открытого файла. read() должен возвратить указанное количество байтов за исключением EOF или ошибки; в противном случае оставшиеся данные будут замещены нулями. Исключением из этого правила является ситуация, когда указан параметр монтирования direct_io ; в этом случае возвращаемое значение системного вызова read() будет отражать возвращаемое значение этой операции.
write: int (*write) (const char *, const char *, size_t, off_t, struct fuse_file_info *); Запись данных в открытый файл. write() должен возвратить указанное количество байтов за исключением возврата ошибки. Исключением из этого правила является ситуация, когда указан параметр монтирования direct_io (аналогично операции read() ).
statfs: int (*statfs) (const char *, struct statfs *); Получение статистики файловой системы. Поля f_type и f_fsid игнорируются.
flush: int (*flush) (const char *, struct fuse_file_info *); Сброс на диск кэшированных данных. Эта функция не эквивалентна fsync() - это не запрос на синхронизацию измененных данных. flush() вызывается при каждой операции close() дескриптора файла, то есть, если файловая система хочет возвратить ошибки записи в close() , и файл кэшировал измененные данные, это хорошее место для восстановления измененных данных и возврата каких-либо ошибок. Поскольку многие приложения игнорируют ошибки в close() , это не всегда приносит пользу.
Примечание: Метод flush() может быть вызван более одного раза для каждой операции open() . Это происходит тогда, когда один дескриптор файла ссылается на открытый файл через вызовы dup() , dup2() или fork() . Невозможно определить, является ли сброс последним, поэтому каждый сброс должен быть обработан одинаково. Множественные последовательности записи-сброса относительно редки, поэтому это не должно быть проблемой.
release: int (*release) (const char *, struct fuse_file_info *); Освобождение открытого файла. release() вызывается тогда, когда больше нет ссылок на открытый файл - все дескрипторы файла закрыты, и все отображения памяти не назначены. Для каждого вызова open() имеется ровно один вызов release() с теми же флагами и дескриптором файла. Можно открыть файл более одного раза, тогда будет считаться только последняя функция release , и не будет больше операций чтения/записи с файлом. Возвращаемое значение функции release игнорируется.
fsync: int (*fsync) (const char *, int, struct fuse_file_info *); Синхронизация содержимого файла. Если параметр datasync не равен нулю, только пользовательские данные должны быть сброшены, а не метаданные.
setxattr: int (*setxattr) (const char *, const char *, const char *, size_t, int);
getxattr: int (*getxattr) (const char *, const char *, char *, size_t);
listxattr: int (*listxattr) (const char *, char *, size_t);
removexattr: int (*removexattr) (const char *, const char *); Установка, получение, перечисление и удаление расширенных атрибутов соответственно.
Ваша файловая система будет выглядеть примерно так:
afsfuse_client <--RX[RPC]--> afsfuse_server
|
afsfuse_client будет направлять переданные ей вызовы в afsfuse_server, размещенный на другой машине. Процесс afsfuse_server будет обрабатывать все запросы, передаваемые ему клиентом, и возвращать результаты. Он выполняет всю необходимую работу. Механизмом, используемым для RPC, является RX. Здесь нет кэширования ни данных, ни метаданных.
Перед дальнейшей работой вы должны определить ваш уровень RX RPC. Для этого, создайте файл .xg для rxgen, чтобы описать ваш прокси и код заглушки, которые будут связаны с afsfuse_client.c и afsfuse_server.c. В листинге 2 показано создание файла afsfuse.xg со следующим содержимым:
Листинг 2. Создание файла afsfuse.xg
#define MYMAXPATH 512
%#include <rx/rx.h>
%#include </rx_null.h >
%#define SAMPLE_SERVER_PORT 5000
%#define SAMPLE_SERVICE_PORT 0
/* т.е., порт сервера пользователя */
%#define SAMPLE_SERVICE_ID 4 /* Максимальное количество запросов, которые будут
обрабатываться этой службой одновременно */
/* Это количество будет также гарантировано для параллельного выполнения при
отсутствии обрабатываемых служебных запросов */
%#define SAMPLE_MAX 2 /* Минимальное количество запросов, которые гарантированно
будут обработаны немедленно */
%#define SAMPLE_MIN 1 /* Индекс класса защиты "null" в примере службы.
Должен равняться 0 (есть N классов, пронумерованных с 0.
В этом случае, N равно 1) */
%#define SAMPLE_NULL 0 /********************** fuse4_file_info взят из fuse.h
rxgen не понимает fuse.h mystat взят из man 2
mystat необходимы опять rxgen не понимает
struct paras и остановится. **********************/
struct my_file_info { /** Открыть флаги. Доступна в open() и release() */
int flags; /** Дескриптор файла. Может быть заполнен файловой системой в
open(). Доступна для всех других операций с файлами */
unsigned int fh; /** В случае операции записи указывает, вызвана ли она
writepage */
int writepage;
};
struct mystatfs {
afs_uint32 f_type; /* тип файловой системы (см. ниже) */
afs_uint32 f_bsize; /* оптимальный размер передаваемого блока */
afs_uint32 f_blocks; /* общее количество блоков данных в файловой системе */
afs_uint32 f_bfree; /* свободные блоки в fs */
afs_uint32 f_bavail; /* свободные блоки, доступные не superuser*/
afs_uint32 f_files; /* общее число узлов в файловой системе */
afs_uint32 f_ffree; /* свободные файловые узлы в fs */
afs_uint32 f_fsid1; /* id файловой системы*/
afs_uint32 f_fsid2; /* id файловой системы */
afs_uint32 f_namelen; /* максимальная длина названий файлов */
afs_uint32 f_spare[6]; /* резерв */
};
struct mystat {
afs_uint32 st_dev; /* устройство */
afs_uint32 st_ino; /* inode */
afs_uint32 st_mode; /* защита */
afs_uint32 st_nlink; /* число жестких ссылок */
afs_uint32 st_uid; /* user ID владельца */
afs_uint32 st_gid;/* group ID владельца */
afs_uint32 st_rdev; /* тип устройства (если inode) */
afs_uint32 st_size; /* общий размер, в байтах */
afs_uint32 st_blksize; /* blocksize для I/O файловой системы */
afs_uint32 st_blocks; /* число распределенных блоков */
afs_uint32 st_atim; /* время последнего доступа */
afs_uint32 st_mtim; /* время последней модификации */
afs_uint32 st_ctim; /* время последнего изменения */
};
struct my_dirhandle{
afs_uint32 type;
afs_uint32 inode;
char name[MYMAXPATH];
};
typedef my_dirhandle bulkmydirhandles<>;
/********************* функции фазы 1 *********************************************/
rxc_getattr(IN string mypath<MYMAXPATH>, IN int dummy) split = 1;
rxc_getdirWrapper(IN string path<MYMAXPATH>, OUT bulkmydirhandles *handles) = 2;
rxc_read(IN string path<MYMAXPATH>;, IN afs_uint32 size, IN afs_uint32 offset,
IN struct my_file_info *fi) split = 3;
rxc_open(IN string path<MYMAXPATH>, IN int flags, OUT u_int *hd) = 4;
rxc_write(IN string path<MYMAXPATH>,IN afs_uint32 size, IN afs_uint32 offset,
IN struct my_file_info *fi) split = 5;
rxc_chmod(IN string path<MYMAXPATH>, IN afs_uint32 mode) = 6;
rxc_chown(IN string path<MYMAXPATH>, IN afs_uint32 uid, IN afs_uint32 gid) = 7;
rxc_utime(IN string path<MYMAXPATH>, IN afs_uint32 at,IN afs_uint32 mt) = 8;
rxc_mknod(IN string path<MYMAXPATH>, afs_uint32 mode, afs_uint32 rdev) = 9 ;
rxc_mkdir(IN string path<MYMAXPATH>, IN afs_uint32 mode) = 10;
rxc_unlink(IN string path<MYMAXPATH>) = 11 ;
rxc_rmdir(IN string path<MYMAXPATH>) = 12;
rxc_rename(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 13;
rxc_truncate(IN string path<MYMAXPATH>, IN afs_uint32 size) = 14;
rxc_release(IN string path<MYMAXPATH>, IN struct my_file_info *fi) = 15;
rxc_readlink(IN string path<MYMAXPATH>, IN afs_uint32 size,OUT string
data<MYMAXPATH>) = 16;
rxc_symlink(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 17;
rxc_link(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 18;
rxc_statfs(IN string path<MYMAXPATH>, OUT struct mystatfs *stbuf) = 19;
rxc_fsync(IN string path <MYMAXPAT>, IN int isdatasync, IN struct my_file_info
*fi) = 20 ;
rxc_flush(IN string path <MYMAXPATH>, IN struct my_file_info *fi) = 21 ;
|
При определении уровня RX RPC обратите внимание на следующие моменты:
- Вы определили
mystatfs , mystat и my_file_info как оболочки над структурами statfs , stat и fuse_file_info . Они будут преобразованы при помощи сгенерированного XDR-кода (XDR, External Data Representation позволяют данным быть заключенными в оболочку архитектурно-независимым способом, для того чтобы данные могли передаваться между гетерогенными компьютерными системами).
- Вы определили примерно по одной функции для каждого члена структуры
fuse_operations с почти одинаковыми параметрами, поскольку работа afsfuse_client заключается просто в принятии вызовов из файловой системы FUSE и передаче их в afsfuse_server .
- Вы жестко закодировали некоторые значения, например
MYMAXPATH , которые вы должны были бы получить из системы - жесткое кодирование было сделано ради упрощения.
Выполните компиляцию файла afsfuse.xg при помощи rxgen для создания файлов клиента и заглушки. Из каталога, содержащего исходный код afsfuse_server и afsfuse_client, выполните команду openafs-1.2.13/i386_linux24/dest/bin/rxgen afsfuse.xg . При этом будут созданы следующие файлы:
- afsfuse.cs.c - клиентский код заглушки (stub) для связи с afsfuse_client.c.
- afsfuse.h - заголовочный файл, содержащий различные определения для вашего кода FUSE RX.
- afsfuse.ss.c - серверный код заглушки (прокси-код) для связи с кодом afsfuse_server.
- afsfuse.xdr.c - код для маршаллизации древовидных структур, которые вы определили в afsfuse.xg.
Теперь добавьте некоторый код в afsfuse_client.c и afsfuse_server.c для выполнения реальной работы. Большинство из вызовов будет выглядеть следующим образом:
Our_call_in_afs_fuse_client() . Маршаллизация параметров и подготовка для RPC. Вызов приложения afsfuse_server по RX [RPC]. Демаршаллизация параметров. Копирование значений в формальные параметры, переданные в эту функцию.
Our_call_in_afs_fuse_server() . Демаршаллизация параметров. Вызов локальной файловой системы или AFS-функций. Маршаллизация параметров и подготовка для RPC. Вызов RX RPC.
Вызов afsfuse_client.c выглядит следующим образом:
int afsfuse_readlink(const char *path, char *buf, size_t size){
rx_connection *local& int ret& char *buffer = malloc (512)&
memset(buffer,0,512)& memset(buf,0,size)& local = getconnection()&
ret = rxc_rxc_readlink(local,path,512,&buffer) // rpc call
relconnection(local)&
strncpy(buf,buffer,512-1)&
//<- демаршаллизация параметров
return ret&
}
|
Вызов afsfuse_server.c выглядит следующим образом:
Листинг 3. Вызов afsfuse_server.c
int rxc_rxc_readlink( struct rx_call *call, char * path, afs_uint32 size, char**data)
{ int ret& char lbuff[512] ={0}&
translatePath(path,lbuff)& //<- выполнить вызов файловой системы
*data = malloc(512)&
res = readlink(lbuff, *data, 512-1)&
if(res == -1) return -errno& (*data)[res] = '\0'& return 0&
}
|
Аналогично, вы можете добавить код в другие функции для расширения возможностей вашей файловой системы.
Вы должны создать makefile для компилирования вашего кода. Не забудьте включить следующие параметры при компилировании кода для afsfuse_client: -D_FILE_OFFSET_BITS=64 и -DFUSE_USE_VERSION=22 .
Листинг 4. Генерирование файла makefile для компилирования клиентского кода
SRCDIR=./ LIBRX=${SRCDIR}lib/librx.a
LIBS=${LIBRX} ${SRCDIR}lib/liblwp.a
#CC = g++
CFLAGS=-g -I. -I${SRCDIR}include -I${SRCDIR}include/fuse/ -DDEBUG ${XCFLAGS}
-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=22
afsfuse_client: afsfuse_client.o afsfuse.xdr.o ${LIBS} bulk_io.o afsfuse.cs.o
${CC} ${CFLAGS} -o afsfuse_client afsfuse_client.o ${SRCDIR}lib/fuse/fuse.o
${SRCDIR}lib/fuse/mount.o ${SRCDIR}lib/fuse/helper.o
${SRCDIR}lib/fuse/fuse_mt.o bulk_io.o afsfuse.cs.o afsfuse.xdr.o ${LIBS}
afsfuse_server: afsfuse_server.o afsfuse.xdr.o afsfuse.ss.o bulk_io.o ${LIBS}
${CC} ${CFLAGS} -o afsfuse_server afsfuse_server.o bulk_io.o afsfuse.ss.o
afsfuse.xdr.o ${LIBS}
#afsfuse_client.o: afsfuse.h
#afsfuse_server.o: afsfuse.h
bulk_io.o: ${CC} -c -g -I${SRCDIR}include bulk_io.c afsfuse.cs.c afsfuse.ss.c
afsfuse.er.c afsfuse.h afsfuse.xdr.c: afsfuse.xg rxgen afsfuse.xg
afsfuse.xdr.o: afsfuse.xdr.c ${CC} -c -g -I{SRCDIR}include afsfuse.xdr.c
all: afsfuse_server afsfuse_client
clean: rm *.o rm afsfuse_client rm afsfuse_server
|
Помните, что вы по-прежнему должны использовать librx.a и liblwp.a для связывания с RX-кодом и LWP-кодом для RX. fuse/fuse.o, fuse/helper.o и fuse/mount.o - это библиотеки FUSE, с которыми вы должны связать ваш код.
В этой статье вы изучили процесс установки FUSE и OpenAFS, а также как их использовать для создания и настройки вашей собственной файловой системы в пространстве пользователя, полнофункциональной, стабильной файловой системы в Linux, не требующей обновления или перекомпилирования активного ядра - вы даже не должны быть программистом модуля ядра. Вы узнали о двух ключевых концепциях для запуска файловой системы FUSE: как установить и настроить модуль ядра FUSE и как воспользоваться возможностями библиотек FUSE и API.
|