NetBSD Problem Report #48959

From www@NetBSD.org  Wed Jul  2 09:47:36 2014
Return-Path: <www@NetBSD.org>
Received: from mail.netbsd.org (mail.netbsd.org [149.20.53.66])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(Client CN "mail.netbsd.org", Issuer "Postmaster NetBSD.org" (verified OK))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 5D855A6546
	for <gnats-bugs@gnats.NetBSD.org>; Wed,  2 Jul 2014 09:47:36 +0000 (UTC)
Message-Id: <20140702094734.35076A6544@mollari.NetBSD.org>
Date: Wed,  2 Jul 2014 09:47:34 +0000 (UTC)
From: scdbackup@gmx.net
Reply-To: scdbackup@gmx.net
To: gnats-bugs@NetBSD.org
Subject: Misrepresentation of files of 4 GiB or larger in cd9660
X-Send-Pr-Version: www-1.0

>Number:         48959
>Category:       kern
>Synopsis:       Misrepresentation of files of 4 GiB or larger in cd9660
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Jul 02 09:50:00 +0000 2014
>Last-Modified:  Wed Jul 02 10:05:00 +0000 2014
>Originator:     Thomas Schmitt
>Release:        6.99.44
>Organization:
>Environment:
NetBSD netbsdcur.local 6.99.44 NetBSD 6.99.44 (GENERIC) #32: 
Wed Jul  2 07:55:04 UTC 2014  ...:/usr/obj/sys/arch/i386/compile/GENERIC i386

>Description:
Data files of size 4 GiB or larger have to be represented in ISO 9660
in a form which is misrepresented by the cd9660 filesystem driver.

  netbsd# mount_cd9660 /dev/cd0a /mnt/iso
  netbsd# ls -l /mnt/iso/my
  total 16777208
  -rw-r--r--  1 thomas  dbus  4294965248 May  6 15:30 large_file
  -rw-r--r--  1 thomas  dbus  4294965248 May  6 15:30 large_file

Whereas the Debian 6 host operating system of my NetBSD VM says

  $ ls -l /mnt/iso/my
  total 4227906
  -rw-r--r-- 1 thomas thomas 4329375744 May  6 17:30 large_file

Mounting without Rock Ridge POSIX names yields a different result:

  netbsd# mount_cd9660 -o norrip /dev/cd0a /mnt/iso
  netbsd# ls -l /mnt/iso/my
  total 67208
  -r-xr-xr-x  1 root  wheel  34410496 May  6 15:30 large_file

which is indeed the missing end piece of the two identical start
pieces shown with Rock Ridge.
(The reason is unification code which is a precaution for handling
 the rarely used ISO 9660 feature of multiple File Versions.)

>How-To-Repeat:
The problem is demonstrated by

    http://scdbackup.webframe.org/large.iso.bz2

4470 bytes, MD5 7d78dc3efaec8ea3f1801335329f410d.
It inflates to 4,329,897,984 bytes.

Do this only if the fix of kern/48787 is applied.
If not, you will not get rid of the /mnt/iso mount point until reboot !

>Fix:
I have now implemented a changeset which corrects above problem.

An auxiliary text is available at
  http://scdbackup.webframe.org/cd9660_level3_notes.txt
It assesses ISO 9660 specs and current implementation, motivates 
the proposal for a model change in struct iso_node, and publishes
my (now nearly fulfilled) todo sheet.

The changeset anticipates the adoption of PR 48808 (mount option -s).

The goal is to let cd9660 recognize files with multiple file sections
and represent their multiple directory records as a single vnode with
a uniform byte space.

There shall be no duplicate filenames presented to VFS. If files with
equal names are found, then only the last one of those with the highest
ISO 9660 version number will be visible.
This guarantee depends on properly sorted ISO 9660 directories.
The decision which intervals of directory records form a single file
depends on properly set Multi-Extent flag bits.

The filesystem specific vnode.v_data struct iso_node needed a change
to represent the 1:n relation between file and file section.
This change caused code adjustments all over the code of cd9660.
It makes nearly full use of the 64 bits of NetBSD's ino_t and
employs kmem(9) memory for files with more than one section.

Several implementations of interface methods are affected:

- cd9660_readdir() serving as VOP_READDIR(9)

  The case of mount -o norrip,nogens already used a delivery function
  with delayed file candidates: cd9660_vnops.c : iso_shipdir().
  Originally it only had the task to find the youngest version of a
  ISO 9660 data file and to separate associated files from the files
  to which they are bound.
  Now it decides which directory records form a valid inode,
  counts the records of the same file, and skips over them.

- cd9660_lookup() serving as VOP_LOOKUP(9)

  mount -o norrip returned the last record of matching name,
  whereas -o rrip returned the first matching record.
  Now norrip with a healthy ISO 9660 filesystem drops only older
  versions of the same name.
  All three filesystem interpretation types now return the inode number
  based on the byte address of first record of the winning file and on
  its number of file sections.

- cd9660_loadvnode() serving as struct vfsops.vfs_loadvnode to equip
  vnodes with struct iso_node. 
  Used indirectly by VFS_VGET(9), VFS_FHTOVP(9), VOP_LOOKUP(9).

  If the ino_t input parameter indicates a number of file sections larger
  than one, then the created iso_node gets kmem(9) memory attached as
  iso_node.iso_sections.
  The iso_node.i_number will indicate a file section count larger than 1
  only if such memory is attached to the iso_node.

  Because the function was already quite long and my changes added
  more complexity, i outsourced several functions:

  - iso_read_next_isodir()
    enters the linked list of ISO 9660 directory records at the
    first record of a file and may then iterate over the list.
    It uses bread(9) and brelse(9) as appropriate.
    The caller has to make the decision whether the list is at its end
    or whether iso_read_next_isodir() may be called again.

  - iso_register_fsects()
    records the start blocks and byte sizes of one or more file sections.

  - isodir_read_isoextattr()
    reads the ISO 9660 Extended Attribute blocks, if present.

  Regrettably this makes the diff about cd9660_loadvnode() hard to read.

- cd9660_bmap() as VOP_BMAP(9)

  Nothing changes for files with a single file section.
  Those with more file sections will need a loop to find the section
  which holds the desired block. Similar to the case of a single section,
  the last section will be base of the resulting block address,
  regardless whether its size includes the input block.


---------------------------------------------------------------------

The changes are tested by an atf-like test script and two ISO images:

  http://scdbackup.webframe.org/t_cd9660_regression_v02.tgz

with content

  cd9660/t_cd9660_regression.sh
  cd9660/cd9660_rgr.image.at0.bz2.uue
  cd9660/cd9660_rgr.image.at2114008.bz2.uue
  cd9660/exoten.iso.bz2.uue

When executed by
  cd cd9660 && ./t_cd9660_regression.sh
the script runs 5 test cases
 - pr_kern_48787   modified atf test of Martin Husemann/Paul Goyette
 - rock_ridge_rgr  Rock Ridge regression test
 - mount_s         for mount_cd9660 option -s
 - large_file      for large file
 - exotic          for exotic or undigestible file situations

The first four cases are exercised with cd9660_rgr.image, which contains
an ISO 9660 filesystem with large data file, and examples for the
Rock Ridge POSIX file types regular, directory, block device, fifo,
symbolic link.
xorriso perception of Rock Ridge aspect:
  dr-x------    1 1000     0               0 May  6 15:31 '/'
  dr-x------    1 1000     0               0 May  3 14:58 '/dev'
  prw-------    1 1000     0               0 May 24 14:29 '/dev/test.fifo'
  br--------    1 1000     5            0,12 May 14 14:33 '/dev/wd1e'
  dr-x------    1 1000     1000            0 May  6 15:30 '/my'
  -r--------    1 1000     1000     4329375744 May  6 15:30 '/my/large_file'
  dr-x------    1 1000     0               0 Jan 19 14:41 '/reg'
  -r-x------    1 1000     0          133411 Jan 19 14:41 '/reg/tar'
  lr-x------    1 1000     0               0 May 24 14:29 '/reg/to_regfile' -> 'tar'
  -r--------    1 1000     1000            6 May  6 15:34 '/small_file'

exoten.iso challenges rather exotic situations. It began its life as
normal xorriso ISO and was manipulated by binary editing.
- File "01" exists in three versions.
  The last one "01.;3" has two sections of 4 KB each.
  The Rock Ridge names of those three versions differ, as they ought.
- File "07" has a first file section of 4095 bytes. It is therefore
  not digestible for cd9660_bmap()/VOP_BMAP(9).
- File "09" contains an ISO 9660 Extended Attribute block with
  user id 1234, group id 5678, and a timestamp of june 2014.
- File "12" has an associated file (Rock Ridge name "11").
  Associated files are shown by cd9660 with a leading "=".
  (Whether this is a wise choice is a different question.)

-o norrip,gens :
  netbsd$ ls -l /mnt/iso/exotic
  ls: 07.;1: Operation not supported
  total 224
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 00.;1
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 01.;1
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 01.;2
  -r-xr-xr-x  1 root  wheel  8192 May 26 08:00 01.;3
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 05.;1
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 08.;1
  -r-xr-xr-x  1 root  wheel  2048 May 26 08:00 09.;1
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 10.;1
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 12.;1
  ...
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 =12.;1

-o norrip :
  ls: 07: Operation not supported
  total 208
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 00
  -r-xr-xr-x  1 root  wheel  8192 May 26 08:00 01
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 05
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 08
  -r-xr-xr-x  1 root  wheel  2048 May 26 08:00 09
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 10
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 12
  ...
  -r-xr-xr-x  1 root  wheel  4096 May 26 08:00 =12

