Coverage Report

Created: 2025-07-18 07:04

/src/sleuthkit/tsk/fs/iso9660.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
** The Sleuth Kit
3
**
4
** This software is subject to the IBM Public License ver. 1.0,
5
** which was displayed prior to download and is included in the readme.txt
6
** file accompanying the Sleuth Kit files.  It may also be requested from:
7
** Crucial Security Inc.
8
** 14900 Conference Center Drive
9
** Chantilly, VA 20151
10
**
11
** Wyatt Banks [wbanks@crucialsecurity.com]
12
** Copyright (c) 2005 Crucial Security Inc.  All rights reserved.
13
**
14
** Brian Carrier [carrier <at> sleuthkit [dot] org]
15
** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
16
** Copyright (c) 2007-2011 Brian Carrier.  All rights reserved
17
**
18
** Copyright (c) 1997,1998,1999, International Business Machines
19
** Corporation and others. All Rights Reserved.
20
*/
21
/* TCT
22
 * LICENSE
23
 *      This software is distributed under the IBM Public License.
24
 * AUTHOR(S)
25
 *      Wietse Venema
26
 *      IBM T.J. Watson Research
27
 *      P.O. Box 704
28
 *      Yorktown Heights, NY 10598, USA
29
 --*/
30
/*
31
** You may distribute the Sleuth Kit, or other software that incorporates
32
** part of all of the Sleuth Kit, in object code form under a license agreement,
33
** provided that:
34
** a) you comply with the terms and conditions of the IBM Public License
35
**    ver 1.0; and
36
** b) the license agreement
37
**     i) effectively disclaims on behalf of all Contributors all warranties
38
**        and conditions, express and implied, including warranties or
39
**        conditions of title and non-infringement, and implied warranties
40
**        or conditions of merchantability and fitness for a particular
41
**        purpose.
42
**    ii) effectively excludes on behalf of all Contributors liability for
43
**        damages, including direct, indirect, special, incidental and
44
**        consequential damages such as lost profits.
45
**   iii) states that any provisions which differ from IBM Public License
46
**        ver. 1.0 are offered by that Contributor alone and not by any
47
**        other party; and
48
**    iv) states that the source code for the program is available from you,
49
**        and informs licensees how to obtain it in a reasonable manner on or
50
**        through a medium customarily used for software exchange.
51
**
52
** When the Sleuth Kit or other software that incorporates part or all of
53
** the Sleuth Kit is made available in source code form:
54
**     a) it must be made available under IBM Public License ver. 1.0; and
55
**     b) a copy of the IBM Public License ver. 1.0 must be included with
56
**        each copy of the program.
57
*/
58
59
/**
60
 * \file iso9660.c
61
 * Contains the internal TSK ISO9660 file system code to handle basic file
62
 * system processing for opening file system, processing sectors, and directory entries.
63
 */
64
65
#include "tsk_fs_i.h"
66
#include "tsk_iso9660.h"
67
#include <ctype.h>
68
69
#include <memory>
70
71
/* free all memory used by inode linked list */
72
static void
73
iso9660_inode_list_free(TSK_FS_INFO * fs)
74
0
{
75
0
    ISO_INFO *iso = (ISO_INFO *) fs;
76
77
0
    while (iso->in_list) {
78
0
        iso9660_inode_node *tmp = iso->in_list;
79
0
        iso->in_list = iso->in_list->next;
80
0
        free(tmp);
81
0
    }
82
0
    iso->in_list = NULL;
83
0
}
84
85
86
/**
87
 * Process the System Use Sharing Protocol (SUSP) data.  Typically,
88
 * rockridge data are stored in this.
89
 *
90
 * @param fs File system to process
91
 * @param buf Buffer of data to process
92
 * @param count Length of buffer in bytes.
93
 * @param hFile File handle to print details to  (or NULL for no printing)
94
 * @param recursion_depth Recursion depth to limit the number of self-calls
95
 * @returns NULL on error
96
 */
97
static rockridge_ext *
98
parse_susp(TSK_FS_INFO * fs, char *buf, int count, FILE * hFile, int recursion_depth)
99
0
{
100
0
    rockridge_ext *rr;
101
0
    ISO_INFO *iso = (ISO_INFO *) fs;
102
103
0
    char *end = buf + count - 1;
104
105
0
    if (tsk_verbose)
106
0
        tsk_fprintf(stderr, "parse_susp: count is: %d\n", count);
107
108
    // 32 is an arbitrary chosen value.
109
0
    if (recursion_depth > 32) {
110
0
        return NULL;
111
0
    }
112
113
    // allocate the output data structure
114
0
    rr = (rockridge_ext *) tsk_malloc(sizeof(rockridge_ext));
115
0
    if (rr == NULL) {
116
0
        return NULL;
117
0
    }
118
119
0
    while ((uintptr_t)buf + sizeof(iso9660_susp_head) <= (uintptr_t)end) {
120
0
        iso9660_susp_head *head = (iso9660_susp_head *) buf;
121
122
0
        if (buf + head->len - 1 > end)
123
0
            break;
124
125
        /* Identify the entry type -- listed in the order
126
         * that they are listed in the specs */
127
128
        // SUSP Continuation Entry
129
0
        if ((head->sig[0] == 'C') && (head->sig[1] == 'E')) {
130
0
            iso9660_susp_ce *ce = (iso9660_susp_ce *) buf;
131
132
0
            if ((uintptr_t)buf + sizeof(iso9660_susp_ce) - 1 > (uintptr_t)end) {
133
0
                if (tsk_verbose)
134
0
                    tsk_fprintf(stderr, "parse_susp: not enough room for CE structure\n");
135
0
                break;
136
0
            }
137
138
0
            if (hFile) {
139
0
                fprintf(hFile, "CE Entry\n");
140
0
                fprintf(hFile, "* Block: %" PRIu32 "\n",
141
0
                    tsk_getu32(fs->endian, ce->blk_m));
142
0
                fprintf(hFile, "* Offset: %" PRIu32 "\n",
143
0
                    tsk_getu32(fs->endian, ce->offset_m));
144
0
                fprintf(hFile, "* Len: %" PRIu32 "\n",
145
0
                    tsk_getu32(fs->endian, ce->celen_m));
146
0
            }
147
148
            // read the continued buffer and parse it
149
0
            if ((tsk_getu32(fs->endian, ce->blk_m) < fs->last_block) &&
150
0
                (tsk_getu32(fs->endian, ce->offset_m) < fs->block_size)) {
151
0
                TSK_OFF_T off;
152
0
                char *buf2;
153
154
0
                off =
155
0
                    tsk_getu32(fs->endian,
156
0
                    ce->blk_m) * fs->block_size + tsk_getu32(fs->endian,
157
0
                    ce->offset_m);
158
0
                buf2 =
159
0
                    (char *) tsk_malloc(tsk_getu32(fs->endian,
160
0
                        ce->celen_m));
161
162
0
                if (buf2 != NULL) {
163
0
                    ssize_t cnt =
164
0
                        tsk_fs_read(fs, off, buf2,
165
0
                        tsk_getu32(fs->endian, ce->celen_m));
166
167
0
                    if (cnt == tsk_getu32(fs->endian, ce->celen_m)) {
168
0
                        rockridge_ext *rr_sub_entry = parse_susp(fs, buf2, (int) cnt, hFile, recursion_depth + 1);
169
170
                        // Prevent an infinite loop
171
0
                        if (rr_sub_entry == NULL) {
172
0
                          free(buf2);
173
0
                          free(rr);
174
0
                          return NULL;
175
0
      }
176
0
                        free(rr_sub_entry);
177
0
                    }
178
0
                    else if (tsk_verbose) {
179
0
                        fprintf(stderr,
180
0
                            "parse_susp: error reading CE entry\n");
181
0
                        tsk_error_print(stderr);
182
0
                        tsk_error_reset();
183
0
                    }
184
0
                    free(buf2);
185
0
                }
186
0
                else {
187
0
                    if (tsk_verbose)
188
0
                        fprintf(stderr,
189
0
                            "parse_susp: error allocating memory to process CE entry\n");
190
0
                    tsk_error_reset();
191
0
                }
192
0
            }
193
0
            else {
194
0
                if (tsk_verbose)
195
0
                    fprintf(stderr,
196
0
                        "parse_susp: CE offset or block too large to process\n");
197
0
            }
198
199
0
            buf += head->len;
200
0
        }
201
        // SUSP Padding Entry
202
0
        else if ((head->sig[0] == 'P') && (head->sig[1] == 'D')) {
203
0
            if (hFile) {
204
0
                fprintf(hFile, "PD Entry\n");
205
0
            }
206
0
            buf += head->len;
207
0
        }
208
        // SUSP Sharing Protocol Entry -- we ignore
209
0
        else if ((head->sig[0] == 'S') && (head->sig[1] == 'P')) {
210
0
            iso9660_susp_sp *sp = (iso9660_susp_sp *) buf;
211
0
            if (hFile) {
212
0
                fprintf(hFile, "SP Entry\n");
213
0
                fprintf(hFile, "* SKip Len: %d\n", sp->skip);
214
0
            }
215
0
            buf += head->len;
216
0
        }
217
        // SUSP System Terminator
218
0
        else if ((head->sig[0] == 'S') && (head->sig[1] == 'T')) {
219
0
            if (hFile) {
220
0
                fprintf(hFile, "ST Entry\n");
221
0
            }
222
0
            buf += head->len;
223
0
        }
224
        // SUSP Extension Registration -- not used
225
0
        else if ((head->sig[0] == 'E') && (head->sig[1] == 'R')) {
226
0
            iso9660_susp_er *er = (iso9660_susp_er *) buf;
227
0
            if (hFile) {
228
0
                char buf[258];
229
0
                fprintf(hFile, "ER Entry\n");
230
231
0
                memcpy(buf, er->ext_id, er->len_id);
232
0
                buf[er->len_id] = '\0';
233
0
                fprintf(hFile, "* Extension ID: %s\n", buf);
234
235
0
                memcpy(buf, er->ext_id + er->len_id, er->len_des);
236
0
                buf[er->len_des] = '\0';
237
0
                fprintf(hFile, "* Extension Descriptor: %s\n", buf);
238
239
0
                memcpy(buf, er->ext_id + er->len_id + er->len_des,
240
0
                    er->len_src);
241
0
                buf[er->len_src] = '\0';
242
0
                fprintf(hFile, "* Extension Spec Source: %s\n", buf);
243
0
            }
244
0
            buf += head->len;
245
0
        }
246
        // SUSP Extension Sigs  -- not used
247
0
        else if ((head->sig[0] == 'E') && (head->sig[1] == 'S')) {
248
0
            if (hFile) {
249
0
                fprintf(hFile, "ES Entry\n");
250
0
            }
251
0
            buf += head->len;
252
0
        }
253
254
        /*
255
         * Rock Ridge Extensions
256
         */
257
258
        /* POSIX file attributes */
259
0
        else if ((head->sig[0] == 'P') && (head->sig[1] == 'X')) {
260
0
            iso9660_rr_px_entry *rr_px;
261
262
0
            if ((uintptr_t)buf + sizeof(iso9660_rr_px_entry) - 1> (uintptr_t)end) {
263
0
                if (tsk_verbose)
264
0
                    tsk_fprintf(stderr, "parse_susp: not enough room for POSIX structure\n");
265
0
                break;
266
0
            }
267
268
0
            rr_px = (iso9660_rr_px_entry *) buf;
269
0
            rr->uid = tsk_getu32(fs->endian, rr_px->uid_m);
270
0
            rr->gid = tsk_getu32(fs->endian, rr_px->gid_m);
271
0
            rr->mode = tsk_getu16(fs->endian, rr_px->mode_m);
272
0
            rr->nlink = tsk_getu32(fs->endian, rr_px->links_m);
273
0
            if (hFile) {
274
0
                fprintf(hFile, "PX Entry\n");
275
0
                fprintf(hFile, "* UID: %" PRIuUID "\n", rr->uid);
276
0
                fprintf(hFile, "* GID: %" PRIuGID "\n", rr->gid);
277
0
                fprintf(hFile, "* Mode: %d\n", rr->mode);
278
0
                fprintf(hFile, "* Links: %" PRIu32 "\n", rr->nlink);
279
0
            }
280
0
            buf += head->len;
281
0
        }
282
283
        // RR - device information
284
0
        else if ((head->sig[0] == 'P') && (head->sig[1] == 'N')) {
285
0
            iso9660_rr_pn_entry *rr_pn = (iso9660_rr_pn_entry *) buf;
286
0
            if (hFile) {
287
0
                fprintf(hFile, "PN Entry\n");
288
0
                fprintf(hFile, "* Device ID High: %" PRIu32 "\n",
289
0
                    tsk_getu32(fs->endian, rr_pn->dev_h_m));
290
0
                fprintf(hFile, "* Device ID Low: %" PRIu32 "\n",
291
0
                    tsk_getu32(fs->endian, rr_pn->dev_l_m));
292
0
            }
293
0
            buf += head->len;
294
0
        }
295
296
        // RR - symbolic link
297
0
        else if ((head->sig[0] == 'S') && (head->sig[1] == 'L')) {
298
            //iso9660_rr_sl_entry *rr_sl = (iso9660_rr_sl_entry *) buf;
299
0
            if (hFile) {
300
0
                fprintf(hFile, "SL Entry\n");
301
0
            }
302
0
            buf += head->len;
303
0
        }
304
        // RR -- alternative name
305
0
        else if ((head->sig[0] == 'N') && (head->sig[1] == 'M')) {
306
0
            iso9660_rr_nm_entry *rr_nm;
307
0
            int len;
308
309
0
            if ((uintptr_t)buf + sizeof(iso9660_rr_nm_entry) - 1> (uintptr_t)end) {
310
0
                if (tsk_verbose)
311
0
                    tsk_fprintf(stderr, "parse_susp: not enough room for RR alternative name structure\n");
312
0
                break;
313
0
            }
314
315
0
            rr_nm = (iso9660_rr_nm_entry *) buf;
316
317
0
            if ((rr_nm->len < 6) || ((uintptr_t)&rr_nm->name[0] + (int) rr_nm->len - 5 - 1> (uintptr_t)end)) {
318
0
                if (tsk_verbose)
319
0
                    tsk_fprintf(stderr, "parse_susp: not enough room for RR alternative name\n");
320
0
                break;
321
0
            }
322
323
            // Make sure the name will fit in the buffer
324
0
            len = rr_nm->len - 5;
325
0
            if (len >= ISO9660_MAXNAMLEN_RR) {
326
0
                len = ISO9660_MAXNAMLEN_RR - 1;
327
0
            }
328
329
0
            strncpy(rr->fn, &rr_nm->name[0], len);
330
0
            rr->fn[len] = '\0';
331
0
            if (hFile) {
332
0
                fprintf(hFile, "NM Entry\n");
333
0
                fprintf(hFile, "* %s\n", rr->fn);
334
0
            }
335
0
            buf += head->len;
336
0
        }
337
        // RR - relocated directory
338
0
        else if ((head->sig[0] == 'C') && (head->sig[1] == 'L')) {
339
0
            if (hFile) {
340
0
                fprintf(hFile, "CL Entry\n");
341
0
            }
342
0
            buf += head->len;
343
0
        }
344
        // RR - parent of relocated directory
345
0
        else if ((head->sig[0] == 'P') && (head->sig[1] == 'L')) {
346
0
            if (hFile) {
347
0
                fprintf(hFile, "PL Entry\n");
348
0
            }
349
0
            buf += head->len;
350
0
        }
351
        // RR - relocation signal
352
0
        else if ((head->sig[0] == 'R') && (head->sig[1] == 'E')) {
353
0
            if (hFile) {
354
0
                fprintf(hFile, "RE Entry\n");
355
0
            }
356
0
            buf += head->len;
357
0
        }
358
        // RR - time stamps
359
0
        else if ((head->sig[0] == 'T') && (head->sig[1] == 'F')) {
360
0
            if (hFile) {
361
0
                fprintf(hFile, "TF Entry\n");
362
0
            }
363
0
            buf += head->len;
364
0
        }
365
        // RR - sparse file
366
0
        else if ((head->sig[0] == 'S') && (head->sig[1] == 'F')) {
367
0
            if (hFile) {
368
0
                fprintf(hFile, "SF Entry\n");
369
0
            }
370
0
            buf += head->len;
371
0
        }
372
373
        /* RR is a system use field indicating RockRidge, but not part of RockRidge */
374
0
        else if ((head->sig[0] == 'R') && (head->sig[1] == 'R')) {
375
0
            iso->rr_found = 1;
376
0
            if (hFile) {
377
0
                fprintf(hFile, "RR Entry\n");
378
0
            }
379
0
            buf += head->len;
380
0
        }
381
382
0
        else {
383
0
            buf += 2;
384
0
            if ((uintptr_t) buf % 2)
385
0
                buf--;
386
0
        }
387
0
    }
388
389
0
    return rr;
390
0
}
391
392
393
///////////////////////////////////////////////////////////////////////////
394
// The following functions are responsible for loading all of the file metadata into memory.
395
// The process is that the Path table is processed first.  It contains an entry for each
396
// directory.  That info is then used to locate the directory contents and those contents
397
// are then processed.
398
//
399
// Files do not have a corresponding metadata entry, so we assign them based
400
// on the order that they are loaded.
401
///////////////////////////////////////////////////////////////////////////
402
403
404
/* XXX Instead of loading all of the file metadata, we could instead save a mapping
405
 * between inode number and the byte offset of the metadata (and any other data
406
 * needed for fast lookups).
407
 */
