NetBSD-Bugs archive

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

port-sgimips/60587: sgimips: hybrid multisession CDs fail to mount



>Number:         60587
>Category:       port-sgimips
>Synopsis:       sgimips: hybrid multisession CDs fail to mount
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    port-sgimips-maintainer
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Aug 14 18:30:00 +0000 2026
>Originator:     Imre Kaloz
>Release:        HEAD (also tested 10.1 and 11.0)
>Organization:
>Environment:
NetBSD netbsd 11.0 NetBSD 11.0 (GENERIC32_IP2x) #0: Thu Jul 30 15:23:12 UTC 2026  mkrepro%mkrepro.NetBSD.org@localhost:/usr/src/sys/arch/sgimips/compile/GENERIC32_IP2x sgimips
>Description:
cd(4) stores an MMC disc's last-session offset in p_cdsession, which unions with p_fsize. On sgimips, disklabel_sgimips_to_bsd() then maps the SGI volume-header partitions to FS_BSDFFS and writes p_fsize=1024 into the same word, so iso_mountfs() computes 1024 + 1024 + 16 = 2064 for the PVD search on the official install CD (the PVD is at LBA 1040) and mount fails with EINVAL. With that fixed, cd9660 still applies the session offset only to the PVD probe and volume_space_size, never to the extents it reads from the mounted filesystem, so mount succeeds but every directory read lands at its session-relative block number and the tree is empty.

The proposed patch fixes both: disklabel_sgimips_to_bsd() skips the volume-header copy for FS_ISO9660 partitions on D_SCSI_MMC labels, and cd9660 adds the offset wherever a raw extent becomes a block address ? including directory inode numbers, while file inode numbers derive from already-adjusted parent reads and take none.

The failing mount is sysinst's own default install path ("choose CD-ROM as the install medium"). On current, after r1.106 (kern/59783) rejects the clobbered p_cdsession, the official ISO mounts by coincidence ? its volume header independently encodes offset 1024, numerically the session start, turning the former double-add into a single correct add; disklabel still misreports the partition as 4.2BSD with overlap warnings, and differently-mastered media still fail.
>How-To-Repeat:
On an Indy, attach the official NetBSD/sgimips install ISO (tested 10.1 and 11.0) as cd0.
disklabel cd0 ? partition a shows fstype 4.2BSD with fsize/frag/cpg 1024/8192/16 and "partitions a and c overlap".
mount_cd9660 /dev/cd0a /mnt ? fails with EINVAL.
With only the disksubr.c half of the patch: mount succeeds, ls /mnt is empty.
With the full patch: ls lists the distribution sets and file contents match the ISO (cksum).
>Fix:
Signed-off-by: Imre Kaloz <kaloz%dune.hu@localhost>

diff --git a/sys/arch/sgimips/sgimips/disksubr.c b/sys/arch/sgimips/sgimips/disksubr.c
index 3742e75..e70095a 100644
--- a/sys/arch/sgimips/sgimips/disksubr.c
+++ b/sys/arch/sgimips/sgimips/disksubr.c
@@ -249,6 +249,15 @@ disklabel_sgimips_to_bsd(struct sgi_boot_block *vh, struct disklabel *lp)
 		bp = partition_map[i].bsd_part;
 
 		lpp = &lp->d_partitions[bp];
+
+		/*
+		 * MMC media: cd(4)'s label already carries the session
+		 * offset; the volume header describes disks.
+		 */
+		if ((lp->d_flags & D_SCSI_MMC) != 0 &&
+		    lpp->p_fstype == FS_ISO9660)
+			continue;
+
 		lpp->p_offset = vh->partitions[mp].first;
 		/* XXX ARCS ignores dp_secbytes on calculating offsets */
 		if (lp->d_secsize > DEV_BSIZE)
diff --git a/sys/fs/cd9660/cd9660_bmap.c b/sys/fs/cd9660/cd9660_bmap.c
index c0fea64..b563722 100644
--- a/sys/fs/cd9660/cd9660_bmap.c
+++ b/sys/fs/cd9660/cd9660_bmap.c
@@ -82,7 +82,7 @@ cd9660_bmap(void *v)
 	 * Compute the requested block number
 	 */
 	bshift = ip->i_mnt->im_bshift;
-	*ap->a_bnp = (ip->iso_start + lblkno) << (bshift - DEV_BSHIFT);
+	*ap->a_bnp = cd9660_extent_daddr(ip->i_mnt, ip->iso_start + lblkno);
 
 	/*
 	 * Determine maximum number of readahead blocks following the
diff --git a/sys/fs/cd9660/cd9660_extern.h b/sys/fs/cd9660/cd9660_extern.h
index 3edd85e..8356777 100644
--- a/sys/fs/cd9660/cd9660_extern.h
+++ b/sys/fs/cd9660/cd9660_extern.h
@@ -80,6 +80,8 @@ struct iso_mnt {
 
 	int rr_skip;
 	int rr_skip0;
+
+	int im_sess;		/* session start, in logical blocks */
 };
 
 #define VFSTOISOFS(mp)	((struct iso_mnt *)((mp)->mnt_data))
