Subject: kern/7951: cd9660 does not understand Microsoft Joliet extensions
To: None <gnats-bugs@gnats.netbsd.org>
From: None <kuebart@mathematik.uni-ulm.de>
List: netbsd-bugs
Date: 07/09/1999 18:22:50
>Number:         7951
>Category:       kern
>Synopsis:       Read Microsoft Joliet extensions from CDs if available
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Fri Jul  9 18:20:00 1999
>Last-Modified:
>Originator:     Joachim Kuebart
>Organization:

>Release:        Thu Jul  8 13:51:47 UTC 1999
>Environment:
	i386, NetBSD-current (ELF)
	
System: NetBSD yacht 1.4E NetBSD 1.4E (YACHT) #34: Sat Jul 10 02:07:59 CEST 1999 joki@yacht:/usr/src/sys/arch/i386/compile/YACHT i386


>Description:
	Some CDs use Microsoft Joliet extensions for long filenames
	and Unicode characters in file names.
>How-To-Repeat:
	Mount a current Windows-originated CD. Either you will see no
	files at all or just short file names that have been written
	to the "compatibility tree".
>Fix:
	The following patch allows to read the long file names from
	Joliet extensions. Unicode characters are not supported.

	I have been using a similar version of the patch on FreeBSD
	for quite a while with no trouble.