408
409
/** \internal
410
 * Process the contents of a directory and load the
411
 * information about files in that directory into ISO_INFO.  This is called
412
 * by the methods that process the path table (which contains pointers to the
413
 * various directories).  The results in ISO_INFO are used to identify the
414
 * inode address of files found from dent_walk and for file lookups.
415
 *
416
 * Type: ISO9660_TYPE_PVD for primary volume descriptor, ISO9660_TYPE_SVD for
417
 * supplementary volume descriptor (do Joliet utf-8 conversion).
418
 *
419
 * @param fs File system to analyze
420
 * @param a_offs Byte offset of directory start
421
 * @param count previous file count
422
 * @param ctype Character set used for the names
423
 * @param a_fn Name of the directory to use for the "." entry (in UTF-8)
424
 *
425
 * @returns total number of files or -1 on error
426
 */
427
static int
428
iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count,
429
    int ctype, const char *a_fn, uint8_t is_first)
430
0
{
431
0
    ISO_INFO *iso = (ISO_INFO *) fs;
432
0
    int s_cnt = 1;              // count of sectors needed for dir
433
0
    TSK_OFF_T s_offs = a_offs;  // offset for sector reads
434
0
    int i;
435
436
0
    if (tsk_verbose)
437
0
        tsk_fprintf(stderr,
438
0
            "iso9660_load_inodes_dir: offs: %" PRIdOFF
439
0
            " count: %d ctype: %d fn: %s\n", a_offs, count, ctype, a_fn);
440
441
    // cycle through each sector -- entries will not cross them
442
0
    for (i = 0; i < s_cnt; i++) {
443
0
        ssize_t cnt1;
444
0
        unsigned int b_offs;             // offset in buffer
445
0
        char buf[ISO9660_SSIZE_B];
446
447
0
        cnt1 = tsk_fs_read(fs, s_offs, buf, ISO9660_SSIZE_B);
448
0
        if (cnt1 != ISO9660_SSIZE_B) {
449
0
            if (cnt1 >= 0) {
450
0
                tsk_error_reset();
451
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
452
0
            }
453
0
            tsk_error_set_errstr2("iso_get_dentries");
454
0
            return -1;
455
0
        }
456
457
        /* process the directory entries */
458
0
        for (b_offs = 0; b_offs < ISO9660_SSIZE_B;) {
459
0
            iso9660_inode_node *in_node = NULL;
460
0
            iso9660_dentry *dentry;
461
462
0
            dentry = (iso9660_dentry *) & buf[b_offs];
463
464
0
            if (dentry->entry_len == 0) {
465
0
                b_offs += 2;
466
0
                continue;
467
0
            }
468
            // sanity checks on entry_len
469
0
            else if (dentry->entry_len < sizeof(iso9660_dentry)) {
470
0
                if (tsk_verbose)
471
0
                    tsk_fprintf(stderr,
472
0
                                "iso9660_load_inodes_dir: entry length is shorter than dentry, bailing\n");
473
0
                break;
474
0
            }
475
0
            else if (b_offs + dentry->entry_len > ISO9660_SSIZE_B) {
476
0
                if (tsk_verbose)
477
0
                    tsk_fprintf(stderr,
478
0
                                "iso9660_load_inodes_dir: entry is longer than sector, bailing\n");
479
0
                break;
480
0
            }
481
482
            /* when processing the other volume descriptor directories, we ignore the
483
             * directories because we have no way of detecting if it is a duplicate of
484
             * a directory from the other volume descriptor (they use different blocks).
485
             * We will see the contents of this directory from the path table anyway. */
486
0
            if ((dentry->flags & ISO9660_FLAG_DIR) && (is_first == 0)) {
487
0
                b_offs += dentry->entry_len;
488
0
                continue;
489
0
            }
490
491
            // allocate a node for this entry
492
0
            in_node = (iso9660_inode_node *)
493
0
                tsk_malloc(sizeof(iso9660_inode_node));
494
0
            if (in_node == NULL) {
495
0
                return -1;
496
0
            }
497
498
            // the first entry is for the current directory
499
0
            if ((i == 0) && (b_offs == 0)) {
500
                // should have no name or '.'
501
0
                if (dentry->fi_len > 1) {
502
0
                    if (tsk_verbose)
503
0
                        tsk_fprintf(stderr,
504
0
                                    "iso9660_load_inodes_dir: first entry has name length > 1\n");
505
0
                    free(in_node);
506
0
                    in_node = NULL;
507
0
                    b_offs += dentry->entry_len;
508
0
                    continue;
509
0
                }
510
511
                /* find how many more sectors are in the directory */
512
0
                s_cnt =
513
0
                    tsk_getu32(fs->endian,
514
0
                    dentry->data_len_m) / ISO9660_SSIZE_B;
515
0
                if (tsk_verbose)
516
0
                    tsk_fprintf(stderr, "iso9660_load_inodes_dir: %d number of additional sectors\n", s_cnt);
517
518
                // @@@ Should have a sanity check here on s_cnt, but I'm not sure what it would be...
519
520
                /* use the specified name instead of "." */
521
0
                if (strlen(a_fn) > ISO9660_MAXNAMLEN_STD) {
522
0
                    tsk_error_reset();
523
0
                    tsk_error_set_errno(TSK_ERR_FS_ARG);
524
0
                    tsk_error_set_errstr
525
0
                        ("iso9660_load_inodes_dir: Name argument specified is too long");
526
0
                    free(in_node);
527
0
                    return -1;
528
0
                }
529
0
                strncpy(in_node->inode.fn, a_fn, ISO9660_MAXNAMLEN_STD + 1);
530
531
                /* for all directories except the root, we skip processing the "." and ".." entries because
532
                 * they duplicate the other entires and the dent_walk code will rely on the offset
533
                 * for the entry in the parent directory. */
534
0
                if (count != 0) {
535
0
                    free(in_node);
536
0
                    in_node = NULL;
537
0
                    b_offs += dentry->entry_len;
538
0
                    dentry = (iso9660_dentry *) & buf[b_offs];
539
0
                    b_offs += dentry->entry_len;
540
0
                    continue;
541
0
                }
542
0
            }
543
0
            else {
544
0
                char *file_ver;
545
546
                // the entry has a UTF-16 name
547
0
                if (ctype == ISO9660_CTYPE_UTF16) {
548
0
                    UTF16 *name16;
549
0
                    UTF8 *name8;
550
0
                    int retVal;
551
552
0
                    if (dentry->entry_len < sizeof(iso9660_dentry) + dentry->fi_len) {
553
0
                        if (tsk_verbose)
554
0
                            tsk_fprintf(stderr,
555
0
                                        "iso9660_load_inodes_dir: UTF-16 name length is too large, bailing\n");
556
0
                        free(in_node);
557
0
                        in_node = NULL;
558
0
                        break;
559
0
                    }
560
0
                    if (b_offs >= ISO9660_SSIZE_B - sizeof(iso9660_dentry)) {
561
0
                        if (tsk_verbose)
562
0
                            tsk_fprintf(stderr,
563
0
                                        "iso9660_load_inodes_dir: b_offs out of bounds, bailing\n");
564
0
                        free(in_node);
565
0
                        in_node = NULL;
566
0
                        break;
567
0
                    }
568
569
570
0
                    name16 =
571
0
                        (UTF16 *) & buf[b_offs + sizeof(iso9660_dentry)];
572
                    // the name is in UTF-16 BE -- convert to LE if needed
573
0
                    if (fs->endian & TSK_LIT_ENDIAN) {
574
0
                        int a;
575
576
0
                        for (a = 0; a < dentry->fi_len / 2; a++) {
577
0
                            name16[a] = ((name16[a] & 0xff) << 8) +
578
0
                                ((name16[a] & 0xff00) >> 8);
579
0
                        }
580
0
                    }
581
0
                    name8 = (UTF8 *) in_node->inode.fn;
582
583
0
                    if ((dentry->fi_len % 2) != 0 || dentry->fi_len > ISO9660_SSIZE_B - sizeof(iso9660_dentry) - b_offs) {
584
0
                        if (tsk_verbose)
585
0
                            tsk_fprintf(stderr,
586
0
                                        "iso9660_load_inodes_dir: UTF-16 name length out of bounds, bailing\n");
587
0
                        free(in_node);
588
0
                        in_node = NULL;
589
0
                        break;
590
0
                    }
591
0
                    retVal =
592
0
                        tsk_UTF16toUTF8(fs->endian,
593
0
                        (const UTF16 **) &name16, (UTF16 *) & buf[b_offs + sizeof(iso9660_dentry) + dentry->fi_len],
594
0
                        &name8, (UTF8 *) ((uintptr_t) & in_node->inode.fn[ISO9660_MAXNAMLEN_STD]),
595
0
                        TSKlenientConversion);
596
0
                    if (retVal != TSKconversionOK) {
597
0
                        if (tsk_verbose)
598
0
                            tsk_fprintf(stderr,
599
0
                                "iso9660_load_inodes_dir: Error converting Joliet name to UTF8: %d",
600
0
                                retVal);
601
0
                        in_node->inode.fn[0] = '\0';
602
0
                    }
603
0
                    *name8 = '\0';
604
0
                }
605
606
0
                else if (ctype == ISO9660_CTYPE_ASCII) {
607
0
                    int readlen;
608
609
0
                    readlen = dentry->fi_len;
610
0
                    if (readlen > ISO9660_MAXNAMLEN_STD)
611
0
                        readlen = ISO9660_MAXNAMLEN_STD;
612
613
0
                    if (dentry->entry_len < sizeof(iso9660_dentry) + dentry->fi_len) {
614
0
                        if (tsk_verbose)
615
0
                            tsk_fprintf(stderr,
616
0
                                        "iso9660_load_inodes_dir: ASCII name length is too large, bailing\n");
617
0
                        free(in_node);
618
0
                        in_node = NULL;
619
0
                        break;
620
0
                    }
621
622
623
0
                    memcpy(in_node->inode.fn,
624
0
                        &buf[b_offs + sizeof(iso9660_dentry)], readlen);
625
0
                    in_node->inode.fn[readlen] = '\0';
626
0
                }
627
0
                else {
628
0
                    tsk_error_reset();
629
0
                    tsk_error_set_errno(TSK_ERR_FS_ARG);
630
0
                    tsk_error_set_errstr
631
0
                        ("Invalid ctype in iso9660_load_inodes_dir");
632
0
                    free(in_node);
633
0
                    in_node = NULL;
634
0
                    return -1;
635
0
                }
636
637
                // the version is embedded in the name
638
0
                file_ver = strchr(in_node->inode.fn, ';');
639
0
                if (file_ver) {
640
0
                    in_node->inode.version = atoi(file_ver + 1);
641
0
                    *file_ver = '\0';
642
0
                    file_ver = NULL;
643
0
                }
644
645
0
                size_t name8_len = strnlen(in_node->inode.fn, ISO9660_MAXNAMLEN);
646
0
                if (name8_len > 0 && in_node->inode.fn[name8_len - 1] == '.') {
647
                    // if no extension, remove the final '.'
648
0
                    in_node->inode.fn[name8_len - 1] = '\0';
649
0
                    name8_len -= 1;
650
0
                }
651
0
                if (name8_len == 0) {
652
0
                    if (tsk_verbose)
653
0
                        tsk_fprintf(stderr,
654
0
                                    "iso9660_load_inodes_dir: length of name after processing is 0. bailing\n");
655
0
                    free(in_node);
656
0
                    in_node = NULL;
657
0
                    break;
658
659
0
                }
660
0
            }
661
662
663
664
            // copy the raw dentry data into the node
665
0
            memcpy(&(in_node->inode.dr), dentry, sizeof(iso9660_dentry));
666
667
0
            in_node->inode.ea = NULL;
668
669
            // sanity checks
670
0
            if (tsk_getu32(fs->endian, dentry->ext_loc_m) > fs->last_block) {
671
0
                if (tsk_verbose)
672
0
                    tsk_fprintf(stderr,
673
0
                                "iso9660_load_inodes_dir: file starts past end of image (%" PRIu32 "). bailing\n",
674
0
                                tsk_getu32(fs->endian, dentry->ext_loc_m));
675
0
                free(in_node);
676
0
                in_node = NULL;
677
0
                break;
678
0
            }
679
0
            in_node->offset =
680
0
                tsk_getu32(fs->endian, dentry->ext_loc_m) * fs->block_size;
681
682
0
            if (tsk_getu32(fs->endian, in_node->inode.dr.data_len_m) + in_node->offset > (TSK_OFF_T)(fs->block_count * fs->block_size)) {
683
0
                if (tsk_verbose)
684
0
                    tsk_fprintf(stderr,
685
0
                                "iso9660_load_inodes_dir: file ends past end of image (%" PRIu32 " bytes). bailing\n",
686
0
                                tsk_getu32(fs->endian, in_node->inode.dr.data_len_m) + in_node->offset);
687
0
                free(in_node);
688
0
                in_node = NULL;
689
0
                break;
690
0
            }
691
            /* record size to make sure fifos show up as unique files */
692
0
            in_node->size =
693
0
                tsk_getu32(fs->endian, in_node->inode.dr.data_len_m);
694
695
696
0
            in_node->ea_size = dentry->ext_len;
697
0
            in_node->dentry_offset = s_offs + b_offs;
698
699
0
            if (is_first)
700
0
                in_node->inode.is_orphan = 0;
701
0
            else
702
0
                in_node->inode.is_orphan = 1;
703
704
0
            in_node->inum = count++;
705
706
            /* RockRidge data is located after the name.  See if it is there.  */
707
0
            if ((int) (dentry->entry_len - sizeof(iso9660_dentry) -
708
0
                    dentry->fi_len) > 1) {
709
0
                int extra_bytes =
710
0
                    dentry->entry_len - sizeof(iso9660_dentry) -
711
0
                    dentry->fi_len;
712
713
0
                in_node->inode.rr =
714
0
                    parse_susp(fs,
715
0
                    &buf[b_offs + sizeof(iso9660_dentry) + dentry->fi_len],
716
0
                    extra_bytes, NULL, 0);
717
0
                if (in_node->inode.rr == NULL) {
718
0
                    if (tsk_verbose)
719
0
                        tsk_fprintf(stderr,
720
0
                                    "iso9660_load_inodes_dir: parse_susp returned error (%s). bailing\n", tsk_error_get());
721
0
                    free(in_node);
722
0
                    in_node = NULL;
723
0
                    break;
724
0
                }
725
726
0
                in_node->inode.susp_off =
727
0
                    b_offs + sizeof(iso9660_dentry) + dentry->fi_len +
728
0
                    s_offs;
729
0
                in_node->inode.susp_len = extra_bytes;
730
0
            }
731
0
            else {
732
0
                in_node->inode.rr = NULL;
733
0
                in_node->inode.susp_off = 0;
734
0
                in_node->inode.susp_len = 0;
735
0
            }
736
737
            /* add inode to the list */
738
0
            if (iso->in_list) {
739
0
                iso9660_inode_node *tmp, *prev_tmp;
740
741
0
                for (tmp = iso->in_list; tmp; tmp = tmp->next) {
742
                    /* When processing the "first" volume descriptor, all entries get added to the list.
743
                     * for the later ones, we skip duplicate ones that have content (blocks) that overlaps
744
                     * with entries from a previous volume descriptor. */
745
0
                    if ((in_node->offset == tmp->offset)
746
0
                        && (in_node->size == tmp->size)
747
0
                        && (in_node->size) && (is_first == 0)) {
748
749
                        // if we found rockridge, then update original if needed.
750
0
                        if (in_node->inode.rr) {
751
0
                            if (tmp->inode.rr == NULL) {
752
0
                                tmp->inode.rr = in_node->inode.rr;
753
0
                                tmp->inode.susp_off =
754
0
                                    in_node->inode.susp_off;
755
0
                                tmp->inode.susp_len =
756
0
                                    in_node->inode.susp_len;
757
0
                                in_node->inode.rr = NULL;
758
0
                            }
759
0
                            else {
760
0
                                free(in_node->inode.rr);
761
0
                                in_node->inode.rr = NULL;
762
0
                            }
763
0
                        }
764
765
0
                        if (tsk_verbose)
766
0
                            tsk_fprintf(stderr,
767
0
                                "iso9660_load_inodes_dir: Removing duplicate entry for: %s (orig name: %s start: %d size: %d)\n",
768
0
                                in_node->inode.fn, tmp->inode.fn, in_node->offset, in_node->size);
769
0
                        free(in_node);
770
0
                        in_node = NULL;
771
0
                        count--;
772
0
                        break;
773
0
                    }
774
0
                    prev_tmp = tmp;
775
0
                }
776
777
                // add it to the end (if we didn't get rid of it above)
778
0
                if (in_node) {
779
0
                    prev_tmp->next = in_node;
780
0
                    in_node->next = NULL;
781
0
                }
782
0
            }
783
0
            else {
784
0
                iso->in_list = in_node;
785
0
                in_node->next = NULL;
786
0
            }
787
788
            // skip two entries if this was the root directory (the . and ..).
789
0
            if ((i == 0) && (b_offs == 0) && (count == 1)) {
790
0
                b_offs += dentry->entry_len;
791
0
                dentry = (iso9660_dentry *) & buf[b_offs];
792
0
            }
793
0
            b_offs += dentry->entry_len;
794
0
        }
795
0
        s_offs += cnt1;
796
0
    }
797
0
    return count;
798
0
}
799
800
801
/**
802
 * Process the path table for a joliet secondary volume descriptor
803
 * and load all of the files pointed to it.
804
 * The path table contains an entry for each directory.  This code
805
 * then locates each of the directories and processes the contents.
806
 *
807
 * @param fs File system to process
808
 * @param svd Pointer to the secondary volume descriptor
809
 * @param count Current count of inodes
810
 * @returns updated count of inodes or -1 on error
811
 */
