File-Information

 view release on metacpan or  search on metacpan

lib/File/Information/Tagpool.pm  view on Meta::CPAN

# Copyright (c) 2024-2025 Philipp Schafft <lion@cpan.org>

# licensed under Artistic License 2.0 (see LICENSE file)

# ABSTRACT: generic module for extracting information from filesystems


package File::Information::Tagpool;

use v5.10;
use strict;
use warnings;

use parent 'File::Information::Base';

use Carp;
use File::Spec;
use Sys::Hostname ();
use Scalar::Util qw(weaken);

use File::Information::Lock;

our $VERSION = v0.16;

my $HAVE_FILE_VALUEFILE = eval {require File::ValueFile::Simple::Reader; require File::ValueFile::Simple::Writer; 1;};

my %_properties = (
    tagpool_pool_path => {loader => \&_load_tagpool, rawtype => 'filename'},
);

if ($HAVE_FILE_VALUEFILE) {
    $_properties{tagpool_pool_uuid} = {loader => \&_load_tagpool, rawtype => 'uuid'};
}

sub _new {
    my ($pkg, %opts) = @_;
    my $self = $pkg->SUPER::_new(%opts, properties => \%_properties);

    croak 'No path is given' unless defined $self->{path};

    return $self;
}


#@returns File::Information::Lock
sub lock {
    my ($self) = @_;
    my $locks = $self->{locks} //= {};

    unless (scalar keys %{$locks}) {
        my $lockfile = $self->_catfile('lock');
        my $lockname = $self->_tempfile('lock');
        open(my $out, '>', $lockname) or die $!;
        print $out ".\n";
        close($out);

        for (my $i = 0; $i < 3; $i++) {
            if (link($lockname, $lockfile)) {
                # Success.
                $self->{lockfile} = $lockfile;
                $self->{lockname} = $lockname;
                {
                    my $lock = File::Information::Lock->new(parent => $self, on_unlock => \&_unlock);
                    $locks->{$lock} = $lock;
                    weaken($locks->{$lock}); # it holds a reference to us, so our's will be weak.
                    return $lock;
                }
            }
            sleep(1);
        }

        unlink($lockname);
        croak 'Can not lock pool';
    }

    {
        my $lock = File::Information::Lock->new(parent => $self, on_unlock => \&_unlock);
        $locks->{$lock} = $lock;
        weaken($locks->{$lock}); # it holds a reference to us, so our's will be weak.
        return $lock;
    }
}


sub locked {
    my ($self, $func) = @_;
    my $lock = $self->lock;
    return $func->();
}


sub load_sysfile_cache {
    my ($self) = @_;
    my $locks = $self->{locks} //= {};

    unless (scalar keys %{$locks}) {
        croak 'The pool must be locked to read the sysfile cache';
    }

    unless (defined $self->{sysfile_cache}) {
        my $local_cache = $self->instance->_tagpool_sysfile_cache->{$self->{path}} //= {};
        my $data_path = $self->_catdir('data');
        my %cache;

        opendir(my $dir, $data_path) or croak $!;

        while (my $entry = readdir($dir)) {
            my @c_stat;

            $entry =~ /^file\./ or next; # skip everything that is not a file.* to begin with.

            @c_stat = stat($self->_catfile('data', $entry));
            next unless scalar @c_stat;

            $cache{$c_stat[1].'@'.$c_stat[0]} = $entry;
        }

        %{$local_cache} = (%cache, complete => 1);

        return $self->{sysfile_cache} = \%cache;
    }

    return $self->{sysfile_cache};
}


sub file_add {
    my ($self, $files, %opts) = @_;
    my $instance = $self->instance;
    my $local_cache = $instance->_tagpool_sysfile_cache->{$self->{path}} //= {};
    my $lock;
    my $sysfile_cache;
    my %to_add;

    # First setup %to_add:
    $files = [$files] unless ref($files) eq 'ARRAY';
    foreach my $file (@{$files}) {
        my $link;
        my $inode;



( run in 1.083 second using v1.01-cache-2.11-cpan-39bf76dae61 )