diff options
author | Antoine Ginies <aginies@mandriva.com> | 2011-01-19 10:44:49 +0000 |
---|---|---|
committer | Antoine Ginies <aginies@mandriva.com> | 2011-01-19 10:44:49 +0000 |
commit | 530a16ec071db0e24e6e949e265a96848864967c (patch) | |
tree | fe40cacd28d67b98186754c551b7fd339ebc7e17 /perl-install/resize_fat | |
download | drakx-backup-do-not-use-530a16ec071db0e24e6e949e265a96848864967c.tar drakx-backup-do-not-use-530a16ec071db0e24e6e949e265a96848864967c.tar.gz drakx-backup-do-not-use-530a16ec071db0e24e6e949e265a96848864967c.tar.bz2 drakx-backup-do-not-use-530a16ec071db0e24e6e949e265a96848864967c.tar.xz drakx-backup-do-not-use-530a16ec071db0e24e6e949e265a96848864967c.zip |
add mes5-2.6.33 branch
Diffstat (limited to 'perl-install/resize_fat')
-rw-r--r-- | perl-install/resize_fat/Makefile | 10 | ||||
-rw-r--r-- | perl-install/resize_fat/Makefile.PL | 13 | ||||
-rw-r--r-- | perl-install/resize_fat/README | 8 | ||||
-rw-r--r-- | perl-install/resize_fat/any.pm | 122 | ||||
-rw-r--r-- | perl-install/resize_fat/boot_sector.pm | 110 | ||||
-rw-r--r-- | perl-install/resize_fat/c_rewritten.pm | 14 | ||||
-rw-r--r-- | perl-install/resize_fat/c_rewritten.xs | 265 | ||||
-rw-r--r-- | perl-install/resize_fat/dir_entry.pm | 77 | ||||
-rw-r--r-- | perl-install/resize_fat/directory.pm | 79 | ||||
-rw-r--r-- | perl-install/resize_fat/fat.pm | 135 | ||||
-rw-r--r-- | perl-install/resize_fat/info_sector.pm | 37 | ||||
-rw-r--r-- | perl-install/resize_fat/io.pm | 58 | ||||
-rw-r--r-- | perl-install/resize_fat/main.pm | 166 |
13 files changed, 1094 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..929075ee0 --- /dev/null +++ b/perl-install/resize_fat/any.pm @@ -0,0 +1,122 @@ +package resize_fat::any; # $Id: any.pm 211945 2005-04-13 15:36:36Z prigaux $ + +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 $used_cluster_count = max(last_used($fs), 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..5e1f75a12 --- /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 file system\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..1c822a715 --- /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 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"); +} + |