@@ -89,6 +91,14 @@ struct iso_mnt {
 #define cd9660_lblkno(imp, loc)	((loc) >> (imp)->im_bshift)
 #define cd9660_blksize(imp, ip, lbn)	((imp)->logical_block_size)
 
+/*
+ * Convert a raw on-disc extent to a device block number.  A raw extent
+ * is always session-relative; an ino_t, once built, is not -- do not
+ * apply this to one.
+ */
+#define cd9660_extent_daddr(imp, ext) \
+	(((ext) + (imp)->im_sess) << ((imp)->im_bshift - DEV_BSHIFT))
+
 #ifdef _KERNEL
 
 VFS_PROTOS(cd9660);
diff --git a/sys/fs/cd9660/cd9660_node.c b/sys/fs/cd9660/cd9660_node.c
index c03e978..53cc7e4 100644
--- a/sys/fs/cd9660/cd9660_node.c
+++ b/sys/fs/cd9660/cd9660_node.c
@@ -336,7 +336,9 @@ isodirino(struct iso_directory_record *isodir, struct iso_mnt *imp)
 	 * and also a calculation of the isodir pointer
 	 * from an inode in cd9660_vnops.c:cd9660_readlink()
 	 */
+	/* isodir->extent is session-relative; cd9660_lookup()'s ino is not. */
 	ino = ((ino_t)isonum_733(isodir->extent) +
-		isonum_711(isodir->ext_attr_length)) << imp->im_bshift;
+		isonum_711(isodir->ext_attr_length) +
+		imp->im_sess) << imp->im_bshift;
 	return ino;
 }
diff --git a/sys/fs/cd9660/cd9660_rrip.c b/sys/fs/cd9660/cd9660_rrip.c
index bf45357..5be570a 100644
--- a/sys/fs/cd9660/cd9660_rrip.c
+++ b/sys/fs/cd9660/cd9660_rrip.c
@@ -320,7 +320,8 @@ cd9660_rrip_pclink(void *v, ISO_RRIP_ANALYZE *ana)
 {
 	ISO_RRIP_CLINK  *p = v;
 
-	*ana->inump = isonum_733(p->dir_loc) << ana->imp->im_bshift;
+	*ana->inump = (isonum_733(p->dir_loc) + ana->imp->im_sess) <<
+	    ana->imp->im_bshift;
 	ana->fields &= ~(ISO_SUSP_CLINK | ISO_SUSP_PLINK);
 	return *p->h.type == 'C' ? ISO_SUSP_CLINK : ISO_SUSP_PLINK;
 }
@@ -553,7 +554,7 @@ cd9660_rrip_loop(struct iso_directory_record *isodir, ISO_RRIP_ANALYZE *ana,
 			if (ana->iso_ce_blk >= ana->imp->volume_space_size
 			    || ana->iso_ce_off + ana->iso_ce_len > ana->imp->logical_block_size
 			    || bread(ana->imp->im_devvp,
-				     ana->iso_ce_blk << (ana->imp->im_bshift - DEV_BSHIFT),
+				     cd9660_extent_daddr(ana->imp, ana->iso_ce_blk),
 				     ana->imp->logical_block_size,
 				     0, &bp))
 				/* what to do now? */
diff --git a/sys/fs/cd9660/cd9660_vfsops.c b/sys/fs/cd9660/cd9660_vfsops.c
index 6d9e443..318e9ec 100644
--- a/sys/fs/cd9660/cd9660_vfsops.c
+++ b/sys/fs/cd9660/cd9660_vfsops.c
@@ -485,6 +485,7 @@ iso_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l,
 	}
 
 	isomp->volume_space_size += sess;
+	isomp->im_sess = sess;
 
 	brelse(pribp, BC_AGE);
 	pribp = NULL;
@@ -514,8 +515,8 @@ iso_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l,
 		struct iso_directory_record *rootp;
 
 		if ((error = bread(isomp->im_devvp,
-				   (isomp->root_extent + ext_attr_length) <<
-				   (isomp->im_bshift - DEV_BSHIFT),
+				   cd9660_extent_daddr(isomp,
+				       isomp->root_extent + ext_attr_length),
 				   isomp->logical_block_size,
 				   0, &bp)) != 0)
 		    goto out;
diff --git a/sys/fs/cd9660/cd9660_vnops.c b/sys/fs/cd9660/cd9660_vnops.c
index 6262a6c..314ce73 100644
--- a/sys/fs/cd9660/cd9660_vnops.c
+++ b/sys/fs/cd9660/cd9660_vnops.c
@@ -614,8 +614,8 @@ cd9660_readlink(void *v)
 	 * Get parents directory record block that this inode included.
 	 */
 	error = bread(imp->im_devvp,
-		      (ip->i_number >> imp->im_bshift) <<
-		      (imp->im_bshift - DEV_BSHIFT),
+		      cd9660_lblkno(imp, ip->i_number) <<
+			  (imp->im_bshift - DEV_BSHIFT),
 		      imp->logical_block_size, 0, &bp);
 	if (error) {
 		return (EINVAL);





Home | Main Index | Thread Index | Old Index