root/trunk/fuse/mount-filepaths @ 1198

Revision 1198, 13.2 kB (checked in by nickandrew, 17 months ago)

Fix various typos, spelling and grammar errors

Most of the fixed errors are in comments, but a few are in
messages from the system or build files.

Some examples:

- MogileFS.pm provides a the perl client for the MogileFS application-level
+ MogileFS.pm provides a perl client for the MogileFS application-level

- ABSTRACT => 'Plugin for mogilefs server represending a UNIX like filesystem on top of mogilefs.',
+ ABSTRACT => 'Plugin for mogilefs server representing a UNIX like filesystem on top of mogilefs.',

- des => "Comma-seperated list of filenames to load when a user visits a directory URL, listed in order of preference.",
+ des => "Comma-separated list of filenames to load when a user visits a directory URL, listed in order of preference.",

- "--weight=i" => "Postive numeric weight for device",
+ "--weight=i" => "Positive numeric weight for device",

And so on...

Signed-off-by: Nick Andrew <nick@…>

  • Property svn:executable set to *
RevLine 
[1135]1#!/usr/bin/perl -w
2
3=pod
4
5=head1 NAME
6
7mount-filepaths - A fuse perl script to mount a filepaths enabled mogile system
8
9=head1 SYNOPSIS
10
11mount-filepaths --tracker HOST:PORT --domain DOMAINNAME [ --class CLASSNNAME ] [ --file-perms FILEPERM ] [ --dir-perms DIRPERM ] [ --log LOGFILE ] [ --cache-size NUM ] [ --cache-age SECS ] [ --verbose ] [ --help ]
12
13=head1 DESCRIPTION
14
15This script mounts a FilePaths enabled MogileFS system to your local filesystem.  The FilePaths and MetaData plugins, found here:
16
17  http://search.cpan.org/~hachi/MogileFS-Plugin-FilePaths-0.02
18  http://search.cpan.org/~hachi/MogileFS-Plugin-MetaData-0.01
19
20are required for this script to work.  These plugins store directory hierarchy as well as file size (and in the future, arbitrary values, which would enable this script to allow symlink creation).
21
22The following system calls are supported:
23
24=over 4
25
26=item getattr
27
28=item getdir
29
30=item mkdir
31
32=item open
33
34=item write
35
36=item statfs
37
38=item read
39
40=item mknod
41
42=item unlink
43
44=item rename
45
46=back
47
48This means that you can create, open, write, rename and delete files.  You can create and descend directories.  You can get basic file information about both files and directories.
49
50This code is not production ready (see L<BUGS>) but it is functional.  As a test it has been used to create a directory structure on MogileFS via a local filesystem which was then populated with images and browsed by a gallery viewer (which itself created many thumbnail files in mogile).
51
52=head1 BUGS
53
54Sometimes the tracker connection will go away unexpectedly and this script will die, leaving the mount point mounted, but dead.  This needs to be addressed by detecting when the tracker has disconnected and creating a new connection.
55
56=head1 AUTHORS
57
58Garth Webb E<lt>garth@sixapart.comE<gt>
59
60=cut
61
62#--------------------------------------#
63# Dependencies
64
65use strict;
66
67use Fuse;
68
69use POSIX qw(ENOENT EISDIR EINVAL);
70
71use MogileFS::Client::FilePaths;
72use Getopt::Long;
73
74#--------------------------------------#
75# Constants
76
77use constant DEF_CLASS    => 'original';
78use constant DEF_TRACKER => '127.0.0.1:6001';
79use constant DEF_FILE_PERMS => 0666;
80use constant DEF_DIR_PERMS  => 0777;
81
82use constant DEF_CACHE_SIZE => 5_000_000;
83use constant DEF_CACHE_AGE  => 10;
84
85#--------------------------------------#
86# Global Variables
87
88our ($MOG, @TRACKER, $DOMAIN, $CLASS);
89our ($FILE_PERMS, $DIR_PERMS);
90our (%FILE_CACHE, $CACHE_SIZE, $CACHE_AGE) = (('/' => {size => 0, age => 0}));
91our ($VERBOSE, $LOG, $LOG_FH);
92our $START_TIME = time;
93
94#--------------------------------------#
95# Main Program
96
97my $help;
98GetOptions('tracker|t=s'  => \@TRACKER,
99           'domain|d=s'   => \$DOMAIN,
100           'class|c=s'    => \$CLASS,
101           'file-perms=s' => \$FILE_PERMS,
102           'dir-perms=s'  => \$DIR_PERMS,
103           'log=s'        => \$LOG,
104
105           'cache-size'   => \$CACHE_SIZE,
106           'cache-age'    => \$CACHE_AGE,
107
108           'verbose|v'    => \$VERBOSE,
109           'help|h|?'     => \$help,
110          );
111
112if ($help) {
113    help();
114    exit;
115}
116
117# Default to local trackers
118@TRACKER      = (DEF_TRACKER()) unless @TRACKER;
119$CLASS      ||= DEF_CLASS();
120$FILE_PERMS ||= DEF_FILE_PERMS();
121$DIR_PERMS  ||= DEF_DIR_PERMS();
122$CACHE_SIZE ||= DEF_CACHE_SIZE();
123$CACHE_AGE  ||= DEF_CACHE_AGE();
124
125my ($mountpoint) = @ARGV;
126
127unless ($DOMAIN) {
128    print STDERR "Error: Option '--domain' required\n";
129    exit;
130}
131unless ($mountpoint) {
132    print STDERR "Please supply a mount point\n";
133    exit;
134}
135
136# When running this script directly, it will run fusermount, which will in turn
137# re-run this script.  Hence the funky semantics.
138Fuse::main(
139        mountpoint  => $mountpoint,
140
141    # Supported calls
142    getattr     => "main::e_getattr",
143    getdir      => "main::e_getdir",
144    mkdir       => "main::e_mkdir",
145    open        => "main::e_open",
146    write       => "main::e_write",
147    statfs      => "main::e_statfs",
148    read        => "main::e_read",
149    mknod       => "main::e_mknod",
150    unlink      => "main::e_unlink",
151    rename      => "main::e_rename",
152
153    # Unsupported calls
154    readlink    => "main::e_readlink",
155    rmdir       => "main::e_rmdir",
156    symlink     => "main::e_symlink",
157    link        => "main::e_link",
158    chmod       => "main::e_chmod",
159    chown       => "main::e_chown",
160    truncate    => "main::e_truncate",
161    utime       => "main::e_utime",
162    flush       => "main::e_flush",
163    release     => "main::e_release",
164    fsync       => "main::e_fsync",
165    setxattr    => "main::e_setxattr",
166    getxattr    => "main::e_getxattr",
167    listxattr   => "main::e_listxattr",
168    removexattr => "main::e_removexattr",
169
170    threaded    => 0,
171);
172
173################################################################################
174
175#--------------------------------------#
176# Functions
177
178sub help {
179    my $prog = $0;
180    $prog =~ s!.*/!!g;
181
182    print STDERR qq{
183Usage: $prog [OPTIONS] --domain DOMAIN MOUNT_POINT
184
185Takes a FilePaths enabled MogileFS installation and mounts it via FUSE to the
186local filesystem.
187
188Options:
189    --domain, -d DOMAIN
190        The MogileFS domain to use when querying mogile.  This option is
191        required.
192
193    --tracker, -t HOST:PORT
194        This option can be given multiple times for every tracker configured in
195        your MogileFS pool.  If this option is not given, 'localhost:6001' is
196        assumed.
197
198    --class, -c CLASS
199        This is the class type for any files created while MogileFS is mounted.
200        If you want to support reading/writing more than one class of file, you
201        should mount MogileFS in multiple places, each with a different class
202        and have your app write files within the appropriate mount point.  If
203        this option is not given, defaults to 'original'.
204
205    --file-perms PERMS
206        The default file permissions for all files in the mounted MogileFS
[1198]207        filesystem.  Since MogileFS does not store this information currently
[1135]208        it must be faked.
209
210    --dir-perms PERMS
211        The default directory permissions for all directories in the mounted
212        MogileFS filesystem.  See --file-perms.
213
214};
215}
216
217sub logmsg {
218    my ($verb, $msg) = @_;
219    return if $verb and not $VERBOSE;
220
221    if ($LOG) {
222        unless ($LOG_FH) {
223            open($LOG_FH, '>', $LOG) or die "Can't write log '$LOG': $!\n";
224        }
225        print $LOG_FH $msg, "\n";
226    } else {
227        print STDERR $msg, "\n";
228    }
229}
230
231sub mog_instance {
232    return $MOG if $MOG;
233
234    my $MOG = MogileFS::Client::FilePaths->new(
235                  hosts  => \@TRACKER,
236                  domain => $DOMAIN,
237                );
238
239    return $MOG;
240}
241
242sub filename_fixup {
243    my ($file) = shift;
244
245    # Make sure we start everything from '/'
246    $file = '/' unless length($file);
247    $file = '/' if $file eq '.';
248    $file = '/'.$file unless $file =~ m!^/!;
249
250    return $file;
251}
252
253sub get_file_info {
254    my ($path) = @_;
255
256    my $mog = mog_instance();
257
258    if ($path eq '/') {
259        return {name         => '/',
260                is_directory => 1};
261    }
262
263    my ($dir, $file) = $path =~ m!^(.*/)([^/]+)$!;
264
265    my @files = $mog->list($dir);
266    foreach my $finfo (@files) {
267        return $finfo if $finfo->{name} eq $file;
268    }
269
270    return;
271}
272
273sub get_file_data {
274    my ($file) = @_;
275
276    my $entry = $FILE_CACHE{$file};
277    my $meta  = $FILE_CACHE{'/'};
278
279    if ($entry) {
280        # See if this data is too old
281        if ((time - $entry->{created}) < $CACHE_AGE) {
282            logmsg(1, "-- get_file_data: hit");
283
284            # If its still valid, return it
285            return $entry->{data};
286        } else {
287            logmsg(1, "-- get_file_data: miss - expired");
288
289            rm_file_cache($file);
290        }
291    }
292
293    my $mog = mog_instance();
294    my $cont = $mog->get_file_data($file);
295    my $size = length($$cont);
296
297    if ($meta->{size} + $size > $CACHE_SIZE) {
298        # If adding this would go beyond our max cache size, delete things until
299        # we can fit it
300        foreach my $f (sort {$a->{age} <=> $b->{age}} keys %FILE_CACHE) {
301            next if $f eq '/';
302
303            my $rm_size = rm_file_cache($f);
304
305            logmsg(1, "-- get_file_data: purging - $rm_size bytes");
306
307            last if $meta->{size} + $size < $CACHE_SIZE;
308        }
309    }
310
311    logmsg(1, "-- get_file_data: added - $size bytes");
312
313    # Create a new entry
314    $FILE_CACHE{$file} = {created => time,
315                          size    => $size,
316                          data    => $cont};
317    $meta->{size} += $size;
318
319    return $cont;
320}
321
322sub rm_file_cache {
323    my ($file) = @_;
324    my $entry = delete $FILE_CACHE{$file};
325    return unless $entry;
326
327    # Decrement how large our cache size is
328    my $size = $entry->{size};
329    $FILE_CACHE{'/'}->{size} -= $size;
330
331    return $size;
332}
333
334sub e_getattr {
335    my ($file) = filename_fixup(shift);
336
337    logmsg(1, "e_getattr: $file");
338
339    my $finfo = get_file_info($file);
340
341        return -ENOENT() unless defined $finfo;
342        my ($size) = $finfo->{size} || 0;
343    my $modes;
344
345    # Cook some permissions since we don't store this information in mogile
346    if ($finfo->{is_directory}) {
347        ($modes) = (0040 << 9) + $DIR_PERMS;
348    } else {
349        ($modes) = (0100 << 9) + $FILE_PERMS;
350    }
351
352        my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = (0,0,0,1,0,0,1,1024);
353        my ($atime, $ctime, $mtime);
354        $atime = $ctime = $mtime = $START_TIME;
355
356        # 2 possible types of return values:
357        # return -ENOENT(); # or any other error you care to
358        # print(join(",",($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)),"\n");
359
360        return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
361}
362
363sub e_getdir {
364    my ($file) = filename_fixup(shift);
365
366    logmsg(1, "e_getdir: $file");
367
368    my $mog = mog_instance();
369    my @files = $mog->list($file);
370
371    return ('.', '..', map { $_->{name} } @files),0;
372}
373
374sub e_mkdir {
375        my ($file) = filename_fixup(shift);
376
377    logmsg(1, "e_mkdir: $file");
378
379    my $mog = mog_instance();
380
381    # There is no explicit mkdir in the FilePaths plugin for mogile.  So, create
382    # a temp file in the directory we want to force auto-vivification of the
383    # directories
384    my $tmp_file = $file.'/'.'.create';
385
386    my $fh = $mog->new_file($tmp_file, $CLASS);
387    print $fh '0';
388
389    unless ($fh->close) {
390        logmsg(0, "Error writing file: ".$mog->errcode.': '.$mog->errstr);
391        return -1
392    }
393
394    $mog->delete($tmp_file);
395
396    return 0;
397}
398
399sub e_open {
400    # VFS sanity check; it keeps all the necessary state, not much to do here.
401    my ($file) = filename_fixup(shift);
402
403    logmsg(1, "e_open: $file");
404
405    my $finfo = get_file_info($file);
406
407    return -ENOENT() unless $finfo;
408    return -EISDIR() if $finfo->{is_directory};
409
410    return 0;
411}
412
413sub e_read {
414    # return an error numeric, or binary/text string.  (note: 0 means EOF, "0"
415    # will give a byte (ascii "0") to the reading program)
416    my ($file) = filename_fixup(shift);
417    my ($buf, $off) = @_;
418
419    logmsg(1, "e_read: $file pos=$off len=$buf");
420
421    my $finfo = get_file_info($file);
422
423    return -ENOENT() unless $finfo;
424    return -EINVAL() if $off > $finfo->{size};
425    return 0 if $off == $finfo->{size};
426
427    my $cont = get_file_data($file);
428
429    return substr($$cont, $off, $buf);
430}
431
432sub e_write {
433    my ($file) = filename_fixup(shift);
434    my ($buf, $offset) = @_;
435
436    logmsg(1, "e_write: $file pos=$offset len=".length($buf));
437
438    my $finfo = get_file_info($file);
439
440    return -ENOENT() unless $finfo;
441
442    my $cont = get_file_data($file);
443
444    substr($$cont, $offset, length($buf), $buf);
445
446    my $mog = mog_instance();
447    $mog->store_content($file, $CLASS, $cont);
448    rm_file_cache($file);
449
450    return length($buf);
451}
452
453sub e_mknod {
454    my ($file) = filename_fixup(shift);
455
456    logmsg(1, "e_mknod: $file");
457
458    my $mog = mog_instance();
459    my $fh = $mog->new_file($file, $CLASS);
460    print $fh "\n";
461    unless ($fh->close) {
462        my ($code, $str) = ($mog->errcode || -1, $mog->errstr || '');
463        logmsg(0, "Error creating file:$code: $str");
464        $! = $str;
465        $? = $code;
466        return -1;
467    }
468
469    return 0;
470}
471
472sub e_unlink {
473    my ($file) = filename_fixup(shift);
474
475    logmsg(1, "e_unlink: $file");
476
477    my $mog = mog_instance();
478    $mog->delete($file);
479
480    return 0;
481}
482
483sub e_rename {
484    my ($old) = filename_fixup(shift);
485    my ($new) = filename_fixup(shift);
486
487    logmsg(1, "e_rename: $old -> $new");
488
489    my $mog = mog_instance();
490
491    # Rename this file
492    $mog->rename($old, $new);
493
494    return 0;
495}
496
497sub e_statfs {
498    logmsg(1, "e_statfs: $_[0]");
499    return 255, 1, 1, 1, 1, 2
500}
501
502sub e_readlink    { logmsg(1, "e_readlink: $_[0]");    0; }
503sub e_rmdir       { logmsg(1, "e_rmdir: $_[0]");       0; }
504sub e_symlink     { logmsg(1, "e_symlink: $_[0]");     0; }
505sub e_link        { logmsg(1, "e_link: $_[0]");        0; }
506sub e_chmod       { logmsg(1, "e_chmod: $_[0]");       0; }
507sub e_chown       { logmsg(1, "e_chown: $_[0]");       0; }
508sub e_truncate    { logmsg(1, "e_truncate: $_[0]");    0; }
509sub e_utime       { logmsg(1, "e_utime: $_[0]");       0; }
510sub e_flush       { logmsg(1, "e_flush: $_[0]");       0; }
511sub e_release     { logmsg(1, "e_release: $_[0]");     0; }
512sub e_fsync       { logmsg(1, "e_fsync: $_[0]");       0; }
513sub e_setxattr    { logmsg(1, "e_setxattr: $_[0]");    0; }
514sub e_getxattr    { logmsg(1, "e_getxattr: $_[0]");    0; }
515sub e_listxattr   { logmsg(1, "e_listxattr: $_[0]");   0; }
516sub e_removexattr { logmsg(1, "e_removexattr: $_[0]"); 0; }
517
Note: See TracBrowser for help on using the browser.