-o norrip,extatt
  ...
  -r-x--xr--  1 1234  5678  2048 Jun  4 07:51 09
  ...

default (Rock Ridge) :
  ls: 07: Operation not supported
  total 224
  -rw-r--r--  1 thomas  dbus  4096 May 26 08:00 00
  -rw-r--r--  1 thomas  dbus  4096 May 26 08:00 01
  -rw-r--r--  1 thomas  dbus  4096 May 26 08:00 02
  -rw-r--r--  1 thomas  dbus  8192 May 26 08:00 03
  -rw-r--r--  1 thomas  dbus  4096 May 26 08:00 05
  -rw-r--r--  1 thomas  dbus  4096 May 26 08:00 08
  -rw-r--r--  1 thomas  dbus  4096 May 26 08:00 10
  -rw-r--r--  1 thomas  dbus  4096 May 26 08:00 11
  -rw-r--r--  1 thomas  dbus  4096 May 26 08:00 12
  ...

----------------------------------------------------------------

API/ABI compatibility:

The ABI of struct iso_node was recently broken by Revision 1.16
of cd9660_node.h which removed an obsolete internal handle:
  -       LIST_ENTRY(iso_node) i_hash;

My change proposal is API/ABI compatible to Revision 1.16.

----------------------------------------------------------------

Remaining restrictions:

- ISO 9660 allows a file to be composed of multiple file sections
  with sizes which are not aligned to the filesystem block size.
  cd9660 demands that all but the last file section of a file must
  have sizes which are multiples of the block size. Usually 2 KiB.
  (Debian 6 GNU/Linux accepts unaligned file sections but messes
   up the content by truncating the inner section to block end,
   and filling up the file end by the lost number of bytes from
   the next data file. Not desirable.)

- My change imposes a deliberate limit of 128 on the number of sections
  per file. CD9660_FSECT_MAX can be adjusted in cd9660_node.h.

Remaining problems:

- The inode numbers of Rock Ridge PX are ignored. Hardlink siblings
  will get different ino_t values assigned.

- The name comparison for finding identical names is still not
  surely in sync underneath VOP_READDIR(9) and VOP_LOOKUP(9).
  It is done by two different functions in cd9660_util.c :
  isofntrans() and isofncmp(). For the sake of consistency, it
  would be desirable to unify them.
  I do not propose it now, because it has impact on lookup
  performance, has its own potential for regressions, and is not 
  urgently needed yet.

- I could not yet find ISO images or software which would provide
  test opportunities for ISO 9660 Associated Files or Extented
  Attributes (which are not related to getextattr(1)/extattr(9)).
  So i could only test my self-crafted examples in exoten.iso.

About the inode number inflation:

  Large data files get giant inode numbers, because the file section
  count is encoded above bit 48 of ino_t.

  The hardest reason why this information has to be encoded in ino_t,
  is the desire to implement method VFS_VGET(9). If VOP_LOOKUP(9) would
  be the only method which leads to creation of a vnode, then the address
  and count could be stored in some other members of struct iso_node.
  A simple EOPNOTSUPP would open this path.

  One could cut inode numbers to 32 bit and then port the cd9660
  improvements to FreeBSD. (Not that freebsd-hackers would be much
  interested in cd9660.)

Copyright:

  I do not plan to claim own copyright. But the old copyright texts in
  the cd9660 sources should probably be updated.
  I have seen in a test script
    # Copyright (c) 2014 The NetBSD Foundation, Inc.
  which would be well ok for me.

