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