isofs patch: new mountoption

Gerd Knorr (kraxel@cs.tu-berlin.de)
Fri, 17 Jan 1997 10:37:00 +0100 (MET)


Hi !

Here is a patch for the isofs: It adds a new mount option which allows
to mount any (not only the last) session of multisession CDs. Will be
useful if you do incremental backups with a CD-Writer and you need a older
file. Or to read at least the first two sessions if the writer screwed up
while writing the third one.

I tested this with scsi and ide cdrom drivers, and seems to work fine. If
someone has problems with it, drop me a email. If all is fine, it goes to
Linus within a week or so...

Gerd

----------------------------------------------------------------------------
diff -ur 2.1.21/fs/isofs/inode.c hacking/fs/isofs/inode.c
--- 2.1.21/fs/isofs/inode.c Fri Jan 17 00:47:27 1997
+++ hacking/fs/isofs/inode.c Wed Jan 15 00:23:32 1997
@@ -70,6 +70,7 @@
unsigned char check;
unsigned char conversion;
unsigned int blocksize;
+ unsigned int session;
mode_t mode;
gid_t gid;
uid_t uid;
@@ -86,6 +87,7 @@
popt->check = 's'; /* default: strict */
popt->conversion = 'b'; /* default: no conversion */
popt->blocksize = 1024;
+ popt->session = -1; /* default: last session */
popt->mode = S_IRUGO;
popt->gid = 0;
popt->uid = 0;
@@ -130,6 +132,7 @@
}
else if (value &&
(!strcmp(this_char,"block") ||
+ !strcmp(this_char,"session") ||
!strcmp(this_char,"mode") ||
!strcmp(this_char,"uid") ||
!strcmp(this_char,"gid"))) {
@@ -149,6 +152,9 @@
&& ivalue != 2048) return 0;
popt->blocksize = ivalue;
break;
+ case 's':
+ popt->session = ivalue;
+ break;
case 'u':
popt->uid = ivalue;
break;
@@ -165,6 +171,8 @@
return 1;
}

