Re: [PATCH] isofs: Fix handling of directories with small logical blocks

From: Hengyu Liang

Date: Sun Oct 11 2026 - 22:17:16 EST


Hi Thomas,

On Sun, Oct 11, 2026 at 10:29:07PM +0200, Thomas Schmitt wrote:
> Do i get it right that lbs1024.iso contains in the Primary Volume
> Descriptor at BP 129 to 132 "Logical Block Size" the value 1024
> instead of 2048 ?
> (Where could i get such an ISO image ? And how was it created ?)

Yes, BP 129 to 132 hold 1024.

I do not have such an image from a real medium, and I do not know a
program that writes one. I made it by hand with the script below
("python3 mkiso.py lbs1024.iso 1024"). It is minimal: a Primary Volume
Descriptor with only a few fields set, the terminator, and one sector
with a root directory of 40 empty files. To keep it short the script
writes no path tables. The kernel does not read them, but other
programs may reject the image for that.

# mkiso.py <image> <logical block size>: ISO 9660 image, 40 empty files
import struct, sys
lbs = int(sys.argv[2])
root = 18 * 2048 // lbs
def b16(v): return struct.pack('<H', v) + struct.pack('>H', v)
def b32(v): return struct.pack('<I', v) + struct.pack('>I', v)
def pad(b): return b.ljust(2048, b'\0')
def rec(name, block, size, flags):
r = (bytes([0, 0]) + b32(block) + b32(size) + bytes(7) +
bytes([flags, 0, 0]) + b16(1) + bytes([len(name)]) + name)
r += bytes(len(r) % 2)
return bytes([len(r)]) + r[1:]
d = rec(b'\0', root, 2048, 2) + rec(b'\1', root, 2048, 2)
for i in range(1, 41):
d += rec(b'FILE%04d.TXT;1' % i, 0, 0, 0)
pvd = bytearray(2048)
pvd[0:7] = b'\1CD001\1'
pvd[80:88] = b32(root + 2048 // lbs)
pvd[120:132] = b16(1) + b16(1) + b16(lbs)
pvd[156:190] = d[:34]
img = bytes(16 * 2048) + pvd + pad(b'\xffCD001\1') + pad(d)
open(sys.argv[1], 'wb').write(img)

> The mount option block={512|1024|2048} may set the address unit for
> reading the volume descriptors in isofs_fill_super():
> opt->blocksize = sb_min_blocksize(s, opt->blocksize);
> This means that if it gets through, it is the assumed Logical Sector
> size.

As far as I can see, the option sets the size of the buffer that is
read, but not the place. The loop over the volume descriptors has

block = iso_blknum << (ISOFS_BLOCK_BITS - s->s_blocksize_bits);

so sector N is read at byte N * 2048 for every value of the option.
The sector size is 2048 there as well.

> So if the user is allowed to set an initial Sector size, shouldn't this
> be used for the check in this patch, too ?

I think 2048 is the right value for the check. By ECMA-119 6.1.2 a
Logical Sector has 2048 bytes or more, and isofs_fill_super() fails
when the block size of the device is larger than 2048. So the Logical
Sector has 2048 bytes on every volume that isofs mounts.

If the check used the option, its default of 1024 would make the check
reject every record that crosses a 1024-byte block, which is what
happens now.

> So can mount option block={512|1024|2048} still have an effect at all ?

I see one effect. The mount fails when the logical block size of the
volume is smaller than the option:

if (orig_zonesize < opt->blocksize)
goto out_bad_size;

I tried images of the three sizes from the script with every value of
the option, on a loop device, on v7.2, on v7.3-rc6 and with the patch:

lbs512.iso mounts only with block=512
lbs1024.iso mounts without the option, with block=512 and with
block=1024, but not with block=2048
lbs2048.iso mounts with every value

A failed mount logs, for example:

ISOFS: Logical zone size(512) < hardware blocksize(1024)

When the mount succeeds, the result of "ls /mnt | wc -l" does not
depend on the option. It is 40 on v7.2 and with the patch. On v7.3-rc6
it is 9, 19 and 40 for the three images.

Thanks,
Hengyu