812
static int
813
iso9660_load_inodes_pt_joliet(TSK_FS_INFO * fs, iso9660_svd * svd,
814
    int count, uint8_t is_first)
815
0
{
816
0
    TSK_OFF_T pt_offs;          /* offset of where we are in path table */
817
0
    size_t pt_len;              /* bytes left in path table */
818
819
    // get the location of the path table
820
0
    pt_offs =
821
0
        (TSK_OFF_T) (tsk_getu32(fs->endian,
822
0
            svd->pt_loc_m) * fs->block_size);
823
0
    pt_len = tsk_getu32(fs->endian, svd->pt_size_m);
824
825
0
    while (pt_len > 0) {
826
        // Since further on cnt + 1 is used and cnt can be ISO9660_MAXNAMLEN_JOL
827
        // + 2 ensures utf16_buf is sufficiently large.
828
0
        char utf16_buf[ISO9660_MAXNAMLEN_JOL + 2];      // UTF-16 name from img
829
0
        char utf8buf[2 * ISO9660_MAXNAMLEN_JOL + 1];    // UTF-8 version of name
830
0
        int readlen;
831
0
        TSK_OFF_T extent;       /* offset of extent for current directory */
832
0
        path_table_rec dir;
833
0
        int retVal;
834
0
        ssize_t cnt;
835
836
0
        UTF16 *name16;
837
0
        UTF8 *name8;
838
839
        // Read the path table entry
840
0
        cnt = tsk_fs_read(fs, pt_offs, (char *) &dir, (int) sizeof(dir));
841
0
        if (cnt != sizeof(dir)) {
842
0
            if (cnt >= 0) {
843
0
                tsk_error_reset();
844
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
845
0
            }
846
0
            tsk_error_set_errstr2("iso9660_load_inodes_pt");
847
0
            return -1;
848
0
        }
849
0
        pt_len -= cnt;
850
0
        pt_offs += (TSK_OFF_T) cnt;
851
852
0
        readlen = dir.len_di;
853
0
        if (dir.len_di > ISO9660_MAXNAMLEN_JOL)
854
0
            readlen = ISO9660_MAXNAMLEN_JOL;
855
856
0
        memset(utf16_buf, 0, ISO9660_MAXNAMLEN_JOL + 2);
857
        /* get UCS-2 filename for the entry */
858
0
        cnt = tsk_fs_read(fs, pt_offs, (char *) utf16_buf, readlen);
859
0
        if (cnt != dir.len_di) {
860
0
            if (cnt >= 0) {
861
0
                tsk_error_reset();
862
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
863
0
            }
864
0
            tsk_error_set_errstr2("iso_find_inodes");
865
0
            return -1;
866
0
        }
867
0
        pt_len -= cnt;
868
0
        pt_offs += (TSK_OFF_T) cnt;
869
870
        // ISO stores UTF-16 in BE -- convert to local if we need to
871
0
        if (fs->endian & TSK_LIT_ENDIAN) {
872
0
            int i;
873
0
            for (i = 0; i < cnt; i += 2) {
874
0
                char t = utf16_buf[i];
875
0
                utf16_buf[i] = utf16_buf[i + 1];
876
0
                utf16_buf[i] = t;
877
0
            }
878
0
        }
879
880
0
        name16 = (UTF16 *) utf16_buf;
881
0
        name8 = (UTF8 *) utf8buf;
882
883
0
        retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16,
884
0
            (UTF16 *) ((uintptr_t) & utf16_buf[cnt + 1]), &name8,
885
0
            (UTF8 *) ((uintptr_t) & utf8buf[2 * ISO9660_MAXNAMLEN_JOL]),
886
0
            TSKlenientConversion);
887
0
        if (retVal != TSKconversionOK) {
888
0
            if (tsk_verbose)
889
0
                tsk_fprintf(stderr,
890
0
                    "fsstat: Error converting Joliet name to UTF8: %d",
891
0
                    retVal);
892
0
            utf8buf[0] = '\0';
893
0
        }
894
0
        *name8 = '\0';
895
896
        /* padding byte is there if strlen(file name) is odd */
897
0
        if (dir.len_di % 2) {
898
0
            pt_len--;
899
0
            pt_offs++;
900
0
        }
901
902
0
        extent =
903
0
            (TSK_OFF_T) (tsk_getu32(fs->endian,
904
0
                dir.ext_loc) * fs->block_size);
905
906
        // process the directory contents
907
0
        count =
908
0
            iso9660_load_inodes_dir(fs, extent, count,
909
0
            ISO9660_CTYPE_UTF16, utf8buf, is_first);
910
911
0
        if (count == -1) {
912
0
            return -1;
913
0
        }
914
0
    }
915
0
    return count;
916
0
}
917
918
/**
919
 * Proces the path table and the directories that are listed in it.
920
 * The files in each directory will be stored in ISO_INFO.
921
 *
922
 * @param iso File system to analyze and store results in
923
 * @returns -1 on error or count of inodes found.
924
 */
925
static int
926
iso9660_load_inodes_pt(ISO_INFO * iso)
927
0
{
928
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & iso->fs_info;
929
0
    int count = 0;
930
0
    iso9660_svd_node *s;
931
0
    iso9660_pvd_node *p;
932
0
    char fn[ISO9660_MAXNAMLEN_STD + 1]; /* store current directory name */
933
0
    path_table_rec dir;
934
0
    TSK_OFF_T pt_offs;          /* offset of where we are in path table */
935
0
    TSK_OFF_T extent;           /* offset of extent for current directory */
936
0
    ssize_t cnt;
937
0
    uint8_t is_first = 1;
938
939
0
    if (tsk_verbose)
940
0
        tsk_fprintf(stderr, "iso9660_load_inodes_pt\n");
941
942
    /* initialize in case repeatedly called */
943
0
    iso9660_inode_list_free(fs);
944
0
    iso->in_list = NULL;
945
946
    /* The secondary volume descriptor table will contain the
947
     * longer / unicode files, so we process it first to give them
948
     * a higher priority */
949
0
    for (s = iso->svd; s != NULL; s = s->next) {
950
951
        /* Check if this is Joliet -- there are three possible signatures */
952
0
        if ((s->svd.esc_seq[0] == 0x25) && (s->svd.esc_seq[1] == 0x2F) &&
953
0
            ((s->svd.esc_seq[2] == 0x40) || (s->svd.esc_seq[2] == 0x43)
954
0
                || (s->svd.esc_seq[2] == 0x45))) {
955
0
            count =
956
0
                iso9660_load_inodes_pt_joliet(fs, &(s->svd), count,
957
0
                is_first);
958
0
            if (count == -1) {
959
0
                return -1;
960
0
            }
961
0
            is_first = 0;
962
0
        }
963
0
    }
964
965
966
    /* Now look for unique files in the primary descriptors */
967
0
    for (p = iso->pvd; p != NULL; p = p->next) {
968
0
        size_t pt_len;              /* bytes left in path table */
969
970
0
        pt_offs =
971
0
            (TSK_OFF_T) (tsk_getu32(fs->endian,
972
0
                p->pvd.pt_loc_m) * fs->block_size);
973
0
        pt_len = tsk_getu32(fs->endian, p->pvd.pt_size_m);
974
975
0
        while (pt_len > 0) {
976
0
            int readlen;
977
978
            /* get next path table entry... */
979
0
            cnt = tsk_fs_read(fs, pt_offs, (char *) &dir, sizeof(dir));
980
0
            if (cnt != sizeof(dir)) {
981
0
                if (cnt >= 0) {
982
0
                    tsk_error_reset();
983
0
                    tsk_error_set_errno(TSK_ERR_FS_READ);
984
0
                }
985
0
                tsk_error_set_errstr2("iso_find_inodes");
986
0
                return -1;
987
0
            }
988
0
            pt_len -= cnt;
989
0
            pt_offs += (TSK_OFF_T) cnt;
990
991
0
            readlen = dir.len_di;
992
0
            if (readlen > ISO9660_MAXNAMLEN_STD)
993
0
                readlen = ISO9660_MAXNAMLEN_STD;
994
995
            /* get directory name, this is the only chance */
996
0
            cnt = tsk_fs_read(fs, pt_offs, fn, readlen);
997
0
            if (cnt != readlen) {
998
0
                if (cnt >= 0) {
999
0
                    tsk_error_reset();
1000
0
                    tsk_error_set_errno(TSK_ERR_FS_READ);
1001
0
                }
1002
0
                tsk_error_set_errstr2("iso_find_inodes");
1003
0
                return -1;
1004
0
            }
1005
0
            fn[cnt] = '\0';
1006
1007
0
            pt_len -= cnt;
1008
0
            pt_offs += (TSK_OFF_T) cnt;
1009
1010
            /* padding byte is there if strlen(file name) is odd */
1011
0
            if (dir.len_di % 2) {
1012
0
                pt_len--;
1013
0
                pt_offs++;
1014
0
            }
1015
1016
0
            extent =
1017
0
                (TSK_OFF_T) (tsk_getu32(fs->endian,
1018
0
                    dir.ext_loc) * fs->block_size);
1019
1020
            // process the directory contents
1021
0
            count =
1022
0
                iso9660_load_inodes_dir(fs, extent, count,
1023
0
                ISO9660_CTYPE_ASCII, fn, is_first);
1024
1025
0
            if (count == -1) {
1026
0
                return -1;
1027
0
            }
1028
0
        }
1029
0
    }
1030
0
    return count;
1031
0
}
1032
1033
/**
1034
 * Load the raw "inode" into the cached buffer (iso->dinode)
1035
 *
1036
 * dinode_load (for now) does not check for extended attribute records...
1037
 * my issue is I dont have an iso9660 image with extended attr recs, so I
1038
 * can't test/debug, etc
1039
 *
1040
 * @returns 1 if not found and 0 on succuss
1041
 */
