No-Worries

 view release on metacpan or  search on metacpan

lib/No/Worries/Stat.pm  view on Meta::CPAN

    my($option, $message) = @_;
    my($mode, $action, $number);

    $mode = $option->{mode};
    return unless defined($mode);
    if ($mode =~ /^([\+\-])?(\d+)$/) {
        $action = $1 || "";
        $number = substr($2, 0, 1) eq "0" ? oct($2) : ($2+0);
        # use the canonical form for the message
        $mode = sprintf("%s%05o", $action, $number);
        if ($action eq "+") {
            # check that at least these bits are set
            $option->{mode_set} = $number;
            $option->{mode_clear} = 0;
        } elsif ($action eq "-") {
            # check that at least these bits are cleared
            $option->{mode_set} = 0;
            $option->{mode_clear} = $number;
        } else {
            # check that these bits are exactly the ones set
            $option->{mode_set} = $number;
            $option->{mode_clear} = _IMODE;
        }
    } else {
        dief("invalid mode: %s", $mode);
    }
    $message->{mode} = "mode($mode)";
}

#
# check the mtime option and set message accordingly
#

sub _check_mtime ($$) {
    my($option, $message) = @_;
    my($mtime);

    $mtime = $option->{mtime};
    return unless defined($mtime);
    $message->{mtime} = "mtime($mtime)";
}

#
# ensure proper ownership
#

sub _ensure_owner ($$$$) {
    my($path, $stat, $option, $message) = @_;
    my(@todo);

    @todo = ();
    if ($message->{user} and $stat->[ST_UID] != $option->{uid}) {
        $stat->[ST_UID] = $option->{uid};
        push(@todo, $message->{user});
    }
    if ($message->{group} and $stat->[ST_GID] != $option->{gid}) {
        $stat->[ST_GID] = $option->{gid};
        push(@todo, $message->{group});
    }
    return(0) unless @todo and $option->{callback}->($path, "@todo");
    chown($stat->[ST_UID], $stat->[ST_GID], $path)
        or dief("cannot chown(%d, %d, %s): %s",
                $stat->[ST_UID], $stat->[ST_GID], $path, $!);
    return(1)
}

#
# ensure proper permissions
#

sub _ensure_mode ($$$$) {
    my($path, $stat, $option, $message) = @_;
    my($mode);

    $mode = $stat->[ST_MODE] & _IMODE;
    $mode &= ~$option->{mode_clear};
    $mode |=  $option->{mode_set};
    return(0) if ($stat->[ST_MODE] & _IMODE) == $mode;
    return(0) unless $option->{callback}->($path, $message->{mode});
    chmod($mode, $path)
        or dief("cannot chmod(%05o, %s): %s", $mode, $path, $!);
    return(1)
}

#
# ensure proper modification time
#

sub _ensure_mtime ($$$$) {
    my($path, $stat, $option, $message) = @_;

    return(0) if $stat->[ST_MTIME] == $option->{mtime};
    return(0) unless $option->{callback}->($path, $message->{mtime});
    utime($stat->[ST_ATIME], $option->{mtime}, $path)
        or dief("cannot utime(%d, %d, %s): %s",
                $stat->[ST_ATIME], $option->{mtime}, $path, $!);
    return(1);
}

#
# make sure the the file status is what is expected
#

my %stat_ensure_options = (
    user     => { optional => 1, type => SCALAR, regex => qr/^[\w\-]+$/ },
    group    => { optional => 1, type => SCALAR, regex => qr/^[\w\-]+$/ },
    mode     => { optional => 1, type => SCALAR, regex => qr/^[\+\-]?\d+$/ },
    mtime    => { optional => 1, type => SCALAR, regex => qr/^\d+$/ },
    follow   => { optional => 1, type => BOOLEAN },
    callback => { optional => 1, type => CODEREF },
);

sub stat_ensure ($@) {
    my($path, %option, %message, @stat, $changed);

    $path = shift(@_);
    %option = validate(@_, \%stat_ensure_options) if @_;
    _check_user(\%option, \%message);
    _check_group(\%option, \%message);
    _check_mode(\%option, \%message);
    _check_mtime(\%option, \%message);
    $option{callback} ||= sub { return(1) };

lib/No/Worries/Stat.pm  view on Meta::CPAN


=item C<ST_UID>

user ID of owner

=item C<ST_GID>

group ID of owner

=item C<ST_RDEV>

device ID (if special file)

=item C<ST_SIZE>

total size, in bytes

=item C<ST_ATIME>

time of last access

=item C<ST_MTIME>

time of last modification

=item C<ST_CTIME>

time of last status change

=item C<ST_BLKSIZE>

blocksize for filesystem I/O

=item C<ST_BLOCKS>

number of 512B blocks allocated

=back

In addition, it also optionally exports all the ":mode" constants from L<Fcntl>.

This way, all the stat() related constants can be imported in a uniform way.

=head1 FUNCTIONS

This module provides the following functions (none of them being
exported by default):

=over

=item stat_type(MODE)

given the file mode (C<ST_MODE> field), return the file type as a string;
possible return values are: "block device", "character device", "directory",
"door", "event port", "network file", "pipe", "plain file", "socket",
"symlink", "unknown" and "whiteout".

=item stat_ensure(PATH[, OPTIONS])

make sure the given path has the expected file "status" (w.r.t. stat()) and
call chown(), chmod() or utime() if needed, returning the number of changes
performed; supported options:

=over

=item * C<user>: expected user name or uid

=item * C<group>: expected group name or gid

=item * C<mode>: expected mode specification (see below)

=item * C<mtime>: expected modification time

=item * C<follow>: follow symbolic links (default is to skip them)

=item * C<callback>: code to be executed before changing something (see below)

=back

=back

The C<mode> option of stat_ensure() can be given:

=over

=item I<NUMBER>

an absolute value like 0755, meaning that mode must be equal to it

=item +I<NUMBER>

a list of bits that must be set, e.g. "+0111" for "executable for all"

=item -I<NUMBER>

a list of bits that must be clear, e.g. "-022" for not writable by group or
other

=back

Note: the number after "+" or "-" will be interpreted as being octal only if
it starts with "0". You should therefore use "+0111" or "+".oct(111) to
enable the executable bits but not "+111" which is the same as "+0157".

The C<callback> option of stat_ensure() will receive the given path and a
string describing what is about to be changed. It must return true to tell
stat_ensure() to indeed perform the changes.

Here is for insatnce how a "noaction" option could be implemented:

  sub noaction ($$) {
      my($path, $change) = @_;
  
      printf("did not change %s of %s\n", $change, $path);
      return(0);
  }
  foreach my $path (@paths) {
      stat_ensure($path, user => "root", mode => 0755, callback => \&noaction);
  }

=head1 SEE ALSO



( run in 1.232 second using v1.01-cache-2.11-cpan-92ad3014f07 )