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