1042
uint8_t
1043
iso9660_dinode_load(ISO_INFO * iso, TSK_INUM_T inum,
1044
    iso9660_inode * dinode)
1045
0
{
1046
0
    iso9660_inode_node *n;
1047
1048
0
    n = iso->in_list;
1049
0
    while (n && (n->inum != inum))
1050
0
        n = n->next;
1051
1052
0
    if (n) {
1053
0
        memcpy(dinode, &n->inode, sizeof(iso9660_inode));
1054
0
        return 0;
1055
0
    }
1056
0
    else {
1057
0
        return 1;
1058
0
    }
1059
0
}
1060
1061
1062
static TSK_FS_META_MODE_ENUM
1063
isomode2tskmode(uint16_t a_mode)
1064
0
{
1065
0
    uint16_t mode = 0;
1066
1067
0
    if (a_mode & ISO_EA_IRUSR)
1068
0
        mode |= TSK_FS_META_MODE_IRUSR;
1069
0
    if (a_mode & ISO_EA_IWUSR)
1070
0
        mode |= TSK_FS_META_MODE_IWUSR;
1071
0
    if (a_mode & ISO_EA_IXUSR)
1072
0
        mode |= TSK_FS_META_MODE_IXUSR;
1073
1074
0
    if (a_mode & ISO_EA_IRGRP)
1075
0
        mode |= TSK_FS_META_MODE_IRGRP;
1076
0
    if (a_mode & ISO_EA_IWGRP)
1077
0
        mode |= TSK_FS_META_MODE_IWGRP;
1078
0
    if (a_mode & ISO_EA_IXGRP)
1079
0
        mode |= TSK_FS_META_MODE_IXGRP;
1080
1081
0
    if (a_mode & ISO_EA_IROTH)
1082
0
        mode |= TSK_FS_META_MODE_IROTH;
1083
0
    if (a_mode & ISO_EA_IWOTH)
1084
0
        mode |= TSK_FS_META_MODE_IWOTH;
1085
0
    if (a_mode & ISO_EA_IXOTH)
1086
0
        mode |= TSK_FS_META_MODE_IXOTH;
1087
1088
0
    return (TSK_FS_META_MODE_ENUM) mode;
1089
0
}
1090
1091
/**
1092
 * Copies cached disk inode into generic structure.
1093
 *
1094
 * @returns 1 on error and 0 on success
1095
 */
1096
static uint8_t
1097
iso9660_dinode_copy(ISO_INFO * iso, TSK_FS_META * fs_meta, TSK_INUM_T inum,
1098
    iso9660_inode * dinode)
1099
0
{
1100
0
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & iso->fs_info;
1101
0
    struct tm t;
1102
1103
0
    if (fs_meta == NULL) {
1104
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1105
0
        tsk_error_set_errstr
1106
0
            ("iso9660_dinode_copy: fs_file or meta is NULL");
1107
0
        return 1;
1108
0
    }
1109
1110
0
    fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
1111
0
    if (fs_meta->attr) {
1112
0
        tsk_fs_attrlist_markunused(fs_meta->attr);
1113
0
    }
1114
1115
0
    if (fs_meta->content_len < ISO9660_FILE_CONTENT_LEN) {
1116
0
        if ((fs_meta =
1117
0
                tsk_fs_meta_realloc(fs_meta,
1118
0
                    ISO9660_FILE_CONTENT_LEN)) == NULL) {
1119
0
            return 1;
1120
0
        }
1121
0
    }
1122
1123
0
    fs_meta->addr = inum;
1124
0
    fs_meta->size = tsk_getu32(fs->endian, dinode->dr.data_len_m);
1125
1126
0
    memset(&t, 0, sizeof(struct tm));
1127
0
    t.tm_sec = dinode->dr.rec_time.sec;
1128
0
    t.tm_min = dinode->dr.rec_time.min;
1129
0
    t.tm_hour = dinode->dr.rec_time.hour;
1130
0
    t.tm_mday = dinode->dr.rec_time.day;
1131
0
    t.tm_mon = dinode->dr.rec_time.month - 1;
1132
0
    t.tm_year = dinode->dr.rec_time.year;
1133
    //gmt_hrdiff = iso->dinode->dr.rec_time.gmt_off * 15 / 60;
1134
1135
0
    fs_meta->crtime = mktime(&t);
1136
0
    fs_meta->mtime = fs_meta->atime = fs_meta->ctime = 0;
1137
0
    fs_meta->crtime_nano = fs_meta->mtime_nano = fs_meta->atime_nano =
1138
0
        fs_meta->ctime_nano = 0;
1139
1140
0
    if (dinode->dr.flags & ISO9660_FLAG_DIR)
1141
0
        fs_meta->type = TSK_FS_META_TYPE_DIR;
1142
0
    else
1143
0
        fs_meta->type = TSK_FS_META_TYPE_REG;
1144
1145
0
    if (dinode->ea) {
1146
0
        fs_meta->uid = tsk_getu32(fs->endian, dinode->ea->uid);
1147
0
        fs_meta->gid = tsk_getu32(fs->endian, dinode->ea->gid);
1148
0
        fs_meta->mode =
1149
0
            isomode2tskmode(tsk_getu16(fs->endian, dinode->ea->mode));
1150
0
        fs_meta->nlink = 1;
1151
0
    }
1152
0
    else {
1153
0
        fs_meta->uid = 0;
1154
0
        fs_meta->gid = 0;
1155
0
        fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED;
1156
0
        fs_meta->nlink = 1;
1157
0
    }
1158
1159
0
    ((TSK_DADDR_T *) fs_meta->content_ptr)[0] =
1160
0
        (TSK_DADDR_T) tsk_getu32(fs->endian, dinode->dr.ext_loc_m);
1161
1162
    // mark files that were found from other volume descriptors as unalloc so that they
1163
    // come up as orphan files.
1164
0
    if (dinode->is_orphan) {
1165
0
        fs_meta->flags = (TSK_FS_META_FLAG_ENUM) (TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_USED);
1166
0
    }
1167
0
    else {
1168
0
        fs_meta->flags = (TSK_FS_META_FLAG_ENUM) (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED);
1169
0
    }
1170
0
    return 0;
1171
0
}
1172
1173
static void
1174
iso9660_close(TSK_FS_INFO * fs)
1175
0
{
1176
0
    ISO_INFO *iso = (ISO_INFO *) fs;
1177
1178
0
    fs->tag = 0;
1179
1180
0
    while (iso->pvd != NULL) {
1181
0
        iso9660_pvd_node *p = iso->pvd;
1182
0
        iso->pvd = iso->pvd->next;
1183
0
        free(p);
1184
0
    }
1185
1186
0
    while (iso->svd != NULL) {
1187
0
        iso9660_svd_node *s = iso->svd;
1188
0
        iso->svd = iso->svd->next;
1189
0
        free(s);
1190
0
    }
1191
1192
0
    while (iso->in_list != NULL) {
1193
0
        iso9660_inode_node *in = iso->in_list;
1194
0
        iso->in_list = iso->in_list->next;
1195
0
        if (in->inode.rr != NULL) {
1196
0
            free(in->inode.rr);
1197
0
        }
1198
0
        free(in);
1199
0
    }
1200
1201
0
    tsk_fs_free(fs);
1202
0
}
1203
1204
1205
static uint8_t
1206
iso9660_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
1207
    TSK_INUM_T inum)
1208
0
{
1209
0
    ISO_INFO *iso = (ISO_INFO *) fs;
1210
0
    iso9660_inode *dinode;
1211
1212
0
    if (tsk_verbose)
1213
0
        tsk_fprintf(stderr, "iso9660_inode_lookup: iso:"
1214
0
            " inum: %" PRIuINUM "\n", inum);
1215
1216
0
    if (a_fs_file == NULL) {
1217
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1218
0
        tsk_error_set_errstr("iso9660_inode_lookup: fs_file is NULL");
1219
0
        return 1;
1220
0
    }
1221
1222
0
    if (a_fs_file->meta == NULL) {
1223
0
        if ((a_fs_file->meta =
1224
0
                tsk_fs_meta_alloc(ISO9660_FILE_CONTENT_LEN)) == NULL)
1225
0
            return 1;
1226
0
    }
1227
0
    else {
1228
0
        tsk_fs_meta_reset(a_fs_file->meta);
1229
0
    }
1230
1231
    // see if they are looking for the special "orphans" directory
1232
0
    if (inum == TSK_FS_ORPHANDIR_INUM(fs)) {
1233
0
        if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta)) {
1234
0
            return 1;
1235
0
        }
1236
0
        else {
1237
0
            return 0;
1238
0
        }
1239
0
    }
1240
0
    else {
1241
        /* allocate cache buffers */
1242
        /* dinode */
1243
0
        dinode = (iso9660_inode *) tsk_malloc(sizeof(iso9660_inode));
1244
0
        if (dinode == NULL) {
1245
0
            fs->tag = 0;
1246
0
            iso9660_close(fs);
1247
0
            return 1;
1248
0
        }
1249
1250
        // load the inode into the ISO buffer
1251
0
        if (iso9660_dinode_load(iso, inum, dinode)) {
1252
0
            free(dinode);
1253
0
            return 1;
1254
0
        }
1255
1256
        // copy into the FS_META structure
1257
0
        if (iso9660_dinode_copy(iso, a_fs_file->meta, inum, dinode)) {
1258
0
            free(dinode);
1259
0
            return 1;
1260
0
        }
1261
0
    }
1262
1263
0
    free(dinode);
1264
0
    return 0;
1265
0
}
1266
1267
static uint8_t
1268
iso9660_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start, TSK_INUM_T last,
1269
    TSK_FS_META_FLAG_ENUM flags, TSK_FS_META_WALK_CB action, void *ptr)
1270
0
{
1271
0
    const char *myname = "iso9660_inode_walk";
1272
0
    ISO_INFO *iso = (ISO_INFO *) fs;
1273
0
    TSK_INUM_T inum, end_inum_tmp;
1274
0
    unsigned int myflags;
1275
0
    iso9660_inode *dinode;
1276
1277
    // clean up any error messages that are lying around
1278
0
    tsk_error_reset();
1279
1280
0
    if (tsk_verbose)
1281
0
        tsk_fprintf(stderr, "iso9660_inode_walk: "
1282
0
            " start: %" PRIuINUM " last: %" PRIuINUM " flags: %d"
1283
0
            " action: %" PRIu64 " ptr: %" PRIu64 "\n",
1284
0
            start, last, flags, (uint64_t) action, (uint64_t) ptr);
1285
1286
0
    myflags = TSK_FS_META_FLAG_ALLOC;
1287
1288
    /*
1289
     * Sanity checks.
1290
     */
1291
0
    if (start < fs->first_inum || start > fs->last_inum) {
1292
0
        tsk_error_reset();
1293
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1294
0
        tsk_error_set_errstr("%s: Start inode:  %" PRIuINUM "", myname,
1295
0
            start);
1296
0
        return 1;
1297
0
    }
1298
0
    if (last < fs->first_inum || last > fs->last_inum || last < start) {
1299
0
        tsk_error_reset();
1300
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1301
0
        tsk_error_set_errstr("%s: End inode: %" PRIuINUM "", myname, last);
1302
0
        return 1;
1303
0
    }
1304
1305
    /* If ORPHAN is wanted, then make sure that the flags are correct */
1306
0
    if (flags & TSK_FS_META_FLAG_ORPHAN) {
1307
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_UNALLOC);
1308
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_ALLOC);
1309
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED);
1310
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_UNUSED);
1311
0
    }
1312
0
    else if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) &&
1313
0
        ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) {
1314
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC);
1315
0
    }
1316
1317
    /* If neither of the USED or UNUSED flags are set, then set them
1318
     * both
1319
     */
1320
0
    if (((flags & TSK_FS_META_FLAG_USED) == 0) &&
1321
0
        ((flags & TSK_FS_META_FLAG_UNUSED) == 0)) {
1322
0
        flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED);
1323
0
    }
1324
1325
    /* If we are looking for orphan files and have not yet filled
1326
     * in the list of unalloc inodes that are pointed to, then fill
1327
     * in the list
1328
     * */
1329
0
    if ((flags & TSK_FS_META_FLAG_ORPHAN)) {
1330
0
        if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) {
1331
0
            tsk_error_errstr2_concat
1332
0
                ("- iso9660_inode_walk: identifying inodes allocated by file names");
1333
0
            return 1;
1334
0
        }
1335
0
    }
1336
1337
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
1338
0
        tsk_fs_file_alloc(fs),
1339
0
        tsk_fs_file_close
1340
0
    };
1341
1342
0
    if (!fs_file)
1343
0
        return 1;
1344
1345
0
    if ((fs_file->meta =
1346
0
            tsk_fs_meta_alloc(ISO9660_FILE_CONTENT_LEN)) == NULL)
1347
0
        return 1;
1348
1349
    // we need to handle fs->last_inum specially because it is for the
1350
    // virtual ORPHANS directory.  Handle it outside of the loop.
1351
0
    if (last == TSK_FS_ORPHANDIR_INUM(fs))
1352
0
        end_inum_tmp = last - 1;
1353
0
    else
1354
0
        end_inum_tmp = last;
1355
1356
    /* allocate cache buffers */
1357
    /* dinode */
1358
0
    dinode = (iso9660_inode *) tsk_malloc(sizeof(iso9660_inode));
1359
0
    if (dinode == NULL) {
1360
0
        fs->tag = 0;
1361
0
        iso9660_close(fs);
1362
0
        return 1;
1363
0
    }
1364
    /*
1365
     * Iterate.
1366
     */
1367
0
    for (inum = start; inum <= end_inum_tmp; inum++) {
1368
0
        int retval;
1369
0
        if (iso9660_dinode_load(iso, inum, dinode)) {
1370
0
            free(dinode);
1371
0
            return 1;
1372
0
        }
1373
1374
0
        if (iso9660_dinode_copy(iso, fs_file->meta, inum, dinode)) {
1375
0
            free(dinode);
1376
0
            return 1;
1377
0
        }
1378
0
        myflags = fs_file->meta->flags;
1379
1380
0
        if ((flags & myflags) != myflags)
1381
0
            continue;
1382
1383
        /* If we want only orphans, then check if this
1384
         * inode is in the seen list
1385
         * */
1386
0
        if ((myflags & TSK_FS_META_FLAG_UNALLOC) &&
1387
0
            (flags & TSK_FS_META_FLAG_ORPHAN) &&
1388
0
            (tsk_fs_dir_find_inum_named(fs, inum))) {
1389
0
            continue;
1390
0
        }
1391
1392
0
        retval = action(fs_file.get(), ptr);
1393
0
        if (retval == TSK_WALK_ERROR) {
1394
0
            free(dinode);
1395
0
            return 1;
1396
0
        }
1397
0
        else if (retval == TSK_WALK_STOP) {
1398
0
            break;
1399
0
        }
1400
0
    }
