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