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