1401
1402
    // handle the virtual orphans folder if they asked for it
1403
0
    if ((last == TSK_FS_ORPHANDIR_INUM(fs))
1404
0
        && (flags & TSK_FS_META_FLAG_ALLOC)
1405
0
        && (flags & TSK_FS_META_FLAG_USED)) {
1406
0
        int retval;
1407
1408
0
        if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) {
1409
0
            free(dinode);
1410
0
            return 1;
1411
0
        }
1412
        /* call action */
1413
0
        retval = action(fs_file.get(), ptr);
1414
0
        if (retval == TSK_WALK_STOP) {
1415
0
            free(dinode);
1416
0
            return 0;
1417
0
        }
1418
0
        else if (retval == TSK_WALK_ERROR) {
1419
0
            free(dinode);
1420
0
            return 1;
1421
0
        }
1422
0
    }
1423
1424
    /*
1425
     * Cleanup.
1426
     */
1427
0
    free(dinode);
1428
0
    return 0;
1429
0
}
1430
1431
// @@@ Doesn' this seem to ignore interleave?
1432
/* return 1 if block is allocated in a file's extent, return 0 otherwise */
1433
static int
1434
iso9660_is_block_alloc(TSK_FS_INFO * fs, TSK_DADDR_T blk_num)
1435
0
{
1436
0
    ISO_INFO *iso = (ISO_INFO *) fs;
1437
0
    iso9660_inode_node *in_node;
1438
1439
0
    if (tsk_verbose)
1440
0
        tsk_fprintf(stderr, "iso9660_is_block_alloc: "
1441
0
            " blk_num: %" PRIuDADDR "\n", blk_num);
1442
1443
0
    for (in_node = iso->in_list; in_node; in_node = in_node->next) {
1444
0
        TSK_DADDR_T first_block = in_node->offset / fs->block_size;
1445
0
        TSK_DADDR_T file_size =
1446
0
            tsk_getu32(fs->endian, in_node->inode.dr.data_len_m);
1447
0
        TSK_DADDR_T last_block =
1448
0
            first_block + (file_size / fs->block_size);
1449
0
        if (file_size % fs->block_size)
1450
0
            last_block++;
1451
1452
0
        if ((blk_num >= first_block) && (blk_num <= last_block))
1453
0
            return 1;
1454
0
    }
1455
1456
0
    return 0;
1457
0
}
1458
1459
1460
static TSK_FS_BLOCK_FLAG_ENUM
1461
iso9660_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
1462
0
{
1463
0
    return (iso9660_is_block_alloc(a_fs, a_addr)) ?
1464
0
        TSK_FS_BLOCK_FLAG_ALLOC : TSK_FS_BLOCK_FLAG_UNALLOC;
1465
0
}
1466
1467
1468
/* flags: TSK_FS_BLOCK_FLAG_ALLOC and FS_FLAG_UNALLOC
1469
 * ISO9660 has a LOT of very sparse meta, so in this function a block is only
1470
 * checked to see if it is part of an inode's extent
1471
 */
1472
static uint8_t
1473
iso9660_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T last,
1474
    TSK_FS_BLOCK_WALK_FLAG_ENUM flags, TSK_FS_BLOCK_WALK_CB action,
1475
    void *ptr)
1476
0
{
1477
0
    const char *myname = "iso9660_block_walk";
1478
0
    TSK_DADDR_T addr;
1479
0
    TSK_FS_BLOCK *fs_block;
1480
1481
    // clean up any error messages that are lying around
1482
0
    tsk_error_reset();
1483
1484
0
    if (tsk_verbose)
1485
0
        tsk_fprintf(stderr, "iso9660_block_walk: "
1486
0
            " start: %" PRIuDADDR " last: %" PRIuDADDR " flags: %d"
1487
0
            " action: %" PRIu64 " ptr: %" PRIu64 "\n",
1488
0
            start, last, flags, (uint64_t) action, (uint64_t) ptr);
1489
1490
    /*
1491
     * Sanity checks.
1492
     */
1493
0
    if (start < fs->first_block || start > fs->last_block) {
1494
0
        tsk_error_reset();
1495
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1496
0
        tsk_error_set_errstr("%s: Start block: %" PRIuDADDR "", myname,
1497
0
            start);
1498
0
        return 1;
1499
0
    }
1500
0
    if (last < fs->first_block || last > fs->last_block) {
1501
0
        tsk_error_reset();
1502
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
1503
0
        tsk_error_set_errstr("%s: End block: %" PRIuDADDR "", myname,
1504
0
            last);
1505
0
        return 1;
1506
0
    }
1507
1508
    /* Sanity check on flags -- make sure at least one ALLOC is set */
1509
0
    if (((flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) &&
1510
0
        ((flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) {
1511
0
        flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
1512
0
            (flags | TSK_FS_BLOCK_WALK_FLAG_ALLOC |
1513
0
            TSK_FS_BLOCK_WALK_FLAG_UNALLOC);
1514
0
    }
1515
0
    if (((flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) &&
1516
0
        ((flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) {
1517
0
        flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
1518
0
            (flags | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META);
1519
0
    }
1520
1521
0
    if ((fs_block = tsk_fs_block_alloc(fs)) == NULL) {
1522
0
        return 1;
1523
0
    }
1524
1525
0
    if (tsk_verbose)
1526
0
        tsk_fprintf(stderr,
1527
0
            "isofs_block_walk: Block Walking %" PRIuDADDR " to %" PRIuDADDR
1528
0
            "\n", start, last);
1529
1530
    /* cycle through block addresses */
1531
0
    for (addr = start; addr <= last; addr++) {
1532
0
        int retval;
1533
0
        int myflags = iso9660_block_getflags(fs, addr);
1534
1535
        // test if we should call the callback with this one
1536
0
        if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC)
1537
0
            && (!(flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC)))
1538
0
            continue;
1539
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC)
1540
0
            && (!(flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC)))
1541
0
            continue;
1542
1543
0
        if (flags & TSK_FS_BLOCK_WALK_FLAG_AONLY)
1544
0
            myflags |= TSK_FS_BLOCK_FLAG_AONLY;
1545
1546
0
        if (tsk_fs_block_get_flag(fs, fs_block, addr, (TSK_FS_BLOCK_FLAG_ENUM) myflags) == NULL) {
1547
0
            tsk_error_set_errstr2("iso_block_walk");
1548
0
            tsk_fs_block_free(fs_block);
1549
0
            return 1;
1550
0
        }
1551
1552
0
        retval = action(fs_block, ptr);
1553
0
        if (retval == TSK_WALK_ERROR) {
1554
0
            tsk_fs_block_free(fs_block);
1555
0
            return 1;
1556
0
        }
1557
0
        else if (retval == TSK_WALK_STOP) {
1558
0
            break;
1559
0
        }
1560
0
    }
1561
1562
0
    tsk_fs_block_free(fs_block);
1563
0
    return 0;
1564
0
}
1565
1566
1567
1568
static uint8_t
1569
iso9660_make_data_run(TSK_FS_FILE * a_fs_file)
1570
0
{
1571
0
    ISO_INFO *iso;
1572
0
    iso9660_dentry dd;
1573
0
    TSK_FS_INFO *fs = NULL;
1574
0
    TSK_FS_ATTR *fs_attr = NULL;
1575
0
    TSK_FS_ATTR_RUN *data_run = NULL;
1576
0
    iso9660_inode *dinode;
1577
1578
    // clean up any error messages that are lying around
1579
0
    tsk_error_reset();
1580
1581
0
    if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)
1582
0
        || (a_fs_file->fs_info == NULL)) {
1583
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1584
0
        tsk_error_set_errstr
1585
0
            ("iso9660_make_data_run: fs_file or meta is NULL");
1586
0
        return 1;
1587
0
    }
1588
0
    fs = a_fs_file->fs_info;
1589
0
    iso = (ISO_INFO *) fs;
1590
1591
    // see if we have already loaded the runs
1592
0
    if ((a_fs_file->meta->attr != NULL)
1593
0
        && (a_fs_file->meta->attr_state == TSK_FS_META_ATTR_STUDIED)) {
1594
0
        return 0;
1595
0
    }
1596
0
    else if (a_fs_file->meta->attr_state == TSK_FS_META_ATTR_ERROR) {
1597
0
        return 1;
1598
0
    }
1599
1600
    // not sure why this would ever happen, but...
1601
0
    if (a_fs_file->meta->attr != NULL) {
1602
0
        tsk_fs_attrlist_markunused(a_fs_file->meta->attr);
1603
0
    }
1604
0
    else  {
1605
0
        a_fs_file->meta->attr = tsk_fs_attrlist_alloc();
1606
0
    }
1607
1608
    /* allocate cache buffers */
1609
    /* dinode */
1610
0
    if ((dinode =
1611
0
            (iso9660_inode *) tsk_malloc(sizeof(iso9660_inode))) == NULL) {
1612
0
        fs->tag = 0;
1613
0
        iso9660_close(fs);
1614
0
        return 1;
1615
0
    }
1616
1617
    // copy the raw data
1618
0
    if (iso9660_dinode_load(iso, a_fs_file->meta->addr, dinode)) {
1619
0
        tsk_error_set_errstr2("iso9660_make_data_run");
1620
0
        a_fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
1621
0
        free(dinode);
1622
0
        return 1;
1623
0
    }
1624
0
    memcpy(&dd, &dinode->dr, sizeof(iso9660_dentry));
1625
0
    free(dinode);
1626
0
    dinode = NULL;
1627
1628
0
    if (dd.gap_sz) {
1629
0
        a_fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
1630
0
        tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
1631
0
        tsk_error_set_errstr("file %" PRIuINUM
1632
0
            " has an interleave gap -- not supported",
1633
0
            a_fs_file->meta->addr);
1634
0
        return 1;
1635
0
    }
1636
1637
0
    if ((fs_attr =
1638
0
            tsk_fs_attrlist_getnew(a_fs_file->meta->attr,
1639
0
                TSK_FS_ATTR_NONRES)) == NULL) {
1640
0
        return 1;
1641
0
    }
1642
1643
    // make a non-resident run
1644
0
    data_run = tsk_fs_attr_run_alloc();
1645
0
    if (data_run == NULL) {
1646
0
        return -1;
1647
0
    }
1648
0
    data_run->addr = ((TSK_DADDR_T *) a_fs_file->meta->content_ptr)[0];
1649
0
    data_run->len =
1650
0
        (a_fs_file->meta->size + fs->block_size - 1) / fs->block_size;
1651
0
    data_run->offset = 0;
1652
1653
    // initialize the data run
1654
0
    if (tsk_fs_attr_set_run(a_fs_file, fs_attr, data_run, NULL,
1655
0
            TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
1656
0
            a_fs_file->meta->size, a_fs_file->meta->size,
1657
0
            roundup(a_fs_file->meta->size + dd.ext_len,
1658
0
                fs->block_size) - dd.ext_len, TSK_FS_ATTR_FLAG_NONE, 0)) {
1659
0
        return 1;
1660
0
    }
1661
1662
    // the first bytes in the run could be allocated for the extended attribute.
1663
0
    fs_attr->nrd.skiplen = dd.ext_len;
1664
1665
0
    a_fs_file->meta->attr_state = TSK_FS_META_ATTR_STUDIED;
1666
1667
0
    return 0;
1668
0
}
1669
1670
1671
1672
static uint8_t
1673
iso9660_fscheck(
1674
  [[maybe_unused]] TSK_FS_INFO * fs,
1675
  [[maybe_unused]] FILE * hFile)
1676
0
{
1677
0
    tsk_error_reset();
1678
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
1679
0
    tsk_error_set_errstr("fscheck not implemented for iso9660 yet");
1680
0
    return 1;
1681
0
}
1682
1683
/**
1684
 * Print details about the file system to a file handle.
1685
 *
1686
 * @param fs File system to print details on
1687
 * @param hFile File handle to print text to
1688
 *
1689
 * @returns 1 on error and 0 on success
1690
 */
