1# NOTE: this file tests how large files (>2GB) work with raw system IO. 2# stdio: open(), tell(), seek(), print(), read() is tested in t/op/lfs.t. 3# If you modify/add tests here, remember to update also t/op/lfs.t. 4 5BEGIN { 6 require Config; import Config; 7 # Don't bother if there are no quad offsets. 8 if ($Config{lseeksize} < 8) { 9 print "1..0 # Skip: no 64-bit file offsets\n"; 10 exit(0); 11 } 12 require Fcntl; import Fcntl qw(/^O_/ /^SEEK_/); 13} 14 15use strict; 16use File::Temp 'tempfile'; 17use Test::More; 18 19our @s; 20 21(undef, my $big0) = tempfile(UNLINK => 1); 22(undef, my $big1) = tempfile(UNLINK => 1); 23(undef, my $big2) = tempfile(UNLINK => 1); 24 25my $explained; 26 27sub explain { 28 unless ($explained++) { 29 print <<EOM; 30# 31# If the lfs (large file support: large meaning larger than two 32# gigabytes) tests are skipped or fail, it may mean either that your 33# process (or process group) is not allowed to write large files 34# (resource limits) or that the file system (the network filesystem?) 35# you are running the tests on doesn't let your user/group have large 36# files (quota) or the filesystem simply doesn't support large files. 37# You may even need to reconfigure your kernel. (This is all very 38# operating system and site-dependent.) 39# 40# Perl may still be able to support large files, once you have 41# such a process, enough quota, and such a (file) system. 42# It is just that the test failed now. 43# 44EOM 45 } 46 if (@_) { 47 plan(skip_all => "@_"); 48 } 49} 50 51$| = 1; 52 53print "# checking whether we have sparse files...\n"; 54 55# Known have-nots. 56if ($^O eq 'MSWin32' || $^O eq 'NetWare' || $^O eq 'VMS') { 57 plan(skip_all => "no sparse files in $^O"); 58} 59 60# Known haves that have problems running this test 61# (for example because they do not support sparse files, like UNICOS) 62if ($^O eq 'unicos') { 63 plan(skip_all => "no sparse files in $^O, unable to test large files"); 64} 65 66# Then try heuristically to deduce whether we have sparse files. 67 68# We'll start off by creating a one megabyte file which has 69# only three "true" bytes. If we have sparseness, we should 70# consume less blocks than one megabyte (assuming nobody has 71# one megabyte blocks...) 72 73sysopen(BIG, $big1, O_WRONLY|O_CREAT|O_TRUNC) or 74 die "sysopen $big1 failed: $!"; 75sysseek(BIG, 1_000_000, SEEK_SET) or 76 die "sysseek $big1 failed: $!"; 77syswrite(BIG, "big") or 78 die "syswrite $big1 failed: $!"; 79close(BIG) or 80 die "close $big1 failed: $!"; 81 82my @s1 = stat($big1); 83 84print "# s1 = @s1\n"; 85 86sysopen(BIG, $big2, O_WRONLY|O_CREAT|O_TRUNC) or 87 die "sysopen $big2 failed: $!"; 88sysseek(BIG, 2_000_000, SEEK_SET) or 89 die "sysseek $big2 failed: $!"; 90syswrite(BIG, "big") or 91 die "syswrite $big2 failed: $!"; 92close(BIG) or 93 die "close $big2 failed: $!"; 94 95my @s2 = stat($big2); 96 97print "# s2 = @s2\n"; 98 99unless ($s1[7] == 1_000_003 && $s2[7] == 2_000_003 && 100 $s1[11] == $s2[11] && $s1[12] == $s2[12] && 101 $s1[12] > 0) { 102 plan(skip_all => "no sparse files?"); 103} 104 105print "# we seem to have sparse files...\n"; 106 107# By now we better be sure that we do have sparse files: 108# if we are not, the following will hog 5 gigabytes of disk. Ooops. 109# This may fail by producing some signal; run in a subprocess first for safety 110 111$ENV{LC_ALL} = "C"; 112 113my $perl = '../../perl'; 114unless (-x $perl) { 115 plan(tests => 1); 116 fail("can't find perl: expected $perl"); 117} 118my $r = system $perl, '-I../lib', '-e', <<"EOF"; 119use Fcntl qw(/^O_/ /^SEEK_/); 120sysopen \$big, q{$big0}, O_WRONLY|O_CREAT|O_TRUNC or die qq{sysopen $big0 $!}; 121sysseek \$big, 5_000_000_000, SEEK_SET or die qq{sysseek $big0 $!}; 122syswrite \$big, "big" or die qq{syswrite $big0 $!}; 123close \$big or die qq{close $big0: $!}; 124exit 0; 125EOF 126 127 128sysopen(BIG, $big0, O_WRONLY|O_CREAT|O_TRUNC) or 129 die "sysopen $big0 failed: $!"; 130my $sysseek = sysseek(BIG, 5_000_000_000, SEEK_SET); 131unless (! $r && defined $sysseek && $sysseek == 5_000_000_000) { 132 $sysseek = 'undef' unless defined $sysseek; 133 explain("seeking past 2GB failed: ", 134 $r ? 'signal '.($r & 0x7f) : "$! (sysseek returned $sysseek)"); 135} 136 137# The syswrite will fail if there are are filesize limitations (process or fs). 138my $syswrite = syswrite(BIG, "big"); 139print "# syswrite failed: $! (syswrite returned ", 140 defined $syswrite ? $syswrite : 'undef', ")\n" 141 unless defined $syswrite && $syswrite == 3; 142my $close = close BIG; 143print "# close failed: $!\n" unless $close; 144unless($syswrite && $close) { 145 if ($! =~/too large/i) { 146 explain("writing past 2GB failed: process limits?"); 147 } elsif ($! =~ /quota/i) { 148 explain("filesystem quota limits?"); 149 } else { 150 explain("error: $!"); 151 } 152} 153 154@s = stat($big0); 155 156print "# @s\n"; 157 158unless ($s[7] == 5_000_000_003) { 159 explain("kernel/fs not configured to use large files?"); 160} 161 162sub offset ($$) { 163 local $Test::Builder::Level = $Test::Builder::Level + 1; 164 my ($offset_will_be, $offset_want) = @_; 165 my $offset_is = eval $offset_will_be; 166 unless ($offset_is == $offset_want) { 167 print "# bad offset $offset_is, want $offset_want\n"; 168 my ($offset_func) = ($offset_will_be =~ /^(\w+)/); 169 if (unpack("L", pack("L", $offset_want)) == $offset_is) { 170 print "# 32-bit wraparound suspected in $offset_func() since\n"; 171 print "# $offset_want cast into 32 bits equals $offset_is.\n"; 172 } elsif ($offset_want - unpack("L", pack("L", $offset_want)) - 1 173 == $offset_is) { 174 print "# 32-bit wraparound suspected in $offset_func() since\n"; 175 printf "# %s - unpack('L', pack('L', %s)) - 1 equals %s.\n", 176 $offset_want, 177 $offset_want, 178 $offset_is; 179 } 180 fail($offset_will_be); 181 } else { 182 pass($offset_will_be); 183 } 184} 185 186plan(tests => 17); 187 188is($s[7], 5_000_000_003, 'exercises pp_stat'); 189is(-s $big0, 5_000_000_003, 'exercises pp_ftsize'); 190 191is(-e $big0, 1); 192is(-f $big0, 1); 193 194sysopen(BIG, $big0, O_RDONLY) or die "sysopen failed: $!"; 195 196offset('sysseek(BIG, 4_500_000_000, SEEK_SET)', 4_500_000_000); 197 198offset('sysseek(BIG, 0, SEEK_CUR)', 4_500_000_000); 199 200# If you get 205_032_705 from here it means that 201# your tell() is returning 32-bit values since (I32)4_500_000_001 202# is exactly 205_032_705. 203offset('sysseek(BIG, 1, SEEK_CUR)', 4_500_000_001); 204 205offset('sysseek(BIG, 0, SEEK_CUR)', 4_500_000_001); 206 207offset('sysseek(BIG, -1, SEEK_CUR)', 4_500_000_000); 208 209offset('sysseek(BIG, 0, SEEK_CUR)', 4_500_000_000); 210 211offset('sysseek(BIG, -3, SEEK_END)', 5_000_000_000); 212 213offset('sysseek(BIG, 0, SEEK_CUR)', 5_000_000_000); 214 215my $big; 216 217is(sysread(BIG, $big, 3), 3); 218 219is($big, "big"); 220 221# 705_032_704 = (I32)5_000_000_000 222# See that we don't have "big" in the 705_... spot: 223# that would mean that we have a wraparound. 224isnt(sysseek(BIG, 705_032_704, SEEK_SET), undef); 225 226my $zero; 227 228is(read(BIG, $zero, 3), 3); 229 230is($zero, "\0\0\0"); 231 232explain() unless Test::Builder->new()->is_passing(); 233 234END { 235 # unlink may fail if applied directly to a large file 236 # be paranoid about leaving 5 gig files lying around 237 open(BIG, ">$big0"); # truncate 238 close(BIG); 239} 240 241# eof 242