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