Home | History | Annotate | Line # | Download | only in cd9660
cd9660_write.c revision 1.9
      1 /*	$NetBSD: cd9660_write.c,v 1.9 2008/05/10 19:00:07 skrll Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
      5  * Perez-Rathke and Ram Vedam.  All rights reserved.
      6  *
      7  * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
      8  * Alan Perez-Rathke and Ram Vedam.
      9  *
     10  * Redistribution and use in source and binary forms, with or
     11  * without modification, are permitted provided that the following
     12  * conditions are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above
     16  *    copyright notice, this list of conditions and the following
     17  *    disclaimer in the documentation and/or other materials provided
     18  *    with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
     21  * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     24  * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
     25  * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
     28  * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     29  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
     32  * OF SUCH DAMAGE.
     33  */
     34 
     35 #include "cd9660.h"
     36 #include "iso9660_rrip.h"
     37 
     38 #include <sys/cdefs.h>
     39 #if defined(__RCSID) && !defined(__lint)
     40 __RCSID("$NetBSD: cd9660_write.c,v 1.9 2008/05/10 19:00:07 skrll Exp $");
     41 #endif  /* !__lint */
     42 
     43 static int cd9660_write_volume_descriptors(FILE *);
     44 static int cd9660_write_path_table(FILE *, int, int);
     45 static int cd9660_write_path_tables(FILE *);
     46 static int cd9660_write_file(FILE *, cd9660node *);
     47 static int cd9660_write_filedata(FILE *, int, const unsigned char *, int);
     48 #if 0
     49 static int cd9660_write_buffered(FILE *, int, int, const unsigned char*);
     50 #endif
     51 static int cd9660_write_rr(FILE *, cd9660node *, int, int);
     52 
     53 /*
     54  * Write the image
     55  * Writes the entire image
     56  * @param const char* The filename for the image
     57  * @returns int 1 on success, 0 on failure
     58  */
     59 int
     60 cd9660_write_image(const char* image)
     61 {
     62 	FILE *fd;
     63 	int status;
     64 	char buf[2048];
     65 
     66 	if ((fd = fopen(image, "w+")) == NULL) {
     67 		err(EXIT_FAILURE, "%s: Can't open `%s' for writing", __func__,
     68 		    image);
     69 	}
     70 
     71 	if (diskStructure.verbose_level > 0)
     72 		printf("Writing image\n");
     73 
     74 	if (diskStructure.has_generic_bootimage) {
     75 		status = cd9660_copy_file(fd, 0,
     76 		    diskStructure.generic_bootimage);
     77 		if (status == 0) {
     78 			warnx("%s: Error writing generic boot image",
     79 			    __func__);
     80 			goto cleanup_bad_image;
     81 		}
     82 	}
     83 
     84 	/* Write the volume descriptors */
     85 	status = cd9660_write_volume_descriptors(fd);
     86 	if (status == 0) {
     87 		warnx("%s: Error writing volume descriptors to image",
     88 		    __func__);
     89 		goto cleanup_bad_image;
     90 	}
     91 
     92 	if (diskStructure.verbose_level > 0)
     93 		printf("Volume descriptors written\n");
     94 
     95 	/*
     96 	 * Write the path tables: there are actually four, but right
     97 	 * now we are only concearned with two.
     98 	 */
     99 	status = cd9660_write_path_tables(fd);
    100 	if (status == 0) {
    101 		warnx("%s: Error writing path tables to image", __func__);
    102 		goto cleanup_bad_image;
    103 	}
    104 
    105 	if (diskStructure.verbose_level > 0)
    106 		printf("Path tables written\n");
    107 
    108 	/* Write the directories and files */
    109 	status = cd9660_write_file(fd, diskStructure.rootNode);
    110 	if (status == 0) {
    111 		warnx("%s: Error writing files to image", __func__);
    112 		goto cleanup_bad_image;
    113 	}
    114 
    115 	if (diskStructure.is_bootable) {
    116 		cd9660_write_boot(fd);
    117 	}
    118 
    119 	/* Write padding bits. This is temporary */
    120 	memset(buf, 0, 2048);
    121 	cd9660_write_filedata(fd, diskStructure.totalSectors - 1, buf, 1);
    122 
    123 	if (diskStructure.verbose_level > 0)
    124 		printf("Files written\n");
    125 	fclose(fd);
    126 
    127 	if (diskStructure.verbose_level > 0)
    128 		printf("Image closed\n");
    129 	return 1;
    130 
    131 cleanup_bad_image:
    132 	fclose(fd);
    133 	if (!diskStructure.keep_bad_images)
    134 		unlink(image);
    135 	if (diskStructure.verbose_level > 0)
    136 		printf("Bad image cleaned up\n");
    137 	return 0;
    138 }
    139 
    140 static int
    141 cd9660_write_volume_descriptors(FILE *fd)
    142 {
    143 	volume_descriptor *vd_temp = diskStructure.firstVolumeDescriptor;
    144 	int pos;
    145 
    146 	while (vd_temp != NULL) {
    147 		pos = vd_temp->sector*diskStructure.sectorSize;
    148 		cd9660_write_filedata(fd, vd_temp->sector,
    149 		    vd_temp->volumeDescriptorData, 1);
    150 		vd_temp = vd_temp->next;
    151 	}
    152 	return 1;
    153 }
    154 
    155 /*
    156  * Write out an individual path table
    157  * Used just to keep redundant code to a minimum
    158  * @param FILE *fd Valid file pointer
    159  * @param int Sector to start writing path table to
    160  * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN
    161  * @returns int 1 on success, 0 on failure
    162  */
    163 static int
    164 cd9660_write_path_table(FILE *fd, int sector, int mode)
    165 {
    166 	int path_table_sectors = CD9660_BLOCKS(diskStructure.sectorSize,
    167 	    diskStructure.pathTableLength);
    168 	unsigned char *buffer;
    169 	unsigned char *buffer_head;
    170 	int len;
    171 	path_table_entry temp_entry;
    172 	cd9660node *ptcur;
    173 
    174 	buffer = malloc(diskStructure.sectorSize * path_table_sectors);
    175 	if (buffer == NULL) {
    176 		warnx("%s: Memory allocation error allocating buffer",
    177 		    __func__);
    178 		return 0;
    179 	}
    180 	buffer_head = buffer;
    181 	memset(buffer, 0, diskStructure.sectorSize * path_table_sectors);
    182 
    183 	ptcur = diskStructure.rootNode;
    184 
    185 	while (ptcur != NULL) {
    186 		memset(&temp_entry, 0, sizeof(path_table_entry));
    187 		temp_entry.length[0] = ptcur->isoDirRecord->name_len[0];
    188 		temp_entry.extended_attribute_length[0] =
    189 		    ptcur->isoDirRecord->ext_attr_length[0];
    190 		memcpy(temp_entry.name, ptcur->isoDirRecord->name,
    191 		    temp_entry.length[0] + 1);
    192 
    193 		/* round up */
    194 		len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01);
    195 
    196                 /* todo: function pointers instead */
    197 		if (mode == LITTLE_ENDIAN) {
    198 			cd9660_731(ptcur->fileDataSector,
    199 			    temp_entry.first_sector);
    200 			cd9660_721((ptcur->parent == NULL ?
    201 				1 : ptcur->parent->ptnumber),
    202 			    temp_entry.parent_number);
    203 		} else {
    204 			cd9660_732(ptcur->fileDataSector,
    205 			    temp_entry.first_sector);
    206 			cd9660_722((ptcur->parent == NULL ?
    207 				1 : ptcur->parent->ptnumber),
    208 			    temp_entry.parent_number);
    209 		}
    210 
    211 
    212 		memcpy(buffer, &temp_entry, len);
    213 		buffer += len;
    214 
    215 		ptcur = ptcur->ptnext;
    216 	}
    217 
    218 	return cd9660_write_filedata(fd, sector, buffer_head,
    219 	    path_table_sectors);
    220 }
    221 
    222 
    223 /*
    224  * Write out the path tables to disk
    225  * Each file descriptor should be pointed to by the PVD, so we know which
    226  * sector to copy them to. One thing to watch out for: the only path tables
    227  * stored are in the endian mode that the application is compiled for. So,
    228  * the first thing to do is write out that path table, then to write the one
    229  * in the other endian mode requires to convert the endianness of each entry
    230  * in the table. The best way to do this would be to create a temporary
    231  * path_table_entry structure, then for each path table entry, copy it to
    232  * the temporary entry, translate, then copy that to disk.
    233  *
    234  * @param FILE* Valid file descriptor
    235  * @returns int 0 on failure, 1 on success
    236  */
    237 static int
    238 cd9660_write_path_tables(FILE *fd)
    239 {
    240 	if (cd9660_write_path_table(fd,
    241 	    diskStructure.primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0)
    242 		return 0;
    243 
    244 	if (cd9660_write_path_table(fd,
    245 	    diskStructure.primaryBigEndianTableSector, BIG_ENDIAN) == 0)
    246 		return 0;
    247 
    248 	/* @TODO: handle remaining two path tables */
    249 	return 1;
    250 }
    251 
    252 /*
    253  * Write a file to disk
    254  * Writes a file, its directory record, and its data to disk
    255  * This file is designed to be called RECURSIVELY, so initially call it
    256  * with the root node. All of the records should store what sector the
    257  * file goes in, so no computation should be  necessary.
    258  *
    259  * @param int fd Valid file descriptor
    260  * @param struct cd9660node* writenode Pointer to the file to be written
    261  * @returns int 0 on failure, 1 on success
    262  */
    263 static int
    264 cd9660_write_file(FILE *fd, cd9660node *writenode)
    265 {
    266 	char *buf;
    267 	char *temp_file_name;
    268 	int ret;
    269 	int working_sector;
    270 	int cur_sector_offset;
    271 	int written;
    272 	iso_directory_record_cd9660 temp_record;
    273 	cd9660node *temp;
    274 	int ca = 0, rv = 0;
    275 
    276 	/* Todo : clean up variables */
    277 
    278 	temp_file_name = malloc(CD9660MAXPATH + 1);
    279 	if (temp_file_name == NULL)
    280 		err(EXIT_FAILURE, "%s: malloc", __func__);
    281 
    282 	memset(temp_file_name, 0, CD9660MAXPATH + 1);
    283 
    284 	buf = malloc(diskStructure.sectorSize);
    285 	if (buf == NULL)
    286 		err(EXIT_FAILURE, "%s: malloc", __func__);
    287 
    288 	if ((writenode->level != 0) &&
    289 	    !(writenode->node->type & S_IFDIR)) {
    290 		fsinode *inode = writenode->node->inode;
    291 		/* Only attempt to write unwritten files that have length. */
    292 		if ((inode->flags & FI_WRITTEN) != 0) {
    293 			INODE_WARNX(("%s: skipping written inode %d", __func__,
    294 			    (int)inode->st.st_ino));
    295 		} else if (writenode->fileDataLength > 0) {
    296 			INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32,
    297 			    __func__, (int)inode->st.st_ino, inode->ino));
    298 			inode->flags |= FI_WRITTEN;
    299 			cd9660_compute_full_filename(writenode,
    300 			    temp_file_name, 0);
    301 			ret = cd9660_copy_file(fd, writenode->fileDataSector,
    302 			    temp_file_name);
    303 			if (ret == 0)
    304 				goto out;
    305 		}
    306 	} else {
    307 		/*
    308 		 * Here is a new revelation that ECMA didnt explain
    309 		 * (at least not well).
    310 		 * ALL . and .. records store the name "\0" and "\1"
    311 		 * resepctively. So, for each directory, we have to
    312 		 * make a new node.
    313 		 *
    314 		 * This is where it gets kinda messy, since we have to
    315 		 * be careful of sector boundaries
    316 		 */
    317 		cur_sector_offset = 0;
    318 		working_sector = writenode->fileDataSector;
    319 		fseek(fd, working_sector * diskStructure.sectorSize, SEEK_SET);
    320 
    321 		/*
    322 		 * Now loop over children, writing out their directory
    323 		 * records - beware of sector boundaries
    324 	 	 */
    325 		TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
    326 			/*
    327 			 * Copy the temporary record and adjust its size
    328 			 * if necessary
    329 			 */
    330 			memcpy(&temp_record, temp->isoDirRecord,
    331 			    sizeof(iso_directory_record_cd9660));
    332 
    333 			temp_record.length[0] =
    334 			    cd9660_compute_record_size(temp);
    335 
    336 			if (temp_record.length[0] + cur_sector_offset >=
    337 			    diskStructure.sectorSize) {
    338 				cur_sector_offset = 0;
    339 				working_sector++;
    340 
    341 				/* Seek to the next sector. */
    342 				fseek(fd,
    343 				    working_sector * diskStructure.sectorSize,
    344 				    SEEK_SET);
    345 			}
    346 
    347 			written = fwrite(&temp_record, 1, temp_record.length[0],
    348 			    fd);
    349 			ca = 0;
    350 			if (diskStructure.rock_ridge_enabled) {
    351 				ca = cd9660_write_rr(fd, temp,
    352 				    cur_sector_offset, working_sector);
    353 			}
    354 
    355 			if (ferror(fd)) {
    356 				warnx("%s: write error", __func__);
    357 				goto out;
    358 			}
    359 			cur_sector_offset += temp_record.length[0];
    360 
    361 			/*
    362 			 * If we had to go the the continuation area,
    363 			 * head back to where we should be.
    364 			 */
    365 			if (ca) {
    366 				fseek(fd,
    367 				    working_sector * diskStructure.sectorSize +
    368 					cur_sector_offset,
    369 				    SEEK_SET);
    370 			}
    371 		}
    372 
    373 		/*
    374 		 * Recurse on children.
    375 		 */
    376 		TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
    377 			if ((ret = cd9660_write_file(fd, temp)) == 0)
    378 				goto out;
    379 		}
    380 	}
    381 	rv = 1;
    382 out:
    383 	free(temp_file_name);
    384 	free(buf);
    385 	return rv;
    386 }
    387 
    388 /*
    389  * Wrapper function to write a buffer (one sector) to disk.
    390  * Seeks and writes the buffer.
    391  * NOTE: You dont NEED to use this function, but it might make your
    392  * life easier if you have to write things that align to a sector
    393  * (such as volume descriptors).
    394  *
    395  * @param int fd Valid file descriptor
    396  * @param int sector Sector number to write to
    397  * @param const unsigned char* Buffer to write. This should be the
    398  *                             size of a sector, and if only a portion
    399  *                             is written, the rest should be set to 0.
    400  */
    401 static int
    402 cd9660_write_filedata(FILE *fd, int sector, const unsigned char *buf,
    403 		      int numsecs)
    404 {
    405 	off_t curpos;
    406 	size_t success;
    407 
    408 	curpos = ftello(fd);
    409 
    410 	fseek(fd, sector * diskStructure.sectorSize, SEEK_SET);
    411 
    412 	success = fwrite(buf, diskStructure.sectorSize * numsecs, 1, fd);
    413 
    414 	fseek(fd, curpos, SEEK_SET);
    415 
    416 	if (success == 1)
    417 		success = diskStructure.sectorSize * numsecs;
    418 	return success;
    419 }
    420 
    421 #if 0
    422 static int
    423 cd9660_write_buffered(FILE *fd, int offset, int buff_len,
    424 		      const unsigned char* buffer)
    425 {
    426 	static int working_sector = -1;
    427 	static char buf[2048];
    428 
    429 	return 0;
    430 }
    431 #endif
    432 
    433 int
    434 cd9660_copy_file(FILE *fd, int start_sector, const char *filename)
    435 {
    436 	FILE *rf;
    437 	int bytes_read;
    438 	int sector = start_sector;
    439 	int buf_size = diskStructure.sectorSize;
    440 	char *buf;
    441 
    442 	buf = malloc(buf_size);
    443 	if (buf == NULL)
    444 		err(EXIT_FAILURE, "%s: malloc", __func__);
    445 
    446 	if ((rf = fopen(filename, "rb")) == NULL) {
    447 		warn("%s: cannot open %s", __func__, filename);
    448 		free(buf);
    449 		return 0;
    450 	}
    451 
    452 	if (diskStructure.verbose_level > 1)
    453 		printf("Writing file: %s\n",filename);
    454 
    455 	fseek(fd, start_sector * diskStructure.sectorSize, SEEK_SET);
    456 
    457 	while (!feof(rf)) {
    458 		bytes_read = fread(buf,1,buf_size,rf);
    459 		if (ferror(rf)) {
    460 			warn("%s: fread", __func__);
    461 			free(buf);
    462 			return 0;
    463 		}
    464 
    465 		fwrite(buf,1,bytes_read,fd);
    466 		if (ferror(fd)) {
    467 			warn("%s: fwrite", __func__);
    468 			free(buf);
    469 			return 0;
    470 		}
    471 		sector++;
    472 	}
    473 
    474 	fclose(rf);
    475 	free(buf);
    476 	return 1;
    477 }
    478 
    479 static int
    480 cd9660_write_rr(FILE *fd, cd9660node *writenode, int offset, int sector)
    481 {
    482 	int in_ca = 0;
    483 	struct ISO_SUSP_ATTRIBUTES *myattr;
    484 
    485 	offset += writenode->isoDirRecord->length[0];
    486 
    487 	/* Offset now points at the end of the record */
    488 	TAILQ_FOREACH(myattr, &writenode->head, rr_ll) {
    489 		fseek(fd,
    490 		    in_ca ? offset : sector*diskStructure.sectorSize + offset,
    491 		    SEEK_SET);
    492 		fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd);
    493 
    494 		offset += CD9660_SUSP_ENTRY_SIZE(myattr);
    495 		if (!in_ca) {
    496 			if ((myattr->susp_type == SUSP_TYPE_SUSP) &&
    497 			    (myattr->entry_type == SUSP_ENTRY_SUSP_CE)) {
    498 				/*
    499 				 * Point the offset to the start of this
    500 				 * record's CE area
    501 				 */
    502 				offset = (diskStructure.
    503 					  susp_continuation_area_start_sector *
    504 					    diskStructure.sectorSize)
    505 					+ writenode->susp_entry_ce_start;
    506 				in_ca = 1;
    507 			}
    508 		}
    509 	}
    510 
    511 	return in_ca;
    512 }
    513