[prev in list] [next in list] [prev in thread] [next in thread] 

List:       freedesktop-libburn
Subject:    Re: [Libburn] libisofs patch (2nd revision)
From:       Todd Kulesza <todd () dropline ! net>
Date:       2004-02-22 22:39:56
Message-ID: 1077489596.1265.3.camel () llyr ! dropline ! net
[Download RAW message or body]

Here's another cumulative patch.  It includes all of the changes from my
last two, plus partial support for writing out directory records. 
libisofs can finally create ISO images which are successfully detected
as valid ISO9660 filesystems! (they're completely empty filesystems, but
still... ;)

Todd

On Mon, 2004-02-16 at 01:55, Todd Kulesza wrote:
> Ignore that last patch--this one goes a step further and includes the
> ability to write out the Path Tables.  It also fixes what I think was a
> typo in iso_add_tree_dir() where the parent->children pointer is
> decremented.  I've fixed this to decrement parent->nchildren instead.  I
> also patched up the iso_add_tree_dir() function to update the parent
> pointers for each grandchild node when the child node is reallocated,
> otherwise the parent pointers get all fucked up every time
> iso_add_tree_dir() is called.
> 
> Damn Ben, you weren't kidding when you said this lib is a beast to hack
> on :)
> 
> Todd
> 
> On Sat, 2004-02-14 at 00:06, Todd Kulesza wrote:
> > The attached patch fixes a small bug in the iso_lsb() function, fixes
> > get_path_table_size() to calculate the length of the Path Tables
> > correctly, and adds support for calculating the size of directory
> > records.  Still can't write ISOs, but getting ever-so-slightly closer...
> > 
> > Todd
> > 
> > 

["libisofs.diff" (libisofs.diff)]

