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