1 /* $NetBSD: cd9660_write.c,v 1.6 2005/10/31 08:29:19 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.6 2005/10/31 08:29:19 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 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 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