1 /*
2 Copyright (C) 2003-2009, 2013-2014, 2016-2017 Rocky Bernstein
3 <rocky@gnu.org>
4 Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org>
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*! String inside frame which identifies an ISO 9660 filesystem. This
21 string is the "id" field of an iso9660_pvd_t or an iso9660_svd_t.
22 Note should come *before* #include <cdio/iso9660.h> which does
23 a #define of this name.
24 */
25 const char ISO_STANDARD_ID[] = {'C', 'D', '0', '0', '1'};
26
27 /* Private headers */
28 #include "iso9660_private.h"
29 #include "cdio_assert.h"
30
31 /* Public headers */
32 #include <cdio/bytesex.h>
33 #include <cdio/iso9660.h>
34 #include <cdio/util.h>
35
36 #include <time.h>
37 #include <ctype.h>
38 #ifdef HAVE_STRING_H
39 # include <string.h>
40 #endif
41 #ifdef HAVE_STDIO_H
42 # include <stdio.h>
43 #endif
44 #ifdef HAVE_LIMITS_H
45 # include <limits.h>
46 #endif
47 #ifdef HAVE_STDLIB_H
48 # include <stdlib.h>
49 #endif
50 #ifdef HAVE_SYS_STAT_H
51 # include <sys/stat.h>
52 #endif
53
54 #ifdef HAVE_ERRNO_H
55 #include <errno.h>
56 #endif
57
58 #ifndef HAVE_SETENV
59 static int
setenv(const char * envname,const char * envval,int overwrite)60 setenv(const char *envname, const char *envval, int overwrite)
61 {
62 return -1;
63 }
64 #endif
65
66 #ifndef HAVE_UNSETENV
67 static int
unsetenv(const char * envname)68 unsetenv(const char *envname)
69 {
70 return -1;
71 }
72 #endif
73
74 #ifndef HAVE_TIMEGM
75 static time_t
timegm(struct tm * tm)76 timegm(struct tm *tm)
77 {
78 time_t ret;
79 char *tz;
80
81 tz = getenv("TZ");
82 setenv("TZ", "UTC", 1);
83 tzset();
84 ret = mktime(tm);
85 if (tz)
86 setenv("TZ", tz, 1);
87 else
88 unsetenv("TZ");
89 tzset();
90 return ret;
91 }
92 #endif
93
94 #ifndef HAVE_GMTIME_R
95 static struct tm *
gmtime_r(const time_t * timer,struct tm * result)96 gmtime_r(const time_t *timer, struct tm *result)
97 {
98 struct tm *tmp = gmtime(timer);
99
100 if (tmp) {
101 *result = *tmp;
102 return result;
103 }
104 return tmp;
105 }
106 #endif
107
108 #ifndef HAVE_LOCALTIME_R
109 static struct tm *
localtime_r(const time_t * timer,struct tm * result)110 localtime_r(const time_t *timer, struct tm *result)
111 {
112 struct tm *tmp = localtime(timer);
113
114 if (tmp) {
115 *result = *tmp;
116 return result;
117 }
118 return tmp;
119 }
120 #endif
121
122 /* Variables to hold debugger-helping enumerations */
123 enum iso_enum1_s iso_enums1;
124 enum iso_flag_enum_s iso_flag_enums;
125 enum iso_vd_enum_s iso_vd_enums;
126 enum iso_extension_enum_s iso_extension_enums;
127
128 /* some parameters... */
129 #define SYSTEM_ID "CD-RTOS CD-BRIDGE"
130 #define VOLUME_SET_ID ""
131
132 /*!
133 Change trailing blanks in str to nulls. Str has a maximum size of
134 n characters.
135 */
136 static char *
strip_trail(const char str[],size_t n)137 strip_trail (const char str[], size_t n)
138 {
139 static char buf[1025];
140 int j;
141
142 cdio_assert (n < 1024);
143
144 strncpy (buf, str, n);
145 buf[n] = '\0';
146
147 for (j = strlen (buf) - 1; j >= 0; j--)
148 {
149 if (buf[j] != ' ')
150 break;
151
152 buf[j] = '\0';
153 }
154
155 return buf;
156 }
157
158 static void
159 pathtable_get_size_and_entries(const void *pt, unsigned int *size,
160 unsigned int *entries);
161
162 /*!
163 Get time structure from structure in an ISO 9660 directory index
164 record. Even though tm_wday and tm_yday fields are not explicitly in
165 idr_date, the are calculated from the other fields.
166
167 If tm is to reflect the localtime set b_localtime true, otherwise
168 tm will reported in GMT.
169 */
170 bool
iso9660_get_dtime(const iso9660_dtime_t * idr_date,bool b_localtime,struct tm * p_tm)171 iso9660_get_dtime (const iso9660_dtime_t *idr_date, bool b_localtime,
172 /*out*/ struct tm *p_tm)
173 {
174 if (!idr_date) return false;
175
176 /*
177 Section 9.1.5 of ECMA 119 says:
178 If all seven numbers are zero, it shall mean that the date and
179 time are not specified.
180
181 HACK: However we've seen it happen that everything except gmtoff
182 is zero and the expected date is the beginning of the epoch. So
183 we accept 6 numbers being zero. I'm also not sure if using the
184 beginning of the Epoch is also the right thing to do either.
185 */
186
187 if ( 0 == idr_date->dt_year && 0 == idr_date->dt_month &&
188 0 == idr_date->dt_day && 0 == idr_date->dt_hour &&
189 0 == idr_date->dt_minute && 0 == idr_date->dt_second ) {
190 time_t t = 0;
191 struct tm temp_tm;
192 localtime_r(&t, &temp_tm);
193
194 memcpy(p_tm, &temp_tm, sizeof(struct tm));
195 return true;
196 }
197
198 memset(p_tm, 0, sizeof(struct tm));
199
200 p_tm->tm_year = idr_date->dt_year;
201 p_tm->tm_mon = idr_date->dt_month - 1;
202 p_tm->tm_mday = idr_date->dt_day;
203 p_tm->tm_hour = idr_date->dt_hour;
204 p_tm->tm_min = idr_date->dt_minute;
205 p_tm->tm_sec = idr_date->dt_second - idr_date->dt_gmtoff * (15 * 60);
206 p_tm->tm_isdst = -1; /* information not available */
207
208 #ifdef HAVE_STRUCT_TM_TM_ZONE
209 /* Initialize everything */
210 p_tm->tm_zone = 0;
211 #endif
212
213 /* Recompute tm_wday and tm_yday via mktime. mktime will also renormalize
214 date values to account for the timezone offset. */
215 {
216 time_t t = 0;
217 struct tm temp_tm;
218
219 t = timegm(p_tm);
220
221 if (b_localtime)
222 localtime_r(&t, &temp_tm);
223 else
224 gmtime_r(&t, &temp_tm);
225
226 memcpy(p_tm, &temp_tm, sizeof(struct tm));
227 }
228
229
230 return true;
231 }
232
233 /*
234 A note regarding the strange strtol() testing below as pointed out SMS.
235 From man strtol:
236
237 If an underflow occurs, strtol() returns LONG_MIN. If an overflow
238 occurs, strtol() returns LONG_MAX. In both cases, errno is set to
239 ERANGE.
240
241 This implies that one should only look at errno if the value is
242 LONG_MIN or LONG_MAX.
243 */
244
245 #define set_ltime_field(TM_FIELD, LT_FIELD, ADD_CONSTANT) \
246 { \
247 char num[10]; long tmp; \
248 memcpy(num, p_ldate->LT_FIELD, sizeof(p_ldate->LT_FIELD)); \
249 num[sizeof(p_ldate->LT_FIELD)] = '\0'; \
250 errno = 0; \
251 tmp = strtol(num, \
252 (char **)NULL, 10); \
253 if ( tmp < INT_MIN || tmp > INT_MAX || \
254 ((unsigned long)tmp + ADD_CONSTANT) > INT_MAX || \
255 (tmp + ADD_CONSTANT) < INT_MIN ) \
256 return false; \
257 p_tm->TM_FIELD = tmp + ADD_CONSTANT; \
258 }
259
260 /*!
261 Get "long" time in format used in ISO 9660 primary volume descriptor
262 from a Unix time structure.
263 */
264 bool
iso9660_get_ltime(const iso9660_ltime_t * p_ldate,struct tm * p_tm)265 iso9660_get_ltime (const iso9660_ltime_t *p_ldate,
266 /*out*/ struct tm *p_tm)
267 {
268 if (!p_tm) return false;
269 memset(p_tm, 0, sizeof(struct tm));
270 set_ltime_field(tm_year, lt_year, -1900);
271 set_ltime_field(tm_mon, lt_month, -1);
272 set_ltime_field(tm_mday, lt_day, 0);
273 set_ltime_field(tm_hour, lt_hour, 0);
274 set_ltime_field(tm_min, lt_minute, 0);
275 set_ltime_field(tm_sec, lt_second, 0);
276 p_tm->tm_isdst= -1; /* information not available */
277 #ifndef HAVE_TM_GMTOFF
278 p_tm->tm_sec += p_ldate->lt_gmtoff * (15 * 60);
279 #endif
280 #ifdef HAVE_STRUCT_TM_TM_ZONE
281 /* Initialize everything */
282 p_tm->tm_zone = 0;
283 #endif
284
285 /* Recompute tm_wday and tm_yday via mktime. mktime will also renormalize
286 date values to account for the timezone offset. */
287 {
288 time_t t;
289 struct tm temp_tm;
290
291 t = mktime(p_tm);
292
293 localtime_r(&t, &temp_tm);
294
295 memcpy(p_tm, &temp_tm, sizeof(struct tm));
296 }
297 p_tm->tm_isdst= -1; /* information not available */
298 #ifdef HAVE_TM_GMTOFF
299 p_tm->tm_gmtoff = -p_ldate->lt_gmtoff * (15 * 60);
300 #endif
301 return true;
302 }
303
304 /*!
305 Set time in format used in ISO 9660 directory index record
306 from a Unix time structure. timezone is given as an offset
307 correction in minutes.
308 */
309 void
iso9660_set_dtime_with_timezone(const struct tm * p_tm,int time_zone,iso9660_dtime_t * p_idr_date)310 iso9660_set_dtime_with_timezone (const struct tm *p_tm,
311 int time_zone,
312 /*out*/ iso9660_dtime_t *p_idr_date)
313 {
314 memset (p_idr_date, 0, 7);
315
316 if (!p_tm) return;
317
318 p_idr_date->dt_year = p_tm->tm_year;
319 p_idr_date->dt_month = p_tm->tm_mon + 1;
320 p_idr_date->dt_day = p_tm->tm_mday;
321 p_idr_date->dt_hour = p_tm->tm_hour;
322 p_idr_date->dt_minute = p_tm->tm_min;
323 p_idr_date->dt_second = p_tm->tm_sec;
324
325 /* The ISO 9660 timezone is in the range -48..+52 and each unit
326 represents a 15-minute interval. */
327 p_idr_date->dt_gmtoff = time_zone / 15;
328
329 if (p_idr_date->dt_gmtoff < -48 ) {
330
331 cdio_warn ("Converted ISO 9660 timezone %d is less than -48. Adjusted",
332 p_idr_date->dt_gmtoff);
333 p_idr_date->dt_gmtoff = -48;
334 } else if (p_idr_date->dt_gmtoff > 52) {
335 cdio_warn ("Converted ISO 9660 timezone %d is over 52. Adjusted",
336 p_idr_date->dt_gmtoff);
337 p_idr_date->dt_gmtoff = 52;
338 }
339 }
340
341 /*!
342 Set time in format used in ISO 9660 directory index record
343 from a Unix time structure. */
344 void
iso9660_set_dtime(const struct tm * p_tm,iso9660_dtime_t * p_idr_date)345 iso9660_set_dtime(const struct tm *p_tm, /*out*/ iso9660_dtime_t *p_idr_date)
346 {
347 int time_zone = 0;
348 if (p_tm) {
349 #ifdef HAVE_TM_GMTOFF
350 /* Convert seconds to minutes */
351 time_zone = p_tm->tm_gmtoff / 60;
352 #else
353 time_zone = (p_tm->tm_isdst > 0) ? -60 : 0;
354 #endif
355 }
356 iso9660_set_dtime_with_timezone (p_tm, time_zone, p_idr_date);
357 }
358
359 /*!
360 Set "long" time in format used in ISO 9660 primary volume descriptor
361 from a Unix time structure. timezone is given as an offset
362 correction in minutes.
363 */
364 void
iso9660_set_ltime_with_timezone(const struct tm * p_tm,int time_zone,iso9660_ltime_t * pvd_date)365 iso9660_set_ltime_with_timezone(const struct tm *p_tm,
366 int time_zone,
367 /*out*/ iso9660_ltime_t *pvd_date)
368 {
369 char *_pvd_date = (char *) pvd_date;
370
371 memset (_pvd_date, (int) '0', 16);
372 pvd_date->lt_gmtoff = (iso712_t) 0; /* Start out with time zone GMT. */
373
374 if (!p_tm) return;
375
376 #if defined(__GNUC__) && __GNUC__ >= 5
377 #pragma GCC diagnostic push
378 #pragma GCC diagnostic ignored "-Wformat-truncation"
379 #endif
380 snprintf(_pvd_date, 17,
381 "%4.4d%2.2d%2.2d" "%2.2d%2.2d%2.2d" "%2.2d",
382 p_tm->tm_year + 1900, p_tm->tm_mon + 1, p_tm->tm_mday,
383 p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec,
384 0 /* 1/100 secs */ );
385 #if defined(__GNUC__) && __GNUC__ >= 5
386 #pragma GCC diagnostic pop
387 #endif
388
389 /* Set time zone in 15-minute interval encoding. */
390 pvd_date->lt_gmtoff -= (time_zone / 15);
391 if (pvd_date->lt_gmtoff < -48 ) {
392
393 cdio_warn ("Converted ISO 9660 timezone %d is less than -48. Adjusted",
394 (int) pvd_date->lt_gmtoff);
395 pvd_date->lt_gmtoff = -48;
396 } else if (pvd_date->lt_gmtoff > 52) {
397 cdio_warn ("Converted ISO 9660 timezone %d is over 52. Adjusted",
398 (int) pvd_date->lt_gmtoff);
399 pvd_date->lt_gmtoff = 52;
400 }
401 }
402
403 /*!
404 Set "long" time in format used in ISO 9660 primary volume descriptor
405 from a Unix time structure. */
406 void
iso9660_set_ltime(const struct tm * p_tm,iso9660_ltime_t * pvd_date)407 iso9660_set_ltime(const struct tm *p_tm, /*out*/ iso9660_ltime_t *pvd_date)
408 {
409 int time_zone = 0;
410 if (p_tm) {
411 #ifdef HAVE_TM_GMTOFF
412 /* Set time zone in 15-minute interval encoding. */
413 time_zone = p_tm->tm_gmtoff / 60;
414 #else
415 time_zone = (p_tm->tm_isdst > 0) ? -60 : 0;
416 #endif
417 }
418 iso9660_set_ltime_with_timezone (p_tm, time_zone, pvd_date);
419 }
420
421 /*!
422 Convert an ISO-9660 file name which is in the format usually stored
423 in a ISO 9660 directory entry into what's usually listed as the
424 file name in a listing. Lowercase name, and remove trailing ;1's
425 or .;1's and turn the other ;'s into version numbers.
426
427 @param psz_oldname the ISO-9660 filename to be translated.
428 @param psz_newname returned string. The caller allocates this and
429 it should be at least the size of psz_oldname.
430 @return length of the translated string is returned. It will be no greater
431 than the length of psz_oldname.
432 */
433 int
iso9660_name_translate(const char * psz_oldname,char * psz_newname)434 iso9660_name_translate(const char *psz_oldname, char *psz_newname)
435 {
436 return iso9660_name_translate_ext(psz_oldname, psz_newname, 0);
437 }
438
439 /*!
440 Convert an ISO-9660 file name which is in the format usually stored
441 in a ISO 9660 directory entry into what's usually listed as the
442 file name in a listing. Lowercase name if no Joliet Extension
443 interpretation. Remove trailing ;1's or .;1's and turn the other
444 ;'s into version numbers.
445
446 @param psz_oldname the ISO-9660 filename to be translated.
447 @param psz_newname returned string. The caller allocates this and
448 it should be at least the size of psz_oldname.
449 @param u_joliet_level 0 if not using Joliet Extension. Otherwise the
450 Joliet level.
451 @return length of the translated string is returned. It will be no greater
452 than the length of psz_oldname.
453 */
454 int
iso9660_name_translate_ext(const char * psz_oldname,char * psz_newname,uint8_t u_joliet_level)455 iso9660_name_translate_ext(const char *psz_oldname, char *psz_newname,
456 uint8_t u_joliet_level)
457 {
458 int len = strlen(psz_oldname);
459 int i;
460
461 if (0 == len) return 0;
462 for (i = 0; i < len; i++) {
463 unsigned char c = psz_oldname[i];
464 if (!c)
465 break;
466
467 /* Lower case, unless we have Joliet extensions. */
468 if (!u_joliet_level && isupper(c)) c = tolower(c);
469
470 /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
471 if (c == '.' && i == len - 3
472 && psz_oldname[i + 1] == ';' && psz_oldname[i + 2] == '1')
473 break;
474
475 /* Drop trailing ';1' */
476 if (c == ';' && i == len - 2 && psz_oldname[i + 1] == '1')
477 break;
478
479 /* Convert remaining ';' to '.' */
480 if (c == ';')
481 c = '.';
482
483 psz_newname[i] = c;
484 }
485 psz_newname[i] = '\0';
486 return i;
487 }
488
489 /*!
490 Pad string src with spaces to size len and copy this to dst. If
491 len is less than the length of src, dst will be truncated to the
492 first len characters of src.
493
494 src can also be scanned to see if it contains only ACHARs, DCHARs,
495 7-bit ASCII chars depending on the enumeration _check.
496
497 In addition to getting changed, dst is the return value.
498 Note: this string might not be NULL terminated.
499 */
500 char *
iso9660_strncpy_pad(char dst[],const char src[],size_t len,enum strncpy_pad_check _check)501 iso9660_strncpy_pad(char dst[], const char src[], size_t len,
502 enum strncpy_pad_check _check)
503 {
504 size_t rlen;
505
506 cdio_assert (dst != NULL);
507 cdio_assert (src != NULL);
508 cdio_assert (len > 0);
509
510 switch (_check)
511 {
512 int idx;
513 case ISO9660_NOCHECK:
514 break;
515
516 case ISO9660_7BIT:
517 for (idx = 0; src[idx]; idx++)
518 if ((int8_t) src[idx] < 0)
519 {
520 cdio_warn ("string '%s' fails 7bit constraint (pos = %d)",
521 src, idx);
522 break;
523 }
524 break;
525
526 case ISO9660_ACHARS:
527 for (idx = 0; src[idx]; idx++)
528 if (!iso9660_is_achar (src[idx]))
529 {
530 cdio_warn ("string '%s' fails a-character constraint (pos = %d)",
531 src, idx);
532 break;
533 }
534 break;
535
536 case ISO9660_DCHARS:
537 for (idx = 0; src[idx]; idx++)
538 if (!iso9660_is_dchar (src[idx]))
539 {
540 cdio_warn ("string '%s' fails d-character constraint (pos = %d)",
541 src, idx);
542 break;
543 }
544 break;
545
546 default:
547 cdio_assert_not_reached ();
548 break;
549 }
550
551 rlen = strlen (src);
552
553 if (rlen > len)
554 cdio_warn ("string '%s' is getting truncated to %d characters",
555 src, (unsigned int) len);
556
557 strncpy (dst, src, len);
558 if (rlen < len)
559 memset(dst+rlen, ' ', len-rlen);
560 return dst;
561 }
562
563 /*!
564 Return true if c is a DCHAR - a valid ISO-9660 level 1 character.
565 These are the ASCSII capital letters A-Z, the digits 0-9 and an
566 underscore.
567 */
568 bool
iso9660_is_dchar(int c)569 iso9660_is_dchar (int c)
570 {
571 if (!IN (c, 0x30, 0x5f)
572 || IN (c, 0x3a, 0x40)
573 || IN (c, 0x5b, 0x5e))
574 return false;
575
576 return true;
577 }
578
579
580 /*!
581 Return true if c is an ACHAR -
582 These are the DCHAR's plus some ASCII symbols including the space
583 symbol.
584 */
585 bool
iso9660_is_achar(int c)586 iso9660_is_achar (int c)
587 {
588 if (!IN (c, 0x20, 0x5f)
589 || IN (c, 0x23, 0x24)
590 || c == 0x40
591 || IN (c, 0x5b, 0x5e))
592 return false;
593
594 return true;
595 }
596
597 void
iso9660_set_evd(void * pd)598 iso9660_set_evd(void *pd)
599 {
600 iso_volume_descriptor_t ied;
601
602 cdio_assert (sizeof(iso_volume_descriptor_t) == ISO_BLOCKSIZE);
603
604 cdio_assert (pd != NULL);
605
606 memset(&ied, 0, sizeof(ied));
607
608 ied.type = to_711(ISO_VD_END);
609 iso9660_strncpy_pad (ied.id, ISO_STANDARD_ID, sizeof(ied.id),
610 ISO9660_DCHARS);
611 ied.version = to_711(ISO_VERSION);
612
613 memcpy(pd, &ied, sizeof(ied));
614 }
615
616 void
iso9660_set_pvd(void * pd,const char volume_id[],const char publisher_id[],const char preparer_id[],const char application_id[],uint32_t iso_size,const void * root_dir,uint32_t path_table_l_extent,uint32_t path_table_m_extent,uint32_t path_table_size,const time_t * pvd_time)617 iso9660_set_pvd(void *pd,
618 const char volume_id[],
619 const char publisher_id[],
620 const char preparer_id[],
621 const char application_id[],
622 uint32_t iso_size,
623 const void *root_dir,
624 uint32_t path_table_l_extent,
625 uint32_t path_table_m_extent,
626 uint32_t path_table_size,
627 const time_t *pvd_time
628 )
629 {
630 iso9660_pvd_t ipd;
631 struct tm temp_tm;
632
633 cdio_assert (sizeof(iso9660_pvd_t) == ISO_BLOCKSIZE);
634
635 cdio_assert (pd != NULL);
636 cdio_assert (volume_id != NULL);
637 cdio_assert (application_id != NULL);
638
639 memset(&ipd,0,sizeof(ipd)); /* paranoia? */
640
641 /* magic stuff ... thatis CD XA marker... */
642 strncpy(((char*)&ipd)+ISO_XA_MARKER_OFFSET, ISO_XA_MARKER_STRING,
643 strlen(ISO_XA_MARKER_STRING)+1);
644
645 ipd.type = to_711(ISO_VD_PRIMARY);
646 iso9660_strncpy_pad (ipd.id, ISO_STANDARD_ID, 5, ISO9660_DCHARS);
647 ipd.version = to_711(ISO_VERSION);
648
649 iso9660_strncpy_pad (ipd.system_id, SYSTEM_ID, 32, ISO9660_ACHARS);
650 iso9660_strncpy_pad (ipd.volume_id, volume_id, 32, ISO9660_DCHARS);
651
652 ipd.volume_space_size = to_733(iso_size);
653
654 ipd.volume_set_size = to_723(1);
655 ipd.volume_sequence_number = to_723(1);
656 ipd.logical_block_size = to_723(ISO_BLOCKSIZE);
657
658 ipd.path_table_size = to_733(path_table_size);
659 ipd.type_l_path_table = to_731(path_table_l_extent);
660 ipd.type_m_path_table = to_732(path_table_m_extent);
661
662 /* root_directory_record doesn't contain the 1-byte filename,
663 so we add one for that. */
664 cdio_assert (sizeof(ipd.root_directory_record) == 33);
665 memcpy(&(ipd.root_directory_record), root_dir,
666 sizeof(ipd.root_directory_record));
667 ipd.root_directory_filename='\0';
668 ipd.root_directory_record.length = sizeof(ipd.root_directory_record)+1;
669 iso9660_strncpy_pad (ipd.volume_set_id, VOLUME_SET_ID,
670 ISO_MAX_VOLUMESET_ID, ISO9660_DCHARS);
671
672 iso9660_strncpy_pad (ipd.publisher_id, publisher_id, ISO_MAX_PUBLISHER_ID,
673 ISO9660_ACHARS);
674 iso9660_strncpy_pad (ipd.preparer_id, preparer_id, ISO_MAX_PREPARER_ID,
675 ISO9660_ACHARS);
676 iso9660_strncpy_pad (ipd.application_id, application_id,
677 ISO_MAX_APPLICATION_ID, ISO9660_ACHARS);
678
679 iso9660_strncpy_pad (ipd.copyright_file_id , "", 37, ISO9660_DCHARS);
680 iso9660_strncpy_pad (ipd.abstract_file_id , "", 37, ISO9660_DCHARS);
681 iso9660_strncpy_pad (ipd.bibliographic_file_id, "", 37, ISO9660_DCHARS);
682
683 gmtime_r(pvd_time, &temp_tm);
684 iso9660_set_ltime (&temp_tm, &(ipd.creation_date));
685 gmtime_r(pvd_time, &temp_tm);
686 iso9660_set_ltime (&temp_tm, &(ipd.modification_date));
687 iso9660_set_ltime (NULL, &(ipd.expiration_date));
688 iso9660_set_ltime (NULL, &(ipd.effective_date));
689
690 ipd.file_structure_version = to_711(1);
691
692 /* we leave ipd.application_data = 0 */
693
694 memcpy(pd, &ipd, sizeof(ipd)); /* copy stuff to arg ptr */
695 }
696
697 unsigned int
iso9660_dir_calc_record_size(unsigned int namelen,unsigned int su_len)698 iso9660_dir_calc_record_size(unsigned int namelen, unsigned int su_len)
699 {
700 unsigned int length;
701
702 length = sizeof(iso9660_dir_t);
703 length += namelen;
704 if (length % 2) /* pad to word boundary */
705 length++;
706 length += su_len;
707 if (length % 2) /* pad to word boundary again */
708 length++;
709
710 return length;
711 }
712
713 void
iso9660_dir_add_entry_su(void * dir,const char filename[],uint32_t extent,uint32_t size,uint8_t file_flags,const void * su_data,unsigned int su_size,const time_t * entry_time)714 iso9660_dir_add_entry_su(void *dir,
715 const char filename[],
716 uint32_t extent,
717 uint32_t size,
718 uint8_t file_flags,
719 const void *su_data,
720 unsigned int su_size,
721 const time_t *entry_time)
722 {
723 iso9660_dir_t *idr = dir;
724 uint8_t *dir8 = dir;
725 unsigned int offset = 0;
726 uint32_t dsize = from_733(idr->size);
727 int length, su_offset;
728 struct tm temp_tm;
729 cdio_assert (sizeof(iso9660_dir_t) == 33);
730
731 if (!dsize && !idr->length)
732 dsize = ISO_BLOCKSIZE; /* for when dir lacks '.' entry */
733
734 cdio_assert (dsize > 0 && !(dsize % ISO_BLOCKSIZE));
735 cdio_assert (dir != NULL);
736 cdio_assert (extent > 17);
737 cdio_assert (filename != NULL);
738 cdio_assert (strlen(filename) <= MAX_ISOPATHNAME);
739
740 length = sizeof(iso9660_dir_t);
741 length += strlen(filename);
742 length = _cdio_ceil2block (length, 2); /* pad to word boundary */
743 su_offset = length;
744 length += su_size;
745 length = _cdio_ceil2block (length, 2); /* pad to word boundary again */
746
747 /* find the last entry's end */
748 {
749 unsigned int ofs_last_rec = 0;
750
751 offset = 0;
752 while (offset < dsize)
753 {
754 if (!dir8[offset])
755 {
756 offset++;
757 continue;
758 }
759
760 offset += dir8[offset];
761 ofs_last_rec = offset;
762 }
763
764 cdio_assert (offset == dsize);
765
766 offset = ofs_last_rec;
767 }
768
769 /* be sure we don't cross sectors boundaries */
770 offset = _cdio_ofs_add (offset, length, ISO_BLOCKSIZE);
771 offset -= length;
772
773 cdio_assert (offset + length <= dsize);
774
775 idr = (iso9660_dir_t *) &dir8[offset];
776
777 cdio_assert (offset+length < dsize);
778
779 memset(idr, 0, length);
780
781 idr->length = to_711(length);
782 idr->extent = to_733(extent);
783 idr->size = to_733(size);
784
785 gmtime_r(entry_time, &temp_tm);
786 iso9660_set_dtime (&temp_tm, &(idr->recording_time));
787
788 idr->file_flags = to_711(file_flags);
789
790 idr->volume_sequence_number = to_723(1);
791
792 idr->filename.len = to_711(strlen(filename)
793 ? strlen(filename) : 1); /* working hack! */
794
795 memcpy(&idr->filename.str[1], filename, from_711(idr->filename.len));
796 if (su_size > 0 && su_data)
797 memcpy(&dir8[offset] + su_offset, su_data, su_size);
798 }
799
800 void
iso9660_dir_init_new(void * dir,uint32_t self,uint32_t ssize,uint32_t parent,uint32_t psize,const time_t * dir_time)801 iso9660_dir_init_new (void *dir,
802 uint32_t self,
803 uint32_t ssize,
804 uint32_t parent,
805 uint32_t psize,
806 const time_t *dir_time)
807 {
808 iso9660_dir_init_new_su (dir, self, ssize, NULL, 0, parent, psize, NULL,
809 0, dir_time);
810 }
811
812 void
iso9660_dir_init_new_su(void * dir,uint32_t self,uint32_t ssize,const void * ssu_data,unsigned int ssu_size,uint32_t parent,uint32_t psize,const void * psu_data,unsigned int psu_size,const time_t * dir_time)813 iso9660_dir_init_new_su (void *dir,
814 uint32_t self,
815 uint32_t ssize,
816 const void *ssu_data,
817 unsigned int ssu_size,
818 uint32_t parent,
819 uint32_t psize,
820 const void *psu_data,
821 unsigned int psu_size,
822 const time_t *dir_time)
823 {
824 cdio_assert (ssize > 0 && !(ssize % ISO_BLOCKSIZE));
825 cdio_assert (psize > 0 && !(psize % ISO_BLOCKSIZE));
826 cdio_assert (dir != NULL);
827
828 memset (dir, 0, ssize);
829
830 /* "\0" -- working hack due to padding */
831 iso9660_dir_add_entry_su (dir, "\0", self, ssize, ISO_DIRECTORY, ssu_data,
832 ssu_size, dir_time);
833
834 iso9660_dir_add_entry_su (dir, "\1", parent, psize, ISO_DIRECTORY, psu_data,
835 psu_size, dir_time);
836 }
837
838 /* Zero's out pathable. Do this first. */
839 void
iso9660_pathtable_init(void * pt)840 iso9660_pathtable_init (void *pt)
841 {
842 cdio_assert (sizeof (iso_path_table_t) == 8);
843
844 cdio_assert (pt != NULL);
845
846 memset (pt, 0, ISO_BLOCKSIZE); /* fixme */
847 }
848
849 /*!
850 Returns POSIX mode bitstring for a given file.
851 */
852 mode_t
iso9660_get_posix_filemode(const iso9660_stat_t * p_iso_dirent)853 iso9660_get_posix_filemode(const iso9660_stat_t *p_iso_dirent)
854 {
855 mode_t mode = 0;
856
857 #ifdef HAVE_ROCK
858 if (yep == p_iso_dirent->rr.b3_rock) {
859 return iso9660_get_posix_filemode_from_rock(&p_iso_dirent->rr);
860 } else
861 #endif
862 if (p_iso_dirent->b_xa) {
863 return iso9660_get_posix_filemode_from_xa(p_iso_dirent->xa.attributes);
864 }
865 return mode;
866 }
867
868 static const iso_path_table_t *
pathtable_get_entry(const void * pt,unsigned int entrynum)869 pathtable_get_entry (const void *pt, unsigned int entrynum)
870 {
871 const uint8_t *tmp = pt;
872 unsigned int offset = 0;
873 unsigned int count = 0;
874
875 cdio_assert (pt != NULL);
876
877 while (from_711 (*tmp))
878 {
879 if (count == entrynum)
880 break;
881
882 cdio_assert (count < entrynum);
883
884 offset += sizeof (iso_path_table_t);
885 offset += from_711 (*tmp);
886 if (offset % 2)
887 offset++;
888 tmp = (uint8_t *)pt + offset;
889 count++;
890 }
891
892 if (!from_711 (*tmp))
893 return NULL;
894
895 return (const void *) tmp;
896 }
897
898 void
pathtable_get_size_and_entries(const void * pt,unsigned int * size,unsigned int * entries)899 pathtable_get_size_and_entries (const void *pt,
900 unsigned int *size,
901 unsigned int *entries)
902 {
903 const uint8_t *tmp = pt;
904 unsigned int offset = 0;
905 unsigned int count = 0;
906
907 cdio_assert (pt != NULL);
908
909 while (from_711 (*tmp))
910 {
911 offset += sizeof (iso_path_table_t);
912 offset += from_711 (*tmp);
913 if (offset % 2)
914 offset++;
915 tmp = (uint8_t *)pt + offset;
916 count++;
917 }
918
919 if (size)
920 *size = offset;
921
922 if (entries)
923 *entries = count;
924 }
925
926 unsigned int
iso9660_pathtable_get_size(const void * pt)927 iso9660_pathtable_get_size (const void *pt)
928 {
929 unsigned int size = 0;
930 pathtable_get_size_and_entries (pt, &size, NULL);
931 return size;
932 }
933
934 uint16_t
iso9660_pathtable_l_add_entry(void * pt,const char name[],uint32_t extent,uint16_t parent)935 iso9660_pathtable_l_add_entry (void *pt,
936 const char name[],
937 uint32_t extent,
938 uint16_t parent)
939 {
940 iso_path_table_t *ipt =
941 (iso_path_table_t *)((char *)pt + iso9660_pathtable_get_size (pt));
942 size_t name_len = strlen (name) ? strlen (name) : 1;
943 unsigned int entrynum = 0;
944
945 cdio_assert (iso9660_pathtable_get_size (pt) < ISO_BLOCKSIZE); /*fixme */
946
947 memset (ipt, 0, sizeof (iso_path_table_t) + name_len); /* paranoia */
948
949 ipt->name_len = to_711 (name_len);
950 ipt->extent = to_731 (extent);
951 ipt->parent = to_721 (parent);
952 memcpy (ipt->name, name, name_len);
953
954 pathtable_get_size_and_entries (pt, NULL, &entrynum);
955
956 if (entrynum > 1)
957 {
958 const iso_path_table_t *ipt2
959 = pathtable_get_entry (pt, entrynum - 2);
960
961 cdio_assert (ipt2 != NULL);
962
963 cdio_assert (from_721 (ipt2->parent) <= parent);
964 }
965
966 return entrynum;
967 }
968
969 uint16_t
iso9660_pathtable_m_add_entry(void * pt,const char name[],uint32_t extent,uint16_t parent)970 iso9660_pathtable_m_add_entry (void *pt,
971 const char name[],
972 uint32_t extent,
973 uint16_t parent)
974 {
975 iso_path_table_t *ipt =
976 (iso_path_table_t *)((char *)pt + iso9660_pathtable_get_size (pt));
977 size_t name_len = strlen (name) ? strlen (name) : 1;
978 unsigned int entrynum = 0;
979
980 cdio_assert (iso9660_pathtable_get_size(pt) < ISO_BLOCKSIZE); /* fixme */
981
982 memset(ipt, 0, sizeof (iso_path_table_t) + name_len); /* paranoia */
983
984 ipt->name_len = to_711 (name_len);
985 ipt->extent = to_732 (extent);
986 ipt->parent = to_722 (parent);
987 memcpy (ipt->name, name, name_len);
988
989 pathtable_get_size_and_entries (pt, NULL, &entrynum);
990
991 if (entrynum > 1)
992 {
993 const iso_path_table_t *ipt2
994 = pathtable_get_entry (pt, entrynum - 2);
995
996 cdio_assert (ipt2 != NULL);
997
998 cdio_assert (from_722 (ipt2->parent) <= parent);
999 }
1000
1001 return entrynum;
1002 }
1003
1004 /*!
1005 Check that pathname is a valid ISO-9660 directory name.
1006
1007 A valid directory name should not start out with a slash (/),
1008 dot (.) or null byte, should be less than 37 characters long,
1009 have no more than 8 characters in a directory component
1010 which is separated by a /, and consist of only DCHARs.
1011 */
1012 bool
iso9660_dirname_valid_p(const char pathname[])1013 iso9660_dirname_valid_p (const char pathname[])
1014 {
1015 const char *p = pathname;
1016 int len;
1017
1018 cdio_assert (pathname != NULL);
1019
1020 if (*p == '/' || *p == '.' || *p == '\0')
1021 return false;
1022
1023 if (strlen (pathname) > MAX_ISOPATHNAME)
1024 return false;
1025
1026 len = 0;
1027 for (; *p; p++)
1028 if (iso9660_is_dchar (*p))
1029 {
1030 len++;
1031 if (len > 8)
1032 return false;
1033 }
1034 else if (*p == '/')
1035 {
1036 if (!len)
1037 return false;
1038 len = 0;
1039 }
1040 else
1041 return false; /* unexpected char */
1042
1043 if (!len)
1044 return false; /* last char may not be '/' */
1045
1046 return true;
1047 }
1048
1049 /*!
1050 Check that pathname is a valid ISO-9660 pathname.
1051
1052 A valid pathname contains a valid directory name, if one appears and
1053 the filename portion should be no more than 8 characters for the
1054 file prefix and 3 characters in the extension (or portion after a
1055 dot). There should be exactly one dot somewhere in the filename
1056 portion and the filename should be composed of only DCHARs.
1057
1058 True is returned if pathname is valid.
1059 */
1060 bool
iso9660_pathname_valid_p(const char pathname[])1061 iso9660_pathname_valid_p (const char pathname[])
1062 {
1063 const char *p = NULL;
1064
1065 cdio_assert (pathname != NULL);
1066
1067 if ((p = strrchr (pathname, '/')))
1068 {
1069 bool rc;
1070 char *_tmp = strdup (pathname);
1071
1072 *strrchr (_tmp, '/') = '\0';
1073
1074 rc = iso9660_dirname_valid_p (_tmp);
1075
1076 free (_tmp);
1077
1078 if (!rc)
1079 return false;
1080
1081 p++;
1082 }
1083 else
1084 p = pathname;
1085
1086 if (strlen (pathname) > (MAX_ISOPATHNAME - 6))
1087 return false;
1088
1089 {
1090 int len = 0;
1091 int dots = 0;
1092
1093 for (; *p; p++)
1094 if (iso9660_is_dchar (*p))
1095 {
1096 len++;
1097 if (dots == 0 ? len > 8 : len > 3)
1098 return false;
1099 }
1100 else if (*p == '.')
1101 {
1102 dots++;
1103 if (dots > 1)
1104 return false;
1105 if (!len)
1106 return false;
1107 len = 0;
1108 }
1109 else
1110 return false;
1111
1112 if (dots != 1)
1113 return false;
1114 }
1115
1116 return true;
1117 }
1118
1119 /*!
1120 Take pathname and a version number and turn that into a ISO-9660
1121 pathname. (That's just the pathname followd by ";" and the version
1122 number. For example, mydir/file.ext -> mydir/file.ext;1 for version
1123 1. The resulting ISO-9660 pathname is returned.
1124 */
1125 char *
iso9660_pathname_isofy(const char pathname[],uint16_t version)1126 iso9660_pathname_isofy (const char pathname[], uint16_t version)
1127 {
1128 char tmpbuf[1024] = { 0, };
1129
1130 cdio_assert (strlen (pathname) < (sizeof (tmpbuf) - sizeof (";65535")));
1131
1132 snprintf (tmpbuf, sizeof(tmpbuf), "%s;%d", pathname, version);
1133
1134 return strdup (tmpbuf);
1135 }
1136
1137 /*!
1138 Return the PVD's application ID.
1139 NULL is returned if there is some problem in getting this.
1140 */
1141 char *
iso9660_get_application_id(iso9660_pvd_t * p_pvd)1142 iso9660_get_application_id(iso9660_pvd_t *p_pvd)
1143 {
1144 if (NULL==p_pvd) return NULL;
1145 return strdup(strip_trail(p_pvd->application_id, ISO_MAX_APPLICATION_ID));
1146 }
1147
1148 #ifdef FIXME
1149 lsn_t
iso9660_get_dir_extent(const iso9660_dir_t * idr)1150 iso9660_get_dir_extent(const iso9660_dir_t *idr)
1151 {
1152 if (NULL == idr) return 0;
1153 return from_733(idr->extent);
1154 }
1155 #endif
1156
1157 uint8_t
iso9660_get_dir_len(const iso9660_dir_t * idr)1158 iso9660_get_dir_len(const iso9660_dir_t *idr)
1159 {
1160 if (NULL == idr) return 0;
1161 return idr->length;
1162 }
1163
1164 #ifdef FIXME
1165 uint8_t
iso9660_get_dir_size(const iso9660_dir_t * idr)1166 iso9660_get_dir_size(const iso9660_dir_t *idr)
1167 {
1168 if (NULL == idr) return 0;
1169 return from_733(idr->size);
1170 }
1171 #endif
1172
1173 uint8_t
iso9660_get_pvd_type(const iso9660_pvd_t * pvd)1174 iso9660_get_pvd_type(const iso9660_pvd_t *pvd)
1175 {
1176 if (NULL == pvd) return 255;
1177 return(pvd->type);
1178 }
1179
1180 const char *
iso9660_get_pvd_id(const iso9660_pvd_t * pvd)1181 iso9660_get_pvd_id(const iso9660_pvd_t *pvd)
1182 {
1183 if (NULL == pvd) return "ERR";
1184 return(pvd->id);
1185 }
1186
1187 int
iso9660_get_pvd_space_size(const iso9660_pvd_t * pvd)1188 iso9660_get_pvd_space_size(const iso9660_pvd_t *pvd)
1189 {
1190 if (NULL == pvd) return 0;
1191 return from_733(pvd->volume_space_size);
1192 }
1193
1194 int
iso9660_get_pvd_block_size(const iso9660_pvd_t * pvd)1195 iso9660_get_pvd_block_size(const iso9660_pvd_t *pvd)
1196 {
1197 if (NULL == pvd) return 0;
1198 return from_723(pvd->logical_block_size);
1199 }
1200
1201 /*! Return the primary volume id version number (of pvd).
1202 If there is an error 0 is returned.
1203 */
1204 int
iso9660_get_pvd_version(const iso9660_pvd_t * pvd)1205 iso9660_get_pvd_version(const iso9660_pvd_t *pvd)
1206 {
1207 if (NULL == pvd) return 0;
1208 return pvd->version;
1209 }
1210
1211 /*! Return the LSN of the root directory for pvd.
1212 If there is an error CDIO_INVALID_LSN is returned.
1213 */
1214 lsn_t
iso9660_get_root_lsn(const iso9660_pvd_t * pvd)1215 iso9660_get_root_lsn(const iso9660_pvd_t *pvd)
1216 {
1217 if (NULL == pvd)
1218 return CDIO_INVALID_LSN;
1219 else {
1220 const iso9660_dir_t *idr = &(pvd->root_directory_record);
1221 if (NULL == idr) return CDIO_INVALID_LSN;
1222 return(from_733 (idr->extent));
1223 }
1224 }
1225
1226 /*!
1227 Return a string containing the preparer id with trailing
1228 blanks removed.
1229 */
1230 char *
iso9660_get_preparer_id(const iso9660_pvd_t * pvd)1231 iso9660_get_preparer_id(const iso9660_pvd_t *pvd)
1232 {
1233 if (NULL==pvd) return NULL;
1234 return strdup(strip_trail(pvd->preparer_id, ISO_MAX_PREPARER_ID));
1235 }
1236
1237 /*!
1238 Return a string containing the publisher id with trailing
1239 blanks removed.
1240 */
1241 char *
iso9660_get_publisher_id(const iso9660_pvd_t * pvd)1242 iso9660_get_publisher_id(const iso9660_pvd_t *pvd)
1243 {
1244 if (NULL==pvd) return NULL;
1245 return strdup(strip_trail(pvd->publisher_id, ISO_MAX_PUBLISHER_ID));
1246 }
1247
1248 /*!
1249 Return a string containing the PVD's system id with trailing
1250 blanks removed.
1251 */
1252 char *
iso9660_get_system_id(const iso9660_pvd_t * pvd)1253 iso9660_get_system_id(const iso9660_pvd_t *pvd)
1254 {
1255 if (NULL==pvd) return NULL;
1256 return strdup(strip_trail(pvd->system_id, ISO_MAX_SYSTEM_ID));
1257 }
1258
1259 /*!
1260 Return the PVD's volume ID.
1261 */
1262 char *
iso9660_get_volume_id(const iso9660_pvd_t * pvd)1263 iso9660_get_volume_id(const iso9660_pvd_t *pvd)
1264 {
1265 if (NULL == pvd) return NULL;
1266 return strdup(strip_trail(pvd->volume_id, ISO_MAX_VOLUME_ID));
1267 }
1268
1269 /*!
1270 Return the PVD's volumeset ID.
1271 NULL is returned if there is some problem in getting this.
1272 */
1273 char *
iso9660_get_volumeset_id(const iso9660_pvd_t * pvd)1274 iso9660_get_volumeset_id(const iso9660_pvd_t *pvd)
1275 {
1276 if ( NULL == pvd ) return NULL;
1277 return strdup(strip_trail(pvd->volume_set_id, ISO_MAX_VOLUMESET_ID));
1278 }
1279
1280
1281 /*
1282 * Local variables:
1283 * c-file-style: "gnu"
1284 * tab-width: 8
1285 * indent-tabs-mode: nil
1286 * End:
1287 */
1288