>Audit-Trail:
From: "Thomas Schmitt" <scdbackup@gmx.net>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: kern/48959
Date: Wed, 02 Jul 2014 12:00:46 +0200

 --- sys/fs/cd9660/cd9660_extern.h.ts.mount_s	2014-06-19 08:27:32.000000000 +0000
 +++ sys/fs/cd9660/cd9660_extern.h	2014-06-21 10:32:56.000000000 +0000
 @@ -83,6 +83,8 @@ struct iso_mnt {
  	int rr_skip0;

  	unsigned int im_ssector;
 +
 +	unsigned int root_start_block;
  };

  #define VFSTOISOFS(mp)	((struct iso_mnt *)((mp)->mnt_data))
 @@ -109,9 +111,13 @@ extern int (**cd9660_specop_p)(void *);
  extern int (**cd9660_fifoop_p)(void *);

  int isochar(const u_char *, const u_char *, int, u_int16_t *);
 -int isofncmp(const u_char *, size_t, const u_char *, size_t, int);
 -void isofntrans(const u_char *, int, u_char *, u_short *, int, int, int, int);
 +int isofncmp(const u_char *, size_t, const u_char *, size_t, int, int *);
 +void isofntrans(const u_char *, int, u_char *, u_short *, int *,
 +		int, int, int, int);
  ino_t isodirino(struct iso_directory_record *, struct iso_mnt *);
 +int isodir_read_isoextattr(struct iso_directory_record *,
 +			   struct iso_mnt *, struct buf **);
 +
  #endif /* _KERNEL */

  #endif /* _ISOFS_CD9660_CD9660_EXTERN_H_ */
 --- sys/fs/cd9660/cd9660_node.h.orig	2014-06-16 09:55:49.000000000 +0000
 +++ sys/fs/cd9660/cd9660_node.h	2014-06-21 09:17:18.000000000 +0000
 @@ -61,6 +61,16 @@ typedef	struct	{
  	dev_t		iso_rdev;	/* Major/Minor number for special */
  } ISO_RRIP_INODE;

 +
 +/* Announce the possibility of multiple file sections.
 + */
 +#define CD9660_MULTI_EXTENT	yes
 +
 +struct iso_file_section {
 +	unsigned long isofsc_size;	/* byte count */
 +	unsigned long isofsc_start;	/* block address */
 +};
 +
  struct iso_node {
  	struct	genfs_node i_gnode;
  	struct	vnode *i_vnode;	/* vnode associated with this inode */
 @@ -68,21 +78,94 @@ struct iso_node {
  	u_long	i_flag;		/* see below */
  	dev_t	i_dev;		/* device where inode resides */
  	ino_t	i_number;	/* the identity of the inode */
 -				/* we use the actual starting block of the file */
 +				/* see below:
 +				 *   "Information content of ino_t numbers"
 +				 */
  	struct	iso_mnt *i_mnt;	/* filesystem associated with this inode */
  	struct	lockf *i_lockf;	/* head of byte-level lock list */
  	doff_t	i_endoff;	/* end of useful stuff in directory */
  	doff_t	i_diroff;	/* offset in dir, where we found last entry */
  	doff_t	i_offset;	/* offset of free space in directory */
 -	ino_t	i_ino;		/* inode number of found directory */
 -
 -	unsigned long iso_extent;	/* extent of file */
 -	unsigned long i_size;
 -	unsigned long iso_start;		/* actual start of data of file (may be different */
 -				/* from iso_extent, if file has extended attributes) */
 +	ino_t	i_ino;		/* Effective inode number of lookup'd directory
 +				 * which possibly changes by RRIP relocation.
 +				 */
 +					/* These three numbers represent the
 +					 * first extent and file section.
 +					 * If CD9660_FSECT_FROM_INO(.i_number)
 +					 * is larger than 1, then .iso_sections
 +					 * is valid.
 +					 */
 +	unsigned long iso_extent;	/* will not be used by cd9660 */
 +	unsigned long i_size;		/* byte count */
 +	unsigned long iso_start;	/* block address */
  	ISO_RRIP_INODE  inode;
 +	struct iso_file_section *iso_sections;	/* Holds info about all file
 +						 * sections if
 +						 * CD9660_FSECT_FROM_INO(
 +						 *                   .i_number)
 +						 * is larger than 1.
 +						 */
  };

 +/* Information content of ino_t numbers:
 + *
 + * The inode numbers encode information which is sufficient to lookup the
 + * directory records which describe a particular file object:
 + *  48 bits byte address of first directory record.
 + *          Sufficient because the block size in struct iso_primary_descriptor
 + *          is a 16 bit number and block addresses are 32 bit numbers.
 + *  13 bits number of directory records of the same file to follow.
 + *          I.e. file section count - 1. Enough for filling a maximum fs
 + *          of 2-KiB blocks by 1 GiB sized file sections.
 + *   3 bits are still unused
 + */
 +#define CD9660_INO_ADR_BMASK	0xffffffffffff
 +#define CD9660_INO_FSECT_SHIFT	48
 +#define CD9660_INO_FSECT_BMASK	0x1fff
 +
 +/* For determining the byte address of the first iso_directory_record which
 + * led to the number ino.
 + */
 +#define CD9660_BYTEADR_FROM_INO(ino) \
 +		(((ino_t) (ino)) & CD9660_INO_ADR_BMASK)
 +
 +/* For determining the ISO block number of the byte address.
 + */
 +#define CD9660_BLOCK_FROM_INO(ino, bshift) \
 +		(CD9660_BYTEADR_FROM_INO(ino) >> (bshift))
 +
 +/* For determining the number of file sections resp. extents
 + * (related 1:1 to iso_directory_record).
 + */
 +#define CD9660_FSECT_FROM_INO(ino) \
 +		(((((ino_t) (ino)) >> CD9660_INO_FSECT_SHIFT) \
 +		  & CD9660_INO_FSECT_BMASK) + 1)
 +
 +/* A deliberate limit to avoid resource exhaustion by faulty or malicious
 + * filesystems. With 2 KiB block size and full sized file sections, a value
 + * of 128 allows files up to 512 GiB - 254 KiB - 1 byte.
 + */
 +#define CD9660_FSECT_MAX 128
 +
 +/* A function which should replace the inquiry of iso_node.i_size in
 + * programs which cannot keep themselves from inquiring entrails of
 + * struct iso_node.
 + */
 +#define CD9660_DATA_SIZE_FUNC \
 +off_t \
 +iso_data_count(struct iso_node *ip) \
 +{ \
 +	int i, nsect; \
 +	off_t sum = 0; \
 + \
 +	nsect = CD9660_FSECT_FROM_INO(ip->i_number); \
 +	if (nsect == 1) \
 +		return (off_t) ip->i_size; \
 +	for (i = 0; i < nsect; i++) \
 +		sum += ip->iso_sections[i].isofsc_size; \
 +	return sum; \
 +}
 +
  #define	i_forw		i_chain[0]
  #define	i_back		i_chain[1]

 @@ -130,5 +213,26 @@ void	cd9660_deftstamp(struct iso_directo
  int	cd9660_tstamp_conv7(const u_char *, struct timespec *);
  int	cd9660_tstamp_conv17(const u_char *, struct timespec *);

 +
 +/* This computes an ino_t from (block,byte) addresses with ISO 9660 block size.
 + * It is ok to have blockno == 0 and let byteoffset express the whole address.
 + */
 +#define CD9660_COMPUTE_INO(blockno, bshift, byteoffset, section_count) \
 +		((((((uint64_t) (blockno)) << (uint64_t) (bshift)) \
 +		   + (int64_t) (byteoffset)) & CD9660_INO_ADR_BMASK) \
 +		| ((((uint64_t) (section_count) - 1) & CD9660_INO_FSECT_BMASK) \
 +		   << CD9660_INO_FSECT_SHIFT))
 +
 +/* This is for ino_t from (block,byte) addresses with DEV_BSHIFT block size.
 + */
 +#define CD9660_COMPUTE_INO_DB(blockno, byteoffset, section_count) \
 +		(((dbtob((uint64_t) (blockno)) + (int64_t) (byteoffset)) \
 +		  & CD9660_INO_ADR_BMASK) \
 +		| ((((uint64_t) (section_count) - 1) & CD9660_INO_FSECT_BMASK) \
 +		   << CD9660_INO_FSECT_SHIFT))
 +
 +off_t iso_data_count(struct iso_node *);
 +void iso_set_ino_nfsect(ino_t *, int );
 +
  #endif /* _KERNEL */
  #endif /* _ISOFS_CD9660_CD9660_NODE_H_ */
 --- sys/fs/cd9660/cd9660_bmap.c.orig	2014-06-14 07:39:28.000000000 +0000
 +++ sys/fs/cd9660/cd9660_bmap.c	2014-06-30 13:08:08.000000000 +0000
 @@ -68,6 +68,12 @@ cd9660_bmap(void *v)
  	struct iso_node *ip = VTOI(ap->a_vp);
  	daddr_t lblkno = ap->a_bn;
  	int bshift;
 +	int i;
 +	uint64_t lbyte;
 +	uint64_t sum = 0;
 +	uint64_t prev_sum = 0;
 +	int nfsect = 1;
 +	int64_t nblk = 0;

  	/*
  	 * Check for underlying vnode requests and ensure that logical
 @@ -82,16 +88,39 @@ 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);
 +	nfsect = CD9660_FSECT_FROM_INO(ip->i_number);
 +	if (nfsect > 1) {
 +		lbyte = lblkno << bshift;
 +		for (i = 0; i < nfsect; i++) {
 +			sum += ip->iso_sections[i].isofsc_size;
 +			if (sum > lbyte || i == nfsect - 1) {
 +				*ap->a_bnp = ((uint64_t)
 +					ip->iso_sections[i].isofsc_start
 +					+ lblkno - (prev_sum >> bshift))
 +					<< (bshift - DEV_BSHIFT);
 +				nblk = ((sum - lbyte) >> bshift) - 1;
 +				break;
 +			}
 +			prev_sum = sum;
 +		}
 +	} else {
 +		*ap->a_bnp = ((uint64_t) ip->iso_start + lblkno) <<
 +			     (bshift - DEV_BSHIFT);
 +		nblk = (ip->i_size >> bshift) - (lblkno + 1);
 +	}
 +
 +#ifdef ISOFS_DEBUG_BMAP
 +	printf("cd9660_bmap.c : ino= %llu , lblkno= %llu , a_bnp= %llu\n",
 +		(unsigned long long) ip->i_number,
 +		(unsigned long long) lblkno,
 +		(unsigned long long) *ap->a_bnp);
 +#endif

  	/*
  	 * Determine maximum number of readahead blocks following the
  	 * requested block.
  	 */
  	if (ap->a_runp) {
 -		int nblk;
 -
 -		nblk = (ip->i_size >> bshift) - (lblkno + 1);
  		if (nblk <= 0)
  			*ap->a_runp = 0;
  		else if (nblk >= (MAXBSIZE >> bshift))
 --- sys/fs/cd9660/cd9660_lookup.c.orig	2014-06-16 09:55:49.000000000 +0000
 +++ sys/fs/cd9660/cd9660_lookup.c	2014-07-01 18:15:56.000000000 +0000
 @@ -65,29 +65,25 @@ __KERNEL_RCSID(0, "$NetBSD: cd9660_looku
   *
   * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
   * whether the name is to be looked up, created, renamed, or deleted.
 - * When CREATE, RENAME, or DELETE is specified, information usable in
 - * creating, renaming, or deleting a directory entry may be calculated.
 + * Actually only LOOKUP makes sense, because ISO 9660 is read-only.
 + *
   * If the target of the pathname exists, lookup returns both the target
   * and its parent directory locked.
 - * When creating or renaming, the target may * not be ".".
 - * When deleting , the target may be "."., but the caller must check
 - * to ensure it does an vrele and vput instead of two vputs.
   *
 - * Overall outline of ufs_lookup:
 + * Overall outline of cd9660_lookup:
   *
 - *	check accessibility of directory
 - *	look for name in cache, if found, then if at end of path
 - *	  and deleting or creating, drop it, else return name
 - *	search for name in directory, to found or notfound
 + *	Check accessibility of directory.
 + *	Look for name in cache, if found, then return vnode.
 + *	Search for name in directory:
 + *	  in case of ISO_FTYPE_DEFAULT look for newest version.
 + *	  in case of not ISO_FTYPE_RRIP check for associated file flag.
 + *	Accept an associated file only if followed by the file to which
 + *	it is associated.
   * notfound:
 - *	if creating, return locked directory, leaving info on available slots
 - *	else return error
 + *	return error
   * found:
 - *	if at end of path and deleting, return information to allow delete
 - *	if at end of path and rewriting (RENAME), lock target
 - *	  inode and return info to allow rewrite
 - *	if not at end, add name to cache; if at end and neither creating
 - *	  nor deleting, add name to cache
 + *	obtain vnode
 + *	add vnode to cache
   */
  int
  cd9660_lookup(void *v)
 @@ -110,7 +106,6 @@ cd9660_lookup(void *v)
  	struct vnode *tdp;		/* returned by vcache_get */
  	u_long bmask;			/* block offset mask */
  	int error;
 -	ino_t ino = 0;
  	int reclen;
  	u_short namelen;
  	char altname[ISO_MAXNAMLEN];
 @@ -122,6 +117,12 @@ cd9660_lookup(void *v)
  	kauth_cred_t cred = cnp->cn_cred;
  	int flags;
  	int nameiop = cnp->cn_nameiop;
 +	int section_count = 0;
 +	int already_found = 0;
 +	int is_match;
 +	int ver, found_ver = -1;
 +	int was_multi_extent = 0;
 +	ino_t found_ino = 0;

  	flags = cnp->cn_flags;

 @@ -177,7 +178,7 @@ cd9660_lookup(void *v)
  	 */
  	bmask = imp->im_bmask;
  	if (nameiop != LOOKUP || dp->i_diroff == 0 ||
 -	    dp->i_diroff > dp->i_size) {
 +	    dp->i_diroff > iso_data_count(dp)) {
  		entryoffsetinblock = 0;
  		dp->i_offset = 0;
  		numdirpasses = 1;
 @@ -190,7 +191,7 @@ cd9660_lookup(void *v)
  		numdirpasses = 2;
  		namecache_count_2passes();
  	}
 -	endsearch = dp->i_size;
 +	endsearch = iso_data_count(dp);

  searchloop:
  	while (dp->i_offset < endsearch) {
 @@ -242,66 +243,100 @@ searchloop:
  		 */
  		switch (imp->iso_ftype) {
  		default:
 -			if ((!(isonum_711(ep->flags)&4)) == !assoc) {
 -				if ((len == 1
 -				     && *name == '.')
 -				    || (flags & ISDOTDOT)) {
 -					if (namelen == 1
 -					    && ep->name[0] == ((flags & ISDOTDOT) ? 1 : 0)) {
 -						/*
 -						 * Save directory entry's inode number and
 -						 * release directory buffer.
 -						 */
 -						dp->i_ino = isodirino(ep, imp);
 -						goto found;
 -					}
 -					if (namelen != 1
 -					    || ep->name[0] != 0)
 -						goto notfound;
 -				} else if (!(res = isofncmp(name,len,
 -						   ep->name,namelen,
 -						   imp->im_joliet_level))) {
 -					if (isonum_711(ep->flags)&2)
 -						ino = isodirino(ep, imp);
 -					else
 -						ino = dbtob(bp->b_blkno)
 -							+ entryoffsetinblock;
 +			if (((len == 1 && *name == '.')
 +			    || (flags & ISDOTDOT)) && !assoc) {
 +				if (namelen == 1
 +				    && ep->name[0] ==
 +					 ((flags & ISDOTDOT) ? 1 : 0)) {
 +					/*
 +					 * Save directory entry's inode number
 +					 * and release directory buffer.
 +					 */
 +					dp->i_ino = isodirino(ep, imp);
 +					goto found;
 +				}
 +				if (namelen != 1 || ep->name[0] != 0)
 +					goto notfound;
 +				break; /* switch (imp->iso_ftype) */
 +			}
 +			res = isofncmp(name, len, ep->name, namelen,
 +					imp->im_joliet_level, &ver);
 +			if (res == 0 && 
 +			    (!(isonum_711(ep->flags)&4)) == !assoc) {
 +				if (found_ino && ver <= found_ver &&
 +				    was_multi_extent) {
 +					/* Follow-up file section */
 +					section_count++;
 +					iso_set_ino_nfsect(&found_ino,
 +							   section_count);
 +				} else if (isonum_711(ep->flags)&2) {
 +					section_count = 1;
  					saveoffset = dp->i_offset;
 -				} else if (ino)
 +					found_ino = isodirino(ep, imp);
 +					found_ver = ver;
 +				} else {
 +					section_count = 1;
 +					saveoffset = dp->i_offset;
 +					found_ino = CD9660_COMPUTE_INO_DB(
 +							bp->b_blkno,
 +							entryoffsetinblock,
 +							section_count);
 +					found_ver = ver;
 +				}
 +			} else if (found_ino) {
 +				/* found in previous cycle */
 +				if (!(assoc && res))
  					goto foundino;
 +				/* drop orphaned associated file */
 +				found_ino = 0;
 +			}
 +
  #ifdef	NOSORTBUG	/* On some CDs directory entries are not sorted correctly */
 -				else if (res < 0)
 -					goto notfound;
 -				else if (res > 0 && numdirpasses == 2)
 -					numdirpasses++;
 +			if (res < 0)
 +				goto notfound;
 +			else if (res > 0 && numdirpasses == 2)
 +				numdirpasses++;
  #endif
 -			}
  			break;
  		case ISO_FTYPE_RRIP:
  			if (isonum_711(ep->flags)&2)
 -				ino = isodirino(ep, imp);
 +				dp->i_ino = isodirino(ep, imp);
  			else
 -				ino = dbtob(bp->b_blkno) + entryoffsetinblock;
 -			dp->i_ino = ino;
 +				dp->i_ino = CD9660_COMPUTE_INO_DB(bp->b_blkno,
 +							entryoffsetinblock, 1);
  			cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp);
 +			is_match = 0;
  			if (namelen == cnp->cn_namelen) {
  				if (imp->im_flags & ISOFSMNT_RRCASEINS) {
  					if (strncasecmp(name, altname, namelen) == 0)
 -						goto found;
 +						is_match = 1;
  				} else {
  					if (memcmp(name, altname, namelen) == 0)
 -						goto found;
 +						is_match = 1;
 +				}
 +			}
 +			if (is_match) {
 +				if (!(already_found && was_multi_extent)) {
 +					section_count = 0;
 +					saveoffset = dp->i_offset;
 +					found_ino = dp->i_ino;
  				}
 +				section_count++;
 +				iso_set_ino_nfsect(&found_ino, section_count);
 +				already_found = 1;
 +			} else if (already_found) {
 +				goto foundino;
  			}
 -			ino = 0;
 +			/* no match yet */
  			break;
  		}
 +		was_multi_extent = isonum_711(ep->flags) & 0x80;
  		dp->i_offset += reclen;
  		entryoffsetinblock += reclen;
  	}
 -	if (ino) {
 +	if (found_ino) {
  foundino:
 -		dp->i_ino = ino;
 +		dp->i_ino = found_ino;
  		if (saveoffset != dp->i_offset) {
  			if (cd9660_lblkno(imp, dp->i_offset) !=
  			    cd9660_lblkno(imp, saveoffset)) {
 @@ -342,6 +377,7 @@ found:
  	if (numdirpasses == 2)
  		namecache_count_pass2();
  	brelse(bp, 0);
 +	bp = 0;

  	/*
  	 * Found component in pathname.
 --- sys/fs/cd9660/cd9660_node.c.orig	2014-06-16 09:55:49.000000000 +0000
 +++ sys/fs/cd9660/cd9660_node.c	2014-06-26 14:31:28.000000000 +0000
 @@ -143,6 +143,15 @@ cd9660_reclaim(void *v)
  	/*
  	 * Purge old data structures associated with the inode.
  	 */
 +	if (ip->iso_sections != NULL && CD9660_FSECT_FROM_INO(ip->i_number) > 1)
 +		kmem_free(ip->iso_sections,
 +			  CD9660_FSECT_FROM_INO(ip->i_number)
 +			  * sizeof(struct iso_file_section));
 +	ip->iso_sections = NULL;
 +	ip->i_number = 0;	/* invalidated, single section */
 +	if (ip->i_devvp != NULL)
 +		ip->i_devvp = NULL;
 +
  	genfs_node_destroy(vp);
  	pool_put(&cd9660_node_pool, vp->v_data);
  	vp->v_data = NULL;
 @@ -156,10 +165,9 @@ void
  cd9660_defattr(struct iso_directory_record *isodir, struct iso_node *inop,
  	struct buf *bp)
  {
 -	struct buf *bp2 = NULL;
 +	struct buf *bp2 = 0;
  	struct iso_mnt *imp;
  	struct iso_extended_attributes *ap = NULL;
 -	int off;

  	if (isonum_711(isodir->flags)&2) {
  		inop->inode.iso_mode = S_IFDIR;
 @@ -174,9 +182,8 @@ cd9660_defattr(struct iso_directory_reco
  	}
  	if (!bp
  	    && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT)
 -	    && (off = isonum_711(isodir->ext_attr_length))) {
 -		cd9660_blkatoff(ITOV(inop), (off_t)-(off << imp->im_bshift),
 -		    NULL, &bp2);
 +	    && isonum_711(isodir->ext_attr_length)) {
 +		isodir_read_isoextattr(isodir, imp, &bp2);
  		bp = bp2;
  	}
  	if (bp) {
 @@ -212,21 +219,21 @@ cd9660_defattr(struct iso_directory_reco

  /*
   * Time stamps
 + * This function does not assume that inop is already connected to a vnode.
 + * If bp is not 0, then it contains the ISO 9660 Extended Attribute blocks.
   */
  void
  cd9660_deftstamp(struct iso_directory_record *isodir, struct iso_node *inop,
  	struct buf *bp)
  {
 -	struct buf *bp2 = NULL;
 +	struct buf *bp2 = 0;
  	struct iso_mnt *imp;
  	struct iso_extended_attributes *ap = NULL;
 -	int off;

  	if (!bp
  	    && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT)
 -	    && (off = isonum_711(isodir->ext_attr_length))) {
 -		cd9660_blkatoff(ITOV(inop), (off_t)-(off << imp->im_bshift),
 -		    NULL, &bp2);
 +	    && isonum_711(isodir->ext_attr_length)) {
 +		isodir_read_isoextattr(isodir, imp, &bp2);
  		bp = bp2;
  	}
  	if (bp) {
 @@ -338,14 +345,26 @@ isodirino(struct iso_directory_record *i
  {
  	ino_t ino;

 -	/*
 -	 * Note there is an inverse calculation in
 -	 * cd9660_vfsops.c:cd9660_loadvnode():
 -	 *   ip->iso_start = ino >> imp->im_bshift;
 -	 * and also a calculation of the isodir pointer
 -	 * from an inode in cd9660_vnops.c:cd9660_readlink()
 -	 */
 -	ino = ((ino_t)isonum_733(isodir->extent) +
 -		isonum_711(isodir->ext_attr_length)) << imp->im_bshift;
 +	ino = CD9660_COMPUTE_INO(isonum_733(isodir->extent) +
 +					isonum_711(isodir->ext_attr_length),
 +					imp->im_bshift, 0, 1);
  	return ino;
  }
 +
 +
 +/* This is actually the function
 + *
 +off_t
 +iso_data_count(struct iso_node *ip)
 + *
 + * as defined in cd9660_node.h
 + */
 +CD9660_DATA_SIZE_FUNC
 +
 +
 +void
 +iso_set_ino_nfsect(ino_t *ino, int nfsect)
 +{
 +	*ino = CD9660_COMPUTE_INO(0, 0, CD9660_BYTEADR_FROM_INO(*ino), nfsect);
 +}
 +
 --- sys/fs/cd9660/cd9660_rrip.c.orig	2011-09-27 01:27:44.000000000 +0000
 +++ sys/fs/cd9660/cd9660_rrip.c	2014-06-19 07:46:48.000000000 +0000
 @@ -287,9 +287,10 @@ static void
  cd9660_rrip_defname(void *v, ISO_RRIP_ANALYZE *ana)
  {
  	struct iso_directory_record *isodir = v;
 +	int vdummy;

  	isofntrans(isodir->name, isonum_711(isodir->name_len),
 -		   ana->outbuf, ana->outlen,
 +		   ana->outbuf, ana->outlen, &vdummy,
  		   1, 0, isonum_711(isodir->flags) & 4,
  		   ana->imp->im_joliet_level);
  	switch (ana->outbuf[0]) {
 @@ -314,7 +315,8 @@ cd9660_rrip_pclink(void *v, ISO_RRIP_ANA
  {
  	ISO_RRIP_CLINK  *p = v;

 -	*ana->inump = isonum_733(p->dir_loc) << ana->imp->im_bshift;
 +	*ana->inump = CD9660_COMPUTE_INO(isonum_733(p->dir_loc),
 +					 ana->imp->im_bshift, 0, 1);
  	ana->fields &= ~(ISO_SUSP_CLINK | ISO_SUSP_PLINK);
  	return *p->h.type == 'C' ? ISO_SUSP_CLINK : ISO_SUSP_PLINK;
  }
 --- sys/fs/cd9660/cd9660_util.c.patch_010	2014-06-19 07:33:23.000000000 +0000
 +++ sys/fs/cd9660/cd9660_util.c	2014-06-19 07:46:48.000000000 +0000
 @@ -90,18 +90,43 @@ isochar(const u_char *isofn, const u_cha
  	return 2;
  }

 +static int
 +iso_read_name_version(const u_char **infn, const u_char *infnend,
 +		      int joliet_level)
 +{
 +	u_int16_t c;
 +	int ver;
 +
 +	for (ver = 0; *infn != infnend; ) {
 +		*infn += isochar(*infn, infnend, joliet_level, &c);
 +		if (c < '0' || c > '9') {
 +			ver = 0;
 +			break;
 +		}
 +		ver = ver * 10 + c - '0';
 +	}
 +	return (ver);
 +}
 +
  /*
   * translate and compare a filename
   * Note: Version number plus ';' may be omitted.
 + *
 + * If a version suffix is found in isofn and stripped off, then *ver will
 + * return it as non-negative number. Valid version numbers are in the range of
 + * 1 to 32767. 0 means bad suffix after semicolon.
 + * In case of no stripping, e.g. due to ISOFSMNT_GENS or lack of a semicolon,
 + * *version will be -1.
   */
  int
  isofncmp(const u_char *fn, size_t fnlen, const u_char *isofn, size_t isolen,
 -	int joliet_level)
 +	int joliet_level, int *ver)
  {
 -	int i, j;
 +	int was_semicolon = 0, ret;
  	u_int16_t fc, ic;
 -	const u_char *isoend = isofn + isolen;
 +	const u_char *isoend = isofn + isolen, *ver_isofn = NULL;

 +	*ver = -1;
  	while (fnlen > 0) {
  		fc = wget(&fn, &fnlen, joliet_level);

 @@ -109,23 +134,23 @@ isofncmp(const u_char *fn, size_t fnlen,
  			return fc;
  		isofn += isochar(isofn, isoend, joliet_level, &ic);
  		if (ic == ';') {
 +			ver_isofn = isofn;
  			switch (fc) {
  			default:
  				return fc;
  			case 0:
 -				return 0;
 +				if (was_semicolon)
 +					return ic;
 +				goto match;
  			case ';':
  				break;
  			}
 -			for (i = 0; fnlen-- != 0; i = i * 10 + *fn++ - '0') {
 -				if (*fn < '0' || *fn > '9') {
 -					return -1;
 -				}
 -			}
 -			for (j = 0; isofn != isoend; j = j * 10 + ic - '0')
 -				isofn += isochar(isofn, isoend,
 -						 joliet_level, &ic);
 -			return i - j;
 +			/* Both have a semicolon at the same position.
 +			 * Thus the rest of the names has to match literally.
 +			 * The specs allow only digits after the semicolon,
 +			 * but this code has to stand deviations.
 +			 */
 +			was_semicolon = 1;
  		}
  		if (ic != fc) {
  			if (ic >= 'A' && ic <= 'Z') {
 @@ -146,30 +171,44 @@ isofncmp(const u_char *fn, size_t fnlen,
  			return -1;
  		case '.':
  			if (isofn != isoend) {
 -				isochar(isofn, isoend, joliet_level, &ic);
 -				if (ic == ';')
 -					return 0;
 +				/* peek ahead */
 +				ret = isochar(isofn, isoend, joliet_level, &ic);
 +				if (ic == ';') {
 +					ver_isofn = isofn + ret;
 +					goto match;
 +				}
  			}
  			return -1;
  		case ';':
 -			return 0;
 +			ver_isofn = isofn;
 +			goto match;
  		}
  	}
 +match:
 +	if (ver_isofn != NULL)
 +		*ver = iso_read_name_version(&ver_isofn, isoend, joliet_level);
  	return 0;
  }

  /*
   * translate a filename
 + *
 + * If a version suffix is found and stripped off, then *ver will return
 + * it as non-negative number. Valid version numbers are in the range of
 + * 1 to 32767. 0 means bad suffix after semicolon.
 + * In case of no stripping, e.g. due to !!original or lack of a semicolon,
 + * *version will be -1.
   */
  void
  isofntrans(const u_char *infn, int infnlen, u_char *outfn, u_short *outfnlen,
 -	int original, int casetrans, int assoc, int joliet_level)
 +	   int *ver, int original, int casetrans, int assoc, int joliet_level)
  {
  	int fnidx = 0;
  	const u_char *infnend = infn + infnlen;
  	u_int16_t c;
  	int sz;

 +	*ver = -1;
  	if (assoc) {
  		*outfn++ = ASSOCCHAR;
  		fnidx++;
 @@ -183,6 +222,9 @@ isofntrans(const u_char *infn, int infnl
  		else if (!original && c == ';') {
  			if (fnidx > 0 && outfn[-1] == '.')
  				fnidx--;
 +			/* Decode *version */
 +			*ver = iso_read_name_version(&infn, infnend,
 +						     joliet_level);
  			break;
  		}

 --- sys/fs/cd9660/cd9660_vfsops.c.jhi.ts.mount_s	2014-06-19 08:27:32.000000000 +0000
 +++ sys/fs/cd9660/cd9660_vfsops.c	2014-07-01 14:27:28.000000000 +0000
 @@ -67,6 +67,7 @@ __KERNEL_RCSID(0, "$NetBSD: cd9660_vfsop
  #include <sys/dirent.h>
  #include <sys/kauth.h>
  #include <sys/module.h>
 +#include <sys/kmem.h>

  #include <fs/cd9660/iso.h>
  #include <fs/cd9660/cd9660_extern.h>
 @@ -363,6 +364,7 @@ iso_makemp(struct iso_mnt *isomp, struct

  	if (ea_len != NULL)
  		*ea_len = isonum_711(rootp->ext_attr_length);
 +	isomp->root_start_block = isomp->root_extent + *ea_len;

  	return 0;
  }
 @@ -413,7 +415,7 @@ iso_mountfs(struct vnode *devvp, struct 
  	}
  	if (argp->flags & ISOFSMNT_SSECTOR)
  		sess = argp->ssector;
 -#ifdef ISO_DEBUG
 +#ifdef ISOFS_DBG
  	printf("isofs: session offset (part %"PRId32") %"PRIu32"\n", DISKPART(dev), sess);
  #endif

 @@ -497,7 +499,7 @@ iso_mountfs(struct vnode *devvp, struct 
  		struct iso_directory_record *rootp;

  		if ((error = bread(isomp->im_devvp,
 -				   (isomp->root_extent + ext_attr_length) <<
 +				   (isomp->root_start_block) <<
  				   (isomp->im_bshift - DEV_BSHIFT),
  				   isomp->logical_block_size, NOCRED,
  				   0, &bp)) != 0)
 @@ -732,18 +734,194 @@ cd9660_vget(struct mount *mp, ino_t ino,
  	return 0;
  }

 +/* If *isodir is NULL, then read the block of the surely existing
 + * directory record at byte address *adr and set *isodir to that record.
 + * Else, read the surely existing directory record after *isodir, eventually
 + * switching the block to read from. Update *adr by the byte address of the 
 + * returned *isodir.
 + */
 +static int
 +iso_read_next_isodir(struct iso_mnt *imp, ino_t *adr,
 +		     struct buf **bp,
 +		     struct iso_directory_record **isodir)
 +{
 +	uint32_t lbn, off;
 +	int error, reclen;
 +	ino_t ino, next_ino;
 +	struct iso_directory_record *next_isodir;
 +
 +	ino = CD9660_BYTEADR_FROM_INO(*adr);
 +	if (*isodir != NULL) {
 +		/* Try whether the next linked list item is valid */
 +		reclen = isonum_711((*isodir)->length);
 +		next_ino = ino + reclen;
 +		if (CD9660_BLOCK_FROM_INO(next_ino, imp->im_bshift) ==
 +		    CD9660_BLOCK_FROM_INO(ino, imp->im_bshift)) {
 +			next_isodir = (struct iso_directory_record *)
 +				      (((char *) *isodir) + reclen);
 +			if (isonum_711(next_isodir->length)) {
 +				*isodir = next_isodir;
 +				*adr = next_ino;;
 +				return (0);
 +			}
 +		} else if (next_ino & imp->im_bmask) {
 +			printf(
 +	"iso_read_next_isodir: record crosses block boundary, ino %llu\n",
 +			       (unsigned long long) ino);
 +			return (EINVAL);
 +		}
 +		/* Hop to next block start */
 +		ino = (ino & ~imp->im_bmask) + imp->logical_block_size;
 +		*adr = ino;
 +	}
 +	if (*bp != 0)
 +		brelse(*bp, 0);
 +	*bp = 0;
 +
 +	lbn = cd9660_lblkno(imp, ino);
 +	if (lbn >= imp->volume_space_size) {
 +		printf("iso_read_next_isodir: lbn exceeds volume space %lu\n",
 +		       (unsigned long) lbn);
 +		return (EINVAL);
 +	}
 +
 +	off = cd9660_blkoff(imp, ino);
 +	if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) {
 +		printf(
 +	"iso_read_next_isodir: directory record crosses block boundary %lu\n",
 +			(unsigned long) (off + ISO_DIRECTORY_RECORD_SIZE));
 +		return (EINVAL);
 +	}
 +
 +	error = bread(imp->im_devvp,
 +		      (uint64_t) lbn << (imp->im_bshift - DEV_BSHIFT),
 +		      imp->logical_block_size, NOCRED, 0, bp);
 +	if (error) {
 +		printf("iso_read_next_isodir: bread error %d\n",error);
 +		return (error);
 +	}
 +	*isodir = (struct iso_directory_record *)((char *)(*bp)->b_data + off);
 +
 +	if (off + isonum_711((*isodir)->length) > imp->logical_block_size) {
 +		if (*bp != 0)
 +			brelse(*bp, 0);
 +		*bp = 0;
 +		printf("iso_read_next_isodir: directory record crosses block boundary %lu[off=%lu/len=%d]\n",
 +		       (unsigned long) off + isonum_711((*isodir)->length),
 +		       (unsigned long) off,
 +		       (int) isonum_711((*isodir)->length));
 +			return (EINVAL);
 +	}
 +	return (0);
 +}
 +
 +static int
 +iso_register_fsects(struct iso_mnt *imp, struct iso_node *ip,
 +		    struct buf **bp,
 +		    struct iso_directory_record *isodir, int nfsect)
 +{
 +	int i, error;
 +	ino_t adr;
 +	struct iso_directory_record *dir;
 +
 +	ip->iso_extent = isonum_733(isodir->extent);
 +	ip->i_size = isonum_733(isodir->size);
 +	ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent;
 +
 +	if (nfsect <= 1)
 +		return 0;
 +
 +	/* On ISO 9660 level there are only directories and data files.
 +	 */
 +	if (isonum_711(isodir->flags) & 2) {
 +		printf(
 +	    "cd9660: iso_register_fsects: Directory with multiple sections\n");
 +		return (EINVAL);
 +	}
 +	if (nfsect > CD9660_FSECT_MAX) {
 +		printf(
 +    "cd9660: iso_register_fsects: File with more than %d sections. ino %llu\n",
 +		       CD9660_FSECT_MAX, (unsigned long long) ip->i_number);
 +		return (EOPNOTSUPP);
 +	}
 +
 +	ip->iso_sections = (struct iso_file_section *) kmem_zalloc(
 +				nfsect * sizeof(struct iso_file_section),
 +				KM_SLEEP);
 +	/*
 +	 * Now there is indeed the allocated memory which makes it safe and
 +	 * necessary to publish nfsect in the iso_node.
 +	 */
 +	iso_set_ino_nfsect(&ip->i_number, nfsect);
 +
 +	adr = ip->i_number;
 +	dir = isodir;
 +	for (i = 0; i < nfsect; i++) {
 +		if (i > 0) {
 +			error = iso_read_next_isodir(imp, &adr, bp, &dir);
 +			if (error)
 +				goto failed;
 +		}
 +		ip->iso_sections[i].isofsc_start =
 +					isonum_711(dir->ext_attr_length)
 +					+ isonum_733(dir->extent);
 +		ip->iso_sections[i].isofsc_size = isonum_733(dir->size);
 +
 +		if (i < nfsect - 1 &&
 +		    (ip->iso_sections[i].isofsc_size & imp->im_bmask)) {
 +			printf(
 +		   "cd9660: Unaligned non-last file section with inode %llu\n",
 +			       (unsigned long long) ip->i_number);
 +			error = EOPNOTSUPP;
 +			goto failed;
 +		}
 +	}
 +	return 0;
 +
 +failed:
 +	if (ip->iso_sections != NULL && CD9660_FSECT_FROM_INO(ip->i_number) > 1)
 +		kmem_free(ip->iso_sections, 
 +			  CD9660_FSECT_FROM_INO(ip->i_number)
 +			  * sizeof(struct iso_file_section));
 +	ip->iso_sections = NULL;
 +	ip->i_size = 0;
 +	iso_set_ino_nfsect(&ip->i_number, 1);
 +	return (error);
 +}
 +
 +int
 +isodir_read_isoextattr(struct iso_directory_record *isodir,
 +		       struct iso_mnt *imp, struct buf **bpp)
 +{
 +	daddr_t lbn;
 +	int error, size;
 +
 +	size = isonum_711(isodir->ext_attr_length) << imp->im_bshift;
 +	if (size == 0) {
 +		*bpp = 0;
 +		return 0;
 +	}
 +	lbn = btodb(((uint64_t) isonum_733(isodir->extent)) << imp->im_bshift);
 +	if ((error = bread(imp->im_devvp, lbn, size, NOCRED, 0, bpp)) != 0) {
 +		*bpp = 0;
 +		return (error);
 +	}
 +	return (0);
 +}
 +
  int
  cd9660_loadvnode(struct mount *mp, struct vnode *vp,
      const void *key, size_t key_len, const void **new_key)
  {
  	struct iso_mnt *imp;
 -	struct iso_node *ip;
 +	struct iso_node *ip = NULL;
  	struct iso_directory_record *isodir;
 -	struct buf *bp;
 +	struct buf *bp = 0;
  	dev_t dev;
  	ino_t ino;
 -	int lbn, off;
  	int error;
 +	int nfsect;
 +	ino_t adr;

  	KASSERT(key_len == sizeof(ino));
  	memcpy(&ino, key, key_len);
 @@ -752,69 +930,26 @@ cd9660_loadvnode(struct mount *mp, struc

  	ip = pool_get(&cd9660_node_pool, PR_WAITOK);

 +	/* Never let .i_number bear undefined nfsect bits */
  	memset(ip, 0, sizeof(struct iso_node));
 -	ip->i_vnode = vp;
 +	ip->i_devvp = NULL;
 +
 +	/* Carry the number of file sections separately until eventual memory
 +	 * is really allocated. nfsect > 1 could else cause havoc on free().
 +	 */
 +	nfsect = CD9660_FSECT_FROM_INO(ino);
 +	iso_set_ino_nfsect(&ino, 1);
 +	ip->i_number = ino;		    /* now it is safe to publish */
 +
  	ip->i_dev = dev;
 -	ip->i_number = ino;
  	ip->i_mnt = imp;
  	ip->i_devvp = imp->im_devvp;

 -	lbn = cd9660_lblkno(imp, ino);
 -	if (lbn >= imp->volume_space_size) {
 -		pool_put(&cd9660_node_pool, ip);
 -		printf("fhtovp: lbn exceed volume space %d\n", lbn);
 -		return (ESTALE);
 -	}
 -
 -	off = cd9660_blkoff(imp, ino);
 -	if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) {
 -		pool_put(&cd9660_node_pool, ip);
 -		printf("fhtovp: crosses block boundary %d\n",
 -		    off + ISO_DIRECTORY_RECORD_SIZE);
 -		return (ESTALE);
 -	}
 -
 -	error = bread(imp->im_devvp,
 -		      lbn << (imp->im_bshift - DEV_BSHIFT),
 -		      imp->logical_block_size, NOCRED, 0, &bp);
 -	if (error) {
 -		pool_put(&cd9660_node_pool, ip);
 -		printf("fhtovp: bread error %d\n",error);
 -		return (error);
 -	}
 -	isodir = (struct iso_directory_record *)((char *)bp->b_data + off);
 -
 -	if (off + isonum_711(isodir->length) > imp->logical_block_size) {
 -		pool_put(&cd9660_node_pool, ip);
 -		if (bp != 0)
 -			brelse(bp, 0);
 -		printf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n",
 -		    off +isonum_711(isodir->length), off,
 -		    isonum_711(isodir->length));
 -		return (ESTALE);
 -	}
 -
 -#if 0
 -	if (isonum_733(isodir->extent) +
 -	    isonum_711(isodir->ext_attr_length) != ifhp->ifid_start) {
 -		pool_put(&cd9660_node_pool, ip);
 -		if (bp != 0)
 -			brelse(bp, 0);
 -		printf("fhtovp: file start miss %d vs %d\n",
 -		    isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length),
 -		    ifhp->ifid_start);
 -		return (ESTALE);
 -	}
 -#endif
 -
 -	ip->iso_extent = isonum_733(isodir->extent);
 -	ip->i_size = isonum_733(isodir->size);
 -	ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent;
 -
 -	vp->v_tag = VT_ISOFS;
 -	vp->v_op = cd9660_vnodeop_p;
 -	vp->v_data = ip;
 -	genfs_node_init(vp, &cd9660_genfsops);
 +	adr = ino;
 +	isodir = NULL; /* read block */
 +	error = iso_read_next_isodir(imp, &adr, &bp, &isodir);
 +	if (error)
 +		goto failed;

  	/*
  	 * Setup time stamp, attribute
 @@ -824,28 +959,49 @@ cd9660_loadvnode(struct mount *mp, struc
  	    {
  		struct buf *bp2;
  		if ((imp->im_flags & ISOFSMNT_EXTATT)
 -		    && (off = isonum_711(isodir->ext_attr_length)))
 -			cd9660_blkatoff(vp, (off_t)-(off << imp->im_bshift),
 -			    NULL, &bp2);
 -		else
 -			bp2 = NULL;
 +		    && isonum_711(isodir->ext_attr_length)) {
 +			if ((error = isodir_read_isoextattr(isodir, imp, &bp2))
 +			    != 0)
 +				goto failed;
 +		} else
 +			bp2 = 0;
  		cd9660_defattr(isodir, ip, bp2);
  		cd9660_deftstamp(isodir, ip, bp2);
  		if (bp2)
  			brelse(bp2, 0);
 +		bp2 = 0;
  		break;
  	    }
  	case ISO_FTYPE_RRIP:
  		cd9660_rrip_analyze(isodir, ip, imp);
 +
 +		/* Refuse on non-regular files with multiple file sections */
 +		if (nfsect > 1 && IFTOVT(ip->inode.iso_mode) != VREG) {
 +			error = EINVAL;
 +			goto failed;
 +		}
  		break;
  	}

 +	if ((error = iso_register_fsects(imp, ip, &bp, isodir, nfsect)) != 0)
 +		goto failed;
  	if (bp != 0)
  		brelse(bp, 0);
 +	bp = 0;
 +
 +	/*
 +	 * Attach iso_node to vnode. No bail-out after this point.
 +	 */
 +	vp->v_data = ip;
 +	ip->i_vnode = vp;

  	/*
  	 * Initialize the associated vnode
  	 */
 +	vp->v_tag = VT_ISOFS;
 +	vp->v_op = cd9660_vnodeop_p;
 +	genfs_node_init(vp, &cd9660_genfsops);
 +
  	switch (vp->v_type = IFTOVT(ip->inode.iso_mode)) {
  	case VFIFO:
  		vp->v_op = cd9660_fifoop_p;
 @@ -865,14 +1021,14 @@ cd9660_loadvnode(struct mount *mp, struc
  	case VBAD:
  		break;
  	case VREG:
 -		uvm_vnp_setsize(vp, ip->i_size);
 +		uvm_vnp_setsize(vp, iso_data_count(ip));
  		break;
  	}

  	if (vp->v_type != VREG)
  		uvm_vnp_setsize(vp, 0);

 -	if (ip->iso_extent == imp->root_extent)
 +	if (ip->iso_start == imp->root_start_block)
  		vp->v_vflag |= VV_ROOT;

  	/*
 @@ -881,6 +1037,13 @@ cd9660_loadvnode(struct mount *mp, struc

  	*new_key = &ip->i_number;
  	return 0;
 +
 + failed:
 +	if (bp != 0)
 +		brelse(bp, 0);
 +	if (ip != NULL)
 +		pool_put(&cd9660_node_pool, ip);
 +	return (error);
  }

  /*
 --- sys/fs/cd9660/cd9660_vnops.c.orig	2014-06-14 07:39:29.000000000 +0000
 +++ sys/fs/cd9660/cd9660_vnops.c	2014-07-02 07:54:57.000000000 +0000
 @@ -68,21 +68,32 @@ __KERNEL_RCSID(0, "$NetBSD: cd9660_vnops
   * Structure for reading directories
   */
  struct isoreaddir {
 -	struct dirent saveent;
 -	struct dirent assocent;
 -	struct dirent current;
 -	off_t saveoff;
 -	off_t assocoff;
 -	off_t curroff;
 +	struct dirent saveent;	/* Candidate record for being first of file */
 +	int saved_is_assoc;		/* Candidate is Associated File */
 +	struct dirent assocent;		/* Assoc candidate waiting for its
 +					 * normal file.
 +					 */
 +	struct dirent current;		/* Most recently read record */
 +	off_t saveoff;			/* Record address after candidate */
 +	off_t prevoff;			/* Most recently read record address */
 +	off_t assocoff;			/* prevoff of assoc candidate */
 +	off_t curroff;			/* Next record address to be read */
 +	int save_ver;			/* Version number of candidate */
 +	int current_ver;		/* Version number of recent record */
 +	int prev_multi_extent;		/* Most recent is not last extent */
 +	int nfsect;			/* Count of records (candidate = 1) */
  	struct uio *uio;
 -	off_t uio_off;
 +	off_t uio_off;			/* Restart address for the case that
 +					 * cd9660_readdir() ends its job of
 +					 * reading an interval of files.
 +					 */
  	int eofflag;
  	off_t *cookies;
  	int ncookies;
  };

  int	iso_uiodir(struct isoreaddir *, struct dirent *, off_t);
 -int	iso_shipdir(struct isoreaddir *);
 +int	iso_shipdir(struct isoreaddir *, int, int);

  static int
  cd9660_check_possible(struct vnode *vp, struct iso_node *ip, mode_t mode)
 @@ -168,8 +179,8 @@ cd9660_getattr(void *v)
  	vap->va_ctime	= ip->inode.iso_ctime;
  	vap->va_rdev	= ip->inode.iso_rdev;

 -	vap->va_size	= (u_quad_t) ip->i_size;
 -	if (ip->i_size == 0 && vp->v_type == VLNK) {
 +	vap->va_size	= (u_quad_t) iso_data_count(ip);
 +	if (vap->va_size == 0 && vp->v_type == VLNK) {
  		struct vop_readlink_args rdlnk;
  		struct iovec aiov;
  		struct uio auio;
 @@ -194,7 +205,7 @@ cd9660_getattr(void *v)
  	vap->va_flags	= 0;
  	vap->va_gen = 1;
  	vap->va_blocksize = ip->i_mnt->logical_block_size;
 -	vap->va_bytes	= (u_quad_t) ip->i_size;
 +	vap->va_bytes	= (u_quad_t) iso_data_count(ip);
  	vap->va_type	= vp->v_type;
  	return (0);
  }
 @@ -225,7 +236,7 @@ cd9660_read(void *v)
  		return (0);
  	if (uio->uio_offset < 0)
  		return (EINVAL);
 -	if (uio->uio_offset >= ip->i_size)
 +	if (uio->uio_offset >= iso_data_count(ip))
  		return 0;
  	ip->i_flag |= IN_ACCESS;
  	imp = ip->i_mnt;
 @@ -235,7 +246,8 @@ cd9660_read(void *v)
  		error = 0;

  		while (uio->uio_resid > 0) {
 -			vsize_t bytelen = MIN(ip->i_size - uio->uio_offset,
 +			vsize_t bytelen = MIN(iso_data_count(ip)
 +					      - uio->uio_offset,
  					      uio->uio_resid);

  			if (bytelen == 0)
 @@ -248,18 +260,22 @@ cd9660_read(void *v)
  		goto out;
  	}

 +	/* This code serves mainly for read(2) on directories.
 +	 * Its block addresses are relative to block 0 of the device file
 +	 * which hosts the filesystem.
 +	 */
  	do {
  		lbn = cd9660_lblkno(imp, uio->uio_offset);
  		on = cd9660_blkoff(imp, uio->uio_offset);
  		n = MIN(imp->logical_block_size - on, uio->uio_resid);
 -		diff = (off_t)ip->i_size - uio->uio_offset;
 +		diff = iso_data_count(ip) - uio->uio_offset;
  		if (diff <= 0)
  			return (0);
  		if (diff < n)
  			n = diff;
  		size = cd9660_blksize(imp, ip, lbn);
  		rablock = lbn + 1;
 -		if (cd9660_lblktosize(imp, rablock) < ip->i_size) {
 +		if (cd9660_lblktosize(imp, rablock) < iso_data_count(ip)) {
  			rasize = cd9660_blksize(imp, ip, rablock);
  			error = breadn(vp, lbn, size, &rablock,
  				       &rasize, 1, NOCRED, 0, &bp);
 @@ -287,6 +303,29 @@ iso_uiodir(struct isoreaddir *idp, struc
  	dp->d_name[dp->d_namlen] = 0;
  	dp->d_reclen = _DIRENT_SIZE(dp);

 +#ifdef ISOFS_DBG_SIMULATE_READIR_INTVL
 +
 +	/* Mock-up to simulate the prevented overflow of the buffer which
 +	 * is submitted to cd9660_readdir(). After return -1, the caller
 +	 * is supposed to call again with an empty buffer.
 +	 * Important is that cd9660_readdir() continues seamlessly when the
 +	 * caller requests the next buffer full of dirents.
 +	 */
 +	{ static int count = 0;
 +		if (idp->uio->uio_offset > 0) { /* dirents are recorded */
 +			count++;
 +			if (count > 3) {
 +				printf("iso_uiodir: deliberate return -1\n");
 +				count = 0;
 +				idp->eofflag = 0;
 +				return (-1);
 +			}
 +		} else
 +			count = 0;
 +	}
 +
 +#endif /* ISOFS_DBG_SIMULATE_READIR_INTVL */
 +
  	if (idp->uio->uio_resid < dp->d_reclen) {
  		idp->eofflag = 0;
  		return (-1);
 @@ -308,56 +347,98 @@ iso_uiodir(struct isoreaddir *idp, struc
  	return (0);
  }

 +/* This function decides whether the current item in idp is part of a different
 + * file than the saved item in idp.
 + * If so, then it submits the saved item as result for VOP_READDIR(9) via a
 + * call to iso_uiodir().
 + * If not, then it increments the file section count or registers a new saved
 + * item in idp. This depends on the version number and Multi-Extent flag
 + * of the previous item.
 + * Files with the Associated File flag are submitted to VOP_READDIR(9) only
 + * if their successor has no such flag, has the same name, and will get
 + * submitted.
 + *
 + * So readdir(2) will never return two consequtive identical file names.
 + * The price is that mount_9660 -o norrip,nogens drops older file versions,
 + * and that all name interpretations drop identical names if not affiliated
 + * by their directory record Multi-Extent resp. Associated File flags.
 + */
  int
 -iso_shipdir(struct isoreaddir *idp)
 +iso_shipdir(struct isoreaddir *idp, int multi_extent, int rockridge)
  {
  	struct dirent *dp;
 -	int cl, sl, assoc;
 +	int cl, sl, assoc = 0, name_change, not_assoc_pair;
  	int error;
  	char *cname, *sname;

  	cl = idp->current.d_namlen;
  	cname = idp->current.d_name;

 -	if ((assoc = cl > 1 && *cname == ASSOCCHAR)) {
 +	if ((assoc = cl > 1 && *cname == ASSOCCHAR && !rockridge)) {
  		cl--;
  		cname++;
  	}

  	dp = &idp->saveent;
  	sname = dp->d_name;
 -	if (!(sl = dp->d_namlen)) {
 -		dp = &idp->assocent;
 -		sname = dp->d_name + 1;
 -		sl = dp->d_namlen - 1;
 -	}
 -	if (sl > 0) {
 -		if (sl != cl
 -		    || memcmp(sname, cname, sl)) {
 -			if (idp->assocent.d_namlen) {
 -				error = iso_uiodir(idp, &idp->assocent,
 -						   idp->assocoff);
 -				if (error)
 -					return (error);
 -				idp->assocent.d_namlen = 0;
 -			}
 -			if (idp->saveent.d_namlen) {
 -				error = iso_uiodir(idp, &idp->saveent,
 -						   idp->saveoff);
 -				if (error)
 -					return (error);
 -				idp->saveent.d_namlen = 0;
 -			}
 +	sl = dp->d_namlen;
 +	if (sl > 1 && idp->saved_is_assoc) {
 +		sl--;
 +		sname++;
 +	}
 +	name_change = (sl > 0 && (sl != cl || memcmp(sname, cname, sl)));
 +	if (name_change) {
 +		if (idp->assocent.d_namlen && !idp->saved_is_assoc) {
 +			/* Accept as valid dirent */
 +			error = iso_uiodir(idp, &idp->assocent, idp->assocoff);
 +			if (error)
 +				return (error);
  		}
 +		idp->assocent.d_namlen = 0;
 +		if (!idp->saved_is_assoc) {
 +			/* Accept as valid dirent */
 +			iso_set_ino_nfsect(&idp->saveent.d_fileno, idp->nfsect);
 +			error = iso_uiodir(idp, &idp->saveent, idp->prevoff);
 +			if (error)
 +				return (error);
 +		}
 +		sl = idp->saveent.d_namlen = 0;
  	}
  	idp->current.d_reclen = _DIRENT_SIZE(&idp->current);
 -	if (assoc) {
 -		idp->assocoff = idp->curroff;
 -		memcpy(&idp->assocent, &idp->current, idp->current.d_reclen);
 -	} else {
 -		idp->saveoff = idp->curroff;
 +
 +	not_assoc_pair = name_change || idp->save_ver < idp->current_ver;
 +	if (sl <= 0 || not_assoc_pair || assoc != idp->saved_is_assoc
 +	    || !idp->prev_multi_extent) {
 +		/* A file begins at current, either because it is the first
 +		 * directory entry of the readdir interval, or a file ended
 +		 * before the current entry.
 +		 */
 +		if (idp->saved_is_assoc && sl > 0 && (!assoc)
 +		    && !not_assoc_pair) {
 +			/* Memorize assoc candidate */
 +			memcpy(&idp->assocent, &idp->saveent,
 +			       idp->saveent.d_reclen);
 +			idp->assocoff = idp->prevoff;
 +			iso_set_ino_nfsect(&idp->assocent.d_fileno,
 +					   idp->nfsect);
 +		} else {
 +			/* Discard eventual assoc candidate */
 +			idp->assocent.d_namlen = 0;
 +		}
  		memcpy(&idp->saveent, &idp->current, idp->current.d_reclen);
 +		idp->save_ver = idp->current_ver;
 +		idp->nfsect = 1;
 +		idp->saveoff = idp->curroff;
 +		idp->saved_is_assoc = assoc;
 +	} else {
 +		idp->nfsect++;
  	}
 +	/* The offset for uio. Here it marks the next directory record to read.
 +	 * It will be eventually of use after that record was read. (Other
 +	 * code parts hurry to advance .curroff. So lazy .prevoff is needed.)
 +	 */
 +	idp->prevoff = idp->curroff;
 +	idp->prev_multi_extent = multi_extent;
  	return (0);
  }

 @@ -390,6 +471,7 @@ cd9660_readdir(void *v)
  	u_short namelen;
  	off_t *cookies = NULL;
  	int ncookies = 0;
 +	int multi_extent;

  	if (vdp->v_type != VDIR)
  		return (ENOTDIR);
 @@ -400,6 +482,9 @@ cd9660_readdir(void *v)

  	idp = (struct isoreaddir *)malloc(sizeof(*idp), M_TEMP, M_WAITOK);
  	idp->saveent.d_namlen = idp->assocent.d_namlen = 0;
 +	idp->saved_is_assoc = 0;
 +	idp->save_ver = -1;
 +	idp->nfsect = 1;
  	/*
  	 * XXX
  	 * Is it worth trying to figure out the type?
 @@ -417,13 +502,14 @@ cd9660_readdir(void *v)
  	}
  	idp->eofflag = 1;
  	idp->curroff = uio->uio_offset;
 +	idp->prevoff = 0;

  	if ((entryoffsetinblock = idp->curroff & bmask) &&
  	    (error = cd9660_blkatoff(vdp, (off_t)idp->curroff, NULL, &bp))) {
  		free(idp, M_TEMP);
  		return (error);
  	}
 -	endsearch = dp->i_size;
 +	endsearch = iso_data_count(dp);

  	while (idp->curroff < endsearch) {
  		/*
 @@ -478,23 +564,37 @@ cd9660_readdir(void *v)
  		if (isonum_711(ep->flags)&2)
  			idp->current.d_fileno = isodirino(ep, imp);
  		else
 -			idp->current.d_fileno = dbtob(bp->b_blkno) +
 -				entryoffsetinblock;
 +			idp->current.d_fileno = CD9660_COMPUTE_INO_DB(
 +							bp->b_blkno,
 +							entryoffsetinblock, 1);

  		idp->curroff += reclen;

 +		multi_extent = !!(isonum_711(ep->flags) & 0x80);
 +
 +		/* XXX It is not really ok that iso_shipdir() compares
 +		 *     translated resp. Rock Ridge file names.
 +		 *     Actually it should compare original ISO names.
 +		 *     But the current comparison is nearer to what VFS
 +		 *     will perceive, and thus easier to keep consistent.
 +		 *
 +		 *     Any change here must be mirrored by a change in
 +		 *     cd9660_lookup().
 +		 */
  		switch (imp->iso_ftype) {
  		case ISO_FTYPE_RRIP:
  			cd9660_rrip_getname(ep, idp->current.d_name, &namelen,
  			    &idp->current.d_fileno, imp);
  			idp->current.d_namlen = (u_char)namelen;
 -			if (idp->current.d_namlen)
 -				error = iso_uiodir(idp, &idp->current,
 -				    idp->curroff);
 +			if (idp->current.d_namlen) {
 +				idp->current_ver = -1;
 +				error = iso_shipdir(idp, multi_extent, 1);
 +			}
  			break;
  		default:	/* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 */
  			isofntrans(ep->name, idp->current.d_namlen,
  				   idp->current.d_name, &namelen,
 +				   &idp->current_ver,
  				   imp->iso_ftype == ISO_FTYPE_9660,
  				   (imp->im_flags & ISOFSMNT_NOCASETRANS) == 0,
  				   isonum_711(ep->flags)&4,
 @@ -515,11 +615,7 @@ cd9660_readdir(void *v)
  				break;
  			default:
  				idp->current.d_namlen = (u_char)namelen;
 -				if (imp->iso_ftype == ISO_FTYPE_DEFAULT)
 -					error = iso_shipdir(idp);
 -				else
 -					error = iso_uiodir(idp, &idp->current,
 -					    idp->curroff);
 +				error = iso_shipdir(idp, multi_extent, 0);
  				break;
  			}
  		}
 @@ -529,11 +625,19 @@ cd9660_readdir(void *v)
  		entryoffsetinblock += reclen;
  	}

 -	if (!error && imp->iso_ftype == ISO_FTYPE_DEFAULT) {
 +	if (!error) {
 +		/* Take eventually pending record */
  		idp->current.d_namlen = 0;
 -		error = iso_shipdir(idp);
 +		idp->current_ver = -1;
 +		error = iso_shipdir(idp, 0, 1);
  	}
 -	if (error < 0)
 +	if (error > 0) { /* Real error */
 +		/* Update read cursor to skip last read record
 +		 * on re-entry into cd9660_readdir()
 +		 */
 +		if (idp->prevoff > 0)
 +			idp->uio_off = idp->prevoff;
 +	} else if (error < 0) /* Result buffer is full */
  		error = 0;

  	if (ap->a_ncookies != NULL) {
 @@ -756,9 +860,9 @@ cd9660_pathconf(void *v)
  		return (0);
  	case _PC_NAME_MAX:
  		if (VTOI(ap->a_vp)->i_mnt->iso_ftype == ISO_FTYPE_RRIP)
 -			*ap->a_retval = ISO_MAXNAMLEN;
 +			*ap->a_retval = ISO_MAXNAMLEN; /* (or quite endless) */
  		else
 -			*ap->a_retval = 37;
 +			*ap->a_retval = 221; /* 254 max record length - 33 */
  		return (0);
  	case _PC_PATH_MAX:
  		*ap->a_retval = PATH_MAX;
 @@ -770,13 +874,17 @@ cd9660_pathconf(void *v)
  		*ap->a_retval = 1;
  		return (0);
  	case _PC_NO_TRUNC:
 +
 +		/* XXX There is neither silent name truncation nor error */
 +
  		*ap->a_retval = 1;
  		return (0);
  	case _PC_SYNC_IO:
  		*ap->a_retval = 1;
  		return (0);
  	case _PC_FILESIZEBITS:
 -		*ap->a_retval = 32;
 +		/* Up to (1 << 13) sections of 32 bit size */
 +		*ap->a_retval = 45;
  		return (0);
  	default:
  		return (EINVAL);

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.