1691
static uint8_t
1692
iso9660_fsstat(TSK_FS_INFO * fs, FILE * hFile)
1693
0
{
1694
0
    char str[129];              /* store name of publisher/preparer/etc */
1695
0
    ISO_INFO *iso = (ISO_INFO *) fs;
1696
0
    char *cp;
1697
0
    int i;
1698
1699
0
    iso9660_pvd_node *p = iso->pvd;
1700
0
    iso9660_svd_node *s;
1701
1702
    // clean up any error messages that are lying around
1703
0
    tsk_error_reset();
1704
1705
0
    if (tsk_verbose)
1706
0
        tsk_fprintf(stderr, "iso9660_fsstat:\n");
1707
1708
0
    i = 0;
1709
1710
0
    for (p = iso->pvd; p != NULL; p = p->next) {
1711
0
        i++;
1712
0
        tsk_fprintf(hFile, "\n=== PRIMARY VOLUME DESCRIPTOR %d ===\n", i);
1713
0
        tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
1714
0
        tsk_fprintf(hFile,
1715
0
            "--------------------------------------------\n");
1716
0
        tsk_fprintf(hFile, "File System Type: ISO9660\n");
1717
0
        tsk_fprintf(hFile, "Volume Name: %s\n", p->pvd.vol_id);
1718
0
        tsk_fprintf(hFile, "Volume Set Size: %d\n",
1719
0
            tsk_getu16(fs->endian, p->pvd.vol_set_m));
1720
0
        tsk_fprintf(hFile, "Volume Set Sequence: %d\n",
1721
0
            tsk_getu16(fs->endian, p->pvd.vol_seq_m));
1722
1723
        /* print publisher */
1724
0
        if (p->pvd.pub_id[0] == 0x5f)
1725
            /* publisher is in a file.  TODO: handle this properly */
1726
0
            snprintf(str, 8, "In file");
1727
0
        else
1728
0
            snprintf(str, 128, "%s", p->pvd.pub_id);
1729
1730
0
        cp = &str[127];
1731
        /* find last printable non space character */
1732
0
        while ((!isprint(*cp) || isspace(*cp)) && (cp != str))
1733
0
            cp--;
1734
0
        *++cp = '\0';
1735
0
        tsk_fprintf(hFile, "Publisher: %s\n", str);
1736
0
        memset(str, ' ', 128);
1737
1738
1739
        /* print data preparer */
1740
0
        if (p->pvd.prep_id[0] == 0x5f)
1741
            /* preparer is in a file.  TODO: handle this properly */
1742
0
            snprintf(str, 8, "In file");
1743
0
        else
1744
0
            snprintf(str, 128, "%s", p->pvd.prep_id);
1745
1746
0
        cp = &str[127];
1747
0
        while ((!isprint(*cp) || isspace(*cp)) && (cp != str))
1748
0
            cp--;
1749
0
        *++cp = '\0';
1750
0
        tsk_fprintf(hFile, "Data Preparer: %s\n", str);
1751
0
        memset(str, ' ', 128);
1752
1753
1754
        /* print recording application */
1755
0
        if (p->pvd.app_id[0] == 0x5f)
1756
            /* application is in a file.  TODO: handle this properly */
1757
0
            snprintf(str, 8, "In file");
1758
0
        else
1759
0
            snprintf(str, 128, "%s", p->pvd.app_id);
1760
0
        cp = &str[127];
1761
0
        while ((!isprint(*cp) || isspace(*cp)) && (cp != str))
1762
0
            cp--;
1763
0
        *++cp = '\0';
1764
0
        tsk_fprintf(hFile, "Recording Application: %s\n", str);
1765
0
        memset(str, ' ', 128);
1766
1767
1768
        /* print copyright */
1769
0
        if (p->pvd.copy_id[0] == 0x5f)
1770
            /* copyright is in a file.  TODO: handle this properly */
1771
0
            snprintf(str, 8, "In file");
1772
0
        else
1773
0
            snprintf(str, 37, "%s", p->pvd.copy_id);
1774
0
        cp = &str[36];
1775
0
        while ((!isprint(*cp) || isspace(*cp)) && (cp != str))
1776
0
            cp--;
1777
0
        *++cp = '\0';
1778
0
        tsk_fprintf(hFile, "Copyright: %s\n", str);
1779
0
        memset(str, ' ', 37);
1780
1781
0
        tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
1782
0
        tsk_fprintf(hFile,
1783
0
            "--------------------------------------------\n");
1784
0
        tsk_fprintf(hFile,
1785
0
            "Path Table Location: %" PRIu32 "-%" PRIu32 "\n",
1786
0
            tsk_getu32(fs->endian, p->pvd.pt_loc_m), tsk_getu32(fs->endian,
1787
0
                p->pvd.pt_loc_m) + tsk_getu32(fs->endian,
1788
0
                p->pvd.pt_size_m) / fs->block_size);
1789
1790
0
        tsk_fprintf(hFile, "Inode Range: %" PRIuINUM " - %" PRIuINUM "\n",
1791
0
            fs->first_inum, fs->last_inum);
1792
0
        tsk_fprintf(hFile, "Root Directory Block: %" PRIuDADDR "\n",
1793
0
            tsk_getu32(fs->endian, p->pvd.dir_rec.ext_loc_m));
1794
1795
0
        tsk_fprintf(hFile, "\nCONTENT INFORMATION\n");
1796
0
        tsk_fprintf(hFile,
1797
0
            "--------------------------------------------\n");
1798
0
        tsk_fprintf(hFile, "Sector Size: %d\n", ISO9660_SSIZE_B);
1799
0
        tsk_fprintf(hFile, "Block Size: %d\n", tsk_getu16(fs->endian,
1800
0
                p->pvd.blk_sz_m));
1801
0
        if (fs->block_pre_size) {
1802
0
            tsk_fprintf(hFile, "Raw CD pre-block size: %d\n",
1803
0
                fs->block_pre_size);
1804
0
            tsk_fprintf(hFile, "Raw CD post-block size: %d\n",
1805
0
                fs->block_post_size);
1806
0
        }
1807
1808
0
        tsk_fprintf(hFile, "Total Sector Range: 0 - %d\n",
1809
0
            (int) ((fs->block_size / ISO9660_SSIZE_B) *
1810
0
                (fs->block_count - 1)));
1811
        /* get image slack, ignore how big the image claims itself to be */
1812
0
        tsk_fprintf(hFile, "Total Block Range: 0 - %d\n",
1813
0
            (int) fs->block_count - 1);
1814
0
    }
1815
1816
0
    i = 0;
1817
1818
0
    for (s = iso->svd; s != NULL; s = s->next) {
1819
0
        i++;
1820
0
        tsk_fprintf(hFile,
1821
0
            "\n=== SUPPLEMENTARY VOLUME DESCRIPTOR %d ===\n", i);
1822
0
        tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
1823
0
        tsk_fprintf(hFile,
1824
0
            "--------------------------------------------\n");
1825
0
        tsk_fprintf(hFile, "File System Type: ISO9660\n");
1826
0
        tsk_fprintf(hFile, "Volume Name: %s\n", s->svd.vol_id);
1827
0
        tsk_fprintf(hFile, "Volume Set Size: %d\n",
1828
0
            tsk_getu16(fs->endian, s->svd.vol_set_m));
1829
0
        tsk_fprintf(hFile, "Volume Set Sequence: %d\n",
1830
0
            tsk_getu16(fs->endian, s->svd.vol_seq_m));
1831
1832
1833
1834
        /* print publisher */
1835
0
        if (s->svd.pub_id[0] == 0x5f)
1836
            /* publisher is in a file.  TODO: handle this properly */
1837
0
            snprintf(str, 8, "In file");
1838
0
        else
1839
0
            snprintf(str, 128, "%s", s->svd.pub_id);
1840
1841
0
        cp = &str[127];
1842
        /* find last printable non space character */
1843
0
        while ((!isprint(*cp) || isspace(*cp)) && (cp != str))
1844
0
            cp--;
1845
0
        *++cp = '\0';
1846
0
        tsk_fprintf(hFile, "Publisher: %s\n", str);
1847
0
        memset(str, ' ', 128);
1848
1849
1850
        /* print data preparer */
1851
0
        if (s->svd.prep_id[0] == 0x5f)
1852
            /* preparer is in a file.  TODO: handle this properly */
1853
0
            snprintf(str, 8, "In file");
1854
0
        else
1855
0
            snprintf(str, 128, "%s", s->svd.prep_id);
1856
1857
0
        cp = &str[127];
1858
0
        while ((!isprint(*cp) || isspace(*cp)) && (cp != str))
1859
0
            cp--;
1860
0
        *++cp = '\0';
1861
0
        tsk_fprintf(hFile, "Data Preparer: %s\n", str);
1862
0
        memset(str, ' ', 128);
1863
1864
1865
        /* print recording application */
1866
0
        if (s->svd.app_id[0] == 0x5f)
1867
            /* application is in a file.  TODO: handle this properly */
1868
0
            snprintf(str, 8, "In file");
1869
0
        else
1870
0
            snprintf(str, 128, "%s", s->svd.app_id);
1871
0
        cp = &str[127];
1872
0
        while ((!isprint(*cp) || isspace(*cp)) && (cp != str))
1873
0
            cp--;
1874
0
        *++cp = '\0';
1875
0
        tsk_fprintf(hFile, "Recording Application: %s\n", str);
1876
0
        memset(str, ' ', 128);
1877
1878
1879
        /* print copyright */
1880
0
        if (s->svd.copy_id[0] == 0x5f)
1881
            /* copyright is in a file.  TODO: handle this properly */
1882
0
            snprintf(str, 8, "In file");
1883
0
        else
1884
0
            snprintf(str, 37, "%s", s->svd.copy_id);
1885
0
        cp = &str[36];
1886
0
        while ((!isprint(*cp) || isspace(*cp)) && (cp != str))
1887
0
            cp--;
1888
0
        *++cp = '\0';
1889
0
        tsk_fprintf(hFile, "Copyright: %s\n", str);
1890
0
        memset(str, ' ', 37);
1891
1892
0
        tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
1893
0
        tsk_fprintf(hFile,
1894
0
            "--------------------------------------------\n");
1895
0
        tsk_fprintf(hFile,
1896
0
            "Path Table Location: %" PRIu32 "-%" PRIu32 "\n",
1897
0
            tsk_getu32(fs->endian, s->svd.pt_loc_m), tsk_getu32(fs->endian,
1898
0
                s->svd.pt_loc_m) + tsk_getu32(fs->endian,
1899
0
                s->svd.pt_size_m) / fs->block_size);
1900
1901
0
        tsk_fprintf(hFile, "Root Directory Block: %" PRIuDADDR "\n",
1902
0
            tsk_getu32(fs->endian, s->svd.dir_rec.ext_loc_m));
1903
1904
        /* learn joliet level (1-3) */
1905
0
        if (!strncmp((char *) s->svd.esc_seq, "%/E", 3))
1906
0
            tsk_fprintf(hFile, "Joliet Name Encoding: UCS-2 Level 3\n");
1907
0
        if (!strncmp((char *) s->svd.esc_seq, "%/C", 3))
1908
0
            tsk_fprintf(hFile, "Joliet Name Encoding: UCS-2 Level 2\n");
1909
0
        if (!strncmp((char *) s->svd.esc_seq, "%/@", 3))
1910
0
            tsk_fprintf(hFile, "Joliet Name Encoding: UCS-2 Level 1\n");
1911
0
        if (iso->rr_found)
1912
0
            tsk_fprintf(hFile, "RockRidge Extensions present\n");
1913
1914
1915
0
        tsk_fprintf(hFile, "\nCONTENT INFORMATION\n");
1916
0
        tsk_fprintf(hFile,
1917
0
            "--------------------------------------------\n");
1918
0
        tsk_fprintf(hFile, "Sector Size: %d\n", ISO9660_SSIZE_B);
1919
0
        tsk_fprintf(hFile, "Block Size: %d\n", fs->block_size);
1920
1921
0
        tsk_fprintf(hFile, "Total Sector Range: 0 - %d\n",
1922
0
            (int) ((fs->block_size / ISO9660_SSIZE_B) *
1923
0
                (fs->block_count - 1)));
1924
        /* get image slack, ignore how big the image claims itself to be */
1925
0
        tsk_fprintf(hFile, "Total Block Range: 0 - %d\n",
1926
0
            (int) fs->block_count - 1);
1927
0
    }
1928
1929
0
    return 0;
1930
0
}
1931
1932
1933
/**
1934
 * Make a unix-style permissions string based the flags in dentry and
1935
 * the cached inode in fs, storing results in perm.  Caller must
1936
 * ensure perm can hold 10 chars plus one null char.
1937
 */
1938
static char *
1939
make_unix_perm(TSK_FS_INFO * fs, iso9660_dentry * dd,
1940
    iso9660_inode * dinode, char *perm)
1941
0
{
1942
0
    if (tsk_verbose)
1943
0
        tsk_fprintf(stderr, "make_unix_perm: fs: %" PRIu64
1944
0
            " dd: %" PRIu64 "\n", (uint64_t) fs, (uint64_t) dd);
1945
1946
0
    memset(perm, '-', 10);
1947
0
    perm[10] = '\0';
1948
1949
0
    if (dd->flags & ISO9660_FLAG_DIR)
1950
0
        perm[0] = 'd';
1951
1952
0
    if (dinode->ea) {
1953
0
        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_UR)
1954
0
            perm[1] = 'r';
1955
1956
0
        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_UX)
1957
0
            perm[3] = 'x';
1958
1959
0
        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_GR)
1960
0
            perm[4] = 'r';
1961
1962
0
        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_GX)
1963
0
            perm[6] = 'x';
1964
1965
0
        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_AR)
1966
0
            perm[7] = 'r';
1967
1968
0
        if (tsk_getu16(fs->endian, dinode->ea->mode) & ISO9660_BIT_AX)
1969
0
            perm[9] = 'x';
1970
0
    }
1971
0
    else {
1972
0
        strcpy(&perm[1], "r-xr-xr-x");
1973
0
    }
1974
1975
0
    return perm;
1976
0
}
1977
1978
#if 0
1979
static void
1980
iso9660_print_rockridge(FILE * hFile, rockridge_ext * rr)
1981
{
1982
    char mode_buf[11];
1983
1984
    tsk_fprintf(hFile, "\nROCKRIDGE EXTENSIONS\n");
1985
1986
    tsk_fprintf(hFile, "Owner-ID: ");
1987
    tsk_fprintf(hFile, "%d\t", (int) rr->uid);
1988
1989
    tsk_fprintf(hFile, "Group-ID: ");
1990
    tsk_fprintf(hFile, "%d\n", (int) rr->gid);
1991
1992
    tsk_fprintf(hFile, "Mode: ");
1993
    memset(mode_buf, '-', 11);
1994
    mode_buf[10] = '\0';
1995
1996
    /* file type */
1997
    /* note: socket and symbolic link are multi bit fields */
1998
    if ((rr->mode & MODE_IFSOCK) == MODE_IFSOCK)
1999
        mode_buf[0] = 's';
2000
    else if ((rr->mode & MODE_IFLNK) == MODE_IFLNK)
2001
        mode_buf[0] = 'l';
2002
    else if (rr->mode & MODE_IFDIR)
2003
        mode_buf[0] = 'd';
2004
    else if (rr->mode & MODE_IFIFO)
2005
        mode_buf[0] = 'p';
2006
    else if (rr->mode & MODE_IFBLK)
2007
        mode_buf[0] = 'b';
2008
    else if (rr->mode & MODE_IFCHR)
2009
        mode_buf[0] = 'c';
2010
2011
    /* owner permissions */
2012
    if (rr->mode & TSK_FS_META_MODE_IRUSR)
2013
        mode_buf[1] = 'r';
2014
    if (rr->mode & TSK_FS_META_MODE_IWUSR)
2015
        mode_buf[2] = 'w';
2016
2017
    if ((rr->mode & TSK_FS_META_MODE_IXUSR)
2018
        && (rr->mode & TSK_FS_META_MODE_ISUID))
2019
        mode_buf[3] = 's';
2020
    else if (rr->mode & TSK_FS_META_MODE_IXUSR)
2021
        mode_buf[3] = 'x';
2022
    else if (rr->mode & TSK_FS_META_MODE_ISUID)
2023
        mode_buf[3] = 'S';
2024
2025
    /* group permissions */
2026
    if (rr->mode & TSK_FS_META_MODE_IRGRP)
2027
        mode_buf[4] = 'r';
2028
    if (rr->mode & TSK_FS_META_MODE_IWGRP)
2029
        mode_buf[5] = 'w';
2030
2031
    if ((rr->mode & TSK_FS_META_MODE_IXGRP)
2032
        && (rr->mode & TSK_FS_META_MODE_ISGID))
2033
        mode_buf[6] = 's';
2034
    else if (rr->mode & TSK_FS_META_MODE_IXGRP)
2035
        mode_buf[6] = 'x';
2036
    else if (rr->mode & TSK_FS_META_MODE_ISGID)
2037
        mode_buf[6] = 'S';
2038
2039
    /* other permissions */
2040
    if (rr->mode & TSK_FS_META_MODE_IROTH)
2041
        mode_buf[7] = 'r';
2042
    if (rr->mode & TSK_FS_META_MODE_IWOTH)
2043
        mode_buf[8] = 'w';
2044
2045
    if ((rr->mode & TSK_FS_META_MODE_IXOTH)
2046
        && (rr->mode & TSK_FS_META_MODE_ISVTX))
2047
        mode_buf[9] = 't';
2048
    else if (rr->mode & TSK_FS_META_MODE_IXOTH)
2049
        mode_buf[9] = 'x';
2050
    else if (rr->mode & TSK_FS_META_MODE_ISVTX)
2051
        mode_buf[9] = 'T';
2052
2053
    tsk_fprintf(hFile, "%s\n", mode_buf);
2054
    tsk_fprintf(hFile, "Number links: %" PRIu32 "\n", rr->nlink);
2055
2056
    tsk_fprintf(hFile, "Alternate name: %s\n", rr->fn);
2057
    tsk_fprintf(hFile, "\n");
2058
}
2059
#endif
2060
2061
/**
2062
 * Print details on a specific file to a file handle.
2063
 *
2064
 * @param fs File system file is located in
2065
 * @param hFile File handle to print text to
2066
 * @param inum Address of file in file system
2067
 * @param numblock The number of blocks in file to force print (can go beyond file size)
2068
 * @param sec_skew Clock skew in seconds to also print times in
2069
 *
2070
 * @returns 1 on error and 0 on success
2071
 */
