Filesystem in Userspace (FUSE) merupakan mekanisme sistem operasi untuk sistem operasi Unix-like yang memungkinkan pengguna tidak ber-hak istimewa menciptakan file system mereka sendiri tanpa mengubah kode kernel. Hal ini dicapai dengan menjalankan kode file system di userspace, sedangkan modul FUSE hanya menyediakan "jembatan" untuk antarmuka kernel yang sebenarnya.
Kegunaan fuse ada banyak, salah satunya adalah yang saya sebutkan diatas. Kegunaan lainnya adalah untuk networking. Contohnya adalah google-drive-ocalmfuse, yang memungkinkan user untuk me-mount google drive ke linuxnya, sehingga memungkinkan mengaksesnya seakan-akan google drive tersebut adalah local storage. Contoh lainnya adalah PNGDrive, yang menyimpan data – data di filesystem tersebut ke sebuah gambar berformat .PNG, dan menggunakan file .PNG tersebut seakan-akan ia adalah sebuah filesystem
Untuk memulai membuat FUSE anda sendiri, anda harus men-download dan
meng-install modul FUSE terlebih dahulu yang dapat anda dapatkan di
http://fuse.sourceforge.net/ . lalu anda bisa mendownload template FUSE standar di
http://fuse.sourceforge.net/doxygen/fusexmp_8c.html . dari template tersebut anda bisa berkreasi untuk melakukan hal – hal semau anda. Sebagai contoh, saya ingin setiap file yang saya buka secara otomatis membuat file backupnya ( dengan format <namafile>.bak). Apabila file .bak dibuka maka akan muncul notifikasi peringatan file tersebut adalah file backup dan file tersebut tidak dapat dibuka.
Penasaran bagaimana source codenya, berikut source code yang saya gunakan
#define FUSE_USE_VERSION 30
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef linux
/* For pread()/pwrite()/utimensat() */
#define _XOPEN_SOURCE 700
#endif
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#include <dirent.h>
#endif
static const char *dirpath = "/home/exod/Downloads/";
char cbackup[100]=".bak";
char lastaccess[100],lastaccessbackup[100];
char lastunlink[100],lastunlinkbackup[100];
FILE *fin,*from,*to,*temp;
char buffer[BUFSIZ];
size_t sizetemp;
static int xmp_getattr(const char *path, struct stat *stbuf){
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = lstat(fpath, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_access(const char *path, int mask){
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = access(fpath, mask);
if (res == -1)
return -errno;
strcpy(lastaccess,"NULL");
strcpy(lastaccessbackup,"NULL");
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size){
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = readlink(fpath, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi){
DIR *dp;
struct dirent *de;
(void) offset;
(void) fi;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
dp = opendir(fpath);
if (dp == NULL)
return -errno;
while ((de = readdir(dp)) != NULL) {
struct stat st;
memset(&st, 0, sizeof(st));
st.st_ino = de->d_ino;
st.st_mode = de->d_type << 12;
if (filler(buf, de->d_name, &st, 0))
break;
}
closedir(dp);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev){
int res;
/* On Linux this could just be ‘mknod(path, mode, rdev)’ but this
is more portable */
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
if (S_ISREG(mode)) {
res = open(fpath, O_CREAT | O_EXCL | O_WRONLY, mode);
if (res >= 0)
res = close(res);
}
else if (S_ISFIFO(mode))
res = mkfifo(fpath, mode);
else
res = mknod(fpath, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode){
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = mkdir(fpath, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_unlink(const char *path){
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = unlink(fpath);
if (res == -1)
return -errno;
strcpy(lastunlink,fpath);
strcpy(lastunlinkbackup,fpath);
strcat(lastunlinkbackup,cbackup);
xmp_unlink(lastunlinkbackup);
return 0;
}
static int xmp_rmdir(const char *path){
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = rmdir(fpath);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to){
int res;
res = symlink(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to){
int res;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to){
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode){
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = chmod(fpath, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid){
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = lchown(fpath, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size){
//Change the size of a file
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = truncate(fpath, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2]){
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
/* don’t use utime/utimes since they follow symlinks */
res = utimensat(0, fpath, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
static int xmp_open(const char *path, struct fuse_file_info *fi){
int res,len;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
strcpy(lastaccess,fpath);
strcpy(lastaccessbackup,fpath);
strcat(lastaccessbackup,cbackup);
len=strlen(lastaccess);
if ((len >= 2) && strcmp(&(lastaccess[len - 4]), ".bak") == 0){
char command[100];
sprintf(command,"zenity --error --text='File yang anda buka adalah file backup. File tidak bisa diubah maupun disalin kembali'");
system(command);
return 1;
}
res = open(fpath, fi->flags);
if (res == -1)
return -errno;
if ((len >= 2) && strcmp(&(lastaccess[len - 4]), ".bak") != 0){
if(access(lastaccessbackup,F_OK)==0) remove(lastaccessbackup);
from=fopen(lastaccess,"rb");
to=fopen(lastaccessbackup,"wb");
char a;
while(1){
a=fgetc(from);
if(!feof(from))
fputc(a,to);
else break;
}
char command[100];
sprintf(command,"chmod 444 '%s'",lastaccessbackup);
system(command);
}
close(res);
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi){
int fd;
int res;
(void) fi;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
fd = open(fpath, O_RDONLY);
if (fd == -1)
return -errno;
res = pread(fd, buf, size, offset);
if (res == -1)
res = -errno;
close(fd);
return res;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi){
int fd;
int res;
(void) fi;
while((sizetemp = fread(buffer, 1, BUFSIZ, from)))
{
fwrite(buffer, 1, sizetemp, to);
}
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
fd = open(fpath, O_WRONLY);
if (fd == -1)
return -errno;
res = pwrite(fd, buf, size, offset);
if (res == -1)
res = -errno;
close(fd);
return res;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
//Get file system statistics
{
int res;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
res = statvfs(fpath, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
/*Release an open file
Release is called when there are no more references to an open file:
all file descriptors are closed and all memory mappings are unmapped.*/
{
/* Just a stub. This method is optional and can safely be left
unimplemented */
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
(void) fpath;
(void) fi;
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi) /Synchronize file contents
{
/* Just a stub. This method is optional and can safely be left
unimplemented */
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
(void) fpath;
(void) isdatasync;
(void) fi;
return 0;
}
#ifdef HAVE_POSIX_FALLOCATE
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
//Allocates space for an open file
{
int fd;
int res;
(void) fi;
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
if (mode)
return -EOPNOTSUPP;
fd = open(fpath, O_WRONLY);
if (fd == -1)
return -errno;
res = -posix_fallocate(fd, offset, length);
close(fd);
return res;
}
#endif
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
//Set extended attributes
{
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
int res = lsetxattr(fpath, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
//Get extended attributes
{
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
int res = lgetxattr(fpath, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
//List extended attributes
{
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
int res = llistxattr(fpath, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
char fpath[1000];
sprintf(fpath,"%s%s",dirpath,path);
int res = lremovexattr(fpath, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
static struct fuse_operations xmp_oper = {
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.readdir = xmp_readdir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.open = xmp_open,
.read = xmp_read,
.write = xmp_write,
.statfs = xmp_statfs,
.release = xmp_release,
.fsync = xmp_fsync,
#ifdef HAVE_POSIX_FALLOCATE
.fallocate = xmp_fallocate,
#endif
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
Ganti bagian static const char *dirpath = "/home/exod/Downloads" dengan static const char *dirpath = "/home/[nama user]/Downloads"
Simpan kemudian compile dengan menggunakan perintah: gcc -Wall [nama file].c `pkg-config fuse --cflags --libs` -o [nama file]
Kemudian buat sebuah direktori, misalnya: /tmp/fuse
Sekarang yang anda lakukan adalah me mount program anda ke /tmp/fuse. Yaitu dengan cara : ./[nama file] /tmp/fuse
Maka semua isi direktori /home/[nama user]/Downloads akan dimount ke direktori /tmp/fuse
Coba masuk ke /tmp/fuse dan jalankan perintah ls, maka semua isi direktori tersebut akan ditampilkan.
Terlihat bahwa yang diubah dari template adalah beberapa variabel global dan di dalam fungsi open. Karena hanya setiap membuka file akan dibuat backupnya, maka hanya fungsi open yang saya ubah sedikit.
Berikut beberapa istilah atau kegunaan setiap fungsi :
getattr = xmp_getattr, => mengambil atribut file
.access = xmp_access, => mengambil direktori
.readlink = xmp_readlink, => membaca target dari symbolic link
.readdir = xmp_readdir, => membaca direktori
.mknod = xmp_mknod, => membuka node file
.mkdir = xmp_mkdir, => membuat directory
.symlink = xmp_symlink, => membua symbolic link
.unlink = xmp_unlink, => menghapus file
.rmdir = xmp_rmdir, => menghapus directory
.rename = xmp_rename, => memperbaharui nama file
.link = xmp_link, => menciptakan hardlink ke file
.chmod = xmp_chmod, => mengubah permission file
.chown = xmp_chown, => mengubah kepemilikan file
.truncate = xmp_truncate, => merubah ukuran file
.utimens = xmp_utimens, => mengubah akses dan modifikasi waktu dari file dengan resolusi nanosecond
.open = xmp_open, => membuka file
.read = xmp_read, => membaca data dari file yang dibuka
.write = xmp_write, => menulis data ke file yang telah dibuka
.release = xmp_release, => melepaskan file yang sudah dibuka
.fsync = xmp_fsync, => sinkronisasi isi file
Cukup sekian ilmu yang dapat saya bagi hari ini. Jika ada kesalahan saya mohon maaf. Semoga bermanfaat :)