Home | History | Annotate | Line # | Download | only in cd9660
cd9660_write.c revision 1.10
      1 /*	$NetBSD: cd9660_write.c,v 1.10 2009/01/10 22:06:29 bjh21 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.10 2009/01/10 22:06:29 bjh21 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 void 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 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 			/* Write out the basic ISO directory record */
    347 			written = fwrite(&temp_record, 1,
    348 			    temp->isoDirRecord->length[0], fd);
    349 			if (diskStructure.rock_ridge_enabled) {
    350 				cd9660_write_rr(fd, temp,
    351 				    cur_sector_offset, working_sector);
    352 			}
    353 			fseek(fd,
    354 			    working_sector * diskStructure.sectorSize +
    355 			    cur_sector_offset + temp_record.length[0] -
    356 			    temp->su_tail_size,
    357 			    SEEK_SET);
    358 			if (temp->su_tail_size > 0)
    359 				fwrite(temp->su_tail_data, 1,
    360 				    temp->su_tail_size, fd);
    361 			if (ferror(fd)) {
    362 				warnx("%s: write error", __func__);
    363 				goto out;
    364 			}
    365 			cur_sector_offset += temp_record.length[0];
    366 
    367 		}
    368 
    369 		/*
    370 		 * Recurse on children.
    371 		 */
    372 		TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
    373 			if ((ret = cd9660_write_file(fd, temp)) == 0)
    374 				goto out;
    375 		}
    376 	}
    377 	rv = 1;
    378 out:
    379 	free(temp_file_name);
    380 	free(buf);
    381 	return rv;
    382 }
    383 
    384 /*
    385  * Wrapper function to write a buffer (one sector) to disk.
    386  * Seeks and writes the buffer.
    387  * NOTE: You dont NEED to use this function, but it might make your
    388  * life easier if you have to write things that align to a sector
    389  * (such as volume descriptors).
    390  *
    391  * @param int fd Valid file descriptor
    392  * @param int sector Sector number to write to
    393  * @param const unsigned char* Buffer to write. This should be the
    394  *                             size of a sector, and if only a portion
    395  *                             is written, the rest should be set to 0.
    396  */
    397 static int
    398 cd9660_write_filedata(FILE *fd, int sector, const unsigned char *buf,
    399 		      int numsecs)
    400 {
    401 	off_t curpos;
    402 	size_t success;
    403 
    404 	curpos = ftello(fd);
    405 
    406 	fseek(fd, sector * diskStructure.sectorSize, SEEK_SET);
    407 
    408 	success = fwrite(buf, diskStructure.sectorSize * numsecs, 1, fd);
    409 
    410 	fseek(fd, curpos, SEEK_SET);
    411 
    412 	if (success == 1)
    413 		success = diskStructure.sectorSize * numsecs;
    414 	return success;
    415 }
    416 
    417 #if 0
    418 static int
    419 cd9660_write_buffered(FILE *fd, int offset, int buff_len,
    420 		      const unsigned char* buffer)
    421 {
    422 	static int working_sector = -1;
    423 	static char buf[2048];
    424 
    425 	return 0;
    426 }
    427 #endif
    428 
    429 int
    430 cd9660_copy_file(FILE *fd, int start_sector, const char *filename)
    431 {
    432 	FILE *rf;
    433 	int bytes_read;
    434 	int sector = start_sector;
    435 	int buf_size = diskStructure.sectorSize;
    436 	char *buf;
    437 
    438 	buf = malloc(buf_size);
    439 	if (buf == NULL)
    440 		err(EXIT_FAILURE, "%s: malloc", __func__);
    441 
    442 	if ((rf = fopen(filename, "rb")) == NULL) {
    443 		warn("%s: cannot open %s", __func__, filename);
    444 		free(buf);
    445 		return 0;
    446 	}
    447 
    448 	if (diskStructure.verbose_level > 1)
    449 		printf("Writing file: %s\n",filename);
    450 
    451 	fseek(fd, start_sector * diskStructure.sectorSize, SEEK_SET);
    452 
    453 	while (!feof(rf)) {
    454 		bytes_read = fread(buf,1,buf_size,rf);
    455 		if (ferror(rf)) {
    456 			warn("%s: fread", __func__);
    457 			free(buf);
    458 			return 0;
    459 		}
    460 
    461 		fwrite(buf,1,bytes_read,fd);
    462 		if (ferror(fd)) {
    463 			warn("%s: fwrite", __func__);
    464 			free(buf);
    465 			return 0;
    466 		}
    467 		sector++;
    468 	}
    469 
    470 	fclose(rf);
    471 	free(buf);
    472 	return 1;
    473 }
    474 
    475 static void
    476 cd9660_write_rr(FILE *fd, cd9660node *writenode, int offset, int sector)
    477 {
    478 	int in_ca = 0;
    479 	struct ISO_SUSP_ATTRIBUTES *myattr;
    480 
    481 	offset += writenode->isoDirRecord->length[0];
    482 	fseek(fd, sector * diskStructure.sectorSize + offset, SEEK_SET);
    483 	/* Offset now points at the end of the record */
    484 	TAILQ_FOREACH(myattr, &writenode->head, rr_ll) {
    485 		fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd);
    486 
    487 		if (!in_ca) {
    488 			offset += CD9660_SUSP_ENTRY_SIZE(myattr);
    489 			if (myattr->last_in_suf) {
    490 				/*
    491 				 * Point the offset to the start of this
    492 				 * record's CE area
    493 				 */
    494 				fseek(fd, (diskStructure.
    495 					susp_continuation_area_start_sector *
    496 					diskStructure.sectorSize)
    497 				    + writenode->susp_entry_ce_start,
    498 				    SEEK_SET);
    499 				in_ca = 1;
    500 			}
    501 		}
    502 	}
    503 
    504 	/*
    505 	 * If we had to go the the continuation area, head back to
    506 	 * where we should be.
    507 	 */
    508 	if (in_ca)
    509 		fseek(fd, sector * diskStructure.sectorSize + offset, SEEK_SET);
    510 }
    511