NetBSD-Bugs archive

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

Re: kern/48959



The following reply was made to PR kern/48959; it has been noted by GNATS.

From: "Thomas Schmitt" <scdbackup%gmx.net@localhost>
To: gnats-bugs%NetBSD.org@localhost
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);
 



Home | Main Index | Thread Index | Old Index