This text exposes background info and details about a NetBSD PR which shall enable handling of large files in ISO 9660 filesystems. 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. --------------------------------------------------------------------- Lengthy Assessment of the current state of cd9660: --------------------------------------------------------------------- Assessment of the relation of node objects and extent objects in NetBSD kernel 6.1.3 and 6.99.44 as of Jun 17 2014. --------------------------------------------------------------------- What the specs say: ECMA-119 is the public and free-of-charge version of ISO 9660 specs. http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf 6.4.1 defines an "Extent" as an interval of blocks. 6.5 "File structure" states that a data file consists of one or more "File Sections", which are byte intervals, each stored in an extent to which an own directory record points. The sequence of records in the directory's record list gives the sequence of concatenation for the byte intervals. File sections may be shared among several files. I see no statement which demands that all but the last file section would have to bear block aligned sizes. 6.5.4. states that an Associated File has the same name as the file to which it is associated. 6.8.1 states that a directory consists of a single file section, may not be Associated File, and may not have the same name as any sibling in its parent directory. This implies that it may not have an Associated File. 9.1.4 makes clear that a file section contains at most 2 exp 32 - 1 bytes. I.e. larger data files have to be represented by multiple file sections. 9.1.5 describes the Multi-Extent bit in the File Flags byte of a directory record. It marks directory records for extents which are not the last one of the file. 9.2 "Consistency of File Attributes between Directory Records of a File" states that each directory record of a file shall show the same "File Identifier field" (i.e. leaf name). 9.3 "Order of Directory Records" says that the records are stored in a sorting sequence depending on their file names and Associated File flags. Thus the records of a multi-extent file form a continuous interval in the directory's record list. The directory records of an Associated File are stored before those of the file to which it is associated. 10 "Levels of interchange" states that data files of levels 1 and 2 may only consist of a single file section. Level 3 lifts this restriction. There is no global indication specified which would announce the level of interchange for the whole filesystem. 6.8.1.1 "Directory Record" describes the directory records in a directory extent as a list which is implemented by an array of blocks with independent linked lists in them. The end of the overall list is marked by the size of the directory file section. This means that one cannot always tell from a directory record whether it is the last list item. The next record lenght may be 0 because the list ends there, or it may be 0 because the next item of the list did not fit into the remaining bytes of the block. The location of the directory file section and its size is recorded in the directory record which describes the parent directory. I.e. possibly quite far away. So the border between two files is defined by a change of the directory record property tuple (name, version, associated file flag) or by the fact that the first of the two compared records has no multi-extent flag set. Both concepts (tuple versus multi-extent flag) are overlapping but not necessarily consistent. They are brought into sync by the prescription 9.2 of ECMA-119. As if this was not enough, there come the Rock Ridge extensions, with their POSIX compliant name semantics. No versions, no associated files. But still multi-extent flag and equal names which are supposed to be in sync. This is another dimension to be coordinated, because each Rock Ridge name is actually only an alternative name for an ECMA-119 directory record. Rock Ridge RRIP-1.12 and its boss SUSP-1.12 are around in the web as susp112.ps / .pdf and rr112.ps / .pdf. Hosts vary. --------------------------------------------------------------------- Current state of cd9660 implementation: --------------------------------------------------------------------- So Files and File Sections are in a m:n relation. Currently cd9660 has a 1:1 relation implemented. The m:1 aspect does not have to be explicitely implemented, because the read-only filesystem implictely tolerates content sharing. So one could as well say that cd9660 currently models m Files related to 1 File Section. The 1:n relation between single file object and multiple directory records resp. file sections is not yet represented in NetBSD cd9660. sys/fs/cd9660/cd9660_node.h defines: struct iso_node { ... unsigned long iso_extent; /* extent of file */ unsigned long i_size; unsigned long iso_start; /* actual start of data of file (may be different */ ... }; For ISO 9660 level 3, iso_node needs multiple file sections. The overall size will be the sum of section sizes. Member .iso_extent is only used to recognize the root directory in cd9660_vfsops.c: if (ip->iso_extent == imp->root_extent) vp->v_vflag |= VV_ROOT; Member .i_size is the byte count of the data file. Member .iso_start currently gives the start of file content data of the first file section. So this is the natural value for the block address in the representation of file sections. ----------------------------- Creation and destruction of struct iso_node: iso_node objects are created in cd9660_vfsops.c : cd9660_loadvnode() which implements vfsops.vfs_loadvnode. They are destroyed in cd9660_node.c : cd9660_reclaim() which implements vnodeopv_entry_desc.vop_reclaim_desc , VOP_RECLAIM(9). The iso_nodes have to be fixed size objects, because they are obtained and released by pool_get(9) / pool_put(9). (The planned struct iso_file_section arrays will have variable size.) ---------------------------- ISOFSMNT_EXTATT: The blocks between .iso_extent and .iso_start are ISO 9660 Extended Attribute. They may be used if mount(2) flags bit ISOFSMNT_EXTATT is given. In this case , parts of the attributes of the file section get copied into the ISO_RRIP_INODE of the struct iso_node. The attributes are not addressed via .iso_extent, but rather by a reverse computation in cd9660_vfsops.c. It subtracts the .ext_attr_length of the struct iso_directory_record from the data start block .iso_start of the vnode's iso_node: if ((imp->im_flags & ISOFSMNT_EXTATT) && (off = isonum_711(isodir->ext_attr_length))) cd9660_blkatoff(vp, (off_t)-(off << imp->im_bshift), NULL, &bp2); struct iso_directory_record isodir represents the raw ISO 9660 Directory Record that is the origin of the iso_node information underneath struct vnode *vp. (Note that ISO 9660 Extended Attributes are not what extattr(9) describes and what MNT_EXTATTR should retrieve. The mount options "-o extattr" and "-o extatt" are deceivingly similar. ISO 9660 by its SUSP extension can record things like extattr(9). E.g. in the form described in http://libburnia-project.org/wiki/AAIP ) ---------------------------- struct ifid: struct ifid is the cd9660-specific variant of struct fid as used with VFS_FHTOVP and VFS_VPTOFH (for NFS). It is declared in cd9660_vfsops.c with comment "File handle to vnode". struct ifid { ushort ifid_len; ushort ifid_pad; ino_t ifid_ino; #ifdef ISOFS_DBG u_long ifid_start; #endif }; Member .ifid_start registers the start block of the first file section of the iso_node: ifh.ifid_start = ip->iso_start But it is used merely for debugging purposes. struct ifid.ifid_start can be kept for that purpose. There is no need to expand it to a full list of file sections. -------------------------------------------------------------- Distinction between consumers of struct iso_node.i_size, which mean the single extent, versus those which mean the overall file size: -------- cd9660_bmap.c : cd9660_bmap() *ap->a_bnp = (ip->iso_start + lblkno) << (bshift - DEV_BSHIFT); ... /* * Determine maximum number of readahead blocks following the * requested block. */ nblk = (ip->i_size >> bshift) - (lblkno + 1); ( >>> ??? Does NetBSD have a CD TAO readahead bug ? Could be that not. Readahead seems to be handled per file. TAO tracks tell a size which includes two non-data CD sectors at the end of the track. If readahead is per device then the driver can easily run into an i/o error and drop valid blocks of the last big read chunk. Generous padding at write time is the traditional remedy.) => Single file section size -------- cd9660_lookup.c : cd9660_lookup() endsearch = dp->i_size; dp is a directory node pointer. Because directories have only one file section, this maps 1:1 to the first file section of dp in the new representation. => Single file section size. == Sum of file section sizes. -------- cd9660_vfsops.c : cd9660_loadvnode() 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; This will have to be replaced by the creation of the new representation of multiple file sections. => All single file section sizes. -------- cd9660_vfsops.c : cd9660_loadvnode() uvm_vnp_setsize(vp, ip->i_size); => Sum of file section sizes. -------- cd9660_vnops.c : cd9660_getattr() vap->va_size = (u_quad_t) ip->i_size; => Sum of file section sizes. -------- cd9660_vnops.c : cd9660_getattr() if (ip->i_size == 0 && vp->v_type == VLNK) { => Sum of file section sizes. -------- cd9660_vnops.c : cd9660_getattr() vap->va_bytes = (u_quad_t) ip->i_size; => Sum of file section sizes. -------- cd9660_vnops.c : cd9660_read() if (uio->uio_offset >= ip->i_size) return 0; ... vsize_t bytelen = MIN(ip->i_size - uio->uio_offset, uio->uio_resid); => Sum of file section sizes. -------- cd9660_vnops.c : cd9660_read() diff = (off_t)ip->i_size - uio->uio_offset; ... if (cd9660_lblktosize(imp, rablock) < ip->i_size) { This is not related to the task of concatenating the file sections. It is part of the loop which handles non-regular files. Actually i can reach it only by read(2)ing directories. Special files and fifos have their own read functions { &vop_read_desc, spec_read }, /* read */ { &vop_read_desc, vn_fifo_bypass }, /* read */ Directory reading can trigger it: netbsd$ ls -ld /mnt/iso/dev drwxr-xr-x 1 root wheel 4096 May 3 14:58 /mnt/iso/dev netbsd$ cat /mnt/iso/dev | wc 4 38 4096 A peculiarity of this code is that its block addressing is done relative to block 0 of the device which hosts the ISO filesystem. I.e. it is not relative to the extent start of the directory or other file object. => Single file section size. == Sum of file section sizes. -------- cd9660_vnops.c : cd9660_readdir() endsearch = dp->i_size; dp is a directory node pointer. Only one file section possible. => Single file section size. == Sum of file section sizes. -------- The consumers of "Sum of file section sizes" are safe for off_t. -------------------------------------------------------------------- Spots of inode number usage: Data types containing ino_t members: cd9660_node.h : struct iso_node . i_number The inode number of the iso_node itself. cd9660_node.h : struct iso_node . i_ino In cd9660_lookup() it is set to the found ino and then eventually modified if directory relocation happens. iso_rrip.h : ISO_RRIP_ANALYZE . *inump A backdoor to struct iso_node.i_ino by which it learns the inode number of the relocated directory. They are connected by cd9660_rrip_getname() which then runs cd9660_rrip_loop() to look for relocation related entries. cd9660_vfsops.c : struct ifid . ifid_ino A file handle for NFS. cd9660_vnops.c : struct isoreaddir : struct dirent : d_fileno struct isoreaddir contains three struct dirent which contain ino_t d_fileno. ------------ cd9660_lookup.c : cd9660_lookup() ino_t ino = 0; ... switch (imp->iso_ftype) { default: ... dp->i_ino = isodirino(ep, imp); goto found; ... ino = isodirino(ep, imp); ... ino = dbtob(bp->b_blkno) + entryoffsetinblock; Will become CD9660_COMPUTE_INO_DB(). Need to determine the number of file sections here. ... case ISO_FTYPE_RRIP: ... } else if (ino) goto foundino; ... ino = isodirino(ep, imp); ... ino = dbtob(bp->b_blkno) + entryoffsetinblock; ... dp->i_ino = ino; cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp); Will become CD9660_COMPUTE_INO_DB(). Need to determine the number of file sections here. ... if (ino) { foundino: dp->i_ino = ino; ... if (dp->i_number == dp->i_ino) { vref(vdp); /* we want ourself, ie "." */ ... } else { error = vcache_get(vdp->v_mount, &dp->i_ino, sizeof(dp->i_ino), &tdp); ------------ cd9660_node.c : cd9660_reclaim() vcache_remove(vp->v_mount, &ip->i_number, sizeof(ip->i_number)); ------------ cd9660_node.c : isodirino() ino = ((ino_t)isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length)) << imp->im_bshift; Will become CD9660_COMPUTE_INO() ------------ cd9660_rrip.c : cd9660_rrip_pclink() *ana->inump = isonum_733(p->dir_loc) << ana->imp->im_bshift; Will become CD9660_COMPUTE_INO() ------------ cd9660_rrip.c : cd9660_rrip_getname() cd9660_rrip_getname(struct iso_directory_record *isodir, char *outbuf, u_short *outlen, ino_t *inump, struct iso_mnt *imp) ... analyze.inump = inump; This installs a backdoor to iso_node.i_number, which will be used to change .i_number if directory relocation happens. ------------ cd9660_vfsops.c : cd9660_root() ino_t ino = isodirino(dp, imp); return cd9660_vget(mp, ino, vpp); ------------ cd9660_vfsops.c : cd9660_fhtovp() printf("fhtovp: ino %d, start %ld\n", ifh.ifid_ino, ifh.ifid_start); ... if ((error = VFS_VGET(mp, ifh.ifid_ino, &nvp)) != 0) { ------------ cd9660_vfsops.c : cd9660_vget() cd9660_vget(struct mount *mp, ino_t ino, struct vnode **vpp) ... error = vcache_get(mp, &ino, sizeof(ino), vpp); ------------ cd9660_vfsops.c : cd9660_loadvnode() cd9660_loadvnode(struct mount *mp, struct vnode *vp, const void *key, size_t key_len, const void **new_key) ino_t ino; ... KASSERT(key_len == sizeof(ino)); memcpy(&ino, key, key_len); ... ip->i_number = ino; ... lbn = cd9660_lblkno(imp, ino); Will become CD9660_BYTEADR_FROM_INO(ino) ... off = cd9660_blkoff(imp, ino); Will become CD9660_BYTEADR_FROM_INO(ino) ... ifh.ifid_ino = ip->i_number; ------------ cd9660_vfsops.c : cd9660_vptofh() ifh.ifid_ino = ip->i_number; ... printf("vptofh: ino %d, start %ld\n", ifh.ifid_ino,ifh.ifid_start); Formatter will become "vptofh: ino %"PRIu64", start %lu\n" ------------ cd9660_vnops.c : cd9660_readdir() idp->current.d_fileno = isodirino(ep, imp); ... idp->current.d_fileno = dbtob(bp->b_blkno) + entryoffsetinblock; Will become CD9660_COMPUTE_INO_DB(). Need to determine the number of file sections here. This code must probably be moved behind bundling of directory record. ... cd9660_rrip_getname(ep, idp->current.d_name, &namelen, &idp->current.d_fileno, imp); d_fileno is copied inside cd9660_rrip_getname() but the copy seems not to be read anywhere. -------------------------------------------------------------------- >>> Open questions: What error to throw on undigestible file section situation ? (E.g. unaligned non-last file section.) Some violations of ISO 9660 specs already yield EINVAL. But isn't that an error for wrong user (agent) input ? Some of the cases in question are filesystem damage, weirdness, or even attack via removable medium. A problem does not necessarily have to be malicious or illegal: ISO 9660 allows all file sections to be of sizes which are not aligned to the block size. The only place where i could spot a translation from file internal byte address to filesystem global address is the mapper for block addresses: cd9660_bmap(). So it appears impossible to implement a reader for file sections which are not aligned to a block end and which are not the last file section in each file where they occur. Similarly undigestible but also illegal would be multi-extent directories. Most of the code will assume that directories have a single extent. -------------------------------------------------------------------- Reason why mount_9660 with and without -o norrip shows such different results with multi-extent files: The norrip case of mount_cd9660 leads to ISO_FTYPE_DEFAULT. The further effects happen in cd9660_vnops.c: cd9660_readdir() uses iso_shipdir() to bring a node into the resulting list of directory entries. iso_shipdir() works with delay to compare the previous dirent struct isoreaddir.saveent with the current one: .current It calls iso_uiodir() only if the names of .saveent and .current differ. This is the case with the last dirent of each file. So it seems to intentionally unify duplicate dirents. Probably for getting the youngest file version without having the version numbers. But it does not know any version numbers, and thus mistakes the first file section for an outdated file version. The case of ISO_FTYPE_RRIP does no such delay and comparison. Therefore two iso_nodes emerge. /usr/src/bin/ls/ls.c relies on fts_children(3), which returns a linked list of FTSENT structs. Obviously this list contains the duplicate. The function cd9660_getattr() is called only once for the large file, when both its identities get listed by ls -l. ISO_FTYPE_9660 does not use iso_shipdir(). It has the intention to show all ISO 9660 versions of files with the same name. So it shows two files with properties of the first file section, like is done by ISO_FTYPE_RRIP. -------------------------------------------------------------------- The meaning of struct iso_mnt.iso_ftype. ISO_FTYPE_9660 is in effect if struct iso_mount.flags bit ISOFSMNT_GENS is given. ISO_FTYPE_DEFAULT is in effect if ISOFSMNT_NORRIP is given, but not ISOFSMNT_GENS. By default, ISO_FTYPE_RRIP is in effect. ISO_FTYPE_RRIP: - Uses Rock Ridge extensions (SUSP, RRIP) for: - Long POSIX file names - POSIX file types and attributes - Symbolic links - Does not support the concept of ECMA-119 6.5.4 "Associated file". ISO_FTYPE_DEFAULT (-o norrip,nojoliet): - Ignores files with identical name except the last one. Probably it wants the newest version and fails to compare version numbers. This needs to be fixed. Function isofntrans() should get yet another parameter which tells the found version number. ISO_FTYPE_9660 (-o gens): - Leaves semicolon and version number at end of file name. - does not unify files with same name before semicolon -------------------------------------------------------------------- Model implementation proposal: --------------------------------------------------------------------- The individual properties of an ISO 9660 File Section shall be described by struct iso_file_section { uint32_t start_block; uint32_t byte_count; }; uint32_t is chosen here because it is the natural ISO 9660 type for block addresses and file section sizes. The 1:n relation of iso_node and iso_file_section shall be described by struct iso_file_section *iso_sections; augmenting the existing members unsigned long iso_extent; unsigned long i_size; unsigned long iso_start; The number of file sections shall be encoded in the inode number, which already holds the address of the directory record which describes the first file section. The overall byte size of the file has to be computed by a loop There should be a curb for num_file_sections. 2049 would be the maximum number of file sections needed to cover the maximum number of blocks in an ISO 9660 image. That would be a file of 8 TiB. Motivation: The inode number of a cd9660 file shall tell the information needed to identify all ISO 9660 Directory Records of that file. A shortcomming of ISO 9660 specs can best be worked around by encoding the number of File Sections in the inode number, where 48 bits are already occupied by the byte address of the first Directory Record of the file. Normally there are not many multi-section files. In order to save storage and kmem(9) management cost with single-section files, their section information will continue to be stored in the traditional iso_node members .iso_extent, .i_size, .iso_start. This also keeps maximum API compatibility to older versions of struct iso_node. fstat(1) and pmap(1) do include which is a copy of src/sys/fs/cd9660/cd9660_node.h. The ABI of struct iso_node was recently broken by Revision 1.16 which removed an obsolete internal handle: - LIST_ENTRY(iso_node) i_hash; My change proposal is API/ABI compatible to Revision 1.16. New struct member for struct iso_mnt: For a previous model sketch, i planned to drop some API compatibility by giving up .iso_extent. I augmented struct iso_mnt (cd9660_extern.h) by a new member uint32_t root_start_block; which can be recognized from the surely maintained start block address of the first file section. Meanwhile this aspect could be revoked. But i deem the new member helpful anyway, because the existing iso_mnt.root_extent points to the Extended Attributes of /-directory and not to its directory records. -------------------------------------------------------------------- Todo list -------------------------------------------------------------------- "+++" means that an item is completely implemented and tested "++?" means that it works and is tested. But there are open questions. ">>>" means that there is still work to be done Goal is to introduce 1:n relation of struct iso_node and ISO 9660 File Sections: +++ cd9660_lookup.c, cd9660_node.h, cd9660_rrip.c, cd9660_vfsops.c, cd9660_vnops.c, cd9660_node.c +++ Introduce macros for computation of inode numbers and for the reverse computation to byte and block addresses. +++ Change all such computation to the use of the new macros. >>> Create test images and test script to ensure that all ino_t related operations survived and that large files are handled properly. >>> /home/thomas/projekte/netbsd_dir/tests/fs/cd9660 >>> Set up an atf test environment. Martin Husemann stated that i'd need an installation from "daily build". +++ I now have that pure 6.44.99 system >>> Need atf tutor, or a sponsor who does the last adaptions. +++ t_cd9660_regression.sh Halfways atf-ready test script with standalone code. Test cases: - pr_kern_48787 from 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 +++ cd9660_rgr.image.at0.bz2.uue cd9660_rgr.image.at2114008.bz2.uue Construction kit for image from create_test_iso_level_3.sh. Used with all test cases except "exotic". +++ exoten.iso.bz2.uue Image with exotic file situations. +++ Create function or macro which tells the overall size of the file sections: +++ Semi-public cd9660_node.h macro CD9660_DATA_SIZE_FUNC implements off_t iso_data_count(struct iso_node *) +++ Add to struct iso_mnt a member uint32_t root_start_block; which tells the start of the root directory record array after the eventual ISO Extended Attributes of the root directory extent. (The latter is already recorded as iso_mnt.root_extent.) Replace the only use of iso_node.iso_extent (in cd9660_vfsops.c): if (ip->iso_extent == imp->root_extent) by if (ip->iso_start == imp->root_start_block) +++ cd9660_node.h: Introduce macro which indicates the new entrails to those who cannot keep themselves from including cd9660_node.h: #define CD9660_MULTI_EXTENT yes +++ cd9660_node.h: Augment unsigned long iso_extent; unsigned long i_size; unsigned long iso_start; by struct iso_file_section *iso_sections; A separate section counter can be omitted, because the implementation needs the section count in .i_number anyway. +++ Change the debug code for struct ifid to refer with .ifid_start to the first iso_file_section. +++ Add a remark to the non-VREG code in cd9660_read() that this is for read(2) on directories and that addressing is relative to block 0 of the storage device of the filesystem. +++ Let function cd9660_util.c : isofntrans() return the version number. +++ Implemented and compilable +++ Make use of this in cd9660_vnops.c : iso_shipdir() +++ Implemented +++ Testing with a manually modifed large file as versions 1 and 2 +++ Still properly excludes older version +++ Testing with large file whether only one file appears ++? In cd9660_vnops.c : cd9660_readdir() when handing out ino_t numbers. +++ Let ISO_FTYPE_DEFAULT drop directory records which have lower version numbers than the highest one in the family of records with the same name. (Currently it drops all but the last record of the same name. See behavior with large files after mount_cd9660 -o norrip) ++? Let any ISO_FTYPE_* unify the surviving directory records with same file name, same version number, same assoc flag, and properly set multi-extent flags. The first record will by its byte address provide the lower 43 bits of the ino_t number. (Previously the records of non-ISO_FTYPE_DEFAULT creates an iso_node each. See behavior with large files mount_cd9660 without any -o.) +++ demand multi-extent bit in directory record file flag +++ Expand the use of iso_shipdir() to all iso_ftypes. ??? Base iso_shipdir() comparison on ISO names and not on translated names or RRIP names. But avoid the risk of duplicate RRIP names. +++ Encode in ino_t: 48 bits of byte address of first record (as is done already now) 13 bits of file section count - 1. Enough for filling the whole fs by 1 GiB pieces. Possibly cd9660_loadvnode() should curb to a much lower number. count - 1 will keep numbers unchanged for single-extent files. Count 0 is really impossible, not only illegal. 3 bits are still unused. +++ Change any usage of iso_node.i_size according to the lengthy assessment. Change some of the usages of iso_node.iso_start. cd9660_lookup.c : cd9660_lookup() ++? Determine the section count of the inodes for which numbers get computed +++ teach isofncmp to distinguish versions +++ demand multi-extent bit in directory record file flag ??? Base comparison on ISO names and not on translated names or RRIP names. But avoid the risk of duplicate RRIP names. +++ In cd9660_vfsops.c : cd9660_loadvnode() when creating an iso_node: +++ Decode ino_t to file section count and directory record address +++ If single file section: register directly in iso_node +++ If multiple file sections: +++ Check whether this is really a regular file +++ Directory +++ Rock Ridge non-regular files +++ Check file sections except last one for block alignment. +++ kmem_zalloc(9) an array of iso_file_section +++ Register sections: iso_register_fsects() +++ In cd9660_node.c : cd9660_reclaim() when disposing an iso_node free the eventual .iso_sections array and set the section count to 0. +++ Change cd9660_bmap.c : cd9660_bmap() so that it interprets the new members of iso_node. ++? Test that i did not damage Extended Attributes or Associated Files. ??? Are there any ISO images which make non-trivial use of these ? +++ Fake Extended Attributes and test recognition +++ Fake associated file and test recognition. +++ make part of test script +++ Test that the call-again protocol of cd9660_readdir() still works seamlessly. +++ cd9660_vnops.c : ISOFS_DBG_SIMULATE_READIR_INTVL causes deliberate intermediate returns of cd9660_readdir(). It did not change test results. ??? Shall readdir() avoid to report the existence of undigestible files ? --------------------------------------------------------------------