1############################################################
2#
3#   $Id: Filesystem.pm 185 2010-07-15 19:25:30Z trevor $
4#   Sys::Filesystem - Retrieve list of filesystems and their properties
5#
6#   Copyright 2004,2005,2006 Nicola Worthington
7#   Copyright 2008,2009 Jens Rehsack
8#
9#   Licensed under the Apache License, Version 2.0 (the "License");
10#   you may not use this file except in compliance with the License.
11#   You may obtain a copy of the License at
12#
13#       http://www.apache.org/licenses/LICENSE-2.0
14#
15#   Unless required by applicable law or agreed to in writing, software
16#   distributed under the License is distributed on an "AS IS" BASIS,
17#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18#   See the License for the specific language governing permissions and
19#   limitations under the License.
20#
21############################################################
22
23package Sys::Filesystem;
24
25# vim:ts=4:sw=4:tw=78
26
27use 5.006;
28
29my @query_order;
30
31use strict;
32use warnings;
33use vars qw($VERSION $AUTOLOAD);
34use Carp qw(croak cluck confess);
35use Module::Pluggable
36  require => 1,
37  only =>
38  [ @query_order = map { __PACKAGE__ . '::' . $_ } ucfirst( lc($^O) ), $^O =~ m/Win32/i ? 'Win32' : 'Unix', 'Dummy' ],
39  inner       => 0,
40  search_path => ['Sys::Filesystem'];
41use Params::Util qw(_INSTANCE);
42use Scalar::Util qw(blessed);
43
44use constant DEBUG => $ENV{SYS_FILESYSTEM_DEBUG} ? 1 : 0;
45use constant SPECIAL => ( 'darwin' eq $^O ) ? 0 : undef;
46#use constant SPECIAL => undef;
47
48$VERSION = '1.30';
49
50my ( $FsPlugin, $Supported );
51
52BEGIN
53{
54    Sys::Filesystem->plugins();
55
56    foreach my $qo (@query_order)
57    {
58        next unless ( UNIVERSAL::isa( $qo, $qo ) );
59        $FsPlugin = $qo;
60        last;
61    }
62
63    $Supported = ( $FsPlugin ne 'Sys::Filesystem::Unix' ) && ( $FsPlugin ne 'Sys::Filesystem::Dummy' );
64}
65
66sub new
67{
68    # Check we're being called correctly with a class name
69    ref( my $class = shift ) && croak 'Class name required';
70
71    # Check we've got something sane passed
72    croak 'Odd number of elements passed when even number was expected' if ( @_ % 2 );
73    my %args = @_;
74
75    # Double check the key pairs for stuff we recognise
76    while ( my ( $k, $v ) = each(%args) )
77    {
78        unless ( grep( /^$k$/i, qw(fstab mtab xtab) ) )
79        {
80            croak("Unrecognised paramater '$k' passed to module $class");
81        }
82    }
83
84    my $self = {%args};
85
86    # Filesystem property aliases
87    $self->{aliases} = {
88                         device          => [qw(fs_spec dev)],
89                         filesystem      => [qw(fs_file mount_point)],
90                         mount_point     => [qw(fs_file filesystem)],
91                         type            => [qw(fs_vfstype vfs)],
92                         format          => [qw(fs_vfstype vfs vfstype)],
93                         options         => [qw(fs_mntops)],
94                         check_frequency => [qw(fs_freq)],
95                         check_order     => [qw(fs_passno)],
96                         boot_order      => [qw(fs_mntno)],
97                         volume          => [qw(fs_volume fs_vol vol)],
98                         label           => [qw(fs_label)],
99                       };
100
101    # Debug
102    DUMP( '$self', $self ) if (DEBUG);
103
104    $self->{filesystems} = $FsPlugin->new(%args);
105
106    # Maybe upchuck a little
107    croak "Unable to create object for OS type '$self->{osname}'" unless ( $self->{filesystems} );
108
109    # Bless and return
110    bless( $self, $class );
111    return $self;
112}
113
114sub filesystems
115{
116    my $self = shift;
117    unless ( defined( _INSTANCE( $self, __PACKAGE__ ) ) )
118    {
119        unshift @_, $self unless ( 0 == ( scalar(@_) % 2 ) );
120        $self = __PACKAGE__->new();
121    }
122
123    # Check we've got something sane passed
124    croak 'Odd number of elements passed when even number was expected' if ( @_ % 2 );
125    my $params = {@_};
126    for my $param ( keys %{$params} )
127    {
128        croak "Illegal paramater '$param' passed to filesystems() method"
129          unless grep( m/^$param$/, qw(mounted unmounted special device regular) );
130    }
131
132    # Invert logic for regular
133    if ( exists $params->{regular} )
134    {
135        delete $params->{regular};
136        if ( exists( $params->{special} ) )
137        {
138            carp("Both parameters specified, 'special' and 'regular', which are mutually exclusive");
139        }
140        $params->{special} = SPECIAL;
141    }
142
143    my @filesystems = ();
144
145    # Return list of all filesystems
146    unless ( keys %{$params} )
147    {
148        @filesystems = sort( keys( %{ $self->{filesystems} } ) );
149
150        # Return list of specific filesystems
151    }
152    else
153    {
154        for my $fs ( sort( keys( %{ $self->{filesystems} } ) ) )
155        {
156            for my $requirement ( keys( %{$params} ) )
157            {
158                my $fsreqname = $requirement;
159                if (   !exists( $self->{filesystems}->{$fs}->{$requirement} )
160                     && exists( $self->{aliases}->{$requirement} ) )
161                {
162                    foreach my $fsreqdef ( @{ $self->{aliases}->{$requirement} } )
163                    {
164                        if ( exists( $self->{filesystems}->{$fs}->{$fsreqdef} ) )
165                        {
166                            $fsreqname = $fsreqdef;
167                            last;
168                        }
169                    }
170                }
171                if (
172                     (
173                          ( defined( $params->{$requirement} ) && exists( $self->{filesystems}->{$fs}->{$fsreqname} ) )
174                       && ( $self->{filesystems}->{$fs}->{$fsreqname} eq $params->{$requirement} )
175                     )
176                     || ( !defined( $params->{$requirement} ) && !exists( $self->{filesystems}->{$fs}->{$fsreqname} ) )
177                   )
178                {
179                    push( @filesystems, $fs );
180                    last;
181                }
182            }
183        }
184    }
185
186    # Return
187    return @filesystems;
188}
189
190sub supported()
191{
192    return $Supported;
193}
194
195sub mounted_filesystems
196{
197    return $_[0]->filesystems( mounted => 1 );
198}
199
200sub unmounted_filesystems
201{
202    return $_[0]->filesystems( unmounted => 1 );
203}
204
205sub special_filesystems
206{
207    return $_[0]->filesystems( special => 1 );
208}
209
210sub regular_filesystems
211{
212    return $_[0]->filesystems( special => SPECIAL );
213}
214
215sub DESTROY { }
216
217sub AUTOLOAD
218{
219    my ( $self, $fs ) = @_;
220
221    croak "$self is not an object" unless ( blessed($self) );
222    croak "No filesystem passed where expected" unless ($fs);
223
224    ( my $name = $AUTOLOAD ) =~ s/.*://;
225
226    # No such filesystem
227    unless ( exists $self->{filesystems}->{$fs} )
228    {
229        croak "No such filesystem";
230    }
231    else
232    {
233        # Found the property
234        if ( exists $self->{filesystems}->{$fs}->{$name} )
235        {
236            return $self->{filesystems}->{$fs}->{$name};
237        }
238        elsif ( exists $self->{aliases}->{$name} )
239        {    # Didn't find the property, but check any aliases
240            for my $alias ( @{ $self->{aliases}->{$name} } )
241            {
242                if ( exists $self->{filesystems}->{$fs}->{$alias} )
243                {    # Found the Alias
244                    return $self->{filesystems}->{$fs}->{$alias};
245                }
246            }
247        }
248    }
249
250    return undef;
251}
252
253sub TRACE
254{
255    return unless DEBUG;
256    warn( $_[0] );
257}
258
259sub DUMP
260{
261    return unless DEBUG;
262    eval {
263        require Data::Dumper;
264        warn( shift() . ': ' . Data::Dumper::Dumper( shift() ) );
265    };
266}
267
2681;
269
270=pod
271
272=head1 NAME
273
274Sys::Filesystem - Retrieve list of filesystems and their properties
275
276=head1 SYNOPSIS
277
278    use strict;
279    use Sys::Filesystem ();
280
281    # Method 1
282    my $fs = Sys::Filesystem->new();
283    my @filesystems = $fs->filesystems();
284    for (@filesystems)
285    {
286        printf("%s is a %s filesystem mounted on %s\n",
287                          $fs->mount_point($_),
288                          $fs->format($_),
289                          $fs->device($_)
290                   );
291    }
292
293    # Method 2
294    my $weird_fs = Sys::Filesystem->new(
295                          fstab => '/etc/weird/vfstab.conf',
296                          mtab  => '/etc/active_mounts',
297                          xtab  => '/etc/nfs/mounts'
298                    );
299    my @weird_filesystems = $weird_fs->filesystems();
300
301    # Method 3 (nice but naughty)
302    my @filesystems = Sys::Filesystem->filesystems();
303
304=head1 DESCRIPTION
305
306Sys::Filesystem is intended to be a portable interface to list and query
307filesystem names and their properties. At the time of writing there were only
308Solaris and Win32 modules available on CPAN to perform this kind of operation.
309This module hopes to provide a consistant API to list all, mounted, unmounted
310and special filesystems on a system, and query as many properties as possible
311with common aliases wherever possible.
312
313=head1 INHERITANCE
314
315  Sys::Filesystem
316  ISA UNIVERSAL
317
318=head1 METHODS
319
320=over 4
321
322=item new
323
324Creates a new Sys::Filesystem object. new() accepts 3 optional key pair values
325to help or force where mount information is gathered from. These values are
326not otherwise defaulted by the main Sys::Filesystem object, but left to the
327platform specific helper modules to determine as an exercise of common sense.
328
329=over 4
330
331=item fstab
332
333Specify the full path and filename of the filesystem table (or fstab for
334short).
335
336=item mtab
337
338Specify the full path and filename of the mounted filesystem table (or mtab
339for short). Not all platforms have such a file and so this option may be
340ignored on some systems.
341
342=item xtab
343
344Specify the full path and filename of the mounted NFS filesystem table
345(or xtab for short). This is usually only pertinant to Unix bases systems.
346Not all helper modules will query NFS mounts as a separate exercise, and
347therefore this option may be ignored on some systems.
348
349=back
350
351=item supported
352
353Returns true if the operating system is supported by Sys::Filesystem.
354Unsupported operating systems may get less information, e.g. the mount
355state couldn't determined or which file system type is special ins't
356known.
357
358=back
359
360=head2 Listing Filesystems
361
362=over 4
363
364=item filesystems()
365
366Returns a list of all filesystem. May accept an optional list of key pair
367values in order to filter/restrict the results which are returned. The
368restrictions are evaluated to match as much as possible, so asking for
369regular and special file system (or mounted and special file systems),
370you'll get all.
371
372For better understanding, please imagine the parameters like:
373
374  @fslist = $fs->filesystems( mounted => 1, special => 1 );
375  # results similar as
376  SELECT mountpoint FROM filesystems WHERE mounted = 1 OR special = 1
377
378If you need other selection choices, please take a look at L<DBD::Sys>.
379
380Valid values are as follows:
381
382=over 4
383
384=item device => "string"
385
386Returns only filesystems that are mounted using the device of "string".
387For example:
388
389    my $fdd_filesytem = Sys::Filesystem->filesystems(device => "/dev/fd0");
390
391=item mounted => 1
392
393Returns only filesystems which can be confirmed as actively mounted.
394(Filesystems which are mounted).
395
396The mounted_filesystems() method is an alias for this syntax.
397
398=item unmounted => 1
399
400Returns only filesystems which cannot be confirmed as actively mounted.
401(Filesystems which are not mounted).
402
403The unmounted_filesystems() method is an alias for this syntax.
404
405=item special => 1
406
407Returns only filesystems which are regarded as special in some way. A
408filesystem is marked as special by the operating specific helper
409module. For example, a tmpfs type filesystem on one operating system
410might be regarded as a special filesystem, but not on others. Consult
411the documentation of the operating system specific helper module for
412further information about your system. (Sys::Filesystem::Linux for Linux
413or Sys::Filesystem::Solaris for Solaris etc).
414
415This parameter is mutually exclusive to C<regular>.
416
417The special_filesystems() method is an alias for this syntax.
418
419=item regular => 1
420
421Returns only fileystems which are not regarded as special. (Normal
422filesystems).
423
424This parameter is mutually exclusive to C<special>.
425
426The regular_filesystems() method is an alias for this syntax.
427
428=back
429
430=item mounted_filesystems()
431
432Returns a list of all filesystems which can be verified as currently
433being mounted.
434
435=item unmounted_filesystems()
436
437Returns a list of all filesystems which cannot be verified as currently
438being mounted.
439
440=item special_filesystems()
441
442Returns a list of all fileystems which are considered special. This will
443usually contain meta and swap partitions like /proc and /dev/shm on Linux.
444
445=item regular_filesystems()
446
447Returns a list of all filesystems which are not considered to be special.
448
449=back
450
451=head2 Filesystem Properties
452
453Available filesystem properties and their names vary wildly between platforms.
454Common aliases have been provided wherever possible. You should check the
455documentation of the specific platform helper module to list all of the
456properties which are available for that platform. For example, read the
457Sys::Filesystem::Linux documentation for a list of all filesystem properties
458available to query under Linux.
459
460=over 4
461
462=item mount_point() or filesystem()
463
464Returns the friendly name of the filesystem. This will usually be the same
465name as appears in the list returned by the filesystems() method.
466
467=item mounted()
468
469Returns boolean true if the filesystem is mounted.
470
471=item label()
472
473Returns the fileystem label.
474
475This functionality may need to be retrofitted to some original OS specific
476helper modules as of Sys::Filesystem 1.12.
477
478=item volume()
479
480Returns the volume that the filesystem belongs to or is mounted on.
481
482This functionality may need to be retrofitted to some original OS specific
483helper modules as of Sys::Filesystem 1.12.
484
485=item device()
486
487Returns the physical device that the filesystem is connected to.
488
489=item special()
490
491Returns boolean true if the filesystem type is considered "special".
492
493=item type() or format()
494
495Returns the type of filesystem format. fat32, ntfs, ufs, hpfs, ext3, xfs etc.
496
497=item options()
498
499Returns the options that the filesystem was mounted with. This may commonly
500contain information such as read-write, user and group settings and
501permissions.
502
503=item mount_order()
504
505Returns the order in which this filesystem should be mounted on boot.
506
507=item check_order()
508
509Returns the order in which this filesystem should be consistency checked
510on boot.
511
512=item check_frequency()
513
514Returns how often this filesystem is checked for consistency.
515
516=back
517
518=head1 OS SPECIFIC HELPER MODULES
519
520=head2 Dummy
521
522The Dummy module is there to provide a default failover result to the main
523Sys::Filesystem module if no suitable platform specific module can be found
524or successfully loaded. This is the last module to be tried, in order of
525platform, Unix (if not on Win32), and then Dummy.
526
527=head2 Unix
528
529The Unix module is intended to provide a "best guess" failover result to the
530main Sys::Filesystem module if no suitable platform specific module can be
531found, and the platform is not 'MSWin32'.
532
533This module requires additional work to improve it's guestimation abilities.
534
535=head2 Darwin
536
537First written by Christian Renz <crenz@web42.com>.
538
539=head2 Win32
540
541Provides C<mount_point> and C<device> of mounted filesystems on Windows.
542
543=head2 AIX
544
545Please be aware that the AIX /etc/filesystems file has both a "type" and
546"vfs" field. The "type" field should not be confused with the filesystem
547format/type (that is stored in the "vfs" field). You may wish to use the
548"format" field when querying for filesystem types, since it is aliased to
549be more reliable accross different platforms.
550
551=head2 Other
552
553Linux, Solaris, Cygwin, FreeBSD, NetBSD, HP-UX.
554
555=head2 OS Identifiers
556
557The following list is taken from L<perlport>. Please refer to the original
558source for the most up to date version. This information should help anyone
559who wishes to write a helper module for a new platform. Modules should have
560the same name as ^O in title caps. Thus 'openbsd' becomes 'Openbsd.pm'.
561
562=head1 REQUIREMENTS
563
564Sys::Filesystem requires Perl >= 5.6 to run.
565
566=head1 TODO
567
568Add support for Tru64, MidnightBSD, Haiku, Minix, DragonflyBSD and OpenBSD.
569Please contact me if you would like to provide code for these operating
570systems.
571
572=head1 SUPPORT
573
574You can find documentation for this module with the perldoc command.
575
576    perldoc Sys::Filesystem
577
578You can also look for information at:
579
580=over 4
581
582=item * RT: CPAN's request tracker
583
584L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Sys-Filesystem>
585
586=item * AnnoCPAN: Annotated CPAN documentation
587
588L<http://annocpan.org/dist/Sys-Filesystem>
589
590=item * CPAN Ratings
591
592L<http://cpanratings.perl.org/s/Sys-Filesystem>
593
594=item * Search CPAN
595
596L<http://search.cpan.org/dist/Sys-Filesystem/>
597
598=back
599
600=head1 SEE ALSO
601
602L<perlport>, L<Solaris::DeviceTree>, L<Win32::DriveInfo>
603
604=head1 VERSION
605
606$Id: Filesystem.pm 185 2010-07-15 19:25:30Z trevor $
607
608=head1 AUTHOR
609
610Nicola Worthington <nicolaw@cpan.org> - L<http://perlgirl.org.uk>
611
612Jens Rehsack <rehsack@cpan.org> - L<http://www.rehsack.de/>
613
614=head1 ACKNOWLEDGEMENTS
615
616See CREDITS in the distribution tarball.
617
618=head1 COPYRIGHT
619
620Copyright 2004,2005,2006 Nicola Worthington.
621
622Copyright 2008-2010 Jens Rehsack.
623
624This software is licensed under The Apache Software License, Version 2.0.
625
626L<http://www.apache.org/licenses/LICENSE-2.0>
627
628=cut
629
630