xref: /netbsd-src/usr.sbin/makefs/cd9660/cd9660_write.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: cd9660_write.c,v 1.8 2006/04/22 17:33:55 christos 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.8 2006/04/22 17:33:55 christos 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 	/* Write the volume descriptors */
75 	status = cd9660_write_volume_descriptors(fd);
76 	if (status == 0) {
77 		warnx("%s: Error writing volume descriptors to image",
78 		    __func__);
79 		goto cleanup_bad_image;
80 	}
81 
82 	if (diskStructure.verbose_level > 0)
83 		printf("Volume descriptors written\n");
84 
85 	/*
86 	 * Write the path tables: there are actually four, but right
87 	 * now we are only concearned with two.
88 	 */
89 	status = cd9660_write_path_tables(fd);
90 	if (status == 0) {
91 		warnx("%s: Error writing path tables to image", __func__);
92 		goto cleanup_bad_image;
93 	}
94 
95 	if (diskStructure.verbose_level > 0)
96 		printf("Path tables written\n");
97 
98 	/* Write the directories and files */
99 	status = cd9660_write_file(fd, diskStructure.rootNode);
100 	if (status == 0) {
101 		warnx("%s: Error writing files to image", __func__);
102 		goto cleanup_bad_image;
103 	}
104 
105 	if (diskStructure.is_bootable) {
106 		cd9660_write_boot(fd);
107 	}
108 
109 	/* Write padding bits. This is temporary */
110 	memset(buf, 0, 2048);
111 	cd9660_write_filedata(fd, diskStructure.totalSectors - 1, buf, 1);
112 
113 	if (diskStructure.verbose_level > 0)
114 		printf("Files written\n");
115 	fclose(fd);
116 
117 	if (diskStructure.verbose_level > 0)
118 		printf("Image closed\n");
119 	return 1;
120 
121 cleanup_bad_image:
122 	fclose(fd);
123 	if (!diskStructure.keep_bad_images)
124 		unlink(image);
125 	if (diskStructure.verbose_level > 0)
126 		printf("Bad image cleaned up\n");
127 	return 0;
128 }
129 
130 static int
131 cd9660_write_volume_descriptors(FILE *fd)
132 {
133 	volume_descriptor *vd_temp = diskStructure.firstVolumeDescriptor;
134 	int pos;
135 
136 	while (vd_temp != NULL) {
137 		pos = vd_temp->sector*diskStructure.sectorSize;
138 		cd9660_write_filedata(fd, vd_temp->sector,
139 		    vd_temp->volumeDescriptorData, 1);
140 		vd_temp = vd_temp->next;
141 	}
142 	return 1;
143 }
144 
145 /*
146  * Write out an individual path table
147  * Used just to keep redundant code to a minimum
148  * @param FILE *fd Valid file pointer
149  * @param int Sector to start writing path table to
150  * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN
151  * @returns int 1 on success, 0 on failure
152  */
153 static int
154 cd9660_write_path_table(FILE *fd, int sector, int mode)
155 {
156 	int path_table_sectors = CD9660_BLOCKS(diskStructure.sectorSize,
157 	    diskStructure.pathTableLength);
158 	unsigned char *buffer;
159 	unsigned char *buffer_head;
160 	int len;
161 	path_table_entry temp_entry;
162 	cd9660node *ptcur;
163 
164 	buffer = malloc(diskStructure.sectorSize * path_table_sectors);
165 	if (buffer == NULL) {
166 		warnx("%s: Memory allocation error allocating buffer",
167 		    __func__);
168 		return 0;
169 	}
170 	buffer_head = buffer;
171 	memset(buffer, 0, diskStructure.sectorSize * path_table_sectors);
172 
173 	ptcur = diskStructure.rootNode;
174 
175 	while (ptcur != NULL) {
176 		memset(&temp_entry, 0, sizeof(path_table_entry));
177 		temp_entry.length[0] = ptcur->isoDirRecord->name_len[0];
178 		temp_entry.extended_attribute_length[0] =
179 		    ptcur->isoDirRecord->ext_attr_length[0];
180 		memcpy(temp_entry.name, ptcur->isoDirRecord->name,
181 		    temp_entry.length[0] + 1);
182 
183 		/* round up */
184 		len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01);
185 
186                 /* todo: function pointers instead */
187 		if (mode == LITTLE_ENDIAN) {
188 			cd9660_731(ptcur->fileDataSector,
189 			    temp_entry.first_sector);
190 			cd9660_721((ptcur->parent == NULL ?
191 				1 : ptcur->parent->ptnumber),
192 			    temp_entry.parent_number);
193 		} else {
194 			cd9660_732(ptcur->fileDataSector,
195 			    temp_entry.first_sector);
196 			cd9660_722((ptcur->parent == NULL ?
197 				1 : ptcur->parent->ptnumber),
198 			    temp_entry.parent_number);
199 		}
200 
201 
202 		memcpy(buffer, &temp_entry, len);
203 		buffer += len;
204 
205 		ptcur = ptcur->ptnext;
206 	}
207 
208 	return cd9660_write_filedata(fd, sector, buffer_head,
209 	    path_table_sectors);
210 }
211 
212 
213 /*
214  * Write out the path tables to disk
215  * Each file descriptor should be pointed to by the PVD, so we know which
216  * sector to copy them to. One thing to watch out for: the only path tables
217  * stored are in the endian mode that the application is compiled for. So,
218  * the first thing to do is write out that path table, then to write the one
219  * in the other endian mode requires to convert the endianness of each entry
220  * in the table. The best way to do this would be to create a temporary
221  * path_table_entry structure, then for each path table entry, copy it to
222  * the temporary entry, translate, then copy that to disk.
223  *
224  * @param FILE* Valid file descriptor
225  * @returns int 0 on failure, 1 on success
226  */
227 static int
228 cd9660_write_path_tables(FILE *fd)
229 {
230 	if (cd9660_write_path_table(fd,
231 	    diskStructure.primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0)
232 		return 0;
233 
234 	if (cd9660_write_path_table(fd,
235 	    diskStructure.primaryBigEndianTableSector, BIG_ENDIAN) == 0)
236 		return 0;
237 
238 	/* @TODO: handle remaining two path tables */
239 	return 1;
240 }
241 
242 /*
243  * Write a file to disk
244  * Writes a file, its directory record, and its data to disk
245  * This file is designed to be called RECURSIVELY, so initially call it
246  * with the root node. All of the records should store what sector the
247  * file goes in, so no computation should be  necessary.
248  *
249  * @param int fd Valid file descriptor
250  * @param struct cd9660node* writenode Pointer to the file to be written
251  * @returns int 0 on failure, 1 on success
252  */
253 static int
254 cd9660_write_file(FILE *fd, cd9660node *writenode)
255 {
256 	char *buf;
257 	char *temp_file_name;
258 	int ret;
259 	int working_sector;
260 	int cur_sector_offset;
261 	int written;
262 	iso_directory_record_cd9660 temp_record;
263 	cd9660node *temp;
264 	int ca = 0, rv = 0;
265 
266 	/* Todo : clean up variables */
267 
268 	temp_file_name = malloc(CD9660MAXPATH + 1);
269 	if (temp_file_name == NULL)
270 		err(EXIT_FAILURE, "%s: malloc", __func__);
271 
272 	memset(temp_file_name, 0, CD9660MAXPATH + 1);
273 
274 	buf = malloc(diskStructure.sectorSize);
275 	if (buf == NULL)
276 		err(EXIT_FAILURE, "%s: malloc", __func__);
277 
278 	if ((writenode->level != 0) &&
279 	    !(writenode->node->type & S_IFDIR)) {
280 		fsinode *inode = writenode->node->inode;
281 		/* Only attempt to write unwritten files that have length. */
282 		if ((inode->flags & FI_WRITTEN) != 0) {
283 			INODE_WARNX(("%s: skipping written inode %d", __func__,
284 			    (int)inode->st.st_ino));
285 		} else if (writenode->fileDataLength > 0) {
286 			INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32,
287 			    __func__, (int)inode->st.st_ino, inode->ino));
288 			inode->flags |= FI_WRITTEN;
289 			cd9660_compute_full_filename(writenode,
290 			    temp_file_name, 0);
291 			ret = cd9660_copy_file(fd, writenode->fileDataSector,
292 			    temp_file_name);
293 			if (ret == 0)
294 				goto out;
295 		}
296 	} else {
297 		/*
298 		 * Here is a new revelation that ECMA didnt explain
299 		 * (at least not well).
300 		 * ALL . and .. records store the name "\0" and "\1"
301 		 * resepctively. So, for each directory, we have to
302 		 * make a new node.
303 		 *
304 		 * This is where it gets kinda messy, since we have to
305 		 * be careful of sector boundaries
306 		 */
307 		cur_sector_offset = 0;
308 		working_sector = writenode->fileDataSector;
309 		fseek(fd, working_sector * diskStructure.sectorSize, SEEK_SET);
310 
311 		/*
312 		 * Now loop over children, writing out their directory
313 		 * records - beware of sector boundaries
314 	 	 */
315 		TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
316 			/*
317 			 * Copy the temporary record and adjust its size
318 			 * if necessary
319 			 */
320 			memcpy(&temp_record, temp->isoDirRecord,
321 			    sizeof(iso_directory_record_cd9660));
322 
323 			temp_record.length[0] =
324 			    cd9660_compute_record_size(temp);
325 
326 			if (temp_record.length[0] + cur_sector_offset >=
327 			    diskStructure.sectorSize) {
328 				cur_sector_offset = 0;
329 				working_sector++;
330 
331 				/* Seek to the next sector. */
332 				fseek(fd,
333 				    working_sector * diskStructure.sectorSize,
334 				    SEEK_SET);
335 			}
336 
337 			written = fwrite(&temp_record, 1, temp_record.length[0],
338 			    fd);
339 			ca = 0;
340 			if (diskStructure.rock_ridge_enabled) {
341 				ca = cd9660_write_rr(fd, temp,
342 				    cur_sector_offset, working_sector);
343 			}
344 
345 			if (ferror(fd)) {
346 				warnx("%s: write error", __func__);
347 				goto out;
348 			}
349 			cur_sector_offset += temp_record.length[0];
350 
351 			/*
352 			 * If we had to go the the continuation area,
353 			 * head back to where we should be.
354 			 */
355 			if (ca) {
356 				fseek(fd,
357 				    working_sector * diskStructure.sectorSize +
358 					cur_sector_offset,
359 				    SEEK_SET);
360 			}
361 		}
362 
363 		/*
364 		 * Recurse on children.
365 		 */
366 		TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
367 			if ((ret = cd9660_write_file(fd, temp)) == 0)
368 				goto out;
369 		}
370 	}
371 	rv = 1;
372 out:
373 	free(temp_file_name);
374 	free(buf);
375 	return rv;
376 }
377 
378 /*
379  * Wrapper function to write a buffer (one sector) to disk.
380  * Seeks and writes the buffer.
381  * NOTE: You dont NEED to use this function, but it might make your
382  * life easier if you have to write things that align to a sector
383  * (such as volume descriptors).
384  *
385  * @param int fd Valid file descriptor
386  * @param int sector Sector number to write to
387  * @param const unsigned char* Buffer to write. This should be the
388  *                             size of a sector, and if only a portion
389  *                             is written, the rest should be set to 0.
390  */
391 static int
392 cd9660_write_filedata(FILE *fd, int sector, const unsigned char *buf,
393 		      int numsecs)
394 {
395 	off_t curpos;
396 	size_t success;
397 
398 	curpos = ftello(fd);
399 
400 	fseek(fd, sector * diskStructure.sectorSize, SEEK_SET);
401 
402 	success = fwrite(buf, diskStructure.sectorSize * numsecs, 1, fd);
403 
404 	fseek(fd, curpos, SEEK_SET);
405 
406 	if (success == 1)
407 		success = diskStructure.sectorSize * numsecs;
408 	return success;
409 }
410 
411 #if 0
412 static int
413 cd9660_write_buffered(FILE *fd, int offset, int buff_len,
414 		      const unsigned char* buffer)
415 {
416 	static int working_sector = -1;
417 	static char buf[2048];
418 
419 	return 0;
420 }
421 #endif
422 
423 int
424 cd9660_copy_file(FILE *fd, int start_sector, const char *filename)
425 {
426 	FILE *rf;
427 	int bytes_read;
428 	int sector = start_sector;
429 	int buf_size = diskStructure.sectorSize;
430 	char *buf;
431 
432 	buf = malloc(buf_size);
433 	if (buf == NULL)
434 		err(EXIT_FAILURE, "%s: malloc", __func__);
435 
436 	if ((rf = fopen(filename, "rb")) == NULL) {
437 		warn("%s: cannot open %s", __func__, filename);
438 		free(buf);
439 		return 0;
440 	}
441 
442 	if (diskStructure.verbose_level > 1)
443 		printf("Writing file: %s\n",filename);
444 
445 	fseek(fd, start_sector * diskStructure.sectorSize, SEEK_SET);
446 
447 	while (!feof(rf)) {
448 		bytes_read = fread(buf,1,buf_size,rf);
449 		if (ferror(rf)) {
450 			warn("%s: fread", __func__);
451 			free(buf);
452 			return 0;
453 		}
454 
455 		fwrite(buf,1,bytes_read,fd);
456 		if (ferror(fd)) {
457 			warn("%s: fwrite", __func__);
458 			free(buf);
459 			return 0;
460 		}
461 		sector++;
462 	}
463 
464 	fclose(rf);
465 	free(buf);
466 	return 1;
467 }
468 
469 static int
470 cd9660_write_rr(FILE *fd, cd9660node *writenode, int offset, int sector)
471 {
472 	int in_ca = 0;
473 	struct ISO_SUSP_ATTRIBUTES *myattr;
474 
475 	offset += writenode->isoDirRecord->length[0];
476 
477 	/* Offset now points at the end of the record */
478 	TAILQ_FOREACH(myattr, &writenode->head, rr_ll) {
479 		fseek(fd,
480 		    in_ca ? offset : sector*diskStructure.sectorSize + offset,
481 		    SEEK_SET);
482 		fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd);
483 
484 		offset += CD9660_SUSP_ENTRY_SIZE(myattr);
485 		if (!in_ca) {
486 			if ((myattr->susp_type == SUSP_TYPE_SUSP) &&
487 			    (myattr->entry_type == SUSP_ENTRY_SUSP_CE)) {
488 				/*
489 				 * Point the offset to the start of this
490 				 * record's CE area
491 				 */
492 				offset = (diskStructure.
493 					  susp_continuation_area_start_sector *
494 					    diskStructure.sectorSize)
495 					+ writenode->susp_entry_ce_start;
496 				in_ca = 1;
497 			}
498 		}
499 	}
500 
501 	return in_ca;
502 }
503