Je suis en train de développer un système de fichiers en utilisant Fuse, pour le moment c'est en perl pour le prototype. J'ai pas encore de nom, mais je pensais à zFS parce que j'aime bien les Z et qu'avec cette casse c'est pas encore pris :trololo:
Pour le moment j'ai implémenté les fonctions suivantes :
Code : Tout sélectionner
getattr=>"main::e_getattr",
getdir =>"main::e_getdir",
open =>"main::e_open",
statfs =>"main::e_statfs",
read =>"main::e_read",
write =>"main::e_write",
create =>"main::e_create",
utime =>"main::e_utime",
unlink =>"main::e_unlink",
mkdir =>"main::e_mkdir",
J'ai encore à gérer les liens physiques et symboliques (ça va etre compliqué pour les liens physiques je le sens

j'hésite encore à implémenter la compression et la déduplication, et je sais pas si je fais mettre du copy-on-write pour gérer des snapshots, ça me semble bien trop compliqué pour l'usage que j'en souhaite.
Le principe c'est d'utiliser une base de données mongodb en réseau en "back-end", l'intérêt est d'avoir un système de fichiers distribué, facile à mettre en place et maintenir et qui coûte très peu.
Le code au 1er octobre 2015.
Code : Tout sélectionner
#!/usr/bin/env perl
use strict;
use warnings;
use Fuse;
use File::Basename;
use MongoDB;
use MongoDB::OID;
use Data::Dumper;
use Time::HiRes;
use POSIX qw(ENOENT EISDIR EINVAL);
use File::stat;
use File::Stat::Bits;
use threads;
use threads::shared;
use constant DEBUG => 1;
use constant DEBUGFUSE => 0;
my $client = MongoDB::MongoClient->new(
host => '127.0.0.1',
auto_reconnect => 1,
auto_connect => 1,
timeout => 10000
);
my $db = $client->get_database('fs');
my $users = $db->get_collection('files');
my %TYPES = (
"S_IFDIR" => S_IFDIR,
"S_IFREG" => S_IFREG,
"S_IFLNK" => S_IFLNK
);
my ($debut,$fin);
sub e_utime {
print "e_utime\n" if DEBUG;
my $path = shift;
my $atime = shift;
my $ctime = shift;
$users->update({ "path" => $path }, {'$set' =>{'atime' => "$atime"}});
$users->update({ "path" => $path }, {'$set' =>{'ctime' => "$ctime"}});
return 0;
}
sub e_unlink {
my $path = shift;
$users->remove( { "path" => $path });
return 0;
}
sub e_rmdir {
my $path = shift;
my $check = $users->find({ "parent" => $path })->limit(1)->count();
if($check == 0) { # le dossier est vide
$users->remove( { "path" => $path });
return 0;
} else {
return -1;
}
}
sub e_create {
my $path = shift;
my $mask = shift;
print "e_create : $path \n" if DEBUG;
my $mode = 0b111111111;
$mode &= $mask;
# dossier parent et nom
my ($name,$chemin) = fileparse $path;
my $time = time();
$users->insert(
{
"path" => $path,
"parent" => $chemin,
"name" => $name,
"data" => undef,
"size" => 0,
"mode" => $mode,
"uid" => 0,
"gid" => 0,
"rdev" => 0,
"atime" => "$time",
"ctime" => "$time",
"mtime" => "$time",
"nlink" => 1,
"type" => "S_IFREG"
});
return 0;
}
sub e_symlink {
my $dest = shift;
my $path = shift;
print "e_symlink -> $path $dest\n" if DEBUG;
# dossier parent et nom
my ($name,$chemin) = fileparse $path;
my $time = time();
$users->insert(
{
"path" => $path,
"parent" => $chemin,
"dest" => $dest,
"name" => $name,
"size" => 0,
"mode" => 0777,
"uid" => 0,
"gid" => 0,
"rdev" => 0,
"atime" => "$time",
"ctime" => "$time",
"mtime" => "$time",
"nlink" => 1,
"type" => "S_IFLNK"
});
return 0;
}
sub e_mkdir {
my $path = shift;
my $mode = shift;
print "e_mkdir -> $path\n" if DEBUG;
# dossier parent et nom
my ($name,$chemin) = fileparse $path;
my $time = time();
$users->insert(
{
"path" => $path,
"parent" => $chemin,
"name" => $name,
"size" => 0,
"mode" => $mode,
"uid" => 0,
"gid" => 0,
"rdev" => 0,
"atime" => "$time",
"ctime" => "$time",
"mtime" => "$time",
"nlink" => 1,
"type" => "S_IFDIR"
});
return 0;
}
sub e_read {
my ($path,$size,$offset,$fh) = @_;
print "e_read -> $path \n" if DEBUG;
my $file = $users->find_one({ "path" => $path});
my $data = $file->{"data"};
my $value;
if($offset > 0 || $size != $file->{size}) {
$value = substr $data, $offset, $size;
} else {
$value = $data;
}
return $value;
}
sub e_write {
# chemin complet
my $path = shift;
print "e_write => $path\n" if DEBUG;
# dossier parent et nom
my ($name,$chemin) = fileparse $path;
# contenu
my $buffer = shift;
# offset
my $offset = shift;
# result à enregistrer
my $data;
my $file = $users->find({ "path" => $path })->next;
# si le fichier n'est pas vide
if($file->{size} > 0) {
# si on ajoute au fichier (offset = taille)
if($offset >= $file->{size}) {
$data = $file->{data} . $buffer;
}
# si on réécrit la fin et plus que la taille actuelle
elsif ( ($offset+length($buffer)) >= $file->{size} && $offset > 0) {
print "$offset - " . length($buffer) . " TODO\n";
}
# si on écrase une partie du fichier
else {
$data = substr($file->{data}, $offset, length($buffer), $buffer);
}
} else {
$data = $buffer;
}
my $length = length($data);
if($length > 16777216) {
print "[ERROR] File to large (> 16 Mb) -> $path \n";
return -1;
} else {
$users->update({ "path" => $path }, {'$set' =>{'data' => $data}});
$users->update({ "path" => $path }, {'$set' =>{'size' => length($data)}});
}
return length($buffer);
}
sub e_getattr {
my $node = shift;
print "e_getattr : $node\n" if DEBUG;
my $type;
my $size = 0;
my ($uid,$gid);
my $mode;
my ($atime, $ctime, $mtime);
if($node eq "/") {
$type = S_IFDIR;
($uid,$gid) = (0,0);
$mode = 0777;
$mode |= $type;
$atime = $ctime = $mtime = time()-1000;
} else {
my $name = $users->find( { path => $node } )->fields({ data => 0, _id => 0 })->next;
return -ENOENT() unless exists($name->{name});
$size = $name->{size};
$type = $TYPES{$name->{type}};
$uid = $name->{uid};
$gid = $name->{gid};
$mode = $name->{mode};
$mode |= $type;
$mtime = $name->{mtime};
$ctime = $name->{ctime};
$atime = $name->{atime};
}
my ($dev,$ino,$rdev, $blocks, $nlink, $blksize) = (0,0,0,1,1,1024);
return ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks);
}
sub e_statfs {
print "e_statfs\n";
return 255, 2, 1, 1, 1, 1;
}
# formate le FS
sub fs_format {
$db->drop();
}
sub e_open {
print "e_open\n";
return 0;
}
sub e_readdir {
my $dir = shift;
print "e_readdir => $dir \n" if DEBUG;
$dir .= "/" if($dir ne "/");
my @result;
my $files = $users->find( { parent => $dir } )->fields({ data => 0, _id => 0 });
my ($dev,$ino,$rdev, $blocks, $nlink, $blksize,$size) = (0,0,0,1,1,1024,0);
while(my $name = $files->next) {
my $type;
my ($uid,$gid);
my $mode;
my ($atime, $ctime, $mtime);
$size = $name->{size};
$type = $TYPES{$name->{type}};
$uid = $name->{uid};
$gid = $name->{gid};
$mode = $name->{mode};
$mode |= $type;
$mtime = $name->{mtime};
$ctime = $name->{ctime};
$atime = $name->{atime};
push(@result, [0,$name->{name}, [$dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks]]);
}
push(@result, 0);
return @result;
}
# écrasé par e_readir
sub e_getdir {
my $dir = shift;
print "e_getdir => $dir \n" if DEBUG;
$dir .= "/" if($dir ne "/");
my $files = $users->find({ parent => $dir })->fields({name => 1});
my @result;
while(my $name = $files->next) {
push(@result, $name->{name});
}
push(@result, 0);
return @result;
}
sub e_truncate {
print "e_truncate \n" if DEBUG;
my $path = shift;
my $offset = shift;
my $file = $users->find_one({ "path" => $path});
my $value;
if($offset < $file->{"size"}) {
my $data = $file->{"data"};
$value = substr $data, 0, $offset;
$users->update({ "path" => $path }, {'$set' =>{'data' => $data}});
$users->update({ "path" => $path }, {'$set' =>{'size' => length($data)}});
}
return 0;
}
sub e_readlink {
my $path = shift;
print "e_readlink : $path\n" if DEBUG;
my $file = $users->find({ "path" => $path })->fields({ dest => 1})->next;
return $file->{dest};
}
my ($mountpoint) = "";
$mountpoint = shift(@ARGV) if @ARGV;
Fuse::main(
mountpoint=>$mountpoint,
mountopts => "big_writes",
getattr =>"main::e_getattr",
getdir =>"main::e_getdir",
open =>"main::e_open",
statfs =>"main::e_statfs",
read =>"main::e_read",
write =>"main::e_write",
create =>"main::e_create",
utime =>"main::e_utime",
unlink =>"main::e_unlink",
mkdir =>"main::e_mkdir",
rmdir =>"main::e_rmdir",
truncate =>"main::e_truncate",
symlink =>"main::e_symlink",
readlink =>"main::e_readlink",
readdir =>"main::e_readdir",
threaded => 1,
debug => DEBUGFUSE
);