1 /*
2   Copyright (C) 2005, 2008, 2010-2011, 2014, 2017 Rocky Bernstein
3   <rocky@gnu.org>
4 
5   Adapted from GNU/Linux fs/isofs/rock.c (C) 1992, 1993 Eric Youngdale
6 
7   This program is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16 
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 /* Rock Ridge Extensions to iso9660 */
21 
22 
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26 
27 #ifdef HAVE_STRING_H
28 # include <string.h>
29 #endif
30 
31 #ifdef HAVE_STDLIB_H
32 # include <stdlib.h>
33 #endif
34 
35 #ifdef HAVE_SYS_STAT_H
36 # include <sys/stat.h>
37 #endif
38 
39 #include <cdio/iso9660.h>
40 #include <cdio/logging.h>
41 #include <cdio/bytesex.h>
42 #include "filemode.h"
43 
44 #define CDIO_MKDEV(ma,mi)	((ma)<<16 | (mi))
45 
46 enum iso_rock_enums iso_rock_enums;
47 iso_rock_nm_flag_t iso_rock_nm_flag;
48 iso_rock_sl_flag_t iso_rock_sl_flag;
49 iso_rock_tf_flag_t iso_rock_tf_flag;
50 
51 /* Our own realloc routine tailored for the iso9660_stat_t symlink
52    field.  I can't figure out how to make realloc() work without
53    valgrind complaint.
54 */
55 static bool
realloc_symlink(iso9660_stat_t * p_stat,uint8_t i_grow)56 realloc_symlink(/*in/out*/ iso9660_stat_t *p_stat, uint8_t i_grow)
57 {
58   if (!p_stat->rr.i_symlink) {
59     const uint16_t i_max = 2*i_grow+1;
60     p_stat->rr.psz_symlink = (char *) calloc(1, i_max);
61     p_stat->rr.i_symlink_max = i_max;
62     return (NULL != p_stat->rr.psz_symlink);
63   } else {
64     unsigned int i_needed = p_stat->rr.i_symlink + i_grow ;
65     if ( i_needed <= p_stat->rr.i_symlink_max)
66       return true;
67     else {
68       char * psz_newsymlink = (char *) calloc(1, 2*i_needed);
69       if (!psz_newsymlink) return false;
70       p_stat->rr.i_symlink_max = 2*i_needed;
71       memcpy(psz_newsymlink, p_stat->rr.psz_symlink, p_stat->rr.i_symlink);
72       free(p_stat->rr.psz_symlink);
73       p_stat->rr.psz_symlink = psz_newsymlink;
74       return true;
75     }
76   }
77 }
78 
79 /* These functions are designed to read the system areas of a directory record
80  * and extract relevant information.  There are different functions provided
81  * depending upon what information we need at the time.  One function fills
82  * out an inode structure, a second one extracts a filename, a third one
83  * returns a symbolic link name, and a fourth one returns the extent number
84  * for the file. */
85 
86 #define SIG(A,B) ((A) | ((B) << 8)) /* isonum_721() */
87 
88 
89 /* This is a way of ensuring that we have something in the system
90    use fields that is compatible with Rock Ridge */
91 #define CHECK_SP(FAIL)	       			\
92       if(rr->u.SP.magic[0] != 0xbe) FAIL;	\
93       if(rr->u.SP.magic[1] != 0xef) FAIL;       \
94       p_stat->rr.s_rock_offset = rr->u.SP.skip;
95 /* We define a series of macros because each function must do exactly the
96    same thing in certain places.  We use the macros to ensure that everything
97    is done correctly */
98 
99 #define CONTINUE_DECLS \
100   int cont_extent = 0, cont_offset = 0, cont_size = 0;   \
101   void *buffer = NULL
102 
103 #define CHECK_CE				 \
104   { cont_extent = from_733(*rr->u.CE.extent);	 \
105     cont_offset = from_733(*rr->u.CE.offset);	 \
106     cont_size = from_733(*rr->u.CE.size);	 \
107     (void)cont_extent; (void)cont_offset, (void)cont_size; }
108 
109 #define SETUP_ROCK_RIDGE(DE,CHR,LEN)	      		      	\
110   {								\
111     LEN= sizeof(iso9660_dir_t) + DE->filename.len;		\
112     if(LEN & 1) LEN++;						\
113     CHR = ((unsigned char *) DE) + LEN;				\
114     LEN = *((unsigned char *) DE) - LEN;			\
115     if (0xff != p_stat->rr.s_rock_offset)			\
116       {								\
117 	LEN -= p_stat->rr.s_rock_offset;		       	\
118 	CHR += p_stat->rr.s_rock_offset;		       	\
119 	if (LEN<0) LEN=0;					\
120       }								\
121   }
122 
123 /* Copy a long or short time from the iso_rock_tf_t into
124    the specified field of a iso_rock_statbuf_t.
125    non-paramater variables are p_stat, rr, and cnt.
126 */
127 #define add_time(FLAG, TIME_FIELD)				  \
128   if (rr->u.TF.flags & FLAG) {					  \
129     p_stat->rr.TIME_FIELD.b_used = true;			  \
130     p_stat->rr.TIME_FIELD.b_longdate =				  \
131       (0 != (rr->u.TF.flags & ISO_ROCK_TF_LONG_FORM));		  \
132     if (p_stat->rr.TIME_FIELD.b_longdate) {			  \
133       memcpy(&(p_stat->rr.TIME_FIELD.t.ltime),			  \
134 	     &(rr->u.TF.time_bytes[cnt]),			  \
135 	     sizeof(iso9660_ltime_t)); 				  \
136       cnt += sizeof(iso9660_ltime_t);				  \
137     } else {							  \
138       memcpy(&(p_stat->rr.TIME_FIELD.t.dtime),			  \
139 	     &(rr->u.TF.time_bytes[cnt]),			  \
140 	     sizeof(iso9660_dtime_t)); 				  \
141       cnt += sizeof(iso9660_dtime_t);				  \
142     }								  \
143   }
144 
145 /*!
146   Get
147   @return length of name field; 0: not found, -1: to be ignored
148 */
149 int
get_rock_ridge_filename(iso9660_dir_t * p_iso9660_dir,char * psz_name,iso9660_stat_t * p_stat)150 get_rock_ridge_filename(iso9660_dir_t * p_iso9660_dir,
151 			/*out*/ char * psz_name,
152 			/*in/out*/ iso9660_stat_t *p_stat)
153 {
154   int len;
155   unsigned char *chr;
156   int symlink_len = 0;
157   CONTINUE_DECLS;
158   int i_namelen = 0;
159   int truncate=0;
160 
161   if (!p_stat || nope == p_stat->rr.b3_rock) return 0;
162   *psz_name = 0;
163 
164   SETUP_ROCK_RIDGE(p_iso9660_dir, chr, len);
165   /*repeat:*/
166   {
167     iso_extension_record_t * rr;
168     int sig;
169     int rootflag;
170 
171     while (len > 1){ /* There may be one byte for padding somewhere */
172       rr = (iso_extension_record_t *) chr;
173       sig = *chr+(*(chr+1) << 8);
174 
175       /* We used to check for some vaid values of SIG, specifically
176 	 SP, CE, ER, RR, PX, PN, SL, NM, CL, PL, TF, and ZF.
177 	 However there are various extensions to this set. So we
178 	 skip checking now.
179       */
180 
181       if (rr->len == 0) goto out; /* Something got screwed up here */
182       chr += rr->len;
183       len -= rr->len;
184 
185       switch(sig){
186       case SIG('S','P'):
187 	CHECK_SP(goto out);
188 	break;
189       case SIG('C','E'):
190 	{
191 	  iso711_t i_fname = from_711(p_iso9660_dir->filename.len);
192 	  if ('\0' == p_iso9660_dir->filename.str[1] && 1 == i_fname)
193 	    break;
194 	  if ('\1' == p_iso9660_dir->filename.str[1] && 1 == i_fname)
195 	    break;
196 	}
197 	CHECK_CE;
198 	break;
199       case SIG('E','R'):
200 	p_stat->rr.b3_rock = yep;
201 	cdio_debug("ISO 9660 Extensions: ");
202 	{
203 	  int p;
204 	  for(p=0;p<rr->u.ER.len_id;p++) cdio_debug("%c",rr->u.ER.data[p]);
205 	}
206 	break;
207       case SIG('N','M'):
208 	/* Alternate name */
209 	p_stat->rr.b3_rock = yep;
210 	if (truncate) break;
211 	if (rr->u.NM.flags & ISO_ROCK_NM_PARENT) {
212 	  i_namelen = sizeof("..");
213 	  strncat(psz_name, "..", i_namelen);
214 	  break;
215 	} else if (rr->u.NM.flags & ISO_ROCK_NM_CURRENT) {
216 	  i_namelen = sizeof(".");
217 	  strncat(psz_name, ".", i_namelen);
218 	  break;
219 	}
220 
221 	if (rr->u.NM.flags & ~1) {
222 	  cdio_info("Unsupported NM flag settings (%d)",rr->u.NM.flags);
223 	  break;
224 	}
225 	if((strlen(psz_name) + rr->len - 5) >= 254) {
226 	  truncate = 1;
227 	  break;
228 	}
229 	strncat(psz_name, rr->u.NM.name, rr->len - 5);
230 	i_namelen += rr->len - 5;
231 	break;
232       case SIG('P','X'):
233 	/* POSIX file attributes */
234 	p_stat->rr.st_mode   = from_733(rr->u.PX.st_mode);
235 	p_stat->rr.st_nlinks = from_733(rr->u.PX.st_nlinks);
236 	p_stat->rr.st_uid    = from_733(rr->u.PX.st_uid);
237 	p_stat->rr.st_gid    = from_733(rr->u.PX.st_gid);
238 	p_stat->rr.b3_rock    = yep;
239 	break;
240       case SIG('S','L'):
241 	{
242 	  /* Symbolic link */
243 	  uint8_t slen;
244 	  iso_rock_sl_part_t * p_sl;
245 	  iso_rock_sl_part_t * p_oldsl;
246 	  slen = rr->len - 5;
247 	  p_sl = &rr->u.SL.link;
248 	  p_stat->rr.i_symlink = symlink_len;
249 	  while (slen > 1){
250 	    rootflag = 0;
251 	    switch(p_sl->flags &~1){
252 	    case 0:
253 	      realloc_symlink(p_stat, p_sl->len);
254 	      memcpy(&(p_stat->rr.psz_symlink[p_stat->rr.i_symlink]),
255 		     p_sl->text, p_sl->len);
256 	      p_stat->rr.i_symlink += p_sl->len;
257 	      break;
258 	    case 4:
259 	      realloc_symlink(p_stat, 1);
260 	      p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '.';
261 	      /* continue into next case. */
262 	    case 2:
263 	      realloc_symlink(p_stat, 1);
264 	      p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '.';
265 	      break;
266 	    case 8:
267 	      rootflag = 1;
268 	      realloc_symlink(p_stat, 1);
269 	      p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '/';
270 	      break;
271 	    default:
272 	      cdio_warn("Symlink component flag not implemented");
273 	    }
274 	    slen -= p_sl->len + 2;
275 	    p_oldsl = p_sl;
276 	    p_sl = (iso_rock_sl_part_t *) (((char *) p_sl) + p_sl->len + 2);
277 
278 	    if (slen < 2) {
279 	      if (((rr->u.SL.flags & 1) != 0) && ((p_oldsl->flags & 1) == 0))
280 		p_stat->rr.i_symlink += 1;
281 	      break;
282 	    }
283 
284 	    /*
285 	     * If this component record isn't continued, then append a '/'.
286 	     */
287 	    if (!rootflag && (p_oldsl->flags & 1) == 0) {
288 	      realloc_symlink(p_stat, 1);
289 	      p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '/';
290 	    }
291 	  }
292 	}
293 	symlink_len = p_stat->rr.i_symlink;
294 	realloc_symlink(p_stat, 1);
295 	p_stat->rr.psz_symlink[symlink_len]='\0';
296 	break;
297       case SIG('R','E'):
298 	free(buffer);
299 	return -1;
300       case SIG('T','F'):
301 	/* Time stamp(s) for a file */
302 	{
303 	  int cnt = 0;
304 	  add_time(ISO_ROCK_TF_CREATE,     create);
305 	  add_time(ISO_ROCK_TF_MODIFY,     modify);
306 	  add_time(ISO_ROCK_TF_ACCESS,     access);
307 	  add_time(ISO_ROCK_TF_ATTRIBUTES, attributes);
308 	  add_time(ISO_ROCK_TF_BACKUP,     backup);
309 	  add_time(ISO_ROCK_TF_EXPIRATION, expiration);
310 	  add_time(ISO_ROCK_TF_EFFECTIVE,  effective);
311 	  p_stat->rr.b3_rock = yep;
312 	  break;
313 	}
314       default:
315 	break;
316       }
317     }
318   }
319   free(buffer);
320   return i_namelen; /* If 0, this file did not have a NM field */
321  out:
322   free(buffer);
323   return 0;
324 }
325 
326 static int
parse_rock_ridge_stat_internal(iso9660_dir_t * p_iso9660_dir,iso9660_stat_t * p_stat,int regard_xa)327 parse_rock_ridge_stat_internal(iso9660_dir_t *p_iso9660_dir,
328 			       iso9660_stat_t *p_stat, int regard_xa)
329 {
330   int len;
331   unsigned char * chr;
332   int symlink_len = 0;
333   CONTINUE_DECLS;
334 
335   if (nope == p_stat->rr.b3_rock) return 0;
336 
337   SETUP_ROCK_RIDGE(p_iso9660_dir, chr, len);
338   if (regard_xa)
339     {
340       chr+=14;
341       len-=14;
342       if (len<0) len=0;
343     }
344 
345   /* repeat:*/
346   {
347     int sig;
348     iso_extension_record_t * rr;
349     int rootflag;
350 
351     while (len > 1){ /* There may be one byte for padding somewhere */
352       rr = (iso_extension_record_t *) chr;
353       if (rr->len == 0) goto out; /* Something got screwed up here */
354       sig = from_721(*chr);
355       chr += rr->len;
356       len -= rr->len;
357 
358       switch(sig){
359       case SIG('S','P'):
360 	CHECK_SP(goto out);
361 	break;
362       case SIG('C','E'):
363 	CHECK_CE;
364 	break;
365       case SIG('E','R'):
366 	p_stat->rr.b3_rock = yep;
367 	cdio_debug("ISO 9660 Extensions: ");
368 	{ int p;
369 	  for(p=0;p<rr->u.ER.len_id;p++) cdio_debug("%c",rr->u.ER.data[p]);
370 	}
371 	break;
372       case SIG('P','X'):
373 	p_stat->rr.st_mode   = from_733(rr->u.PX.st_mode);
374 	p_stat->rr.st_nlinks = from_733(rr->u.PX.st_nlinks);
375 	p_stat->rr.st_uid    = from_733(rr->u.PX.st_uid);
376 	p_stat->rr.st_gid    = from_733(rr->u.PX.st_gid);
377 	break;
378       case SIG('P','N'):
379 	/* Device major,minor number */
380 	{ int32_t high, low;
381 	  high = from_733(rr->u.PN.dev_high);
382 	  low = from_733(rr->u.PN.dev_low);
383 	  /*
384 	   * The Rock Ridge standard specifies that if sizeof(dev_t) <= 4,
385 	   * then the high field is unused, and the device number is completely
386 	   * stored in the low field.  Some writers may ignore this subtlety,
387 	   * and as a result we test to see if the entire device number is
388 	   * stored in the low field, and use that.
389 	   */
390 	  if((low & ~0xff) && high == 0) {
391 	    p_stat->rr.i_rdev = CDIO_MKDEV(low >> 8, low & 0xff);
392 	  } else {
393 	    p_stat->rr.i_rdev = CDIO_MKDEV(high, low);
394 	  }
395 	}
396 	break;
397       case SIG('T','F'):
398 	/* Time stamp(s) for a file */
399 	{
400 	  int cnt = 0;
401 	  add_time(ISO_ROCK_TF_CREATE,     create);
402 	  add_time(ISO_ROCK_TF_MODIFY,     modify);
403 	  add_time(ISO_ROCK_TF_ACCESS,     access);
404 	  add_time(ISO_ROCK_TF_ATTRIBUTES, attributes);
405 	  add_time(ISO_ROCK_TF_BACKUP,     backup);
406 	  add_time(ISO_ROCK_TF_EXPIRATION, expiration);
407 	  add_time(ISO_ROCK_TF_EFFECTIVE,  effective);
408 	  p_stat->rr.b3_rock = yep;
409 	  break;
410 	}
411       case SIG('S','L'):
412 	{
413 	  /* Symbolic link */
414 	  uint8_t slen;
415 	  iso_rock_sl_part_t * p_sl;
416 	  iso_rock_sl_part_t * p_oldsl;
417 	  slen = rr->len - 5;
418 	  p_sl = &rr->u.SL.link;
419 	  p_stat->rr.i_symlink = symlink_len;
420 	  while (slen > 1){
421 	    rootflag = 0;
422 	    switch(p_sl->flags &~1){
423 	    case 0:
424 	      realloc_symlink(p_stat, p_sl->len);
425 	      if (p_sl->len)
426 		memcpy(&(p_stat->rr.psz_symlink[p_stat->rr.i_symlink]),
427 		       p_sl->text, p_sl->len);
428 	      p_stat->rr.i_symlink += p_sl->len;
429 	      break;
430 	    case 4:
431 	      realloc_symlink(p_stat, 1);
432 	      p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '.';
433 	      /* continue into next case. */
434 	    case 2:
435 	      realloc_symlink(p_stat, 1);
436 	      p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '.';
437 	      break;
438 	    case 8:
439 	      rootflag = 1;
440 	      realloc_symlink(p_stat, 1);
441 	      p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '/';
442 	      p_stat->rr.i_symlink++;
443 	      break;
444 	    default:
445 	      cdio_warn("Symlink component flag not implemented");
446 	    }
447 	    slen -= p_sl->len + 2;
448 	    p_oldsl = p_sl;
449 	    p_sl = (iso_rock_sl_part_t *) (((char *) p_sl) + p_sl->len + 2);
450 
451 	    if (slen < 2) {
452 	      if (((rr->u.SL.flags & 1) != 0) && ((p_oldsl->flags & 1) == 0))
453 		p_stat->rr.i_symlink += 1;
454 	      break;
455 	    }
456 
457 	    /*
458 	     * If this component record isn't continued, then append a '/'.
459 	     */
460 	    if (!rootflag && (p_oldsl->flags & 1) == 0) {
461 	      realloc_symlink(p_stat, 1);
462 	      p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '/';
463 	    }
464 	  }
465 	}
466 	symlink_len = p_stat->rr.i_symlink;
467 	realloc_symlink(p_stat, 1);
468 	p_stat->rr.psz_symlink[symlink_len]='\0';
469 	break;
470       case SIG('R','E'):
471 	cdio_warn("Attempt to read p_stat for relocated directory");
472 	goto out;
473 #ifdef FINISHED
474       case SIG('C','L'):
475 	{
476 	  iso9660_stat_t * reloc;
477 	  ISOFS_I(p_stat)->i_first_extent = from_733(rr->u.CL.location);
478 	  reloc = isofs_iget(p_stat->rr.i_sb, p_stat->rr.i_first_extent, 0);
479 	  if (!reloc)
480 	    goto out;
481 	  p_stat->rr.st_mode   = reloc->st_mode;
482 	  p_stat->rr.st_nlinks = reloc->st_nlinks;
483 	  p_stat->rr.st_uid    = reloc->st_uid;
484 	  p_stat->rr.st_gid    = reloc->st_gid;
485 	  p_stat->rr.i_rdev    = reloc->i_rdev;
486 	  p_stat->rr.i_symlink = reloc->i_symlink;
487 	  p_stat->rr.i_blocks  = reloc->i_blocks;
488 	  p_stat->rr.i_atime   = reloc->i_atime;
489 	  p_stat->rr.i_ctime   = reloc->i_ctime;
490 	  p_stat->rr.i_mtime   = reloc->i_mtime;
491 	  iput(reloc);
492 	}
493 	break;
494 #endif
495       default:
496 	break;
497       }
498     }
499   }
500  out:
501   free(buffer);
502   return 0;
503 }
504 
505 int
parse_rock_ridge_stat(iso9660_dir_t * p_iso9660_dir,iso9660_stat_t * p_stat)506 parse_rock_ridge_stat(iso9660_dir_t *p_iso9660_dir,
507 		      /*out*/ iso9660_stat_t *p_stat)
508 {
509   int result;
510 
511   if (!p_stat) return 0;
512 
513   result = parse_rock_ridge_stat_internal(p_iso9660_dir, p_stat, 0);
514   /* if Rock-Ridge flag was reset and we didn't look for attributes
515    * behind eventual XA attributes, have a look there */
516   if (0xFF == p_stat->rr.s_rock_offset && nope != p_stat->rr.b3_rock) {
517     result = parse_rock_ridge_stat_internal(p_iso9660_dir, p_stat, 14);
518   }
519   return result;
520 }
521 
522 #define BUF_COUNT 16
523 #define BUF_SIZE sizeof("drwxrwxrwx")
524 
525 /* Return a pointer to a internal free buffer */
526 static char *
_getbuf(void)527 _getbuf (void)
528 {
529   static char _buf[BUF_COUNT][BUF_SIZE];
530   static int _i = -1;
531 
532   _i++;
533   _i %= BUF_COUNT;
534 
535   memset (_buf[_i], 0, BUF_SIZE);
536 
537   return _buf[_i];
538 }
539 
540 /*!
541   Returns a string which interpreting the POSIX mode st_mode.
542   For example:
543   \verbatim
544   drwxrws---
545   -rw-rw-r--
546   lrwxrwxrwx
547   \endverbatim
548 
549   A description of the characters in the string follows
550   The 1st character is either "b" for a block device,
551   "c" for a character device, "d" if the entry is a directory, "l" for
552   a symbolic link, "p" for a pipe or FIFO, "s" for a "socket",
553   or "-" if none of the these.
554 
555   The 2nd to 4th characters refer to permissions for a user while the
556   the 5th to 7th characters refer to permissions for a group while, and
557   the 8th to 10h characters refer to permissions for everyone.
558 
559   In each of these triplets the first character (2, 5, 8) is "r" if
560   the entry is allowed to be read.
561 
562   The second character of a triplet (3, 6, 9) is "w" if the entry is
563   allowed to be written.
564 
565   The third character of a triplet (4, 7, 10) is "x" if the entry is
566   executable but not user (for character 4) or group (for characters
567   6) settable and "s" if the item has the corresponding user/group set.
568 
569   For a directory having an executable property on ("x" or "s") means
570   the directory is allowed to be listed or "searched". If the execute
571   property is not allowed for a group or user but the corresponding
572   group/user is set "S" indicates this. If none of these properties
573   holds the "-" indicates this.
574 */
575 const char *
iso9660_get_rock_attr_str(posix_mode_t st_mode)576 iso9660_get_rock_attr_str(posix_mode_t st_mode)
577 {
578   char *result = _getbuf();
579 
580   if (S_ISBLK(st_mode))
581     result[ 0] = 'b';
582   else if (S_ISDIR(st_mode))
583     result[ 0] = 'd';
584   else if (S_ISCHR(st_mode))
585     result[ 0] = 'c';
586   else if (S_ISLNK(st_mode))
587     result[ 0] = 'l';
588   else if (S_ISFIFO(st_mode))
589     result[ 0] = 'p';
590   else if (S_ISSOCK(st_mode))
591     result[ 0] = 's';
592   /* May eventually fill in others.. */
593   else
594     result[ 0] = '-';
595 
596   result[ 1] = (st_mode & ISO_ROCK_IRUSR) ? 'r' : '-';
597   result[ 2] = (st_mode & ISO_ROCK_IWUSR) ? 'w' : '-';
598 
599   if (st_mode & ISO_ROCK_ISUID)
600     result[ 3] = (st_mode & ISO_ROCK_IXUSR) ? 's' : 'S';
601   else
602     result[ 3] = (st_mode & ISO_ROCK_IXUSR) ? 'x' : '-';
603 
604   result[ 4] = (st_mode & ISO_ROCK_IRGRP) ? 'r' : '-';
605   result[ 5] = (st_mode & ISO_ROCK_IWGRP) ? 'w' : '-';
606 
607   if (st_mode & ISO_ROCK_ISGID)
608     result[ 6] = (st_mode & ISO_ROCK_IXGRP) ? 's' : 'S';
609   else
610     result[ 6] = (st_mode & ISO_ROCK_IXGRP) ? 'x' : '-';
611 
612   result[ 7] = (st_mode & ISO_ROCK_IROTH) ? 'r' : '-';
613   result[ 8] = (st_mode & ISO_ROCK_IWOTH) ? 'w' : '-';
614   result[ 9] = (st_mode & ISO_ROCK_IXOTH) ? 'x' : '-';
615 
616   result[11] = '\0';
617 
618   return result;
619 }
620 
621 /*!
622   Returns POSIX mode bitstring for a given file.
623 */
624 mode_t
iso9660_get_posix_filemode_from_rock(const iso_rock_statbuf_t * rr)625 iso9660_get_posix_filemode_from_rock(const iso_rock_statbuf_t *rr)
626 {
627   return (mode_t) rr->st_mode;
628 }
629