Re: Missing files on Joliet CD-ROMs

Gordon Chaffee (chaffee@cs.berkeley.edu)
Tue, 11 May 1999 22:51:20 -0700 (PDT)


David Schleef writes:
> I'm experiencing missing file issues on a commercial Joliet
> CD-ROM. Kernel is 2.2.1, using scsi host adapter emulation for
> the ATAPI CD-ROM drive. CD-ROM drive is Hewlett-Packard CD-Writer
> Plus 8100.
>
> On sucessive mountings of the same disk, I get the following
> outputs from 'find /mnt/cdrom | wc -l':
>
> 9790 corel.list.1
> 9790 corel.list.2
> 9724 corel.list.3 (-o nojoliet)
> 9668 corel.list.4
> 9661 corel.list.5
> 9722 corel.list.6

Could you send me a partial dump of the CD?
Do
dd if=/dev/cdrom bs=1024 count=2048 | gzip -c -f > cddata.gz

and send me the result. I don't think this patch will help, but
you could try. It fixes problems with reading some older CDs. I'll
see what I can turn up with the data although it will probably take me
a few days.

- Gordon

--- linux-2.2.8-clean/fs/isofs/namei.c Tue May 11 21:29:02 1999
+++ linux/fs/isofs/namei.c Tue May 11 21:31:08 1999
@@ -49,202 +49,64 @@
return dentry->d_op->d_compare(dentry, &dentry->d_name, &qstr);
}

