summaryrefslogtreecommitdiffstats
path: root/perl-install/resize_fat
diff options
context:
space:
mode:
Diffstat (limited to 'perl-install/resize_fat')
-rw-r--r--perl-install/resize_fat/Makefile10
-rw-r--r--perl-install/resize_fat/Makefile.PL13
-rw-r--r--perl-install/resize_fat/README8
-rw-r--r--perl-install/resize_fat/any.pm130
-rw-r--r--perl-install/resize_fat/boot_sector.pm110
-rw-r--r--perl-install/resize_fat/c_rewritten.pm14
-rw-r--r--perl-install/resize_fat/c_rewritten.xs265
-rw-r--r--perl-install/resize_fat/dir_entry.pm77
-rw-r--r--perl-install/resize_fat/directory.pm79
-rw-r--r--perl-install/resize_fat/fat.pm135
-rw-r--r--perl-install/resize_fat/info_sector.pm37
-rw-r--r--perl-install/resize_fat/io.pm58
-rw-r--r--perl-install/resize_fat/main.pm166
13 files changed, 1102 insertions, 0 deletions
diff --git a/perl-install/resize_fat/Makefile b/perl-install/resize_fat/Makefile
new file mode 100644
index 000000000..9078b361a
--- /dev/null
+++ b/perl-install/resize_fat/Makefile
@@ -0,0 +1,10 @@
+.PHONY: clean
+
+c_rewritten: %: %.xs
+ test -e Makefile_c || perl Makefile.PL
+ $(MAKE) -f Makefile_c LD_RUN_PATH= || $(MAKE) -f Makefile_c LD_RUN_PATH=
+ rm -f ../auto/resize_fat ; ln -s ../resize_fat/blib/arch/auto ../auto/resize_fat
+
+clean:
+ test ! -e Makefile_c || $(MAKE) -f Makefile_c clean
+ rm -f *~ *.o
diff --git a/perl-install/resize_fat/Makefile.PL b/perl-install/resize_fat/Makefile.PL
new file mode 100644
index 000000000..712f4e395
--- /dev/null
+++ b/perl-install/resize_fat/Makefile.PL
@@ -0,0 +1,13 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+
+WriteMakefile(
+ 'MAKEFILE' => 'Makefile_c',
+ 'NAME' => 'c_rewritten',
+ 'OPTIMIZE' => '-Os',
+ 'VERSION_FROM' => 'c_rewritten.pm', # finds $VERSION
+ 'LIBS' => '', # e.g., '-lm'
+ 'DEFINE' => '', # e.g., '-DHAVE_SOMETHING'
+ 'INC' => '', # e.g., '-I/usr/include/other'
+);
diff --git a/perl-install/resize_fat/README b/perl-install/resize_fat/README
new file mode 100644
index 000000000..12b64dea9
--- /dev/null
+++ b/perl-install/resize_fat/README
@@ -0,0 +1,8 @@
+TODO:
+
+resize_fat::fat::update($fs) should be called before doing undoable things
+(before the sync in construct_dir_tree)
+
+BUGS:
+no known bugs :)
+if you found one, please mail pixel@mandrakesoft.com !!
diff --git a/perl-install/resize_fat/any.pm b/perl-install/resize_fat/any.pm
new file mode 100644
index 000000000..59f4addd7
--- /dev/null
+++ b/perl-install/resize_fat/any.pm
@@ -0,0 +1,130 @@
+package resize_fat::any; # $Id: any.pm 267555 2010-04-15 12:45:18Z pterjan $
+
+use diagnostics;
+use strict;
+use vars qw($FREE $FILE $DIRECTORY $UNMOVEABLE);
+
+use common;
+use resize_fat::fat;
+use resize_fat::directory;
+use resize_fat::dir_entry;
+use resize_fat::c_rewritten;
+
+
+$FREE = 0;
+$FILE = 1;
+$DIRECTORY = 2;
+$UNMOVEABLE = 8;
+
+
+1;
+
+
+#- returns the number of clusters for a given filesystem type
+sub min_cluster_count($) {
+ my ($fs) = @_;
+ (1 << ${{ FAT16 => 12, FAT32 => 12 }}{$fs->{fs_type}}) - 12;
+}
+sub max_cluster_count($) {
+ my ($fs) = @_;
+ (1 << ${{ FAT16 => 16, FAT32 => 28 }}{$fs->{fs_type}}) - 11;
+}
+
+
+
+#- patch to get the function last_used that return the last used cluster of a fs.
+sub last_used($) {
+ my ($fs) = @_;
+
+ #- count in negative so absolute value count back to 2.
+ foreach (-($fs->{nb_clusters}+1)..-2) { return -$_ if resize_fat::c_rewritten::flag(-$_) }
+ die "any: empty FAT table of $fs->{nb_clusters} clusters";
+}
+#- patch to get the function last_unmoveable that return the last unmoveable cluster of a fs.
+sub last_unmoveable($) {
+ my ($fs) = @_;
+
+ #- count in negative so absolute value count back to 2.
+ foreach (-($fs->{nb_clusters}+1)..-2) { return -$_ if 0x8 & resize_fat::c_rewritten::flag(-$_) }
+
+ #- Oh at this point there are no unmoveable blocks!
+ 2;
+}
+
+#- calculates the minimum size of a partition, in physical sectors
+sub min_size($) {
+ my ($fs) = @_;
+ my $count = $fs->{clusters}{count};
+
+ #- directories are both in `used' and `dirs', so are counted twice
+ #- It's done on purpose since we're moving all directories. So at the worse
+ #- moment, 2 directories are there, but that way nothing wrong can happen :)
+ my $min_cluster_count = max(2 + $count->{used} + $count->{bad} + $count->{dirs}, min_cluster_count($fs));
+ $min_cluster_count = max($min_cluster_count, last_unmoveable($fs));
+
+ my $size = $min_cluster_count * divide($fs->{cluster_size}, $SECTORSIZE) +
+ divide($fs->{cluster_offset}, $SECTORSIZE) +
+ 64*1024*1024 / $SECTORSIZE; #- help with such more sectors (ie 64Mb).
+
+ #- help zindozs again with 512Mb+ at least else partition is ignored.
+ if ($resize_fat::isFAT32) {
+ $size = max($size, 524*1024*1024 / $SECTORSIZE);
+ }
+ $size;
+
+}
+#- calculates the maximum size of a partition, in physical sectors
+sub max_size($) {
+ my ($fs) = @_;
+
+ my $max_cluster_count = min($fs->{nb_fat_entries} - 2, max_cluster_count($fs));
+
+ $max_cluster_count * divide($fs->{cluster_size}, $SECTORSIZE) +
+ divide($fs->{cluster_offset}, $SECTORSIZE);
+}
+#- calculates used size in order to avoid modifying anything.
+sub used_size($) {
+ my ($fs) = @_;
+
+ my $last_used;
+ my $used_cluster_count;
+
+ eval { $last_used = last_used($fs) };
+ if ($@) {
+ # Empty FAT
+ $last_used = 0;
+ }
+ $used_cluster_count = max($last_used, min_cluster_count($fs));
+
+ $used_cluster_count * divide($fs->{cluster_size}, $SECTORSIZE) +
+ divide($fs->{cluster_offset}, $SECTORSIZE);
+}
+
+#- fills in fat_flag_map in c_rewritten.
+#- Each FAT entry is flagged as either FREE, FILE or DIRECTORY.
+sub flag_clusters {
+ my ($fs) = @_;
+ my ($cluster, $curr_dir_name, $entry, $type, $nb_dirs);
+
+ my $f = sub {
+ ($curr_dir_name, $entry) = @_;
+ $cluster = resize_fat::dir_entry::get_cluster($entry);
+
+ if (resize_fat::dir_entry::is_file($entry)) {
+ $type = $FILE;
+ $type |= $UNMOVEABLE if resize_fat::dir_entry::is_unmoveable($entry);
+ } elsif (resize_fat::dir_entry::is_directory($entry)) {
+ $type = $DIRECTORY;
+ } else { return }
+
+ my $nb = resize_fat::c_rewritten::checkFat($cluster, $type, "$curr_dir_name/$entry->{name}");
+ $nb_dirs += $nb if $type == $DIRECTORY;
+ 0;
+ };
+
+ #- this must call allocate_fat_flag that zeroes the buffer allocated.
+ resize_fat::c_rewritten::allocate_fat_flag($fs->{nb_clusters} + 2);
+
+ resize_fat::directory::traverse_all($fs, $f);
+ $fs->{clusters}{count}{dirs} = $nb_dirs;
+}
diff --git a/perl-install/resize_fat/boot_sector.pm b/perl-install/resize_fat/boot_sector.pm
new file mode 100644
index 000000000..8496166a4
--- /dev/null
+++ b/perl-install/resize_fat/boot_sector.pm
@@ -0,0 +1,110 @@
+package resize_fat::boot_sector; # $Id: boot_sector.pm 210307 2004-12-13 15:56:00Z tvignaud $
+
+use diagnostics;
+use strict;
+
+use common;
+use resize_fat::io;
+use resize_fat::any;
+use resize_fat::directory;
+
+
+#- Oops, this will be unresizable on big-endian machine. trapped by signature.
+my $format = "a3 a8 S C S C S S C S S S I I I S S I S S a458 S";
+my @fields = (
+ 'boot_jump', #- boot strap short or near jump
+ 'system_id', #- Name - can be used to special case partition manager volumes
+ 'sector_size', #- bytes per logical sector
+ 'cluster_size_in_sectors', #- sectors/cluster
+ 'nb_reserved', #- reserved sectors
+ 'nb_fats', #- number of FATs
+ 'nb_root_dir_entries', #- number of root directory entries
+ 'small_nb_sectors', #- number of sectors: big_nb_sectors supersedes
+ 'media', #- media code
+ 'fat16_fat_length', #- sectors/FAT for FAT12/16
+ 'sectors_per_track',
+ 'nb_heads',
+ 'nb_hidden', #- (unused)
+ 'big_nb_sectors', #- number of sectors (if small_nb_sectors == 0)
+
+#- FAT32-only entries
+ 'fat32_fat_length', #- size of FAT in sectors
+ 'fat32_flags', #- bit8: fat mirroring,
+ #- low4: active fat
+ 'fat32_version', #- minor * 256 + major
+ 'fat32_root_dir_cluster',
+ 'info_offset_in_sectors',
+ 'fat32_backup_sector',
+
+#- Common again...
+ 'boot_code', #- Boot code (or message)
+ 'boot_sign', #- 0xAA55
+);
+
+1;
+
+
+#- trimfs_init_boot_sector() - reads in the boot sector - gets important info out
+#- of boot sector, and puts in main structure - performs sanity checks - returns 1
+#- on success, 0 on failureparameters: filesystem an empty structure to fill.
+sub read($) {
+ my ($fs) = @_;
+
+ my $boot = eval { resize_fat::io::read($fs, 0, $SECTORSIZE) }; $@ and die "reading boot sector failed on device $fs->{fs_name}";
+ @$fs{@fields} = unpack $format, $boot;
+
+ $fs->{nb_sectors} = $fs->{small_nb_sectors} || $fs->{big_nb_sectors};
+ $fs->{cluster_size} = $fs->{cluster_size_in_sectors} * $fs->{sector_size};
+
+ $fs->{boot_sign} == 0xAA55 or die "Invalid signature for a MS-based filesystem.\n";
+ $fs->{nb_sectors} < 32 and die "Too few sectors for viable filesystem\n";
+ $fs->{nb_fats} == 2 or cdie "Weird number of FATs: $fs->{nb_fats}, not 2.\n";
+ $fs->{sector_size} == 512 or cdie "Strange sector_size != 512\n";
+
+ if ($fs->{fat16_fat_length}) {
+ #- asserting FAT16, will be verified later on
+ $resize_fat::isFAT32 = 0;
+ $fs->{fs_type} = 'FAT16';
+ $fs->{fs_type_size} = 16;
+ $fs->{fat_length} = $fs->{fat16_fat_length};
+ $resize_fat::bad_cluster_value = 0xfff7; #- 2**16 - 1
+ } else {
+ $resize_fat::isFAT32 = 1;
+ $fs->{fs_type} = 'FAT32';
+ $fs->{fs_type_size} = 32;
+ $fs->{fat_length} = $fs->{fat32_fat_length};
+
+ $fs->{nb_root_dir_entries} = 0;
+ $fs->{info_offset} = $fs->{info_offset_in_sectors} * $fs->{sector_size};
+ $resize_fat::bad_cluster_value = 0x0ffffff7;
+ }
+
+ $fs->{fat_offset} = $fs->{nb_reserved} * $fs->{sector_size};
+ $fs->{fat_size} = $fs->{fat_length} * $fs->{sector_size};
+ $fs->{root_dir_offset} = $fs->{fat_offset} + $fs->{fat_size} * $fs->{nb_fats};
+ $fs->{root_dir_size} = $fs->{nb_root_dir_entries} * resize_fat::directory::entry_size();
+ $fs->{cluster_offset} = $fs->{root_dir_offset} + $fs->{root_dir_size} - 2 * $fs->{cluster_size};
+
+ $fs->{nb_fat_entries} = divide($fs->{fat_size}, $fs->{fs_type_size} / 8);
+
+ #- - 2 because clusters 0 & 1 does not exist
+ $fs->{nb_clusters} = divide($fs->{nb_sectors} * $fs->{sector_size} - $fs->{cluster_offset}, $fs->{cluster_size}) - 2;
+
+ $fs->{dir_entries_per_cluster} = divide($fs->{cluster_size}, psizeof($format));
+
+#- $fs->{nb_clusters} >= resize_fat::any::min_cluster_count($fs) or die "error: not enough sectors for a $fs->{fs_type}\n";
+ $fs->{nb_clusters} < resize_fat::any::max_cluster_count($fs) or die "error: too many sectors for a $fs->{fs_type}\n";
+}
+
+sub write($) {
+ my ($fs) = @_;
+ my $boot = pack($format, @$fs{@fields});
+
+ eval { resize_fat::io::write($fs, 0, $SECTORSIZE, $boot) }; $@ and die "writing the boot sector failed on device $fs->{fs_name}";
+
+ if ($resize_fat::isFAT32) {
+ #- write backup
+ eval { resize_fat::io::write($fs, $fs->{fat32_backup_sector} * $SECTORSIZE, $SECTORSIZE, $boot) };
+ $@ and die "writing the backup boot sector (#$fs->{fat32_backup_sector}) failed on device $fs->{fs_name}";
+ }
+}
diff --git a/perl-install/resize_fat/c_rewritten.pm b/perl-install/resize_fat/c_rewritten.pm
new file mode 100644
index 000000000..177d3fc9c
--- /dev/null
+++ b/perl-install/resize_fat/c_rewritten.pm
@@ -0,0 +1,14 @@
+package resize_fat::c_rewritten; # $Id: c_rewritten.pm 200790 2002-11-28 12:47:16Z prigaux $
+
+use strict;
+
+require DynaLoader;
+
+our @ISA = qw(DynaLoader Exporter);
+our $VERSION = '0.01';
+our @EXPORT_OK = qw(next set_next);
+
+resize_fat::c_rewritten->bootstrap($VERSION);
+
+1;
+
diff --git a/perl-install/resize_fat/c_rewritten.xs b/perl-install/resize_fat/c_rewritten.xs
new file mode 100644
index 000000000..2bca483c0
--- /dev/null
+++ b/perl-install/resize_fat/c_rewritten.xs
@@ -0,0 +1,265 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+/* set by scan_fat, used by next */
+short *fat = NULL;
+char *fat_flag_map = NULL;
+unsigned int *fat_remap = NULL;
+int fat_remap_size;
+int type_size, nb_clusters, bad_cluster_value;
+
+void free_all() {
+#define FREE(p) if (p) free(p), p = NULL;
+ FREE(fat);
+ FREE(fat_flag_map);
+ FREE(fat_remap);
+#undef FREE
+}
+
+unsigned int next(unsigned int cluster) {
+ short *p = fat + type_size * cluster;
+ if (!fat) {
+ free_all();
+ croak("fat::next: trying to use null pointer");
+ }
+ if (cluster >= nb_clusters + 2) {
+ free_all();
+ croak("fat::next: cluster %d outside filesystem", cluster);
+ }
+ return type_size == 1 ? *p : *((unsigned int *) p);
+}
+
+void set_next(unsigned int cluster, unsigned int val) {
+ short *p = fat + type_size * cluster;
+ if (!fat) {
+ free_all();
+ croak("fat::set_next: trying to use null pointer");
+ }
+ if (cluster >= nb_clusters + 2) {
+ free_all();
+ croak("fat::set_next: cluster %d outside filesystem", cluster);
+ }
+ if (type_size == 1)
+ *p = val;
+ else
+ *((unsigned int *) p) = val;
+}
+
+MODULE = resize_fat::c_rewritten PACKAGE = resize_fat::c_rewritten
+
+PROTOTYPES: DISABLE
+
+
+void
+read_fat(fd, offset, size, magic)
+ int fd
+ int offset
+ int size
+ unsigned char magic
+ PPCODE:
+{
+ fat = (short *) malloc(size);
+ if (!fat) {
+ free_all();
+ croak("read_fat: not enough memory");
+ }
+ if (lseek(fd, offset, SEEK_SET) != offset ||
+ read(fd, fat, size) != size) {
+ free_all();
+ croak("read_fat: reading FAT failed");
+ }
+ if (magic != *(unsigned char *) fat) {
+ free_all();
+ croak("read_fat: FAT has invalid signature");
+ }
+}
+
+void
+write_fat(fd, size)
+ int fd
+ int size
+ PPCODE:
+{
+ if (write(fd, fat, size) != size) {
+ free_all();
+ croak("write_fat: write failed");
+ }
+}
+
+void
+free_all()
+ PPCODE:
+ free_all();
+
+void
+scan_fat(nb_clusters_, type_size_)
+ int nb_clusters_
+ int type_size_
+ PPCODE:
+{
+ unsigned int v;
+ int free = 0, bad = 0, used = 0;
+ short *p;
+
+ type_size = type_size_; nb_clusters = nb_clusters_;
+ bad_cluster_value = type_size == 32 ? 0x0ffffff7 : 0xfff7;
+
+ if (type_size % 16) {
+ free_all();
+ croak("scan_fat: unable to handle FAT%d", type_size);
+ }
+ type_size /= 16;
+
+ for (p = fat + 2 * type_size; p < fat + type_size * (nb_clusters + 2); p += type_size) {
+ v = type_size == 1 ? *p : *((unsigned int *) p);
+
+ if (v == 0) free++;
+ else if (v == bad_cluster_value) bad++;
+ }
+ used = nb_clusters - free - bad;
+ EXTEND(SP, 3);
+ PUSHs(sv_2mortal(newSViv(free)));
+ PUSHs(sv_2mortal(newSViv(bad)));
+ PUSHs(sv_2mortal(newSViv(used)));
+}
+
+unsigned int
+next(unused, cluster)
+ void *unused
+ unsigned int cluster
+ CODE:
+ RETVAL = next(cluster);
+ OUTPUT:
+ RETVAL
+
+void
+set_next(unused, cluster, val)
+ void *unused
+ unsigned int cluster
+ unsigned int val
+ CODE:
+ set_next(cluster, val);
+
+void
+allocate_fat_flag(size)
+ int size
+ CODE:
+ fat_flag_map = calloc(size, 1);
+ if (!fat_flag_map) {
+ free_all();
+ croak("allocate_fat_flag: not enough memory");
+ }
+
+int
+checkFat(cluster, type, name)
+ unsigned int cluster
+ int type
+ char *name
+ CODE:
+ int nb = 0;
+
+ if (!fat_flag_map) {
+ free_all();
+ croak("Bad FAT: trying to use null pointer");
+ }
+ for (; cluster < bad_cluster_value; cluster = next(cluster)) {
+ if (cluster == 0) {
+ free_all();
+ croak("Bad FAT: unterminated chain for %s\n", name);
+ }
+ if (cluster >= nb_clusters + 2) {
+ free_all();
+ croak("Bad FAT: chain outside filesystem for %s\n", name);
+ }
+ if (fat_flag_map[cluster]) {
+ free_all();
+ croak("Bad FAT: cluster %d is cross-linked for %s\n", cluster, name);
+ }
+ fat_flag_map[cluster] = type;
+ nb++;
+ }
+ RETVAL = nb;
+ OUTPUT:
+ RETVAL
+
+unsigned int
+flag(cluster)
+ unsigned int cluster
+ CODE:
+ if (!fat_flag_map) {
+ free_all();
+ croak("Bad FAT: trying to use null pointer");
+ }
+ if (cluster >= nb_clusters + 2) {
+ free_all();
+ croak("Bad FAT: going outside filesystem");
+ }
+ RETVAL = fat_flag_map[cluster];
+ OUTPUT:
+ RETVAL
+
+void
+set_flag(cluster, flag)
+ unsigned int cluster
+ int flag
+ CODE:
+ if (!fat_flag_map) {
+ free_all();
+ croak("Bad FAT: trying to use null pointer");
+ }
+ if (cluster >= nb_clusters + 2) {
+ free_all();
+ croak("Bad FAT: going outside filesystem");
+ }
+ fat_flag_map[cluster] = flag;
+
+void
+allocate_fat_remap(size)
+ int size
+ CODE:
+ fat_remap_size = size;
+ fat_remap = (unsigned int *) calloc(size, sizeof(unsigned int *));
+ if (!fat_remap) {
+ free_all();
+ croak("allocate_fat_remap: not enough memory");
+ }
+
+unsigned int
+fat_remap(cluster)
+ unsigned int cluster
+ CODE:
+ if (!fat_remap) {
+ free_all();
+ croak("fat_remap: trying to use null pointer");
+ }
+ if (cluster >= bad_cluster_value) {
+ RETVAL = cluster; /* special cases */
+ } else {
+ if (cluster >= fat_remap_size) {
+ free_all();
+ croak("fat_remap: cluster %d >= %d in fat_remap", cluster, fat_remap_size);
+ }
+ RETVAL = fat_remap[cluster];
+ }
+ OUTPUT:
+ RETVAL
+
+void
+set_fat_remap(cluster, val)
+ unsigned int cluster
+ unsigned int val
+ CODE:
+ if (!fat_remap) {
+ free_all();
+ croak("set_fat_remap: trying to use null pointer");
+ }
+ if (cluster >= fat_remap_size) {
+ free_all();
+ croak("set_fat_remap: cluster %d >= %d in set_fat_remap", cluster, fat_remap_size);
+ }
+ if (val < bad_cluster_value && val >= fat_remap_size) {
+ free_all();
+ croak("set_fat_remap: remapping cluster %d to cluster %d >= %d in set_fat_remap", cluster, val, fat_remap_size);
+ }
+ fat_remap[cluster] = val;
diff --git a/perl-install/resize_fat/dir_entry.pm b/perl-install/resize_fat/dir_entry.pm
new file mode 100644
index 000000000..b00edb397
--- /dev/null
+++ b/perl-install/resize_fat/dir_entry.pm
@@ -0,0 +1,77 @@
+package resize_fat::dir_entry; # $Id: dir_entry.pm 205391 2004-01-05 13:45:42Z prigaux $
+
+use diagnostics;
+use strict;
+
+
+my $DELETED_FLAG = 0xe5;
+
+my $_READ_ONLY_ATTR = 0x01;
+my $HIDDEN_ATTR = 0x02;
+my $SYSTEM_ATTR = 0x04;
+my $VOLUME_LABEL_ATTR = 0x08;
+my $VFAT_ATTR = 0x0f;
+my $DIRECTORY_ATTR = 0x10;
+
+1;
+
+sub get_cluster($) {
+ my ($entry) = @_;
+ $entry->{first_cluster} + ($resize_fat::isFAT32 ? $entry->{first_cluster_high} * (1 << 16) : 0);
+}
+sub set_cluster($$) {
+ my ($entry, $val) = @_;
+ $entry->{first_cluster} = $val & ((1 << 16) - 1);
+ $entry->{first_cluster_high} = $val >> 16 if $resize_fat::isFAT32;
+}
+
+sub is_unmoveable($) {
+ my ($entry) = @_;
+ $entry->{attributes} & $HIDDEN_ATTR || $entry->{attributes} & $SYSTEM_ATTR;
+}
+
+sub is_directory($) {
+ my ($entry) = @_;
+ $entry->{attributes} & $DIRECTORY_ATTR && $entry->{name} !~ /^\.\.? / && !is_special_entry($entry);
+}
+
+sub is_volume($) {
+ my ($entry) = @_;
+ !is_special_entry($entry) && $entry->{attributes} & $VOLUME_LABEL_ATTR;
+}
+
+sub is_file($) {
+ my ($entry) = @_;
+ !is_special_entry($entry) && !is_directory($entry) && !is_volume($entry) && $entry->{length};
+}
+
+
+sub is_special_entry($) {
+ my ($entry) = @_;
+ my ($c) = unpack "C", $entry->{name};
+
+ #- skip empty slots, deleted files, and 0xF6?? (taken from kernel)
+ $c == 0 || $c == $DELETED_FLAG || $c == 0xF6 and return 1;
+
+ $entry->{attributes} == $VFAT_ATTR and return 1;
+ 0;
+}
+
+
+#- return true if entry has been modified
+#- curr_dir_name is added to contains current directory name, "" for root.
+sub remap {
+ my ($_curr_dir_name, $entry) = @_;
+
+ is_special_entry($entry) and return;
+
+ my $cluster = get_cluster($entry);
+ my $new_cluster = resize_fat::c_rewritten::fat_remap($cluster);
+
+ #-print "remapping cluster ", get_cluster($entry), " to $new_cluster";
+
+ $new_cluster == $cluster and return; #- no need to modify
+
+ set_cluster($entry, $new_cluster);
+ 1;
+}
diff --git a/perl-install/resize_fat/directory.pm b/perl-install/resize_fat/directory.pm
new file mode 100644
index 000000000..addf757d0
--- /dev/null
+++ b/perl-install/resize_fat/directory.pm
@@ -0,0 +1,79 @@
+package resize_fat::directory; # $Id: directory.pm 202834 2003-04-24 19:00:12Z prigaux $
+
+use diagnostics;
+use strict;
+
+use common;
+use resize_fat::dir_entry;
+use resize_fat::io;
+
+
+my $format = "a8 a3 C C C S7 I";
+my @fields = (
+ 'name',
+ 'extension',
+ 'attributes',
+ 'is_upper_case_name',
+ 'creation_time_low', #- milliseconds
+ 'creation_time_high',
+ 'creation_date',
+ 'access_date',
+ 'first_cluster_high', #- for FAT32
+ 'time',
+ 'date',
+ 'first_cluster',
+ 'length',
+);
+my $psizeof_format = psizeof($format);
+
+1;
+
+sub entry_size() { $psizeof_format }
+
+#- call `f' for each entry of the directory
+#- if f return true, then modification in the entry are taken back
+sub traverse($$$) {
+ my ($directory, $curr_dir_name, $f) = @_;
+
+ for (my $i = 0;; $i++) {
+ my $raw = \substr($directory, $i * $psizeof_format, $psizeof_format);
+
+ #- empty entry means end of directory
+ $$raw =~ /^\0*$/ and return $directory;
+
+ my $entry; @$entry{@fields} = unpack $format, $$raw;
+
+ &$f($curr_dir_name, $entry)
+ and $$raw = pack $format, @$entry{@fields};
+ }
+ $directory;
+}
+
+sub traverse_all($$) {
+ my ($fs, $f) = @_;
+
+ my $traverse_all; $traverse_all = sub {
+ my ($curr_dir_name, $entry) = @_;
+
+ &$f($curr_dir_name, $entry);
+
+ resize_fat::dir_entry::is_directory($entry)
+ and traverse(resize_fat::io::read_file($fs, resize_fat::dir_entry::get_cluster($entry)), "$curr_dir_name/$entry->{name}", $traverse_all);
+
+ undef; #- no need to write back (cf traverse)
+ };
+
+ my $directory = $resize_fat::isFAT32 ?
+ resize_fat::io::read_file($fs, $fs->{fat32_root_dir_cluster}) :
+ resize_fat::io::read($fs, $fs->{root_dir_offset}, $fs->{root_dir_size});
+ traverse($directory, "", $traverse_all);
+ undef $traverse_all; #- circular reference is no good for perl's poor GC :(
+}
+
+
+#- function used by construct_dir_tree to translate the `cluster' fields in each
+#- directory entry
+sub remap($$) {
+ my ($_fs, $directory) = @_;
+ traverse($directory, "", \&resize_fat::dir_entry::remap);
+}
diff --git a/perl-install/resize_fat/fat.pm b/perl-install/resize_fat/fat.pm
new file mode 100644
index 000000000..a3372a905
--- /dev/null
+++ b/perl-install/resize_fat/fat.pm
@@ -0,0 +1,135 @@
+package resize_fat::fat; # $Id: fat.pm 210307 2004-12-13 15:56:00Z tvignaud $
+
+use diagnostics;
+use strict;
+
+use resize_fat::any;
+use resize_fat::io;
+use resize_fat::c_rewritten qw(next set_next);
+
+sub read($) {
+ my ($fs) = @_;
+
+ resize_fat::c_rewritten::read_fat(fileno $fs->{fd}, $fs->{fat_offset}, $fs->{fat_size}, $fs->{media});
+
+ @{$fs->{clusters}{count}}{qw(free bad used)} =
+ resize_fat::c_rewritten::scan_fat($fs->{nb_clusters}, $fs->{fs_type_size});
+}
+
+sub write($) {
+ my ($fs) = @_;
+
+ sysseek $fs->{fd}, $fs->{fat_offset}, 0 or die "write_fat: seek failed";
+ foreach (1..$fs->{nb_fats}) {
+ resize_fat::c_rewritten::write_fat(fileno $fs->{fd}, $fs->{fat_size});
+ }
+}
+
+
+
+#- allocates where all the clusters will be moved to. Clusters before cut_point
+#- remain in the same position, however cluster that are part of a directory are
+#- moved regardless (this is a mechanism to prevent data loss) (cut_point is the
+#- first cluster that will not occur in the new fs)
+sub allocate_remap {
+ my ($fs, $cut_point) = @_;
+ my ($cluster, $new_cluster);
+ my $remap = sub { resize_fat::c_rewritten::set_fat_remap($cluster, $new_cluster) };
+ my $get_new = sub {
+ $new_cluster = get_free($fs);
+ 0 < $new_cluster && $new_cluster < $cut_point or die "no free clusters";
+ set_eof($fs, $new_cluster); #- mark as used
+ #-log::ld("resize_fat: [$cluster,", &next($fs, $cluster), "...]->$new_cluster...");
+ };
+
+ #- this must call allocate_fat_remap that zeroes the buffer allocated.
+ resize_fat::c_rewritten::allocate_fat_remap($fs->{nb_clusters} + 2);
+
+ $fs->{last_free_cluster} = 2;
+ for ($cluster = 2; $cluster < $fs->{nb_clusters} + 2; $cluster++) {
+ if ($cluster < $cut_point) {
+ if (resize_fat::c_rewritten::flag($cluster) == $resize_fat::any::DIRECTORY) {
+ &$get_new();
+ } else {
+ $new_cluster = $cluster;
+ }
+ &$remap();
+ } elsif (!is_empty(&next($fs, $cluster))) {
+ &$get_new();
+ &$remap();
+ }
+ }
+}
+
+
+#- updates the fat for the resized filesystem
+sub update {
+ my ($fs) = @_;
+
+ for (my $cluster = 2; $cluster < $fs->{nb_clusters} + 2; $cluster++) {
+ if (resize_fat::c_rewritten::flag($cluster)) {
+ my $old_next = &next($fs, $cluster);
+ my $new = resize_fat::c_rewritten::fat_remap($cluster);
+ my $new_next = resize_fat::c_rewritten::fat_remap($old_next);
+
+ set_available($fs, $cluster);
+
+ is_eof($old_next) ?
+ set_eof($fs, $new) :
+ set_next($fs, $new, $new_next);
+ }
+ }
+}
+
+
+sub endianness16($) { (($_[0] & 0xff) << 8) + ($_[0] >> 8) }
+sub endianness($$) {
+ my ($val, $nb_bits) = @_;
+ my $r = 0;
+ for (; $nb_bits > 0; $nb_bits -= 8) {
+ $r = $r << 8;
+ $r += $val & 0xff;
+ $val = $val >> 8;
+ }
+ $nb_bits < 0 and die "error: endianness only handle numbers divisible by 8";
+ $r;
+}
+
+
+
+sub get_free($) {
+ my ($fs) = @_;
+ for (my $i = 0; $i < $fs->{nb_clusters}; $i++) {
+ my $cluster = ($i + $fs->{last_free_cluster} - 2) % $fs->{nb_clusters} + 2;
+ is_available(&next($fs, $cluster)) and return $fs->{last_free_cluster} = $cluster;
+ }
+ die "no free clusters";
+}
+
+#- returns true if <cluster> represents an EOF marker
+sub is_eof($) {
+ my ($cluster) = @_;
+ $cluster >= $resize_fat::bad_cluster_value;
+}
+sub set_eof($$) {
+ my ($fs, $cluster) = @_;
+ set_next($fs, $cluster, $resize_fat::bad_cluster_value + 1);
+}
+
+#- returns true if <cluster> is empty. Note that this includes bad clusters.
+sub is_empty($) {
+ my ($cluster) = @_;
+ $cluster == 0 || $cluster == $resize_fat::bad_cluster_value;
+}
+
+#- returns true if <cluster> is available.
+sub is_available($) {
+ my ($cluster) = @_;
+ $cluster == 0;
+}
+sub set_available($$) {
+ my ($fs, $cluster) = @_;
+ set_next($fs, $cluster, 0);
+}
+
+1;
diff --git a/perl-install/resize_fat/info_sector.pm b/perl-install/resize_fat/info_sector.pm
new file mode 100644
index 000000000..902f26f3f
--- /dev/null
+++ b/perl-install/resize_fat/info_sector.pm
@@ -0,0 +1,37 @@
+package resize_fat::info_sector; # $Id: info_sector.pm 199276 2002-07-31 22:40:00Z prigaux $
+
+use diagnostics;
+use strict;
+
+use common;
+use resize_fat::io;
+
+#- Oops, this will be unresizable on big-endian machine. trapped by signature.
+my $format = "a484 I I I a16";
+my @fields = (
+ 'unused',
+ 'signature', #- should be 0x61417272
+ 'free_clusters', #- -1 for unknown
+ 'next_cluster', #- most recently allocated cluster
+ 'unused2',
+);
+
+1;
+
+
+sub read($) {
+ my ($fs) = @_;
+ my $info = resize_fat::io::read($fs, $fs->{info_offset}, psizeof($format));
+ @{$fs->{info_sector}}{@fields} = unpack $format, $info;
+ $fs->{info_sector}{signature} == 0x61417272 or die "Invalid information sector signature\n";
+}
+
+sub write($) {
+ my ($fs) = @_;
+ $fs->{info_sector}{free_clusters} = $fs->{clusters}{count}{free};
+ $fs->{info_sector}{next_cluster} = 2;
+
+ my $info = pack $format, @{$fs->{info_sector}}{@fields};
+
+ resize_fat::io::write($fs, $fs->{info_offset}, psizeof($format), $info);
+}
diff --git a/perl-install/resize_fat/io.pm b/perl-install/resize_fat/io.pm
new file mode 100644
index 000000000..68443ad2a
--- /dev/null
+++ b/perl-install/resize_fat/io.pm
@@ -0,0 +1,58 @@
+package resize_fat::io; # $Id: io.pm 208669 2004-08-12 04:20:25Z prigaux $
+
+use diagnostics;
+use strict;
+
+use resize_fat::fat;
+use c;
+
+1;
+
+
+sub read($$$) {
+ my ($fs, $pos, $size) = @_;
+ my $buf = "\0" x $size;
+ sysseek $fs->{fd}, $pos, 0 or die "seeking to byte #$pos failed on device $fs->{fs_name}";
+ sysread $fs->{fd}, $buf, $size or die "reading at byte #$pos failed on device $fs->{fs_name}";
+ $buf;
+}
+sub write($$$$) {
+ my ($fs, $pos, $_size, $buf) = @_;
+ sysseek $fs->{fd}, $pos, 0 or die "seeking to byte #$pos failed on device $fs->{fs_name}";
+ syswrite $fs->{fd}, $buf or die "writing at byte #$pos failed on device $fs->{fs_name}";
+}
+
+sub read_cluster($$) {
+ my ($fs, $cluster) = @_;
+ my $buf;
+ my $pos = $fs->{cluster_offset} / 512 + $cluster * ($fs->{cluster_size} / 512);
+
+ c::lseek_sector(fileno $fs->{fd}, $pos, 0) or die "seeking to sector #$pos failed on device $fs->{fs_name}";
+ sysread $fs->{fd}, $buf, $fs->{cluster_size} or die "reading at sector #$pos failed on device $fs->{fs_name}";
+ $buf;
+}
+sub write_cluster($$$) {
+ my ($fs, $cluster, $buf) = @_;
+ my $pos = $fs->{cluster_offset} / 512 + $cluster * ($fs->{cluster_size} / 512);
+
+ c::lseek_sector(fileno $fs->{fd}, $pos, 0) or die "seeking to sector #$pos failed on device $fs->{fs_name}";
+ syswrite $fs->{fd}, $buf or die "writing at sector #$pos failed on device $fs->{fs_name}";
+}
+
+sub read_file($$) {
+ my ($fs, $cluster) = @_;
+ my $buf = '';
+
+ for (; !resize_fat::fat::is_eof($cluster); $cluster = resize_fat::fat::next($fs, $cluster)) {
+ $cluster == 0 and die "Bad FAT: unterminated chain\n";
+ $buf .= read_cluster($fs, $cluster);
+ }
+ $buf;
+}
+
+sub open {
+ my ($fs) = @_;
+
+ sysopen $fs->{fd}, $fs->{fs_name}, 2 or
+ sysopen $fs->{fd}, $fs->{fs_name}, 0 or die "error opening device $fs->{fs_name} for writing\n";
+}
diff --git a/perl-install/resize_fat/main.pm b/perl-install/resize_fat/main.pm
new file mode 100644
index 000000000..34bf2f9bf
--- /dev/null
+++ b/perl-install/resize_fat/main.pm
@@ -0,0 +1,166 @@
+package resize_fat::main; # $Id: main.pm 212705 2005-06-10 07:52:38Z prigaux $
+
+# This is mainly a perl rewrite of the work of Andrew Clausen (libresize)
+
+use diagnostics;
+use strict;
+
+use log;
+use common;
+use resize_fat::boot_sector;
+use resize_fat::info_sector;
+use resize_fat::directory;
+use resize_fat::io;
+use resize_fat::fat;
+use resize_fat::any;
+
+
+1;
+
+#- - reads in the boot sector/partition info., and tries to make some sense of it
+sub new($$$) {
+ my ($type, $device, $fs_name) = @_;
+ my $fs = { device => $device, fs_name => $fs_name };
+
+ eval {
+ resize_fat::io::open($fs);
+ resize_fat::boot_sector::read($fs);
+ $resize_fat::isFAT32 and eval { resize_fat::info_sector::read($fs) };
+ resize_fat::fat::read($fs);
+ resize_fat::any::flag_clusters($fs);
+ };
+ if ($@) {
+ close $fs->{fd};
+ die;
+ }
+ bless $fs, $type;
+}
+
+sub DESTROY {
+ my ($fs) = @_;
+ close $fs->{fd};
+ resize_fat::c_rewritten::free_all();
+}
+
+#- copy all clusters >= <start_cluster> to a new place on the partition, less
+#- than <start_cluster>. Only copies files, not directories.
+#- (use of buffer needed because the seeks slow like hell the hard disk drive)
+sub copy_clusters {
+ my ($fs, $cluster) = @_;
+ my @buffer;
+ my $flush = sub {
+ while (@buffer) {
+ my $cluster = shift @buffer;
+ resize_fat::io::write_cluster($fs, $cluster, shift @buffer);
+ }
+ };
+ for (; $cluster < $fs->{nb_clusters} + 2; $cluster++) {
+ resize_fat::c_rewritten::flag($cluster) == $resize_fat::any::FILE or next;
+ push @buffer,
+ resize_fat::c_rewritten::fat_remap($cluster),
+ resize_fat::io::read_cluster($fs, $cluster);
+ @buffer > 50 and &$flush();
+ }
+ &$flush();
+}
+
+#- Constructs the new directory tree to match the new file locations.
+sub construct_dir_tree {
+ my ($fs) = @_;
+
+ if ($resize_fat::isFAT32) {
+ #- fat32's root must remain in the first 64k clusters
+ #- so do not set it as DIRECTORY, it will be specially handled
+ resize_fat::c_rewritten::set_flag($fs->{fat32_root_dir_cluster}, $resize_fat::any::FREE);
+ }
+
+ for (my $cluster = 2; $cluster < $fs->{nb_clusters} + 2; $cluster++) {
+ resize_fat::c_rewritten::flag($cluster) == $resize_fat::any::DIRECTORY or next;
+
+ resize_fat::io::write_cluster($fs,
+ resize_fat::c_rewritten::fat_remap($cluster),
+ resize_fat::directory::remap($fs, resize_fat::io::read_cluster($fs, $cluster)));
+ }
+
+ MDK::Common::System::sync();
+
+ #- until now, only free clusters have been written. it's a null operation if we stop here.
+ #- it means no corruption :)
+ #
+ #- now we must be as fast as possible!
+
+ #- remapping non movable root directory
+ if ($resize_fat::isFAT32) {
+ my $cluster = $fs->{fat32_root_dir_cluster};
+
+ resize_fat::io::write_cluster($fs,
+ resize_fat::c_rewritten::fat_remap($cluster),
+ resize_fat::directory::remap($fs, resize_fat::io::read_cluster($fs, $cluster)));
+ } else {
+ resize_fat::io::write($fs, $fs->{root_dir_offset}, $fs->{root_dir_size},
+ resize_fat::directory::remap($fs, resize_fat::io::read($fs, $fs->{root_dir_offset}, $fs->{root_dir_size})));
+ }
+}
+
+sub min_size($) { &resize_fat::any::min_size }
+sub max_size($) { &resize_fat::any::max_size }
+sub used_size($) { &resize_fat::any::used_size }
+
+#- resize
+#- - size is in sectors
+#- - checks boundaries before starting
+#- - copies all data beyond new_cluster_count behind the frontier
+sub resize {
+ my ($fs, $size) = @_;
+
+ my ($min, $max) = (min_size($fs), max_size($fs));
+
+ $size += $min if $size =~ /^\+/;
+
+ $size >= $min or die "Minimum filesystem size is $min sectors";
+ $size <= $max or die "Maximum filesystem size is $max sectors";
+
+ log::l("resize_fat: Partition size will be " . (($size * $SECTORSIZE) >> 20) . "Mb (well exactly ${size} sectors)");
+
+ my $new_data_size = $size * $SECTORSIZE - $fs->{cluster_offset};
+ my $new_nb_clusters = divide($new_data_size, $fs->{cluster_size});
+ my $used_size = used_size($fs);
+
+ log::l("resize_fat: Break point for moving files is " . (($used_size * $SECTORSIZE) >> 20) . " Mb ($used_size sectors)");
+ if ($size < $used_size) {
+ log::l("resize_fat: Allocating new clusters");
+ resize_fat::fat::allocate_remap($fs, $new_nb_clusters);
+
+ log::l("resize_fat: Copying files");
+ copy_clusters($fs, $new_nb_clusters);
+
+ log::l("resize_fat: Copying directories");
+ construct_dir_tree($fs);
+
+ log::l("Writing new FAT...");
+ resize_fat::fat::update($fs);
+ resize_fat::fat::write($fs);
+ } else {
+ log::l("resize_fat: Nothing need to be moved");
+ }
+
+ $fs->{nb_sectors} = $size;
+ $fs->{nb_clusters} = $new_nb_clusters;
+ $fs->{clusters}{count}{free} =
+ $fs->{nb_clusters} - $fs->{clusters}{count}{used} - $fs->{clusters}{count}{bad} - 2;
+
+ $fs->{system_id} = 'was here!';
+ $fs->{small_nb_sectors} = 0;
+ $fs->{big_nb_sectors} = $size;
+
+ log::l("resize_fat: Writing new boot sector...");
+
+ resize_fat::boot_sector::write($fs);
+
+ $resize_fat::isFAT32 and eval { resize_fat::info_sector::write($fs) }; #- does not matter if this fails - its pretty useless!
+
+ MDK::Common::System::sync();
+ close $fs->{fd};
+ log::l("resize_fat: done");
+}
+