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