-/*
- * isofs_find_entry()
- *
- * finds an entry in the specified directory with the wanted name. It
- * returns the cache buffer in which the entry was found, and the entry
- * itself (as an inode number). It does NOT read the inode of the
- * entry - you'll have to do that yourself if you want to.
- */
-static struct buffer_head *
-isofs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
+struct isofs_find_info {
+ int found;
+ struct dentry *dentry;
+ ino_t ino;
+};
+
+static int
+isofs_lookup_cb(filldir_t filldir, void * cbarg,
+ const char * name, int namelen, off_t offset, ino_t ino)
{
- unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
- unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
- unsigned int block, i, f_pos, offset,
- inode_number = 0; /* shut gcc up */
- struct buffer_head * bh , * retval = NULL;
- unsigned int old_offset;
- int dlen, match;
- char * dpnt;
- unsigned char *page = NULL;
- struct iso_directory_record * de = NULL; /* shut gcc up */
- char de_not_in_buf = 0; /* true if de is in kmalloc'd memory */
- char c;
-
- *ino = 0;
-
- if (!(block = dir->u.isofs_i.i_first_extent)) return NULL;
-
- f_pos = 0;
-
- offset = f_pos & (bufsize - 1);
- block = isofs_bmap(dir,f_pos >> bufbits);
-
- if (!block || !(bh = bread(dir->i_dev,block,bufsize))) return NULL;
-
- while (f_pos < dir->i_size) {
-
- /* if de is in kmalloc'd memory, do not point to the
- next de, instead we will move to the next sector */
- if(!de_not_in_buf) {
- de = (struct iso_directory_record *)
- (bh->b_data + offset);
- }
- inode_number = (block << bufbits) + (offset & (bufsize - 1));
-
- /* If byte is zero, or we had to fetch this de past
- the end of the buffer, this is the end of file, or
- time to move to the next sector. Usually 2048 byte
- boundaries. */
-
- if (*((unsigned char *) de) == 0 || de_not_in_buf) {
- if(de_not_in_buf) {
- /* james@bpgc.com: Since we slopped
- past the end of the last buffer, we
- must start some way into the new
- one */
- de_not_in_buf = 0;
- kfree(de);
- f_pos += offset;
- }
- else {
- offset = 0;
- f_pos = ((f_pos & ~(ISOFS_BLOCK_SIZE - 1))
- + ISOFS_BLOCK_SIZE);
- }
- brelse(bh);
- bh = NULL;
-
- if (f_pos >= dir->i_size)
- break;
-
- block = isofs_bmap(dir,f_pos>>bufbits);
- if (!block || !(bh = bread(dir->i_dev,block,bufsize)))
- break;
-
- continue; /* Will kick out if past end of directory */
- }
-
- old_offset = offset;
- offset += *((unsigned char *) de);
- f_pos += *((unsigned char *) de);
-
- /* james@bpgc.com: new code to handle case where the
- directory entry spans two blocks. Usually 1024
- byte boundaries */
- if (offset >= bufsize) {
- struct buffer_head *bh_next;
-
- /* james@bpgc.com: read the next block, and
- copy the split de into a newly kmalloc'd
- buffer */
- block = isofs_bmap(dir,f_pos>>bufbits);
- if (!block ||
- !(bh_next = bread(dir->i_dev,block,bufsize)))
- break;
-
- de = (struct iso_directory_record *)
- kmalloc(offset - old_offset, GFP_KERNEL);
- memcpy((char *)de, bh->b_data + old_offset,
- bufsize - old_offset);
- memcpy((char *)de + bufsize - old_offset,
- bh_next->b_data, offset - bufsize);
- brelse(bh_next);
- de_not_in_buf = 1;
- offset -= bufsize;
- }
- dlen = de->name_len[0];
- dpnt = de->name;
-
- if (dir->i_sb->u.isofs_sb.s_rock ||
- dir->i_sb->u.isofs_sb.s_joliet_level ||
- dir->i_sb->u.isofs_sb.s_mapping == 'n' ||
- dir->i_sb->u.isofs_sb.s_mapping == 'a') {
- if (! page) {
- page = (unsigned char *)
- __get_free_page(GFP_KERNEL);
- if (!page) break;
- }
- }
- if (dir->i_sb->u.isofs_sb.s_rock &&
- ((i = get_rock_ridge_filename(de, page, dir)))) {
- dlen = i;
- dpnt = page;
-#ifdef CONFIG_JOLIET
- } else if (dir->i_sb->u.isofs_sb.s_joliet_level) {
- dlen = get_joliet_filename(de, dir, page);
- dpnt = page;
-#endif
- } else if (dir->i_sb->u.isofs_sb.s_mapping == 'a') {
- dlen = get_acorn_filename(de, page, dir);
- dpnt = page;
- } else if (dir->i_sb->u.isofs_sb.s_mapping == 'n') {
- for (i = 0; i < dlen; i++) {
- c = dpnt[i];
- /* lower case */
- if (c >= 'A' && c <= 'Z') c |= 0x20;
- if (c == ';' && i == dlen-2 && dpnt[i+1] == '1') {
- dlen -= 2;
- break;
- }
- if (c == ';') c = '.';
- page[i] = c;
- }
- /* This allows us to match with and without
- * a trailing period. */
- if(page[dlen-1] == '.' && dentry->d_name.len == dlen-1)
- dlen--;
- dpnt = page;
- }
- /*
- * Skip hidden or associated files unless unhide is set
- */
- match = 0;
- if ((!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
- || dir->i_sb->u.isofs_sb.s_unhide == 'y') && dlen)
- {
- match = (isofs_cmp(dentry,dpnt,dlen) == 0);
- }
- if (match) {
- if(inode_number == -1) {
- /* Should only happen for the '..' entry */
- inode_number =
- isofs_lookup_grandparent(dir,
- find_rock_ridge_relocation(de,dir));
- }
- *ino = inode_number;
- retval = bh;
- bh = NULL;
- break;
- }
+ struct isofs_find_info *info = cbarg;
+ if (isofs_cmp(info->dentry, name, namelen) != 0) {
+ return 0;
}
- if (page) free_page((unsigned long) page);
- if (bh) brelse(bh);
- if(de_not_in_buf)
- kfree(de);
- return retval;
+ info->found = 1;
+ info->ino = ino;
+ return -1;
}

struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry)
{
- unsigned long ino;
- struct buffer_head * bh;
struct inode *inode;
+ char * tmpname;
+ struct iso_directory_record * tmpde;
+ struct isofs_find_info info;
+ struct file fil;
+ int res;

#ifdef DEBUG
printk("lookup: %x %s\n",dir->i_ino, dentry->d_name.name);
#endif
- dentry->d_op = dir->i_sb->s_root->d_op;

- bh = isofs_find_entry(dir, dentry, &ino);
+ fil.f_pos = 0;
+ fil.f_dentry = dentry->d_parent;
+ info.found = 0;
+ info.dentry = dentry;
+
+ if (!dir)
+ return -ENOENT;

- inode = NULL;
- if (bh) {
- brelse(bh);
+ if (!S_ISDIR(dir->i_mode))
+ return -ENOENT;
+
+ dentry->d_op = dir->i_sb->s_root->d_op;

- inode = iget(dir->i_sb,ino);
+ tmpname = (char *) __get_free_page(GFP_KERNEL);
+ if (!tmpname)
+ return -ENOMEM;
+ tmpde = (struct iso_directory_record *) (tmpname+1024);
+ res = isofs_readdirx(dir, &fil,&info, isofs_lookup_cb, NULL, tmpname, tmpde);
+ free_page((unsigned long) tmpname);
+
+ if (res < 0)
+ return res;
+ if (!info.found) {
+ inode = NULL;
+ } else {
+ inode = iget(dir->i_sb,info.ino);
if (!inode)
return ERR_PTR(-EACCES);
}
--- linux-2.2.8-clean/fs/isofs/inode.c Tue Dec 29 11:40:35 1998
+++ linux/fs/isofs/inode.c Mon May 10 08:26:54 1999
@@ -1232,143 +1232,6 @@
return;
}

-/* There are times when we need to know the inode number of a parent of
- a particular directory. When control passes through a routine that
- has access to the parent information, it fills it into the inode structure,
- but sometimes the inode gets flushed out of the queue, and someone
- remembers the number. When they try to open up again, we have lost
- the information. The '..' entry on the disc points to the data area
- for a particular inode, so we can follow these links back up, but since
- we do not know the inode number, we do not actually know how large the
- directory is. The disc is almost always correct, and there is
- enough error checking on the drive itself, but an open ended search
- makes me a little nervous.
-
- The BSD iso filesystem uses the extent number for an inode, and this
- would work really nicely for us except that the read_inode function
- would not have any clean way of finding the actual directory record
- that goes with the file. If we had such info, then it would pay
- to change the inode numbers and eliminate this function.
-*/
-
-int isofs_lookup_grandparent(struct inode * parent, int extent)
-{
- unsigned long bufsize = ISOFS_BUFFER_SIZE(parent);
- unsigned char bufbits = ISOFS_BUFFER_BITS(parent);
- unsigned int block,offset;
- int parent_dir, inode_number;
- int result;
- int directory_size;
- struct buffer_head * bh;
- struct iso_directory_record * de;
-
- offset = 0;
- block = extent << (ISOFS_ZONE_BITS(parent) - bufbits);
- if (!(bh = bread(parent->i_dev, block, bufsize))) return -1;
-
- while (1 == 1) {
- de = (struct iso_directory_record *) (bh->b_data + offset);
- if (*((unsigned char *) de) == 0)
- {
- brelse(bh);
- printk("Directory .. not found\n");
- return -1;
- }
-
- offset += *((unsigned char *) de);
-
- if (offset >= bufsize)
- {
- printk(".. Directory not in first block"
- " of directory.\n");
- brelse(bh);
- return -1;
- }
-
- if (de->name_len[0] == 1 && de->name[0] == 1)
- {
- parent_dir = find_rock_ridge_relocation(de, parent);
- directory_size = isonum_733 (de->size);
- brelse(bh);
- break;
- }
- }
-#ifdef DEBUG
- printk("Parent dir:%x\n",parent_dir);
-#endif
- /* Now we know the extent where the parent dir starts on. */
-
- result = -1;
-
- offset = 0;
- block = parent_dir << (ISOFS_ZONE_BITS(parent) - bufbits);
- if (!block || !(bh = bread(parent->i_dev,block, bufsize)))
- {
- return -1;
- }
-
- for(;;)
- {
- de = (struct iso_directory_record *) (bh->b_data + offset);
- inode_number = (block << bufbits)+(offset & (bufsize - 1));
-
- /* If the length byte is zero, we should move on to the next
- CDROM sector. If we are at the end of the directory, we
- kick out of the while loop. */
-
- if ((*((unsigned char *) de) == 0) || (offset == bufsize) )
- {
- brelse(bh);
- offset = 0;
- block++;
- directory_size -= bufsize;
- if(directory_size < 0) return -1;
- if((block & 1) && (ISOFS_ZONE_BITS(parent) - bufbits) == 1)
- {
- return -1;
- }
- if((block & 3) && (ISOFS_ZONE_BITS(parent) - bufbits) == 2)
- {
- return -1;
- }
- if (!block
- || !(bh = bread(parent->i_dev,block, bufsize)))
- {
- return -1;
- }
- continue;
- }
-
- /* Make sure that the entire directory record is in the current
- bh block. If not, we malloc a buffer, and put the two
- halves together, so that we can cleanly read the block. */
-
- offset += *((unsigned char *) de);
-
- if (offset > bufsize)
- {
- printk("Directory overrun\n");
- goto out;
- }
-
- if (find_rock_ridge_relocation(de, parent) == extent){
- result = inode_number;
- goto out;
- }
-
- }
-
- /* We go here for any condition we cannot handle.
- We also drop through to here at the end of the directory. */
-
- out:
- brelse(bh);
-#ifdef DEBUG
- printk("Resultant Inode %d\n",result);
-#endif
- return result;
-}
-
#ifdef LEAK_CHECK
#undef malloc
#undef free_s
--- linux-2.2.8-clean/fs/isofs/dir.c Mon May 10 03:29:17 1999
+++ linux/fs/isofs/dir.c Mon May 10 03:31:09 1999
@@ -121,9 +121,10 @@
/*
* This should _really_ be cleaned up some day..
*/
-static int do_isofs_readdir(struct inode *inode, struct file *filp,
- void *dirent, filldir_t filldir,
- char * tmpname, struct iso_directory_record * tmpde)
+int
+isofs_readdirx(struct inode *inode, struct file *filp,
+ void *cbarg, isofs_filldir_t ifilldir, filldir_t filldir,
+ char * tmpname, struct iso_directory_record * tmpde)
{
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
@@ -151,7 +152,7 @@
return 0;

while (filp->f_pos < inode->i_size) {
- int de_len;
+ unsigned int de_len, next_offset;
#ifdef DEBUG
printk("Block, offset, f_pos: %x %x %x\n",
block, offset, filp->f_pos);
@@ -160,19 +161,13 @@
de = (struct iso_directory_record *) (bh->b_data + offset);
if(first_de) inode_number = (block << bufbits) + (offset & (bufsize - 1));

- de_len = *(unsigned char *) de;
-#ifdef DEBUG
- printk("de_len = %ld\n", de_len);
-#endif
-
-
/* If the length byte is zero, we should move on to the next
CDROM sector. If we are at the end of the directory, we
kick out of the while loop. */

- if ((de_len == 0) || (offset >= bufsize) ) {
+ if ((offset >= bufsize) || (de->length[0] == 0)) {
brelse(bh);
- if (de_len == 0) {
+ if (de->length[0] == 0) {
filp->f_pos = ((filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1))
+ ISOFS_BLOCK_SIZE);
offset = 0;
@@ -190,21 +185,42 @@
bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size);
if (!bh)
return 0;
+ de = (struct iso_directory_record *) (bh->b_data + offset);
continue;
}

- offset += de_len;
- if (offset > bufsize) {
+ de_len = de->length[0];
+
+ next_offset = offset + de_len;
+ if (offset + de_len > bufsize) {
/*
* This would only normally happen if we had
* a buggy cdrom image. All directory
* entries should terminate with a null size
* or end exactly at the end of the sector.
- */
+ * However, some older CDs did not follow the
+ * standard quite as well as they should, so we
+ * need to support the case of the directory entry
+ * crossing over the end of a logical sector.
+ */
+#ifdef DEBUG
printk("next_offset (%x) > bufsize (%lx)\n",
- offset,bufsize);
- break;
+ next_offset,bufsize);
+#endif
+ next_offset &= (bufsize - 1);
+ memcpy(tmpde, de, bufsize - offset);
+ brelse(bh);
+
+ block = isofs_bmap(inode, (filp->f_pos + de_len) >> bufbits);
+ if (!block)
+ return 0;
+ bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size);
+ if (!bh)
+ return 0;
+ memcpy(bufsize - offset + (char *) tmpde, bh->b_data, next_offset);
+ de = tmpde;
}
+ offset = next_offset;

if(de->flags[-high_sierra] & 0x80) {
first_de = 0;
@@ -215,7 +231,7 @@

/* Handle the case of the '.' directory */
if (de->name_len[0] == 1 && de->name[0] == 0) {
- if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0)
+ if (ifilldir(filldir, cbarg, ".", 1, filp->f_pos, inode->i_ino) < 0)
break;
filp->f_pos += de_len;
continue;
@@ -226,7 +242,7 @@
/* Handle the case of the '..' directory */
if (de->name_len[0] == 1 && de->name[0] == 1) {
inode_number = filp->f_dentry->d_parent->d_inode->i_ino;
- if (filldir(dirent, "..", 2, filp->f_pos, inode_number) < 0)
+ if (ifilldir(filldir, cbarg, "..", 2, filp->f_pos, inode_number) < 0)
break;
filp->f_pos += de_len;
continue;
@@ -271,7 +287,7 @@
}
}
if (len > 0) {
- if (filldir(dirent, p, len, filp->f_pos, inode_number) < 0)
+ if (ifilldir(filldir, cbarg, p, len, filp->f_pos, inode_number) < 0)
break;
}
filp->f_pos += de_len;
@@ -282,10 +298,18 @@
return 0;
}

+static int
+isofs_filldir(filldir_t filldir, void * dirent,
+ const char * name, int name_len, off_t offset, ino_t ino)
+{
+ return filldir(dirent, name, name_len, offset, ino);
+}
+
+
/*
* Handle allocation of temporary space for name translation and
* handling split directory entries.. The real work is done by
- * "do_isofs_readdir()".
+ * "isofs_readdirx()".
*/
static int isofs_readdir(struct file *filp,
void *dirent, filldir_t filldir)
@@ -300,7 +324,8 @@
return -ENOMEM;
tmpde = (struct iso_directory_record *) (tmpname+1024);

- result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
+ result = isofs_readdirx(inode, filp, dirent, isofs_filldir, filldir,
+ tmpname, tmpde);

free_page((unsigned long) tmpname);
return result;
--- linux-2.2.8-clean/include/linux/iso_fs.h Mon May 10 03:29:25 1999
+++ linux/include/linux/iso_fs.h Mon May 10 08:27:06 1999
@@ -142,7 +142,7 @@
there is an extra reserved byte after the flags */

struct iso_directory_record {
- char length [ISODCL (1, 1)]; /* 711 */
+ unsigned char length [ISODCL (1, 1)]; /* 711 */
char ext_attr_length [ISODCL (2, 2)]; /* 711 */
char extent [ISODCL (3, 10)]; /* 733 */
char size [ISODCL (11, 18)]; /* 733 */
@@ -155,6 +155,9 @@
char name [0];
};

+typedef int
+(*isofs_filldir_t)(filldir_t filldir, void *, const char *, int, off_t, ino_t);
+
#define ISOFS_BLOCK_BITS 11
#define ISOFS_BLOCK_SIZE 2048

@@ -194,6 +197,9 @@
extern int isofs_new_block(int dev);
extern int isofs_free_block(int dev, int block);
extern int isofs_bmap(struct inode *,int);
+extern int isofs_readdirx(struct inode *inode, struct file *filp, void *dirent,
+ isofs_filldir_t ifilldir, filldir_t filldir,
+ char * tmpname, struct iso_directory_record * tmpde);

extern void isofs_put_super(struct super_block *);
extern struct super_block *isofs_read_super(struct super_block *,void *,int);
@@ -204,7 +210,6 @@

extern int isofs_lseek(struct inode *, struct file *, off_t, int);
extern int isofs_read(struct inode *, struct file *, char *, int);
-extern int isofs_lookup_grandparent(struct inode *, int);

extern struct inode_operations isofs_file_inode_operations;
extern struct inode_operations isofs_dir_inode_operations;

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/