+extern struct file_operations * get_blkfops(unsigned int);
+
/*
* look if the driver can tell the multi session redirection value
*
@@ -188,7 +196,6 @@
struct cdrom_multisession ms_info;
unsigned int vol_desc_start;
struct inode inode_fake;
- extern struct file_operations * get_blkfops(unsigned int);
int i;

vol_desc_start=0;
@@ -224,6 +231,121 @@
return vol_desc_start;
}

+/*
+ * Search for a session. Allows to mount others than the last session
+ * of multisession CD's.
+ *
+ * How it works:
+ * 1) Ask the isofs superblock for the size of the first session.
+ * 2) Walk throuth the TOC and watch for a data track starting
+ * behind the first session. This is probably the start of the
+ * next session.
+ * 3) Check if it is really a new session (check the 'CD001' ID)
+ * 4) read the size of the second session and play the game again
+ * to find the third one. Then the fourth, the fifth,....
+ * I hope you get the idea :-)
+ * 5) If we have found the desired session (or if there are no more
+ * sessions), we stop and return the start sector.
+ *
+ * This obviously depends on correct values for the isofs size. There
+ * are CD's where this is _not_ the case. So don't expect this works
+ * perfect.
+ * For CD-Writers there are probably better solutions, as they can tell
+ * the offset for every session, not only the last one. But I hav'nt
+ * one to play with (yet?).
+ *
+ * Gerd Knorr <kraxel@cs.tu-berlin.de>
+ *
+ */
+
+static unsigned int isofs_search_session(kdev_t dev, unsigned int session,
+ unsigned int bs, unsigned int bs_bits)
+{
+ struct inode inode_fake;
+ struct cdrom_tochdr toc_hdr;
+ struct cdrom_tocentry toc_entry;
+ unsigned int session_start = 0;
+ unsigned int session_nr = 0;
+ unsigned int session_size = 0;
+ unsigned int track_start;
+ unsigned int track_nr;
+ struct iso_volume_descriptor *vdp = NULL;
+ struct iso_primary_descriptor *pri = NULL;
+ struct buffer_head *bh=NULL;
+ int b,i;
+
+ inode_fake.i_rdev=dev;
+ set_fs(KERNEL_DS);
+ i=get_blkfops(MAJOR(dev))->ioctl
+ (&inode_fake,NULL,
+ CDROMREADTOCHDR,(unsigned long) &toc_hdr);
+ set_fs(USER_DS);
+ if (0 != i) {
+ printk("isofs: read TOC header failed\n");
+ return 0;
+ }
+#if 0
+ printk("isofs: CD has tracks %i - %i\n",
+ toc_hdr.cdth_trk0,toc_hdr.cdth_trk1);
+#endif
+ for (track_nr = toc_hdr.cdth_trk0;
+ track_nr <= toc_hdr.cdth_trk1; track_nr++) {
+ toc_entry.cdte_track = track_nr;
+ toc_entry.cdte_format = CDROM_LBA;
+ set_fs(KERNEL_DS);
+ i=get_blkfops(MAJOR(dev))->ioctl
+ (&inode_fake,NULL,
+ CDROMREADTOCENTRY,(unsigned long) &toc_entry);
+ set_fs(USER_DS);
+ if (0 != i) {
+ printk("isofs: read TOC entry (track %i) failed\n",
+ track_nr);
+ break;
+ }
+ if (!(toc_entry.cdte_ctrl & CDROM_DATA_TRACK)) {
+ printk("isofs: track %i: audio track, stopping search\n",
+ track_nr);
+ break;
+ }
+ track_start = toc_entry.cdte_addr.lba;
+#if 0
+ printk("isofs: data track %i: start=%i\n",track_nr,track_start);
+#endif
+
+ if (track_start >= session_size) {
+ /* probably new session found */
+ b = (16+track_start) << (ISOFS_BLOCK_BITS-bs_bits);
+ if (!(bh = bread(dev,b,bs))) {
+ printk("isofs: bread sector %i failed\n",
+ 16+track_start);
+ break;
+ }
+ vdp = (struct iso_volume_descriptor *)bh->b_data;
+ pri = (struct iso_primary_descriptor *)bh->b_data;
+ if (strncmp(vdp->id,ISO_STANDARD_ID,sizeof vdp->id) != 0) {
+ printk("isofs: no iso9660 ID found\n");
+ break;
+ }
+ session_start = track_start;
+ session_nr++;
+ session_size = isonum_733(pri->volume_space_size);
+#if 1
+ printk("isofs: session %i starts at track %i, lba=%i\n",
+ session_nr,track_nr,session_start);
+#endif
+ brelse(bh);
+
+ /* this one we are looking for */
+ if (session_nr == session)
+ break;
+ }
+ }
+ if (session_nr && session_nr < session)
+ printk("isofs: asked for session %i, but there are only %i\n",
+ session, session_nr);
+ return session_start;
+}
+
struct super_block *isofs_read_super(struct super_block *s,void *data,
int silent)
{
@@ -260,6 +382,7 @@
printk("cruft = %c\n", opt.cruft);
printk("unhide = %c\n", opt.unhide);
printk("conversion = %c\n", opt.conversion);
+ printk("session = %d\n", opt.session);
printk("blocksize = %d\n", opt.blocksize);
printk("gid = %d\n", opt.gid);
printk("uid = %d\n", opt.uid);
@@ -279,7 +402,11 @@

s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */

- vol_desc_start = isofs_get_last_session(dev);
+ if (opt.session == -1)
+ vol_desc_start = isofs_get_last_session(dev);
+ else
+ vol_desc_start = isofs_search_session
+ (dev,opt.session, opt.blocksize,blocksize_bits);

for (iso_blknum = vol_desc_start+16;
iso_blknum < vol_desc_start+100; iso_blknum++) {