? libburn/null.lo
Index: libisofs/tree.c
===================================================================
RCS file: /cvs/burn/burn/libisofs/tree.c,v
retrieving revision 1.6
diff -p -u -r1.6 tree.c
--- libisofs/tree.c	10 Dec 2003 05:45:26 -0000	1.6
+++ libisofs/tree.c	22 Feb 2004 22:35:32 -0000
@@ -76,7 +76,7 @@ iso_tree_add_dir (struct iso_tree_dir **
 {
 	struct iso_tree_dir *parent;
 	struct iso_tree_dir *d;
-	int i;
+	int i, j;
 
 	if (!(dparent && *dparent && name)) return NULL;
 
@@ -90,7 +90,7 @@ iso_tree_add_dir (struct iso_tree_dir **
 
 	if (!iso_tree_dir_set_name (&d, name)) {
 		/* we don't unalloc the end of the array but that's ok :> */
-		parent->children--;
+		parent->nchildren--;
 		d = NULL;
 	} else {
 		d->volume = volume;
@@ -99,8 +99,14 @@ iso_tree_add_dir (struct iso_tree_dir **
 		d->volset = parent->volset;
 	}
 
-	for (i = 0; i < parent->nchildren; ++i)
+	/* since 'parent->children' was set to a new memory location, fix the
+	 * 'parent' references */
+	for (i = 0; i < parent->nchildren; ++i) {
 		*parent->children[i].me = &parent->children[i];
+		for (j = 0; j < parent->children[i].nchildren; ++j) {
+			parent->children[i].children[j].parent = &parent->children[i];
+		}
+	}
 
 	return d->me;
 }
Index: libisofs/tree.h
===================================================================
RCS file: /cvs/burn/burn/libisofs/tree.h,v
retrieving revision 1.6
diff -p -u -r1.6 tree.h
--- libisofs/tree.h	10 Dec 2003 05:45:26 -0000	1.6
+++ libisofs/tree.h	22 Feb 2004 22:35:32 -0000
@@ -58,9 +58,12 @@ struct iso_tree_dir
 	/* the logical block at which the dir will be placed
 	   in the volume */
 	int logical_block;
-	/* XXX not really sure what this is the size of */
+	/* the number of bytes needed to hold the directory record
+	 * for each file and subdirectory of this directory */
 	int size;
-
+	/* the position of the directory in the path table */
+	short position;
+	
 	struct iso_tree_dir *parent;
 
 	struct iso_tree_dir *children;
Index: libisofs/util.c
===================================================================
RCS file: /cvs/burn/burn/libisofs/util.c,v
retrieving revision 1.3
diff -p -u -r1.3 util.c
--- libisofs/util.c	10 Feb 2004 12:45:20 -0000	1.3
+++ libisofs/util.c	22 Feb 2004 22:35:32 -0000
@@ -131,7 +131,7 @@ iso_lsb(unsigned char *buf, int num, int
 	assert (bytes <= sizeof (int));
 
 	for (i = 0; i < bytes; ++i)
-		buf[i] = num << (sizeof (int)-1-i) >> (sizeof (int) - 1) << i;
+		buf[i] = (num >> (8 * i)) & 0xff;
 }
 
 void
Index: libisofs/writer.c
===================================================================
RCS file: /cvs/burn/burn/libisofs/writer.c,v
retrieving revision 1.15
diff -p -u -r1.15 writer.c
--- libisofs/writer.c	10 Feb 2004 12:45:20 -0000	1.15
+++ libisofs/writer.c	22 Feb 2004 22:35:34 -0000
@@ -10,6 +10,13 @@
 
 #define APPLICATION_ID "LIBBURN SUITE (C) 2002 D.FOREMAN/B.JANSENS"
 
+enum dir_type
+{
+	DIR_TYPE_SELF,
+	DIR_TYPE_PARENT,
+	DIR_TYPE_NORMAL
+};
+
 static void iso_next_state (struct iso_write_target *target);
 static int iso_source_get_size (struct burn_source *src);
 static void iso_source_free (struct burn_source *src);
@@ -31,9 +38,13 @@ static int iso_write_path_table (struct 
 				 int lsb);
 static int iso_write_dir_record (struct iso_write_target *target,
 				 unsigned char *buffer,
-				 struct iso_tree_dir **dir);
+				 struct iso_tree_dir **dir,
+				 enum dir_type type);
 static void iso_write_file_id (unsigned char *buf, int size,
 			       struct iso_tree_file **f);
+static int iso_write_dir_records (struct iso_write_target *t,
+				  unsigned char *buffer,
+				  enum burn_source_status *err);
 
 static int
 get_path_table_size (struct iso_tree_dir **ddir)
@@ -43,8 +54,9 @@ get_path_table_size (struct iso_tree_dir
 	
 	assert (dir);
 	
+	/* a path table record is 8 bytes + the length of the directory identifier */
 	if (iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO))
-		size += strlen (dir->isoname1);
+		size += (8 + strlen (dir->isoname1));
 	else
 	{
 		/* if iso_tree_dir_get_name is NULL, this is the root dir
@@ -52,6 +64,10 @@ get_path_table_size (struct iso_tree_dir
 		size += 10;
 	}
 	
+	/* pad the field if the directory identifier is an odd number of bytes */
+	if (size % 2)
+		size++;
+	
 	for (i = 0; i < dir->nchildren; i++)
 		size += get_path_table_size (dir->children[i].me);
 	
@@ -105,23 +121,46 @@ iso_source_free (struct burn_source *src
 	free (target);
 }
 
+static int
+get_directory_record_length (const char *isoname)
+{
+	int size;
+	
+	/* size = the length of the filename + 33 bytes (9.1) */
+	size = 33 + strlen (isoname);
+	/* if size is odd, pad it with a single byte (9.1.12) */
+	if (size % 2)
+		size++;
+	
+	return size;
+}
+
 void
 iso_dir_layout (struct iso_write_target *target,
 		struct iso_tree_dir **ddir)
 {
-	int i;
+	int i, length;
 	struct iso_tree_dir *dir = *ddir;
 
 	dir->logical_block = target->logical_block++;
-	dir->size = 0; /* XXX wtf goes here? */
+	length = 0;
 
 	for (i = 0; i < dir->nfiles; ++i) {
 		dir->files[i].logical_block = 0;
 		dir->files[i].size = 0;
+		length += get_directory_record_length (dir->files[i].isoname1);
 	}
 
-	for (i = 0; i < dir->nchildren; ++i)
+	for (i = 0; i < dir->nchildren; ++i) {
 		iso_dir_layout (target, dir->children[i].me);
+		length += get_directory_record_length (dir->children[i].isoname1);
+	}
+	
+	/* dir->size is the number of bytes needed to hold the directory record
+	 * for each file and subdirectory of 'dir', padded to use up all of the
+	 * bytes of a physical sector. */
+	dir->size = ((length / target->phys_sector_size) + 1)
+		     * target->phys_sector_size;
 }
 
 void
@@ -145,12 +184,12 @@ iso_target_layout (struct iso_write_targ
 	target->volume_space_size = 18;
 	target->logical_block_size = 2048;
 	target->path_table_size = get_path_table_size (target->volset->root);
-	/* leave a 1-block buffer after each path table */
+	/* put a 1-block buffer before each path table */
 	target->l_path_table_pos = 19;
 	target->volume_space_size += 2;
 	target->logical_block += 2;
 	target->total_size += 2 * target->phys_sector_size;
-	/* leave a 1-block buffer after each path table */
+	/* put a 1-block buffer before each path table */
 	target->m_path_table_pos = 21;
 	target->volume_space_size += 2;
 	target->logical_block += 2;
@@ -193,6 +232,9 @@ iso_next_state (struct iso_write_target 
 	case ISO_WRITE_M_PATH_TABLE:
 		target->state_data.path_tables.sectors = 0;
 		break;
+	case ISO_WRITE_DIR_RECORDS:
+		target->state_data.dir_records.sectors = 0;
+		break;
 	case ISO_WRITE_DONE:
 		break;
 	}
@@ -211,11 +253,13 @@ iso_source_generate (struct burn_source 
 		src->read_sub == NULL &&
 		src->get_size == iso_source_get_size);
 
-
 	/* make sure the app didn't fuck up badly. */
 	if (size != target->phys_sector_size)
 		return BURN_SOURCE_FAILED; /* XXX better code */
 
+	/* make sure 'buffer' doesn't have anything in it */
+	memset (buffer, 0, size);
+	
 	switch (target->state)
 	{
 	case ISO_WRITE_BEFORE:
@@ -225,6 +269,9 @@ iso_source_generate (struct burn_source 
 		next = iso_write_system_area (target, buffer, &err);
 		break;
 	case ISO_WRITE_PRI_VOL_DESC:
+		/* set target->logical_block to the logical block containing
+		 * the root directory record */
+		target->logical_block = 23;
 		next = iso_write_pri_volume (target, buffer, &err);
 		break;
 	case ISO_WRITE_VOL_DESC_TERMINATOR:
@@ -236,6 +283,9 @@ iso_source_generate (struct burn_source 
 	case ISO_WRITE_M_PATH_TABLE:
 		next = iso_write_path_table (target, buffer, &err, 0);
 		break;
+	case ISO_WRITE_DIR_RECORDS:
+		next = iso_write_dir_records (target, buffer, &err);
+		break;
 	case ISO_WRITE_DONE:
 		err = BURN_SOURCE_EOF;
 		break;
@@ -302,7 +352,7 @@ iso_write_pri_volume (struct iso_write_t
 	/* location of optional occurance of type m path table (8.4.17) */
 	iso_msb (&buffer[152], 0, 4);
 	/* directory record for root directory (8.4.18) */
-	iso_write_dir_record (t, &buffer[156], t->volset->root);
+	iso_write_dir_record (t, &buffer[156], t->volset->root, DIR_TYPE_SELF);
 	/* volume set identifier (8.4.19) */
 	iso_d_strcpy (&buffer[190], 128, t->volset->volumeset_id);
 	/* publisher identifier (8.4.20) */
@@ -355,16 +405,18 @@ iso_write_file_id (unsigned char *buf, i
 int
 iso_write_dir_record (struct iso_write_target *t,
 		      unsigned char *buffer,
-		      struct iso_tree_dir **ddir)
+		      struct iso_tree_dir **ddir,
+		      enum dir_type type)
 {
 	int len_fi;
 	const char *fi = NULL;
 	int sz;
 	struct iso_tree_dir *dir = *ddir;
 
-	if (!dir->fullname)
+	if (type != DIR_TYPE_NORMAL)
 		len_fi = 1;
 	else {
+		assert (dir->parent);
 		fi = iso_tree_dir_get_name (ddir, ISO_FILE_NAME_ISO);
 		len_fi = strlen (fi);
 	}
@@ -392,10 +444,15 @@ iso_write_dir_record (struct iso_write_t
 	/* length of file identifier (9.1.10) */
 	buffer[32] = len_fi;
 	/* file identifier */
-	if (!dir->fullname)
+	if (type == DIR_TYPE_SELF)
 		buffer[33] = 0;
 	else
-		iso_d_strcpy (&buffer[33], len_fi, fi);
+	{
+		if (type == DIR_TYPE_PARENT)
+			buffer[33] = 1;
+		else
+			iso_d_strcpy (&buffer[33], len_fi, fi);
+	}
 	if (!(len_fi % 2))
 		buffer[33 + len_fi] = 0;
 
@@ -420,21 +477,254 @@ iso_write_volume_terminator (struct iso_
 	return 1;
 }
 
+/* write the path record for 'ddir' into 'buffer' */
+static int
+write_path_table_record (unsigned char *buffer,
+			 struct iso_tree_dir **ddir,
+			 int *position, 
+			 int lsb)
+{
+	int len, bytes_written;
+	short parent_position;
+	const char *name;
+	struct iso_tree_dir *dir = *ddir;
+	
+	/* set the position in the path table of this directory */
+	dir->position = *position;
+	/* increment the position for the next path table record */
+	*position += 1;
+	if (dir->parent)
+		parent_position = dir->parent->position;
+	else
+		parent_position = 1;
+	
+	name = dir->isoname1;
+	if (name)
+		len = strlen (name);
+	else
+		len = 1;
+	
+	/* the directory identifier length (9.4.1) */
+	buffer[0] = len;
+	/* the extended attribute record length (9.4.2) */
+	buffer[1] = 0;
+	/* location of extent (9.4.3) */
+	if (lsb)
+		iso_lsb (&buffer[2], dir->logical_block, 4);
+	else
+		iso_msb (&buffer[2], dir->logical_block, 4);
+	/* parent directory record (9.4.4) */
+	if (lsb)
+		iso_lsb (&buffer[6], parent_position, 2);
+	else
+		iso_msb (&buffer[6], parent_position, 2);
+	/* the directory identifier (9.4.5) */
+	if (name)
+		memcpy (&buffer[8], name, len);
+	else
+		memset (&buffer[8], 0, len);
+	/* padding field (9.4.6) */
+	if (len % 2) {
+		bytes_written = 9 + len;
+		buffer[bytes_written] = 0;
+	}
+	else
+		bytes_written = 8 + len;
+	
+	return bytes_written;
+}
+
+/* recursive function used to write the path records for each level
+ * of the file system sequentially, i.e. root, then all children of
+ * root, then all grandchildren of root, then all great-grandchildren
+ * of root, etc. */
+static int
+write_path_table_records (unsigned char *buffer, 
+			  struct iso_tree_dir **ddir,
+			  int level,
+			  int *position, 
+			  int lsb)
+{
+	int i, offset;
+	struct iso_tree_dir *dir = *ddir;
+	
+	/* ISO9660 only allows directories to be nested 8-deep */
+	if (level >= 8)
+		return 0;
+	
+	if (!level)
+		offset = write_path_table_record (buffer, ddir, position, lsb);
+	else {
+		offset = 0;
+		for (i = 0; i < dir->nchildren; ++i) {
+			offset += write_path_table_records (buffer + offset, 
+							    dir->children[i].me,
+							    level - 1,
+							    position, 
+							    lsb);
+		}
+	}
+	
+	return offset;
+}
+
 /* writes set of path table records */
-int iso_write_path_table (struct iso_write_target *t,
+int
+iso_write_path_table (struct iso_write_target *t,
 			  unsigned char *buffer,
 			  enum burn_source_status *err,
 			  int lsb)
 {
+	int i, offset, position;
 	struct iso_state_path_tables *state = &t->state_data.path_tables;
-
-	/* XXX replace == 0 with < (size of path table) */
-	if (state->sectors == 0) {
-		/* XXX replace these 0xff's with actual path table data */
-		memset (buffer, 0xff, t->phys_sector_size);
-	} else {
+	
+	if (state->sectors) {
+		offset = 0;
+		position = 1;
+		
+		for (i = 0; i < 8; ++i) {
+			offset += write_path_table_records (buffer + offset, 
+							    t->volset->root, 
+							    i, 
+							    &position, 
+							    lsb);
+		}
+	}
+	else {
 		/* empty buffer sector */
 		memset (buffer, 0, t->phys_sector_size);
 	}
+	
 	return (++state->sectors == 2);
 }
+
+static struct iso_tree_dir **
+find_dir_at_block (struct iso_tree_dir **ddir,
+		   int block)
+{
+	int i;
+	struct iso_tree_dir *dir = *ddir;
+	struct iso_tree_dir **to_write = NULL;
+	
+	if (dir->logical_block == block)
+		to_write = ddir;
+	else
+	{
+		for (i = 0; (i < dir->nchildren) && !to_write; ++i)
+		{
+			to_write = find_dir_at_block (dir->children[i].me, 
+						      block);
+		}
+	}
+	
+	if (to_write)
+		return to_write;
+	else
+		return NULL;
+}
+
+/* write the records for children of 'dir' */
+static void
+write_child_records (struct iso_write_target *t,
+	       unsigned char *buffer,
+	       enum burn_source_status *err,
+	       struct iso_tree_dir *dir)
+{
+	int file_counter, dir_counter, order;
+	
+	file_counter = dir_counter = 0;
+	while ((file_counter < dir->nfiles) ||
+	       (dir_counter < dir->nchildren))
+	{
+		/* if there are files and dirs, compare them
+		 * to figure out which comes 1st alphabetically */
+		if ((file_counter < dir->nfiles) &&
+		    (dir_counter < dir->nchildren))
+		{
+			order =  strcmp (
+				dir->children[dir_counter].isoname1,
+				dir->files[file_counter].isoname1);
+		}
+		else
+		{
+			
+			if (file_counter < dir->nfiles)
+				/* only files are left */
+				order = -1;
+			else
+				/* only dirs are left */
+				order = 1;
+		}
+		
+		if (order > 0)
+		{
+			/* XXX: write dir->children[dir_counter].me */
+			dir_counter++;
+		}
+		else
+		{
+			/* XXX: write dir->files[file_counter].me */
+			file_counter++;
+		}
+		
+	}
+
+	return;
+}
+
+/* write out the next directory record */
+int
+iso_write_dir_records (struct iso_write_target *t,
+		       unsigned char *buffer,
+		       enum burn_source_status *err)
+{
+	int finished, offset;
+	struct iso_tree_dir **ddir, *dir;
+	struct iso_state_dir_records *state = &t->state_data.dir_records;
+	
+	finished = 0; /* set to 1 when all directory records have been written */
+	
+	if (state->sectors++)
+	{
+		/* t->logical_block is the next block to write.  find the dir record
+		 * which belongs there and write it out.  if none exists, we're done
+		 * writing the directory records. */
+	
+		ddir = find_dir_at_block (t->volset->root, t->logical_block);
+		if (ddir)
+		{
+			/* 1) write the record for this directory
+			 * 2) write the record for the parent directory
+			 * 3) write the records for all child files and directories
+			 */
+			dir = *ddir;
+			offset = iso_write_dir_record (t, 
+						       buffer, 
+						       ddir, 
+						       DIR_TYPE_SELF);
+			if (!dir->parent)
+			{
+				/* this is the root directory */
+				offset += iso_write_dir_record (t, 
+								buffer + offset, 
+								ddir, 
+								DIR_TYPE_PARENT);
+			}
+			else
+			{
+				offset += iso_write_dir_record (t,
+								buffer + offset,
+								dir->parent->me,
+								DIR_TYPE_PARENT);
+			}
+			
+			write_child_records (t, buffer + offset, err, dir);
+			
+			t->logical_block++;
+		}
+		else
+			finished = 1;
+	}
+	
+	return finished;
+}
Index: libisofs/writer.h
===================================================================
RCS file: /cvs/burn/burn/libisofs/writer.h,v
retrieving revision 1.10
diff -p -u -r1.10 writer.h
--- libisofs/writer.h	10 Feb 2004 12:45:20 -0000	1.10
+++ libisofs/writer.h	22 Feb 2004 22:35:34 -0000
@@ -17,6 +17,7 @@ enum iso_write_state
 	ISO_WRITE_VOL_DESC_TERMINATOR,
 	ISO_WRITE_L_PATH_TABLE,
 	ISO_WRITE_M_PATH_TABLE,
+	ISO_WRITE_DIR_RECORDS,
 
 	ISO_WRITE_DONE
 };
@@ -45,7 +46,8 @@ struct iso_write_target
 	/* size of the total output */
 	int total_size;
 
-	/* the next available logical block */
+	/* when compiling the iso, this is the next available logical block.
+	 * when writing the iso, this is the next block to write. */
 	int logical_block;
 	/* The number of Logical Blocks for the Volume Space */
 	int volume_space_size;
@@ -71,6 +73,11 @@ struct iso_write_target
 			   written */
 			int sectors;
 		} path_tables;
+		struct iso_state_dir_records {
+			/* how many sectors in the directory records area have
+			   been written */
+			int sectors;
+		} dir_records;
 	} state_data;
 };
 


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic