123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- #!/usr/bin/env perl
- # fix-onedrive-zip
- #
- # Fix OneDrive/Windows Zip files larger than 4Gig that have an invalid
- # 'Total Number of Disks' field in the 'ZIP64 End Central Directory
- # Locator'. The value in this field should be 1, but OneDrive/Windows sets
- # it to 0. This makes it difficult to work with these files using standard
- # unzip utilities.
- #
- # This program changes the 'Total Number of Disks' field value to 1.
- #
- # Copyright (c) 2020-2023 Paul Marquess. All rights reserved.
- #
- # This program is free software; you can redistribute it and/or modify it
- # under the same terms as Perl itself.
- use strict;
- use warnings;
- use Fcntl qw(SEEK_SET SEEK_END);
- use Getopt::Long;
- # Signatures for the headers we need to check
- use constant ZIP_LOCAL_HDR_SIG => 0x04034b50;
- use constant ZIP_END_CENTRAL_HDR_SIG => 0x06054b50;
- use constant ZIP64_END_CENTRAL_LOC_HDR_SIG => 0x07064b50;
- sub Seek;
- sub Read;
- my $VERSION = '1.02' ;
- my $dry_run ;
- BEGIN {
- # Check for a 32-bit Perl
- if (!eval { pack "Q", 1 }) {
- warn "The perl executable you are running is 32-bit.\n" .
- "You need to install a 64-bit perl executable to continue\n";
- exit(1);
- }
- }
- GetOptions ("dry-run" => \$dry_run,
- "help" => \&Usage)
- or Usage("Error in command line arguments\n");
- Usage()
- unless @ARGV >= 1;
- for my $filename (@ARGV)
- {
- open my $fh, "+<$filename"
- or die "Error: Cannot open '$filename': $!\n";
- }
- for my $filename (@ARGV)
- {
- print "\nChecking '$filename'\n";
- open my $fh, "+<$filename"
- or die "Cannot open '$filename': $!\n";
- my $data = Read $filename, $fh, 4;
- my $sig = unpack("V", $data) ;
- die "Error: No Zip signature at start of '$filename'\n"
- unless $sig == ZIP_LOCAL_HDR_SIG ;
- # Assume no comment or other trailing data
- # The last two things in the file are the Z64 & EOCD records
- Seek $filename, $fh, -42 ;
- $data = Read $filename, $fh, 42;
- my $eocd = substr($data, 20);
- my $eocd_sig = unpack("V", substr($eocd, 0, 4)) ;
- die "Error: Cannot find Zip signature at end of '$filename'\n"
- unless $eocd_sig == ZIP_END_CENTRAL_HDR_SIG ;
- my $offset = unpack("VV", substr($eocd, 16, 4)) ;
- die sprintf "Error: bad offset 0x%X at end of '$filename'\n", $offset
- unless $offset == 0xFFFFFFFF ;
- my $z64_sig = unpack("V", substr($data, 0, 4)) ;
- die "Error: Cannot find Zip64 signature at end of '$filename'\n"
- unless $z64_sig == ZIP64_END_CENTRAL_LOC_HDR_SIG ;
- my $total_disks = unpack("V", substr($data, 16, 4)) ;
- if ($total_disks == 1)
- {
- print "Nothing to do: 'Total Number of Disks' field is already 1\n";
- next
- }
- if ($total_disks != 0)
- {
- die "Error: 'Total Number of Disks' field is $total_disks\n";
- }
- Seek $filename, $fh, -42 + 16 ;
- if ($dry_run)
- {
- print "Dry-Run: No change made to '$filename'\n";
- }
- else
- {
- print $fh pack "V", 1 ;
- print "Updated '$filename'\n";
- }
- }
- sub Seek
- {
- my $filename = shift;
- my $fh = shift ;
- my $offset = shift ;
- seek $fh, $offset, SEEK_END
- or die "Cannot seek '$filename': $!\n" ;
- }
- sub Read
- {
- my $filename = shift ;
- my $fh = shift;
- my $size = shift ;
- my $data ;
- read($fh, $data, $size) == $size
- or die "Cannot read from '$filename': $!\n" ;
- return $data;
- }
- sub Usage
- {
- print <<'EOM';
- Usage: fix-onedrive-zip [--dry-run] file1.zip [file2.zip...]
- Fix OneDrive/Windows Zip files larger than 4Gig that have an invalid 'Total
- Number of Disks' field in the 'ZIP64 End Central Directory Locator'. The
- value in this field should be 1, but OneDrive/Windows sets it to 0. This
- makes it difficult to work with these files using standard unzip utilities.
- This program changes the 'Total Number of Disks' field value to 1.
- See https://github.com/pmqs/Fix-OneDrive-Zip for support.
- Copyright (c) 2020-2023 Paul Marquess (pmqs@cpan.org). All rights reserved.
- This program is free software; you can redistribute it and/or modify it
- under the same terms as Perl itself.
- EOM
- exit;
- }
|