[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#635198: ufsutils: mkfs.ufs fails on blockdevice larger than 2 gigabyte



Package: ufsutils
Version: 8.2-1
Severity: important

First, take into account that the following bugfix needs to be in place in
order to continue: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=635173


Next, let's create a 2 gigabyte size ramdisk as follows:

lapedb:~# rmmod brd ; modprobe brd rd_size=2097151
edb@lapedb:~$ sudo dd if=/dev/zero of=/dev/ram0 bs=10M
dd: writing `/dev/ram0': No space left on device
205+0 records in
204+0 records out
2147482624 bytes (2.1 GB) copied, 5.74209 s, 374 MB/s

And let's run mkfs.ufs on it:
lapedb:/home/edb/snot/ufsutils-8.2# ./sbin/newfs/mkfs.ufs /dev/ram0
/dev/ram0: 2048.0MB (4194300 sectors) block size 16384, fragment size 2048
        using 12 cylinder groups of 183.77MB, 11761 blks, 23552 inodes.
super-block backups (for fsck -b #) at:
 160, 376512, 752864, 1129216, 1505568, 1881920, 2258272, 2634624, 3010976,
3387328, 3763680, 4140032

Everything seems to be fine, now let's increase the size of the ramdisk by one
kilobyte (rd_size is expressed in kilobytes)

lapedb:~# rmmod brd ; modprobe brd rd_size=2097152
edb@lapedb:~$ sudo dd if=/dev/zero of=/dev/ram0 bs=10M
205+0 records in
204+0 records out
2147483648 bytes (2.1 GB) copied, 4.89747 s, 438 MB/s

Now let's run mkfs.ufs on it again:
lapedb:/home/edb/snot/ufsutils-8.2# ./sbin/newfs/mkfs.ufs /dev/ram0
mkfs.ufs: /dev/ram0: reserved not less than device size -4194304

Oops, that didn't go so well.

Let's take a deeper look

strace shows a failure after the ioctl() BLKGETSIZE to obtain the size of the
filesystem:

ioctl(3, BLKGETSIZE, 0xbf8c5ab4)        = 0
fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0xb772c000
write(1, "get_block_device_size: device_se"..., 83get_block_device_size:
device_sectors: 4194304  size: 2147483648 result:  overflow
) = 83
write(2, "mkfs.ufs: ", 10mkfs.ufs: )              = 10
write(2, "/dev/ram0: reserved not less tha"..., 54/dev/ram0: reserved not less
than device size -4194304) = 54
write(2, "\n", 1
)                       = 1
exit_group(1)                           = ?


Which is an ideal location for a breakpoint:

lapedb:/home/edb/snot/ufsutils-8.2# gdb  ./sbin/newfs/mkfs.ufs
GNU gdb (GDB) 7.2-debian
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/edb/snot/ufsutils-8.2/sbin/newfs/mkfs.ufs...done.
(gdb) break ioctl
Breakpoint 1 at 0x8048d1c
(gdb) run /dev/ram0
Starting program: /home/edb/snot/ufsutils-8.2/sbin/newfs/mkfs.ufs /dev/ram0

Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82
82      ../sysdeps/unix/syscall-template.S: No such file or directory.
        in ../sysdeps/unix/syscall-template.S
(gdb) n
ioctl () at ../sysdeps/unix/syscall-template.S:83
83      in ../sysdeps/unix/syscall-template.S
(gdb) n
get_block_device_size (fd=5) at blockdev.c:74
74                              size = ((int64_t)device_sectors) * sector_size;
(gdb) print size
$1 = 0
(gdb) print device_sectors
$2 = 4194304
(gdb) print /x size
$6 = 0x80000000
(gdb) print sizeof(size)
$7 = 8
(gdb) n
main (argc=1, argv=0xbffffcb8) at newfs.c:329
329                 if (sectorsize && mediasize)
(gdb) n
330                     getfssize(&fssize, special, mediasize / sectorsize,
reserved);
(gdb) print mediasize
$8 = -2147483648
(gdb) print sizeof(mediasize)
$9 = 4
(gdb) ptype mediasize
type = long int


Or, in blockdev.c we put the size into an int64_t, in newfs.c (which called the
get_block_device_size()) it gets stored into the mediasize variable, which
happens to be a 4 byte long int. This results in an integer overflow and thus
an invalid number of sectors (a large negative number) gets passed into the
getfssize() which later on concludes that the  requested reserved space is less
than what is actually available (the large negative number).


(Actually this problems becomes an interesting one, because once you exceed
0xffffffff  in size (thus overflow the 32-bit value) then the sign bit becomes
zero again, however at this point mkfs.ufs will start reporting odd partitions
sizes (modulo 2^31)).

Now how to fix this ?  A first attempt is to replace the declaration of off_t
mediasize; into int64t_t mediasize; then creating the filesystem succeeds:

lapedb:~# rmmod brd ; modprobe brd rd_size=2097152
lapedb:/home/edb/snot/ufsutils-8.2# ./sbin/newfs/mkfs.ufs /dev/ram0
/dev/ram0: 2048.0MB (4194304 sectors) block size 16384, fragment size 2048
        using 12 cylinder groups of 183.77MB, 11761 blks, 23552 inodes.
super-block backups (for fsck -b #) at:
 160, 376512, 752864, 1129216, 1505568, 1881920, 2258272, 2634624, 3010976,
3387328, 3763680, 4140032


Regarding off_t, the following C-snippet:
#include <unistd.h>

int main()
{
    off_t varoff_t;
    __off_t var__off_t;
    __off64_t var__off64_t;
    printf("off_t %d\n", sizeof(varoff_t));
    printf("__off_t %d\n", sizeof(var__off_t));
    printf("__off64_t %d\n", sizeof(var__off64_t));
}

gives the following output:

off_t 4
__off_t 4
__off64_t 8


This then brings us to the next error, because now the creation fails one whole
kilobyte further

lapedb:~# rmmod brd ; modprobe brd rd_size=2097153
lapedb:/home/edb/snot/ufsutils-8.2# ./sbin/newfs/mkfs.ufs /dev/ram0
mkfs.ufs: wtfs: 512 bytes at sector 4194305: Invalid argument

An strace shows:

pwrite64(3,
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 512,
18446744071562068480) = -1 EINVAL (Invalid argument)
write(2, "mkfs.ufs: ", 10mkfs.ufs: )              = 10
write(2, "wtfs: 512 bytes at sector 419430"..., 33wtfs: 512 bytes at sector
4194305) = 33
write(2, ": ", 2: )                       = 2
write(2, "Invalid argument\n", 17Invalid argument

Putting a breakpoint on pwrite gives:

(gdb) where
#0  __libc_pwrite (fd=5, buf=0x8075008, count=512, offset=-2147483136) at
.../sysdeps/unix/sysv/linux/pwrite.c:75
#1  0x0804eefd in bwrite (disk=0x8054920, blockno=4194305, data=0x8075008,
size=512) at block.c:129
#2  0x0804de41 in wtfs (bno=4194305, size=512, bf=0x8054944 "") at mkfs.c:1027
#3  0x0804a7df in mkfs (pp=0x0, fsys=0xbffffe00 "/dev/ram0") at mkfs.c:164
#4  0x08049e9c in main (argc=1, argv=0xbffffcb8) at newfs.c:384

block.c:129 shows
        cnt = pwrite(disk->d_fd, data, size, (off_t)(blockno * disk->d_bsize));

So although we want to write to blockno*size = 4194305*512 = 0x80000200 but the
argument which strace shows to go into pwrite turns out to be
>>> print "%x" % 18446744071562068480
ffffffff80000200

However when I replace block.c:129 by
        cnt = pwrite64(disk->d_fd, data, size, (blockno * disk->d_bsize));

Then if works as well:

/dev/ram0: 2048.0MB (4194304 sectors) block size 16384, fragment size 2048
        using 12 cylinder groups of 183.77MB, 11761 blks, 23552 inodes.
super-block backups (for fsck -b #) at:
 160, 376512, 752864, 1129216, 1505568, 1881920, 2258272, 2634624, 3010976,
3387328, 3763680, 4140032
....
/dev/ram0: 2929.7MB (6000000 sectors) block size 16384, fragment size 2048
        using 16 cylinder groups of 183.77MB, 11761 blks, 23552 inodes.
super-block backups (for fsck -b #) at:
 160, 376512, 752864, 1129216, 1505568, 1881920, 2258272, 2634624, 3010976,
3387328, 3763680, 4140032, 4516384, 4892736, 5269088, 5645440

....

And this time I can mount the block device:
edb@lapedb:~$ modprobe ufs
edb@lapedb:~$ mount -t ufs -o ro,ufstype=ufs2  /dev/ram0 /tmp/k
edb@lapedb:~$ cd /tmp/k
edb@lapedb:/tmp/k$ ls
edb@lapedb:/tmp/k$ df -h .
Filesystem            Size  Used Avail Use% Mounted on
/dev/ram0             2.8G  4.0K  2.6G   1% /tmp/k







-- System Information:
Debian Release: wheezy/sid
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: i386 (i686)

Kernel: Linux 2.6.39-2-686-pae (SMP w/2 CPU cores)
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)
Shell: /bin/sh linked to /bin/dash

Versions of packages ufsutils depends on:
ii  libbsd0                  0.3.0-1         utility functions from BSD systems
ii  libc6                    2.13-10         Embedded GNU C Library: Shared lib
ii  libedit2                 2.11-20080614-2 BSD editline and history libraries

ufsutils recommends no packages.

ufsutils suggests no packages.

-- no debconf information



Reply to: