cd9660_write.c revision 1.5 1 /* $NetBSD: cd9660_write.c,v 1.5 2005/10/30 09:27:49 dyoung 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.5 2005/10/30 09:27:49 dyoung 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;
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", __func__,
287 (int)inode->st.st_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 free(temp_file_name);
295 return 0;
296 }
297 }
298 } else {
299 /*
300 * Here is a new revelation that ECMA didnt explain
301 * (at least not well).
302 * ALL . and .. records store the name "\0" and "\1"
303 * resepctively. So, for each directory, we have to
304 * make a new node.
305 *
306 * This is where it gets kinda messy, since we have to
307 * be careful of sector boundaries
308 */
309 cur_sector_offset = 0;
310 working_sector = writenode->fileDataSector;
311 fseek(fd, working_sector * diskStructure.sectorSize, SEEK_SET);
312
313 /*
314 * Now loop over children, writing out their directory
315 * records - beware of sector boundaries
316 */
317 TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
318 /*
319 * Copy the temporary record and adjust its size
320 * if necessary
321 */
322 memcpy(&temp_record, temp->isoDirRecord,
323 sizeof(iso_directory_record_cd9660));
324
325 temp_record.length[0] =
326 cd9660_compute_record_size(temp);
327
328 if (temp_record.length[0] + cur_sector_offset >=
329 diskStructure.sectorSize) {
330 cur_sector_offset = 0;
331 working_sector++;
332
333 /* Seek to the next sector. */
334 fseek(fd,
335 working_sector * diskStructure.sectorSize,
336 SEEK_SET);
337 }
338
339 written = fwrite(&temp_record, 1, temp_record.length[0],
340 fd);
341 ca = 0;
342 if (diskStructure.rock_ridge_enabled) {
343 ca = cd9660_write_rr(fd, temp,
344 cur_sector_offset, working_sector);
345 }
346
347 if (ferror(fd)) {
348 warnx("%s: write error", __func__);
349 free(temp_file_name);
350 return 0;
351 }
352 cur_sector_offset += temp_record.length[0];
353
354 /*
355 * If we had to go the the continuation area,
356 * head back to where we should be.
357 */
358 if (ca) {
359 fseek(fd,
360 working_sector * diskStructure.sectorSize +
361 cur_sector_offset,
362 SEEK_SET);
363 }
364 }
365
366 /*
367 * Recurse on children.
368 */
369 TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
370 if ((ret = cd9660_write_file(fd, temp)) == 0) {
371 free(temp_file_name);
372 return 0;
373 }
374 }
375 }
376 free(temp_file_name);
377 return 1;
378 }
379
380 /*
381 * Wrapper function to write a buffer (one sector) to disk.
382 * Seeks and writes the buffer.
383 * NOTE: You dont NEED to use this function, but it might make your
384 * life easier if you have to write things that align to a sector
385 * (such as volume descriptors).
386 *
387 * @param int fd Valid file descriptor
388 * @param int sector Sector number to write to
389 * @param const unsigned char* Buffer to write. This should be the
390 * size of a sector, and if only a portion
391 * is written, the rest should be set to 0.
392 */
393 static int
394 cd9660_write_filedata(FILE *fd, int sector, const unsigned char *buf,
395 int numsecs)
396 {
397 off_t curpos;
398 size_t success;
399
400 curpos = ftello(fd);
401
402 fseek(fd, sector * diskStructure.sectorSize, SEEK_SET);
403
404 success = fwrite(buf, diskStructure.sectorSize * numsecs, 1, fd);
405
406 fseek(fd, curpos, SEEK_SET);
407
408 if (success == 1)
409 success = diskStructure.sectorSize * numsecs;
410 return success;
411 }
412
413 #if 0
414 static int
415 cd9660_write_buffered(FILE *fd, int offset, int buff_len,
416 const unsigned char* buffer)
417 {
418 static int working_sector = -1;
419 static char buf[2048];
420
421 return 0;
422 }
423 #endif
424
425 int
426 cd9660_copy_file(FILE *fd, int start_sector, const char *filename)
427 {
428 FILE *rf;
429 int bytes_read;
430 int sector = start_sector;
431 int buf_size = diskStructure.sectorSize;
432 char *buf;
433
434 buf = malloc(buf_size);
435 if (buf == NULL)
436 err(EXIT_FAILURE, "%s: malloc", __func__);
437
438 if ((rf = fopen(filename, "rb")) == NULL) {
439 warn("%s: cannot open %s", __func__, filename);
440 return 0;
441 }
442
443 if (diskStructure.verbose_level > 1)
444 printf("Writing file: %s\n",filename);
445
446 fseek(fd, start_sector * diskStructure.sectorSize, SEEK_SET);
447
448 while (!feof(rf)) {
449 bytes_read = fread(buf,1,buf_size,rf);
450 if (ferror(rf)) {
451 warn("%s: fread", __func__);
452 return 0;
453 }
454
455 fwrite(buf,1,bytes_read,fd);
456 if (ferror(fd)) {
457 warn("%s: fwrite", __func__);
458 return 0;
459 }
460 sector++;
461 }
462
463 fclose(rf);
464 free(buf);
465 return 1;
466 }
467
468 static int
469 cd9660_write_rr(FILE *fd, cd9660node *writenode, int offset, int sector)
470 {
471 int in_ca = 0;
472 struct ISO_SUSP_ATTRIBUTES *myattr;
473
474 offset += writenode->isoDirRecord->length[0];
475
476 /* Offset now points at the end of the record */
477 TAILQ_FOREACH(myattr, &writenode->head, rr_ll) {
478 fseek(fd,
479 in_ca ? offset : sector*diskStructure.sectorSize + offset,
480 SEEK_SET);
481 fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd);
482
483 offset += CD9660_SUSP_ENTRY_SIZE(myattr);
484 if (!in_ca) {
485 if ((myattr->susp_type == SUSP_TYPE_SUSP) &&
486 (myattr->entry_type == SUSP_ENTRY_SUSP_CE)) {
487 /*
488 * Point the offset to the start of this
489 * record's CE area
490 */
491 offset = (diskStructure.
492 susp_continuation_area_start_sector *
493 diskStructure.sectorSize)
494 + writenode->susp_entry_ce_start;
495 in_ca = 1;
496 }
497 }
498 }
499
500 return in_ca;
501 }
502