package KinoSearch::Store::FSInvIndex; use strict; use warnings; use KinoSearch::Util::ToolSet; use base qw( KinoSearch::Store::InvIndex ); use File::Spec::Functions qw( canonpath catfile catdir tmpdir no_upwards ); use Digest::MD5 qw( md5_hex ); use KinoSearch::Store::InStream; use KinoSearch::Store::OutStream; use vars qw( $LOCK_DIR ); # used by FSLock use KinoSearch::Store::FSLock; use KinoSearch::Index::IndexFileNames; BEGIN { # confirm or create a directory to put lockfiles in $LOCK_DIR = catdir( tmpdir, 'kinosearch_lockdir' ); if ( !-d $LOCK_DIR ) { mkdir $LOCK_DIR or die "couldn't mkdir '$LOCK_DIR': $!"; chmod 0777, $LOCK_DIR; } } our %instance_vars = __PACKAGE__->init_instance_vars(); sub init_instance { my $self = shift; # clean up path. my $path = $self->{path} = canonpath( $self->{path} ); if ( $self->{create} ) { # clear out lockfiles related to this path my $lock_prefix = $self->get_lock_prefix; opendir LOCKDIR, $LOCK_DIR, or confess("couldn't opendir '$LOCK_DIR': $!"); my @lockfiles = grep {/$lock_prefix/} readdir LOCKDIR; closedir LOCKDIR; for (@lockfiles) { $_ = catfile( $LOCK_DIR, $_ ); unlink $_ or confess("couldn't unlink '$_': $!"); } # blast any existing index files if ( -e $path ) { opendir INVINDEX_DIR, $path or confess("couldn't opendir '$path': $!"); my @to_remove = grep { /^\w+\.(?:cfs|del)$/ or $_ eq 'segments' or $_ eq 'deletable' } readdir INVINDEX_DIR; for my $removable (@to_remove) { $removable = catfile( $path, $removable ); unlink $removable or confess "Couldn't unlink file '$removable': $!"; } } if ( !-d $path ) { mkdir $path or confess("Couldn't mkdir '$path': $!"); } } # by now, we should have a directory, so throw an error if we don't if ( !-d $path ) { confess("Can't open invindex location '$path': $! ") unless -e $path; confess("invindex location '$path' isn't a directory"); } } sub open_outstream { my ( $self, $filename ) = @_; my $filepath = catfile( $self->{path}, $filename ); open( my $fh, "+>:unix", $filepath ) # clobbers or confess("Couldn't open file '$filepath': $!"); binmode($fh); return KinoSearch::Store::OutStream->new($fh); } sub open_instream { my ( $self, $filename, $offset, $len ) = @_; my $filepath = catfile( $self->{path}, $filename ); # must be unbuffered, or PerlIO messes up with the shared handles open( my $fh, "<:unix", $filepath ) or confess("Couldn't open file '$filepath': $!"); binmode($fh); return KinoSearch::Store::InStream->new( $fh, $offset, $len ); } sub list { my $self = shift; opendir( my $dir, $self->{path} ) or confess("Couldn't opendir '$self->{path}'"); return no_upwards( readdir $dir ); } sub file_exists { my ( $self, $filename ) = @_; return -e catfile( $self->{path}, $filename ); } sub rename_file { my ( $self, $from, $to ) = @_; $_ = catfile( $self->{path}, $_ ) for ( $from, $to ); rename( $from, $to ) or confess("couldn't rename file '$from' to '$to': $!"); } sub delete_file { my ( $self, $filename ) = @_; $filename = catfile( $self->{path}, $filename ); unlink $filename or confess("couldn't unlink file '$filename': $!"); } sub slurp_file { my ( $self, $filename ) = @_; my $filepath = catfile( $self->{path}, $filename ); open( my $fh, "<", $filepath ) or confess("Couldn't open file '$filepath': $!"); binmode($fh); local $/; return <$fh>; } sub make_lock { my $self = shift; return KinoSearch::Store::FSLock->new( @_, invindex => $self ); } # Create a hashed string derived from this invindex's path. sub get_lock_prefix { my $self = shift; return "kinosearch-" . md5_hex( canonpath( $self->{path} ) ); } sub close { } 1; __END__ =head1 NAME KinoSearch::Store::FSInvIndex - file system InvIndex =head1 SYNOPSIS my $invindex = KinoSearch::Store::FSInvIndex->new( path => '/path/to/invindex', create => 1, ); =head1 DESCRIPTION Implementation of KinoSearch::Store::InvIndex using a single file system directory and multiple files. =head1 CONSTRUCTOR =head2 new C takes two parameters: =over =item B - the location of the invindex. =item B - if set to 1, create a fresh invindex, clobbering an existing one if necessary. Default value is 0, indicating that an existing invindex should be opened. =back =head1 COPYRIGHT Copyright 2005-2006 Marvin Humphrey =head1 LICENSE, DISCLAIMER, BUGS, etc. See L version 0.08. =cut