File-Defrag
view release on metacpan or search on metacpan
stat_ok or $s_skipped++, return; # skip if file looks fishy
my $max_extents = int +($size + CHUNK - 1) / CHUNK;
my $extents = file_extents $src_fh, MAX_GAP
or $s_skipped++, return; # maybe file with holes, or not yet flushed to disk
$extents > $max_extents or $s_linear++, return; # cool, "it's unfragmented"
!$opt_inuse and file_inuse $path
and $s_skipped++, return; # skip files that are currently open
print "$dir/$file: ";
printf "$extents ";
my $dst = "$dir/.defrag.$$";
#my $dst = "/.defrag.$$";
$cleanup{$dst} = $dst;
my $dst_fh = direct_open $dst, O_RDWR | O_CREAT | O_EXCL, 0600
or die "$dst: unable to create new copy\n";
my $index = 0;
my $chunksize = $size < 1<<20 ? 1<<20 : CHUNK;
while (direct_copy $src_fh, $dst_fh, $chunksize, $index) {
$index++;
if ((stat $src_fh)[9] != $mtime) {
print "file was modified, skipping.\n";
$s_skipped++, goto bailout;
}
}
my $after_extents = file_extents $dst_fh, MAX_GAP
or $s_skipped++, goto bailout;
print "=> $after_extents ";
if ($after_extents >= $extents) {
print "couldn't achieve fewer extents, skipping.\n";
$s_fragmented++, goto bailout;
} elsif ($after_extents > 1) {
$s_improved++;
} else {
$s_perfect++;
}
truncate $dst, $size
or die "$dst: unable to truncate copy to correct size\n";
(stat $dst_fh)[7] == $size
or die "$dst: file size mismatch\n";
close $dst_fh
or $s_skipped++, goto bailout;
chown $uid, $gid, $dst
or $s_skipped++, goto bailout;
chmod $mode, $dst
or $s_skipped++, goto bailout;
utime $atime, $mtime, $dst
or $s_skipped++, goto bailout;
my $token = $opt_nofreeze || Sys::FreezeThaw::freeze;
# the next is very slow, unfortunately
!$opt_inuse and file_inuse $path
and die "file is in use by some process\n";
lstat $path
or die "$path: unable to stat\n";
(stat _)[ 0] == $device or die "device differs (WTF?)\n";
(stat _)[ 1] == $inode or die "inode differs\n";
(stat _)[ 4] == $uid or die "uid differs\n";
(stat _)[ 5] == $gid or die "gid differs\n";
(stat _)[ 7] == $size or die "size differs\n";
(stat _)[ 9] == $mtime or die "modification time differs\n";
(stat _)[10] == $ctime or die "change time differs\n";
rename $dst, $path
or die "rename over destination: $!";
$opt_nofreeze or Sys::FreezeThaw::thaw $token;
print "done.\n";
delete $cleanup{$dst};
return;
bailout:
unlink delete $cleanup{$dst};
}
sub defrag_files {
my @files = @_;
while (@files) {
my $path = shift @files;
lstat $path;
if (-d _) {
# traverse directories, depth first
opendir my $dir, $path
or next;
unshift @files, map "$path/$_",
sort
grep $_ ne "." && $_ ne "..",
readdir $dir;
} elsif (-f _) {
stat_ok or next;
defrag_file $path;
( run in 0.863 second using v1.01-cache-2.11-cpan-71847e10f99 )