2072
static uint8_t
2073
iso9660_istat(
2074
  TSK_FS_INFO * fs,
2075
  TSK_FS_ISTAT_FLAG_ENUM istat_flags,
2076
  FILE * hFile,
2077
  TSK_INUM_T inum,
2078
  [[maybe_unused]] TSK_DADDR_T numblock,
2079
  int32_t sec_skew)
2080
0
{
2081
0
    ISO_INFO *iso = (ISO_INFO *) fs;
2082
0
    iso9660_dentry dd;
2083
0
    iso9660_inode *dinode;
2084
0
    char timeBuf[128];
2085
2086
    // clean up any error messages that are lying around
2087
0
    tsk_error_reset();
2088
2089
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
2090
0
        tsk_fs_file_open_meta(fs, NULL, inum),
2091
0
        tsk_fs_file_close
2092
0
    };
2093
2094
0
    if (!fs_file)
2095
0
        return 1;
2096
2097
0
    tsk_fprintf(hFile, "Entry: %" PRIuINUM "\n", inum);
2098
2099
    /* allocate cache buffers */
2100
    /* dinode */
2101
0
    dinode = (iso9660_inode *) tsk_malloc(sizeof(iso9660_inode));
2102
0
    if (dinode == NULL) {
2103
0
        fs->tag = 0;
2104
0
        iso9660_close(fs);
2105
0
        return 1;
2106
0
    }
2107
2108
0
    if (iso9660_dinode_load(iso, inum, dinode)) {
2109
0
        tsk_error_set_errstr2("iso9660_istat");
2110
0
        free(dinode);
2111
0
        return 1;
2112
0
    }
2113
0
    memcpy(&dd, &dinode->dr, sizeof(iso9660_dentry));
2114
2115
0
    tsk_fprintf(hFile, "Type: ");
2116
0
    if (dd.flags & ISO9660_FLAG_DIR)
2117
0
        tsk_fprintf(hFile, "Directory\n");
2118
0
    else
2119
0
        tsk_fprintf(hFile, "File\n");
2120
2121
0
    tsk_fprintf(hFile, "Links: %d\n", fs_file->meta->nlink);
2122
2123
0
    if (dd.gap_sz > 0) {
2124
0
        tsk_fprintf(hFile, "Interleave Gap Size: %d\n", dd.gap_sz);
2125
0
        tsk_fprintf(hFile, "Interleave File Unit Size: %d\n", dd.unit_sz);
2126
0
    }
2127
2128
0
    tsk_fprintf(hFile, "Flags: ");
2129
2130
0
    if (dd.flags & ISO9660_FLAG_HIDE)
2131
0
        tsk_fprintf(hFile, "Hidden, ");
2132
2133
0
    if (dd.flags & ISO9660_FLAG_ASSOC)
2134
0
        tsk_fprintf(hFile, "Associated, ");
2135
2136
0
    if (dd.flags & ISO9660_FLAG_RECORD)
2137
0
        tsk_fprintf(hFile, "Record Format, ");
2138
2139
0
    if (dd.flags & ISO9660_FLAG_PROT)
2140
0
        tsk_fprintf(hFile, "Protected,  ");
2141
2142
    /* check if reserved bits are set, be suspicious */
2143
0
    if (dd.flags & ISO9660_FLAG_RES1)
2144
0
        tsk_fprintf(hFile, "Reserved1, ");
2145
2146
0
    if (dd.flags & ISO9660_FLAG_RES2)
2147
0
        tsk_fprintf(hFile, "Reserved2, ");
2148
2149
0
    if (dd.flags & ISO9660_FLAG_MULT)
2150
0
        tsk_fprintf(hFile, "Non-final multi-extent entry");
2151
0
    putchar('\n');
2152
2153
0
    tsk_fprintf(hFile, "Name: %s\n", dinode->fn);
2154
0
    tsk_fprintf(hFile, "Size: %" PRIu32 "\n", tsk_getu32(fs->endian,
2155
0
            dinode->dr.data_len_m));
2156
2157
0
    if (dinode->ea) {
2158
0
        char perm_buf[11];
2159
0
        tsk_fprintf(hFile, "\nEXTENDED ATTRIBUTE INFO\n");
2160
0
        tsk_fprintf(hFile, "Owner-ID: %" PRIu32 "\n",
2161
0
            tsk_getu32(fs->endian, dinode->ea->uid));
2162
0
        tsk_fprintf(hFile, "Group-ID: %" PRIu32 "\n",
2163
0
            tsk_getu32(fs->endian, dinode->ea->gid));
2164
0
        tsk_fprintf(hFile, "Mode: %s\n", make_unix_perm(fs, &dd, dinode,
2165
0
                perm_buf));
2166
0
    }
2167
0
    else if (dinode->susp_off) {
2168
0
        char *buf2 = (char *) tsk_malloc((size_t) dinode->susp_len);
2169
0
        if (buf2 != NULL) {
2170
0
            ssize_t cnt;
2171
0
            fprintf(hFile, "\nRock Ridge Extension Data\n");
2172
0
            cnt =
2173
0
                tsk_fs_read(fs, dinode->susp_off, buf2,
2174
0
                (size_t) dinode->susp_len);
2175
2176
0
            rockridge_ext *rr_entry = NULL;
2177
0
            if (cnt == dinode->susp_len) {
2178
0
                rr_entry = parse_susp(fs, buf2, (int) cnt, hFile, 0);
2179
0
            }
2180
0
            if (rr_entry == NULL) {
2181
0
                fprintf(hFile, "Error reading Rock Ridge Location\n");
2182
0
                if (tsk_verbose) {
2183
0
                    fprintf(stderr,
2184
0
                        "istat: error reading rock ridge entry\n");
2185
0
                    tsk_error_print(stderr);
2186
0
                }
2187
0
                tsk_error_reset();
2188
0
            }
2189
0
            free(buf2);
2190
0
        }
2191
0
        else {
2192
0
            if (tsk_verbose)
2193
0
                fprintf(stderr,
2194
0
                    "istat: error allocating memory to process rock ridge entry\n");
2195
0
            tsk_error_reset();
2196
0
        }
2197
0
    }
2198
    //else if (iso->dinode->rr) {
2199
    //    iso9660_print_rockridge(hFile, iso->dinode->rr);
2200
    //}
2201
0
    else {
2202
0
        char perm_buf[11];
2203
0
        tsk_fprintf(hFile, "Owner-ID: 0\n");
2204
0
        tsk_fprintf(hFile, "Group-ID: 0\n");
2205
0
        tsk_fprintf(hFile, "Mode: %s\n", make_unix_perm(fs, &dd, dinode,
2206
0
                perm_buf));
2207
0
    }
2208
2209
0
    if (sec_skew != 0) {
2210
0
        tsk_fprintf(hFile, "\nAdjusted File Times:\n");
2211
0
        if (fs_file->meta->mtime)
2212
0
            fs_file->meta->mtime -= sec_skew;
2213
0
        if (fs_file->meta->atime)
2214
0
            fs_file->meta->atime -= sec_skew;
2215
0
        if (fs_file->meta->crtime)
2216
0
            fs_file->meta->crtime -= sec_skew;
2217
2218
0
        tsk_fprintf(hFile, "Written:\t%s\n",
2219
0
            tsk_fs_time_to_str(fs_file->meta->mtime, timeBuf));
2220
0
        tsk_fprintf(hFile, "Accessed:\t%s\n",
2221
0
            tsk_fs_time_to_str(fs_file->meta->atime, timeBuf));
2222
0
        tsk_fprintf(hFile, "Created:\t%s\n",
2223
0
            tsk_fs_time_to_str(fs_file->meta->crtime, timeBuf));
2224
2225
0
        if (fs_file->meta->mtime)
2226
0
            fs_file->meta->mtime += sec_skew;
2227
0
        if (fs_file->meta->atime)
2228
0
            fs_file->meta->atime += sec_skew;
2229
0
        if (fs_file->meta->crtime)
2230
0
            fs_file->meta->crtime += sec_skew;
2231
2232
2233
0
        tsk_fprintf(hFile, "\nOriginal File Times:\n");
2234
0
    }
2235
0
    else {
2236
0
        tsk_fprintf(hFile, "\nFile Times:\n");
2237
0
    }
2238
2239
0
    tsk_fprintf(hFile, "Created:\t%s\n",
2240
0
        tsk_fs_time_to_str(fs_file->meta->crtime, timeBuf));
2241
0
    tsk_fprintf(hFile, "File Modified:\t%s\n",
2242
0
        tsk_fs_time_to_str(fs_file->meta->mtime, timeBuf));
2243
0
    tsk_fprintf(hFile, "Accessed:\t%s\n",
2244
0
        tsk_fs_time_to_str(fs_file->meta->atime, timeBuf));
2245
2246
0
    tsk_fprintf(hFile, "\nSectors:\n");
2247
0
    if (istat_flags & TSK_FS_ISTAT_RUNLIST) {
2248
0
        const TSK_FS_ATTR *fs_attr_default =
2249
0
            tsk_fs_file_attr_get_type(fs_file.get(),
2250
0
                TSK_FS_ATTR_TYPE_DEFAULT, 0, 0);
2251
0
        if (fs_attr_default && (fs_attr_default->flags & TSK_FS_ATTR_NONRES)) {
2252
0
            if (tsk_fs_attr_print(fs_attr_default, hFile)) {
2253
0
                tsk_fprintf(hFile, "\nError creating run lists\n");
2254
0
                tsk_error_print(hFile);
2255
0
                tsk_error_reset();
2256
0
            }
2257
0
        }
2258
0
    }
2259
0
    else {
2260
        /* since blocks are all contiguous, print them here to simplify file_walk */
2261
2262
0
        int block = tsk_getu32(fs->endian, dinode->dr.ext_loc_m);
2263
0
        TSK_OFF_T size = fs_file->meta->size;
2264
0
        int rowcount = 0;
2265
2266
0
        while ((int64_t) size > 0) {
2267
0
            tsk_fprintf(hFile, "%d ", block++);
2268
0
            size -= fs->block_size;
2269
0
            rowcount++;
2270
0
            if (rowcount == 8) {
2271
0
                rowcount = 0;
2272
0
                tsk_fprintf(hFile, "\n");
2273
0
            }
2274
0
        }
2275
0
        tsk_fprintf(hFile, "\n");
2276
0
    }
2277
2278
0
    free(dinode);
2279
0
    return 0;
2280
0
}
2281
2282
2283
2284
2285
static uint8_t
2286
iso9660_jopen(
2287
  [[maybe_unused]] TSK_FS_INFO * fs,
2288
  [[maybe_unused]] TSK_INUM_T inum)
2289
0
{
2290
0
    tsk_error_reset();
2291
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
2292
0
    tsk_error_set_errstr("ISO9660 does not have a journal");
2293
0
    return 1;
2294
0
}
2295
2296
static uint8_t
2297
iso9660_jentry_walk(
2298
  [[maybe_unused]] TSK_FS_INFO * fs,
2299
  [[maybe_unused]] int flags,
2300
  [[maybe_unused]] TSK_FS_JENTRY_WALK_CB action,
2301
  [[maybe_unused]] void *ptr)
2302
0
{
2303
0
    tsk_error_reset();
2304
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
2305
0
    tsk_error_set_errstr("ISO9660 does not have a journal");
2306
0
    return 1;
2307
0
}
2308
2309
static uint8_t
2310
iso9660_jblk_walk(
2311
  [[maybe_unused]] TSK_FS_INFO * fs,
2312
  [[maybe_unused]] TSK_DADDR_T start,
2313
  [[maybe_unused]] TSK_DADDR_T end,
2314
  [[maybe_unused]] int flags,
2315
  [[maybe_unused]] TSK_FS_JBLK_WALK_CB action,
2316
  [[maybe_unused]] void *ptr)
2317
0
{
2318
0
    tsk_error_reset();
2319
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
2320
0
    tsk_error_set_errstr("ISO9660 does not have a journal");
2321
0
    return 1;
2322
0
}
2323
2324
2325
static TSK_FS_ATTR_TYPE_ENUM
2326
iso9660_get_default_attr_type([[maybe_unused]] const TSK_FS_FILE * a_file)
2327
0
{
2328
0
    return TSK_FS_ATTR_TYPE_DEFAULT;
2329
0
}
2330
2331
/** Load the volume descriptors into save the raw data structures in
2332
 * the file system state structure (fs).  Also determines the block size.
2333
 *
2334
 * This is useful for discs which may have 2 volumes on them (no, not
2335
 * multisession CD-R/CD-RW).
2336
 * Design note: If path table address is the same, then you have the same image.
2337
 * Only store unique image info.
2338
 * Uses a linked list even though Ecma-119 says there is only 1 primary vol
2339
 * desc, consider possibility of more.
2340
 *
2341
 * Returns -1 on error and 0 on success
2342
 */