Index: cd9660_extern.h
===================================================================
RCS file: /home/cvs/netbsd/src/sys/isofs/cd9660/cd9660_extern.h,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- cd9660_extern.h	1999/06/14 20:07:07	1.1.1.1
+++ cd9660_extern.h	1999/07/10 00:52:03	1.3
@@ -53,6 +53,7 @@
 
 struct iso_mnt {
 	int im_flags;
+	int im_joliet_level;
 
 	struct mount *im_mountp;
 	dev_t im_dev;
@@ -106,6 +107,7 @@
 extern int (**cd9660_specop_p) __P((void *));
 extern int (**cd9660_fifoop_p) __P((void *));
 
-int isofncmp __P((const u_char *, int, const u_char *, int));
-void isofntrans __P((u_char *, int, u_char *, u_short *, int, int));
+int isochar __P((const u_char *, const u_char *, int, u_char *));
+int isofncmp __P((const u_char *, int, const u_char *, int, int));
+void isofntrans __P((u_char *, int, u_char *, u_short *, int, int, int));
 ino_t isodirino __P((struct iso_directory_record *, struct iso_mnt *));
Index: cd9660_lookup.c
===================================================================
RCS file: /home/cvs/netbsd/src/sys/isofs/cd9660/cd9660_lookup.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -u -r1.1.1.2 -r1.2
--- cd9660_lookup.c	1999/07/09 11:38:51	1.1.1.2
+++ cd9660_lookup.c	1999/07/09 21:15:31	1.2
@@ -321,7 +321,8 @@
 					    || ep->name[0] != 0)
 						goto notfound;
 				} else if (!(res = isofncmp(name,len,
-							    ep->name,namelen))) {
+						   ep->name,namelen,
+						   imp->im_joliet_level))) {
 					if (isonum_711(ep->flags)&2)
 						ino = isodirino(ep, imp);
 					else
Index: cd9660_mount.h
===================================================================
RCS file: /home/cvs/netbsd/src/sys/isofs/cd9660/cd9660_mount.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- cd9660_mount.h	1999/06/14 20:07:07	1.1.1.1
+++ cd9660_mount.h	1999/07/09 21:15:31	1.2
@@ -50,3 +50,4 @@
 #define	ISOFSMNT_NORRIP	0x00000001	/* disable Rock Ridge Ext.*/
 #define	ISOFSMNT_GENS	0x00000002	/* enable generation numbers */
 #define	ISOFSMNT_EXTATT	0x00000004	/* enable extended attributes */
+#define	ISOFSMNT_NOJOLIET 0x00000008	/* disable Joliet extensions */
Index: cd9660_rrip.c
===================================================================
RCS file: /home/cvs/netbsd/src/sys/isofs/cd9660/cd9660_rrip.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- cd9660_rrip.c	1999/06/14 20:07:07	1.1.1.1
+++ cd9660_rrip.c	1999/07/10 00:52:03	1.3
@@ -298,17 +298,19 @@
 {
 	struct iso_directory_record *isodir = v;
 
-	strcpy(ana->outbuf, "..");
-	switch (*isodir->name) {
+	isofntrans(isodir->name, isonum_711(isodir->name_len),
+		   ana->outbuf, ana->outlen,
+		   1, isonum_711(isodir->flags) & 4,
+		   ana->imp->im_joliet_level);
+	switch (ana->outbuf[0]) {
 	default:
-		isofntrans(isodir->name, isonum_711(isodir->name_len),
-			   ana->outbuf, ana->outlen,
-			   1, isonum_711(isodir->flags) & 4);
 		break;
 	case 0:
+		ana->outbuf[0] = '.';
 		*ana->outlen = 1;
 		break;
 	case 1:
+		strcpy(ana->outbuf, "..");
 		*ana->outlen = 2;
 		break;
 	}
@@ -518,6 +520,7 @@
 	register ISO_SUSP_HEADER *pend;
 	struct buf *bp = NULL;
 	char *pwhead;
+	u_char c;
 	int result;
 	
 	/*
@@ -527,10 +530,10 @@
 	pwhead = isodir->name + isonum_711(isodir->name_len);
 	if (!(isonum_711(isodir->name_len) & 1))
 		pwhead++;
+	isochar(isodir->name, pwhead, ana->imp->im_joliet_level, &c);
 	
 	/* If it's not the '.' entry of the root dir obey SP field */
-	if (*isodir->name != 0
-	    || isonum_733(isodir->extent) != ana->imp->root_extent)
+	if (c != 0 || isonum_733(isodir->extent) != ana->imp->root_extent)
 		pwhead += ana->imp->rr_skip;
 	else
 		pwhead += ana->imp->rr_skip0;
@@ -648,6 +651,7 @@
 {
 	ISO_RRIP_ANALYZE analyze;
 	RRIP_TABLE *tab;
+	u_char c;
 	
 	analyze.outbuf = outbuf;
 	analyze.outlen = outlen;
@@ -657,9 +661,10 @@
 	analyze.fields = ISO_SUSP_ALTNAME | ISO_SUSP_RELDIR | ISO_SUSP_CLINK | ISO_SUSP_PLINK;
 	*outlen = 0;
 	
+	isochar(isodir->name, isodir->name + isonum_711(isodir->name_len),
+		imp->im_joliet_level, &c);
 	tab = rrip_table_getname;
-	if (*isodir->name == 0
-	    || *isodir->name == 1) {
+	if (c == 0 || c == 1) {
 		cd9660_rrip_defname(isodir, &analyze);
 		
 		analyze.fields &= ~ISO_SUSP_ALTNAME;
Index: cd9660_util.c
===================================================================
RCS file: /home/cvs/netbsd/src/sys/isofs/cd9660/cd9660_util.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- cd9660_util.c	1999/06/14 20:07:07	1.1.1.1
+++ cd9660_util.c	1999/07/10 00:52:03	1.3
@@ -59,21 +59,51 @@
 #include <isofs/cd9660/cd9660_extern.h>
 
 /*
+ * Get one character out of an iso filename
+ * Return number of bytes consumed
+ */
+int
+isochar(isofn, isoend, joliet_level, c)
+	const u_char *isofn;
+	const u_char *isoend;
+	int joliet_level;
+	u_char *c;
+{
+	*c = *isofn++;
+	if (joliet_level == 0 || isofn == isoend)
+		/* (00) and (01) are one byte in Joliet, too */
+		return 1;
+
+	/* No Unicode support yet :-( */
+	switch (*c) {
+	default:
+		*c = '?';
+		break;
+	case '\0':
+		*c = *isofn;
+		break;
+	}
+	return 2;
+}
+
+/*
  * translate and compare a filename
  * Note: Version number plus ';' may be omitted.
  */
 int
-isofncmp(fn, fnlen, isofn, isolen)
+isofncmp(fn, fnlen, isofn, isolen, joliet_level)
 	const u_char *fn, *isofn;
-	int fnlen, isolen;
+	int fnlen, isolen, joliet_level;
 {
 	int i, j;
 	char c;
-	
+	const u_char *isoend = isofn + isolen;
+
 	while (--fnlen >= 0) {
-		if (--isolen < 0)
+		if (isofn == isoend)
 			return *fn;
-		if ((c = *isofn++) == ';') {
+		isofn += isochar(isofn, isoend, joliet_level, &c);
+		if (c == ';') {
 			switch (*fn++) {
 			default:
 				return *--fn;
@@ -87,7 +117,9 @@
 					return -1;
 				}
 			}
-			for (j = 0; --isolen >= 0; j = j * 10 + *isofn++ - '0');
+			for (j = 0; isofn != isoend; j = j * 10 + c - '0')
+				isofn += isochar(isofn, isoend,
+						 joliet_level, &c);
 			return i - j;
 		}
 		if (((u_char) c) != *fn) {
@@ -103,13 +135,18 @@
 		}
 		fn++;
 	}
-	if (isolen > 0) {
-		switch (*isofn) {
+	if (isofn != isoend) {
+		isofn += isochar(isofn, isoend, joliet_level, &c);
+		switch (c) {
 		default:
 			return -1;
 		case '.':
-			if (isofn[1] != ';')
-				return -1;
+			if (isofn != isoend) {
+				isochar(isofn, isoend, joliet_level, &c);
+				if (c == ';')
+					return 0;
+			}
+			return -1;
 		case ';':
 			return 0;
 		}
@@ -121,30 +158,33 @@
  * translate a filename
  */
 void
-isofntrans(infn, infnlen, outfn, outfnlen, original, assoc)
+isofntrans(infn, infnlen, outfn, outfnlen, original, assoc, joliet_level)
 	u_char *infn, *outfn;
 	int infnlen;
 	u_short *outfnlen;
 	int original;
 	int assoc;
+	int joliet_level;
 {
 	int fnidx = 0;
+	u_char *infnend = infn + infnlen;
 	
 	if (assoc) {
 		*outfn++ = ASSOCCHAR;
 		fnidx++;
-		infnlen++;
 	}
-	for (; fnidx < infnlen; fnidx++) {
-		char c = *infn++;
-		
-		if (!original && c >= 'A' && c <= 'Z')
+	for (; infn != infnend; fnidx++) {
+		char c;
+
+		infn += isochar(infn, infnend, joliet_level, &c);
+
+		if (!original && joliet_level == 0 && c >= 'A' && c <= 'Z')
 			*outfn++ = c + ('a' - 'A');
-		else if (!original && c == '.' && *infn == ';')
-			break;
-		else if (!original && c == ';')
+		else if (!original && c == ';') {
+			if (fnidx > 0 && outfn[-1] == '.')
+				fnidx--;
 			break;
-		else
+		} else
 			*outfn++ = c;
 	}
 	*outfnlen = fnidx;
Index: cd9660_vfsops.c
===================================================================
RCS file: /home/cvs/netbsd/src/sys/isofs/cd9660/cd9660_vfsops.c,v
retrieving revision 1.1.1.2
retrieving revision 1.3
diff -u -r1.1.1.2 -r1.3
--- cd9660_vfsops.c	1999/07/09 11:38:52	1.1.1.2
+++ cd9660_vfsops.c	1999/07/10 00:52:03	1.3
@@ -242,7 +242,7 @@
 	struct iso_args *argp;
 {
 	register struct iso_mnt *isomp = (struct iso_mnt *)0;
-	struct buf *bp = NULL;
+	struct buf *bp = NULL, *pribp = NULL, *supbp = NULL;
 	dev_t dev = devvp->v_rdev;
 	int error = EINVAL;
 	int needclose = 0;
@@ -250,8 +250,10 @@
 	extern struct vnode *rootvp;
 	int iso_bsize;
 	int iso_blknum;
+	int joliet_level;
 	struct iso_volume_descriptor *vdp;
 	struct iso_primary_descriptor *pri;
+	struct iso_supplementary_descriptor *sup;
 	struct iso_directory_record *rootp;
 	int logical_block_size;
 	int sess = 0;
@@ -301,23 +303,70 @@
 			error = EINVAL;
 			goto out;
 		}
-		
-		if (isonum_711 (vdp->type) == ISO_VD_END) {
-			error = EINVAL;
-			goto out;
+
+		switch (isonum_711(vdp->type)) {
+		case ISO_VD_PRIMARY:
+			if (pribp == NULL) {
+				pribp = bp;
+				bp = NULL;
+			}
+			break;
+
+		case ISO_VD_SUPPLEMENTARY:
+			if (supbp == NULL) {
+				supbp = bp;
+				bp = NULL;
+			}
+			break;
+
+		default:
+			break;
 		}
-		
-		if (isonum_711 (vdp->type) == ISO_VD_PRIMARY)
+
+		if (isonum_711 (vdp->type) == ISO_VD_END) {
+			brelse(bp);
+			bp = NULL;
 			break;
-		brelse(bp);
+		}
+
+		if (bp != NULL) {
+			brelse(bp);
+			bp = NULL;
+		}
 	}
-	
-	if (isonum_711 (vdp->type) != ISO_VD_PRIMARY) {
+
+	/* Check the Joliet Extension support */
+	joliet_level = 0;
+	if ((argp->flags & ISOFSMNT_NOJOLIET) == 0 && supbp != NULL) {
+		sup = (struct iso_supplementary_descriptor *)supbp->b_data;
+
+		if ((isonum_711(sup->flags) & 1) == 0) {
+			if (memcmp(sup->escape, "%/@", 3) == 0)
+				joliet_level = 1;
+			if (memcmp(sup->escape, "%/C", 3) == 0)
+				joliet_level = 2;
+			if (memcmp(sup->escape, "%/E", 3) == 0)
+				joliet_level = 3;
+		}
+		if (joliet_level != 0) {
+			if (pribp != NULL)
+				brelse(pribp);
+			pribp = supbp;
+			supbp = NULL;
+		}
+	}
+
+	if (supbp != NULL) {
+		brelse(supbp);
+		supbp = NULL;
+	}
+
+	if (pribp == NULL) {
 		error = EINVAL;
 		goto out;
 	}
-	
-	pri = (struct iso_primary_descriptor *)vdp;
+
+	pri = (struct iso_primary_descriptor *)pribp->b_data;
 	
 	logical_block_size = isonum_723 (pri->logical_block_size);
 	
@@ -336,15 +385,16 @@
 	memcpy(isomp->root, rootp, sizeof(isomp->root));
 	isomp->root_extent = isonum_733 (rootp->extent);
 	isomp->root_size = isonum_733 (rootp->size);
+	isomp->im_joliet_level = joliet_level;
 	
 	isomp->im_bmask = logical_block_size - 1;
 	isomp->im_bshift = 0;
 	while ((1 << isomp->im_bshift) < isomp->logical_block_size)
 		isomp->im_bshift++;
 	
-	bp->b_flags |= B_AGE;
-	brelse(bp);
-	bp = NULL;
+	pribp->b_flags |= B_AGE;
+	brelse(pribp);
+	pribp = NULL;
 	
 	mp->mnt_data = (qaddr_t)isomp;
 	mp->mnt_stat.f_fsid.val[0] = (long)dev;
@@ -382,7 +432,8 @@
 		brelse(bp);
 		bp = NULL;
 	}
-	isomp->im_flags = argp->flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS|ISOFSMNT_EXTATT);
+	isomp->im_flags = argp->flags & (ISOFSMNT_NORRIP | ISOFSMNT_GENS |
+					 ISOFSMNT_EXTATT | ISOFSMNT_NOJOLIET);
 	switch (isomp->im_flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS)) {
 	default:
 	    isomp->iso_ftype = ISO_FTYPE_DEFAULT;
@@ -399,6 +450,10 @@
 out:
 	if (bp)
 		brelse(bp);
+	if (pribp)
+		brelse(pribp);
+	if (supbp)
+		brelse(supbp);
 	if (needclose)
 		(void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
 	if (isomp) {
Index: cd9660_vnops.c
===================================================================
RCS file: /home/cvs/netbsd/src/sys/isofs/cd9660/cd9660_vnops.c,v
retrieving revision 1.1.1.2
retrieving revision 1.3
diff -u -r1.1.1.2 -r1.3
--- cd9660_vnops.c	1999/07/09 11:38:52	1.1.1.2
+++ cd9660_vnops.c	1999/07/10 00:52:03	1.3
@@ -550,21 +550,23 @@
 				error = iso_uiodir(idp,&idp->current,idp->curroff);
 			break;
 		default:	/* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 */
-			strcpy(idp->current.d_name,"..");
-			switch (ep->name[0]) {
+			isofntrans(ep->name,idp->current.d_namlen,
+				   idp->current.d_name, &namelen,
+				   imp->iso_ftype == ISO_FTYPE_9660,
+				   isonum_711(ep->flags)&4,
+				   imp->im_joliet_level);
+			switch (idp->current.d_name[0]) {
 			case 0:
+				idp->current.d_name[0] = '.';
 				idp->current.d_namlen = 1;
 				error = iso_uiodir(idp,&idp->current,idp->curroff);
 				break;
 			case 1:
+				strcpy(idp->current.d_name,"..");
 				idp->current.d_namlen = 2;
 				error = iso_uiodir(idp,&idp->current,idp->curroff);
 				break;
 			default:
-				isofntrans(ep->name,idp->current.d_namlen,
-					   idp->current.d_name, &namelen,
-					   imp->iso_ftype == ISO_FTYPE_9660,
-					   isonum_711(ep->flags)&4);
 				idp->current.d_namlen = (u_char)namelen;
 				if (imp->iso_ftype == ISO_FTYPE_DEFAULT)
 					error = iso_shipdir(idp);
Index: iso.h
===================================================================
RCS file: /home/cvs/netbsd/src/sys/isofs/cd9660/iso.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- iso.h	1999/06/14 20:07:07	1.1.1.1
+++ iso.h	1999/07/09 21:15:31	1.2
@@ -57,6 +57,7 @@
 
 /* volume descriptor types */
 #define ISO_VD_PRIMARY 1
+#define ISO_VD_SUPPLEMENTARY 2
 #define ISO_VD_END 255
 
 #define ISO_STANDARD_ID "CD001"
@@ -98,6 +99,42 @@
 	char unused5			[ISODCL (1396, 2048)];
 };
 #define ISO_DEFAULT_BLOCK_SIZE		2048
+
+struct iso_supplementary_descriptor {
+	char type			[ISODCL (  1,	1)]; /* 711 */
+	char id				[ISODCL (  2,	6)];
+	char version			[ISODCL (  7,	7)]; /* 711 */
+	char flags			[ISODCL (  8,	8)]; /* 711? */
+	char system_id			[ISODCL (  9,  40)]; /* achars */
+	char volume_id			[ISODCL ( 41,  72)]; /* dchars */
+	char unused2			[ISODCL ( 73,  80)];
+	char volume_space_size		[ISODCL ( 81,  88)]; /* 733 */
+	char escape			[ISODCL ( 89, 120)];
+	char volume_set_size		[ISODCL (121, 124)]; /* 723 */
+	char volume_sequence_number	[ISODCL (125, 128)]; /* 723 */
+	char logical_block_size		[ISODCL (129, 132)]; /* 723 */
+	char path_table_size		[ISODCL (133, 140)]; /* 733 */
+	char type_l_path_table		[ISODCL (141, 144)]; /* 731 */
+	char opt_type_l_path_table	[ISODCL (145, 148)]; /* 731 */
+	char type_m_path_table		[ISODCL (149, 152)]; /* 732 */
+	char opt_type_m_path_table	[ISODCL (153, 156)]; /* 732 */
+	char root_directory_record	[ISODCL (157, 190)]; /* 9.1 */
+	char volume_set_id		[ISODCL (191, 318)]; /* dchars */
+	char publisher_id		[ISODCL (319, 446)]; /* achars */
+	char preparer_id		[ISODCL (447, 574)]; /* achars */
+	char application_id		[ISODCL (575, 702)]; /* achars */
+	char copyright_file_id		[ISODCL (703, 739)]; /* 7.5 dchars */
+	char abstract_file_id		[ISODCL (740, 776)]; /* 7.5 dchars */
+	char bibliographic_file_id	[ISODCL (777, 813)]; /* 7.5 dchars */
+	char creation_date		[ISODCL (814, 830)]; /* 8.4.26.1 */
+	char modification_date		[ISODCL (831, 847)]; /* 8.4.26.1 */
+	char expiration_date		[ISODCL (848, 864)]; /* 8.4.26.1 */
+	char effective_date		[ISODCL (865, 881)]; /* 8.4.26.1 */
+	char file_structure_version	[ISODCL (882, 882)]; /* 711 */
+	char unused4			[ISODCL (883, 883)];
+	char application_data		[ISODCL (884, 1395)];
+	char unused5			[ISODCL (1396, 2048)];
+};
 
 struct iso_directory_record {
 	char length			[ISODCL (1, 1)]; /* 711 */
Index: /usr/src/sbin/mount_cd9660/mount_cd9660.c
===================================================================
RCS file: /home/cvs/netbsd/src/sbin/mount_cd9660/mount_cd9660.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -u -r1.1.1.2 -r1.2
--- mount_cd9660.c	1999/06/28 23:40:40	1.1.1.2
+++ mount_cd9660.c	1999/07/10 00:53:44	1.2
@@ -86,13 +86,16 @@
 	char *dev, *dir;
 
 	mntflags = opts = 0;
-	while ((ch = getopt(argc, argv, "ego:r")) != -1)
+	while ((ch = getopt(argc, argv, "egjo:r")) != -1)
 		switch (ch) {
 		case 'e':
 			opts |= ISOFSMNT_EXTATT;
 			break;
 		case 'g':
 			opts |= ISOFSMNT_GENS;
+			break;
+		case 'j':
+			opts |= ISOFSMNT_NOJOLIET;
 			break;
 		case 'o':
 			getmntopts(optarg, mopts, &mntflags, 0);
>Audit-Trail:
>Unformatted: