[mca1001] / bin / du-priv.pl  

mca1001: bin/du-priv.pl

File: [mca1001] / bin / du-priv.pl (download) (as text)
Revision: 1.2, Sun Oct 1 07:44:47 2006 UTC (3 years, 11 months ago) by mca1001
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +2 -2 lines
I did indeed mean 5.008

#! /usr/bin/perl -T

use strict;
use warnings;

$ENV{PATH} = "/bin:/usr/bin";
my @du = ("du");

use 5.008; # or thereabouts, maybe, for open(fh, "-|", @cmd)


=head1 NAME

du-priv.pl - trim du output for privacy

=head1 SYNOPSIS

 du -x /home | du-priv.pl | xdiskusage

 ssh place 'du -x /home | du-priv.pl' | xdiskusage

 du-priv.pl -- -x /home | xdiskusage

=head1 DESCRIPTION

I tend to run (the equivalent of)

 ssh root@fullbox du -x /foofs | xdiskusage

when the filesystem fills.  This is effective but I prefer not to use
the power to peek inside private directories.

This script solves the problem by omitting lines from the C<du> output
when the directory is not publicly readable.  The totals will still
add up, but any subdirectory structure is hidden.

Run with no args to operate in filter mode.  If args are given after
C<-->, du will be run with them; the space before the separator is
reserved for bells and whistles.

=head2 Assumptions

=over 4

=item - un*x filenames

=item - access to the same filesystem as the du

=item - relative filenames start in the same place

The first element of a relative filename is "visible".  This is
convenient for "/", "./" and the non-file "total", but makes it
unsuitable for accepting attacker-chosen input.

=back

=head1 AUTHOR

Copyright (C) 2006 Matthew Astley, all rights reserved.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

[I cheerfully assume you already have a copy of the GPL]

=cut

# $Id: du-priv.pl,v 1.2 2006/10/01 07:44:47 mca1001 Exp $


my ($stream, $piped);

# Deal with ARGV.  Anything before "--" is not yet supported.
if (0 == @ARGV) {
    $stream = \*STDIN;
} elsif ($ARGV[0] eq "--") {
    shift;
    open $stream, "-|", @du, @ARGV
      or die "$0: Failed to pipe from '@du @ARGV': $!";
    $piped = 1;
} else {
    die "Syntax: $0 [-- <du arguments>]\n";
}

filter($stream);


# Tidy up the stream, pick a suitable exit code
my $exit = close $stream ? 0 : 1;
if ($piped) {
    my $sig = $? & 0x7f;
    warn "$0: du caught signal $sig\n" if $sig;
    $exit = $sig ? 2 : $? >> 8;
}

exit $exit;


{
    my %visible; # cache: key = directory name, value = true if OK to see inside
    sub visible {
	my ($path, $recursed) = @_;
	xcimate(\%visible, 3) if !$recursed && keys %visible > 10000;
	return $visible{$path} ||= _visible($path);
    }
}


sub _visible {
    my @path = split "/", (shift);

    return 1 if @path < 2; # Always see the top level

    # Consider the parent
    pop @path;
    my $parent = join "/", @path;
    $parent = "/" if @path < 2 && $path[0] eq '';
    return visible($parent, 1) && public($parent);
}


# A generalisation of decimating a hash.
#
# Deletes the first and subsequent ($mod)th items, in the [untested]
# hope that this will adequately approximate removing a random
# selection.
#
# Doing this during an ||= element assignment will segfault in Perl
# v5.8.4 , see ../doodles/perlbug.delete-during.pl
sub xcimate {
    my ($hashref, $mod) = @_;
    my $num = 0;
    my $k;
    while (defined ($k = each %$hashref)) {
	delete $$hashref{$k} if 0 == $num % $mod;
	$num ++;
    }
}


sub public {
    my $path = shift;
    die "$0: '$path' not found (running on the wrong box?)"
      unless -e $path;
    my @S = stat $path;
    return 05 == ($S[2] & 05);	# symbolically, require o+rx
}


sub filter {
    my $fh = shift;
    while (<$fh>) {
	die "Didn't understand du output '$_'"
	  unless /^\d+\S*\s+(\S.*)$/;
	print if visible($1);
    }
}

Repository owner

Powered by ViewCVS 1.0-dev
(Powered by ViewCVS)

ViewCVS and CVS Help