2343
static int
2344
load_vol_desc(TSK_FS_INFO * fs)
2345
0
{
2346
    //int count = 0;  // set but never used
2347
0
    ISO_INFO *iso = (ISO_INFO *) fs;
2348
0
    TSK_OFF_T offs;
2349
0
    const char *myname = "iso_load_vol_desc";
2350
0
    ssize_t cnt;
2351
0
    iso9660_pvd_node *p;
2352
0
    iso9660_svd_node *s;
2353
0
    uint8_t magic_seen = 0;
2354
2355
0
    iso->pvd = NULL;
2356
0
    iso->svd = NULL;
2357
    //fs->block_size = 0;
2358
0
    fs->dev_bsize = fs->img_info->sector_size;
2359
2360
#if 0
2361
    b = (iso_bootrec *) tsk_malloc(sizeof(iso_bootrec));
2362
    if (b == NULL) {
2363
        return -1;
2364
    }
2365
#endif
2366
2367
    // @@@ Technically, we should seek ahea 16 * sector size
2368
0
    for (offs = ISO9660_SBOFF;; offs += sizeof(iso9660_gvd)) {
2369
0
        iso9660_gvd *vd;
2370
2371
        // allocate a buffer the size of the nodes in the linked list
2372
        // this will be stored in ISO_INFO, so it is not always freed here
2373
0
        if ((vd =
2374
0
                (iso9660_gvd *) tsk_malloc(sizeof(iso9660_pvd_node))) ==
2375
0
            NULL) {
2376
0
            return -1;
2377
0
        }
2378
2379
0
      ISO_RETRY_MAGIC:
2380
2381
        // read the full descriptor
2382
0
        cnt = tsk_fs_read(fs, offs, (char *) vd, sizeof(iso9660_gvd));
2383
0
        if (cnt != sizeof(iso9660_gvd)) {
2384
0
            if (cnt >= 0) {
2385
0
                tsk_error_reset();
2386
0
                tsk_error_set_errno(TSK_ERR_FS_READ);
2387
0
            }
2388
0
            tsk_error_set_errstr2("iso_load_vol_desc: Error reading");
2389
0
            free(vd);
2390
0
            return -1;
2391
0
        }
2392
2393
        // verify the magic value
2394
0
        if (strncmp(vd->magic, ISO9660_MAGIC, 5)) {
2395
0
            if (tsk_verbose)
2396
0
                tsk_fprintf(stderr,
2397
0
                    "%s: Bad volume descriptor: Magic number is not CD001\n",
2398
0
                    myname);
2399
2400
            // see if we have a RAW image
2401
0
            if (magic_seen == 0) {
2402
0
                if (fs->block_pre_size == 0) {
2403
0
                    if (tsk_verbose)
2404
0
                        tsk_fprintf(stderr,
2405
0
                            "Trying RAW ISO9660 with 16-byte pre-block size\n");
2406
0
                    fs->block_pre_size = 16;
2407
0
                    fs->block_post_size = 288;
2408
0
                    goto ISO_RETRY_MAGIC;
2409
0
                }
2410
0
                else if (fs->block_pre_size == 16) {
2411
0
                    if (tsk_verbose)
2412
0
                        tsk_fprintf(stderr,
2413
0
                            "Trying RAW ISO9660 with 24-byte pre-block size\n");
2414
0
                    fs->block_pre_size = 24;
2415
0
                    fs->block_post_size = 280;
2416
0
                    goto ISO_RETRY_MAGIC;
2417
0
                }
2418
0
                else {
2419
0
                    fs->block_pre_size = 0;
2420
0
                    fs->block_post_size = 0;
2421
0
                }
2422
0
            }
2423
0
            free(vd);
2424
0
            return -1;
2425
0
        }
2426
0
        magic_seen = 1;
2427
2428
        // see if we are done
2429
0
        if (vd->type == ISO9660_VOL_DESC_SET_TERM) {
2430
0
            free(vd);
2431
0
            break;
2432
0
        }
2433
2434
0
        switch (vd->type) {
2435
2436
0
        case ISO9660_PRIM_VOL_DESC:
2437
0
            p = (iso9660_pvd_node *) vd;
2438
2439
            /* list not empty */
2440
0
            if (iso->pvd) {
2441
0
                iso9660_pvd_node *ptmp = iso->pvd;
2442
                /* append to list if path table address not found in list */
2443
0
                while ((&(p->pvd.pt_loc_l)[0] != &(ptmp->pvd.pt_loc_l)[0])
2444
0
                    && (ptmp->next))
2445
0
                    ptmp = ptmp->next;
2446
2447
                // we already have it
2448
0
                if (&(p->pvd.pt_loc_l)[0] == &(ptmp->pvd.pt_loc_l)[0]) {
2449
0
                    free(vd);
2450
0
                    p = NULL;
2451
0
                    vd = NULL;
2452
0
                }
2453
0
                else {
2454
0
                    ptmp->next = p;
2455
0
                    p->next = NULL;
2456
                    // count++; // set but never used
2457
0
                }
2458
0
            }
2459
2460
            /* list empty, insert */
2461
0
            else {
2462
0
                iso->pvd = p;
2463
0
                p->next = NULL;
2464
                // count++; // set but never used
2465
0
            }
2466
2467
0
            break;
2468
2469
0
        case ISO9660_SUPP_VOL_DESC:
2470
0
            s = (iso9660_svd_node *) vd;
2471
2472
            /* list not empty */
2473
0
            if (iso->svd) {
2474
0
                iso9660_svd_node *stmp = iso->svd;
2475
                /* append to list if path table address not found in list */
2476
0
                while ((&(s->svd.pt_loc_l)[0] != &(stmp->svd.pt_loc_l)[0])
2477
0
                    && (stmp->next))
2478
0
                    stmp = stmp->next;
2479
2480
                // we already have it
2481
0
                if (&(s->svd.pt_loc_l)[0] == &(stmp->svd.pt_loc_l)[0]) {
2482
0
                    free(vd);
2483
0
                    s = NULL;
2484
0
                    vd = NULL;
2485
0
                }
2486
0
                else {
2487
0
                    stmp->next = s;
2488
0
                    s->next = NULL;
2489
                    // count++;  // set but never used
2490
0
                }
2491
0
            }
2492
2493
            /* list empty, insert */
2494
0
            else {
2495
0
                iso->svd = s;
2496
0
                s->next = NULL;
2497
                // count++;   // set but never used
2498
0
            }
2499
2500
0
            break;
2501
2502
            /* boot records are just read and discarded for now... */
2503
0
        case ISO9660_BOOT_RECORD:
2504
0
            free(vd);
2505
#if 0
2506
            cnt = tsk_fs_read(fs, offs, (char *) b, sizeof(iso_bootrec));
2507
            if (cnt != sizeof(iso_bootrec)) {
2508
                if (cnt >= 0) {
2509
                    tsk_error_reset();
2510
                    tsk_error_set_errno(TSK_ERR_FS_READ);
2511
                }
2512
                tsk_error_set_errstr2("iso_load_vol_desc: Error reading");
2513
                return -1;
2514
            }
2515
            offs += sizeof(iso_bootrec);
2516
#endif
2517
0
            break;
2518
2519
0
        default:
2520
0
            free(vd);
2521
0
            break;
2522
0
        }
2523
0
    }
2524
2525
2526
    /* now that we have all primary and supplementary volume descs, we should cull the list of */
2527
    /* primary that match up with supplems, since supplem has all info primary has plus more. */
2528
    /* this will make jobs such as searching all volumes easier later */
2529
0
    for (s = iso->svd; s != NULL; s = s->next) {
2530
0
        for (p = iso->pvd; p != NULL; p = p->next) {
2531
            // see if they have the same starting address
2532
0
            if (tsk_getu32(fs->endian,
2533
0
                    p->pvd.pt_loc_m) == tsk_getu32(fs->endian,
2534
0
                    s->svd.pt_loc_m)) {
2535
                // see if it is the head of the list
2536
0
                if (p == iso->pvd) {
2537
0
                    iso->pvd = p->next;
2538
0
                }
2539
0
                else {
2540
0
                    iso9660_pvd_node *ptmp = iso->pvd;
2541
0
                    while (ptmp->next != p)
2542
0
                        ptmp = ptmp->next;
2543
0
                    ptmp->next = p->next;
2544
0
                }
2545
0
                p->next = NULL;
2546
0
                free(p);
2547
0
                p = NULL;
2548
                // count--;   // set but never used
2549
0
                break;
2550
0
            }
2551
0
        }
2552
0
    }
2553
2554
0
    if ((iso->pvd == NULL) && (iso->svd == NULL)) {
2555
0
        tsk_error_reset();
2556
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
2557
0
        tsk_error_set_errstr
2558
0
            ("load_vol_desc: primary and secondary volume descriptors null");
2559
0
        return -1;
2560
0
    }
2561
2562
2563
0
    return 0;
2564
0
}
2565
2566
2567
/* iso9660_open -
2568
 * opens an iso9660 filesystem.
2569
 * Design note: This function doesn't read a superblock, since iso9660 doesnt
2570
 * really have one.  Volume info is read in with a call to load_vol_descs().
2571
 */
2572
TSK_FS_INFO *
2573
iso9660_open(
2574
  TSK_IMG_INFO * img_info,
2575
  TSK_OFF_T offset,
2576
  TSK_FS_TYPE_ENUM ftype,
2577
  [[maybe_unused]] const char* a_pass,
2578
  uint8_t test)
2579
0
{
2580
0
    ISO_INFO *iso;
2581
0
    TSK_FS_INFO *fs;
2582
0
    uint8_t tmpguess[4];
2583
2584
0
    if (TSK_FS_TYPE_ISISO9660(ftype) == 0) {
2585
0
        tsk_error_reset();
2586
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
2587
0
        tsk_error_set_errstr("Invalid FS type in iso9660_open");
2588
0
        return NULL;
2589
0
    }
2590
2591
0
    if (img_info->sector_size == 0) {
2592
0
        tsk_error_reset();
2593
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
2594
0
        tsk_error_set_errstr("iso9660_open: sector size is 0");
2595
0
        return NULL;
2596
0
    }
2597
2598
0
    if (tsk_verbose) {
2599
0
        tsk_fprintf(stderr, "iso9660_open img_info: %" PRIu64
2600
0
            " ftype: %" PRIu8 " test: %" PRIu8 "\n", (uint64_t) img_info,
2601
0
            ftype, test);
2602
0
    }
2603
2604
0
    if ((iso = (ISO_INFO *) tsk_fs_malloc(sizeof(ISO_INFO))) == NULL) {
2605
0
        return NULL;
2606
0
    }
2607
0
    fs = &(iso->fs_info);
2608
2609
0
    iso->rr_found = 0;
2610
0
    iso->in_list = NULL;
2611
2612
0
    fs->ftype = TSK_FS_TYPE_ISO9660;
2613
0
    fs->duname = "Block";
2614
0
    fs->flags = TSK_FS_INFO_FLAG_NONE;
2615
0
    fs->tag = TSK_FS_INFO_TAG;
2616
0
    fs->img_info = img_info;
2617
0
    fs->offset = offset;
2618
2619
2620
    /* ISO has no magic to calibrate the endian ordering on and it
2621
     * stores all numbers in big and small endian.  We will use the big
2622
     * endian order so load up a 4-byte array and flags.  We could hardwire
2623
     * the definition, but I would rather use guessu32 in case I later add
2624
     * other initialization data to that function (since all other FSs use
2625
     * it) */
2626
0
    tmpguess[0] = 0;
2627
0
    tmpguess[1] = 0;
2628
0
    tmpguess[2] = 0;
2629
0
    tmpguess[3] = 1;
2630
0
    tsk_fs_guessu32(fs, tmpguess, 1);
2631
2632
    // we need a value here to test for RAW images. So, start with 2048
2633
0
    fs->block_size = 2048;
2634
2635
    /* load_vol_descs checks magic value */
2636
0
    if (load_vol_desc(fs) == -1) {
2637
0
        fs->tag = 0;
2638
0
        iso9660_close(fs);
2639
0
        if (tsk_verbose)
2640
0
            fprintf(stderr,
2641
0
                "iso9660_open: Error loading volume descriptor\n");
2642
0
        if (test)
2643
0
            return NULL;
2644
0
        else {
2645
0
            tsk_error_reset();
2646
0
            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
2647
0
            tsk_error_set_errstr("Invalid FS type in iso9660_open");
2648
0
            return NULL;
2649
0
        }
2650
0
    }
2651
2652
0
    if (iso->pvd) {
2653
0
        fs->block_size = tsk_getu16(fs->endian, iso->pvd->pvd.blk_sz_m);
2654
0
        fs->block_count = tsk_getu32(fs->endian, iso->pvd->pvd.vs_sz_m);
2655
2656
        /* Volume ID */
2657
0
        for (fs->fs_id_used = 0; fs->fs_id_used < 32; fs->fs_id_used++) {
2658
0
            fs->fs_id[fs->fs_id_used] =
2659
0
                iso->pvd->pvd.vol_id[fs->fs_id_used];
2660
0
        }
2661
2662
0
    }
2663
0
    else {
2664
0
        fs->block_size = tsk_getu16(fs->endian, iso->svd->svd.blk_sz_m);
2665
0
        fs->block_count = tsk_getu32(fs->endian, iso->svd->svd.vs_sz_m);
2666
2667
        /* Volume ID */
2668
0
        for (fs->fs_id_used = 0; fs->fs_id_used < 32; fs->fs_id_used++) {
2669
0
            fs->fs_id[fs->fs_id_used] =
2670
0
                iso->svd->svd.vol_id[fs->fs_id_used];
2671
0
        }
2672
0
    }
2673
2674
    /* We have seen this case on an image that seemed to be only
2675
     * setting blk_siz_l instead of both blk_sz_m and _l. We should
2676
     * support both in the future, but this prevents a crash later
2677
     * on when we divide by block_size. */
2678
0
    if (fs->block_size == 0) {
2679
0
        fs->tag = 0;
2680
0
        iso9660_close(fs);
2681
0
        if (tsk_verbose)
2682
0
            fprintf(stderr, "iso9660_open: Block size is 0\n");
2683
0
        if (test)
2684
0
            return NULL;
2685
0
        else {
2686
0
            tsk_error_reset();
2687
0
            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
2688
0
            tsk_error_set_errstr("Block size is 0");
2689
0
            return NULL;
2690
0
        }
2691
0
    }
2692
2693
0
    fs->first_block = 0;
2694
0
    fs->last_block = fs->last_block_act = fs->block_count - 1;
2695
2696
    // determine the last block we have in this image
2697
0
    if ((TSK_DADDR_T) ((img_info->size - offset) / fs->block_size) <
2698
0
        fs->block_count)
2699
0
        fs->last_block_act =
2700
0
            (img_info->size - offset) / fs->block_size - 1;
2701
2702
0
    fs->inum_count = iso9660_load_inodes_pt(iso);
2703
0
    if ((int) fs->inum_count == -1) {
2704
0
        fs->tag = 0;
2705
0
        iso9660_close(fs);
2706
0
        if (tsk_verbose)
2707
0
            fprintf(stderr, "iso9660_open: Error loading primary table\n");
2708
0
        return NULL;
2709
0
    }
2710
0
    fs->inum_count++;           // account for the orphan directory
2711
2712
0
    fs->last_inum = fs->inum_count - 1;
2713
0
    fs->first_inum = ISO9660_FIRSTINO;
2714
0
    fs->root_inum = ISO9660_ROOTINO;
2715
2716
2717
0
    fs->inode_walk = iso9660_inode_walk;
2718
0
    fs->block_walk = iso9660_block_walk;
2719
0
    fs->block_getflags = iso9660_block_getflags;
2720
2721
0
    fs->get_default_attr_type = iso9660_get_default_attr_type;
2722
0
    fs->load_attrs = iso9660_make_data_run;
2723
2724
0
    fs->file_add_meta = iso9660_inode_lookup;
2725
0
    fs->dir_open_meta = iso9660_dir_open_meta;
2726
0
    fs->fsstat = iso9660_fsstat;
2727
0
    fs->fscheck = iso9660_fscheck;
2728
0
    fs->istat = iso9660_istat;
2729
0
    fs->close = iso9660_close;
2730
0
    fs->name_cmp = iso9660_name_cmp;
2731
2732
0
    fs->jblk_walk = iso9660_jblk_walk;
2733
0
    fs->jentry_walk = iso9660_jentry_walk;
2734
0
    fs->jopen = iso9660_jopen;
2735
2736
0
    return fs;
2737
0
}