lkml.org 
[lkml]   [1997]   [Jan]   [17]   [last100]   RSS Feed
Views: [more markup]  [less markup]  [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
DateFri, 17 Jan 1997 10:37:00 +0100 (MET)
FromGerd Knorr <>
Subjectisofs patch: new mountoption
  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++) {

\
 
 \ /
  Last update: 2005-03-22 12:38    [from the cache]
©2003-2008