Coverage Report

Created: 2025-07-23 06:41

/src/sleuthkit/tsk/fs/yaffs.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
** The Sleuth Kit
3
**
4
** Brian Carrier [carrier <at> sleuthkit [dot] org]
5
** Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
6
** Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
7
**
8
** TASK
9
v** Copyright (c) 2002-2003 Brian Carrier, @stake Inc.  All rights reserved
10
**
11
** Copyright (c) 1997,1998,1999, International Business Machines
12
** Corporation and others. All Rights Reserved.
13
*/
14
15
/**
16
*\file yaffs.cpp
17
* Contains the internal TSK YAFFS2 file system functions.
18
*/
19
20
/* TCT
21
* LICENSE
22
* This software is distributed under the IBM Public License.
23
* AUTHOR(S)
24
* Wietse Venema
25
* IBM T.J. Watson Research
26
* P.O. Box 704
27
* Yorktown Heights, NY 10598, USA
28
--*/
29
30
#include <algorithm>
31
#include <map>
32
#include <memory>
33
#include <string>
34
#include <set>
35
#include <vector>
36
37
#include <string.h>
38
39
#include "tsk_fs_i.h"
40
#include "tsk_yaffs.h"
41
#include "tsk_fs.h"
42
43
/*
44
* Implementation Notes:
45
*    - As inode, we use object id and a version number derived from the
46
*      number of unique sequence ids for the object still left in the
47
*      file system.
48
*
49
*    - The version numbers start at 1 and increase as they get closer to
50
*      the the latest version.  Version number 0 is a special version
51
*      that is equivalent to the latest version (without having to know
52
*      the latest version number.)
53
*
54
*    - Since inodes are composed using the object id in the least
55
*      significant bits and the version up higher, requesting the
56
*      inode that matches the object id you are looking for will
57
*      retrieve the latest version of this object.
58
*
59
*    - Files always exist in the latest version of their parent directory
60
*      only.
61
*
62
*    - Filenames are not unique even with attached version numbers, since
63
*      version numbers are namespaced by inode.
64
*
65
*    - The cache stores a lot of info via the structure.  As this is
66
*      used for investigations, we assume these decisions will be updated
67
*      to expose the most useful view of this log based file system.  TSK
68
*      doesn't seem have a real way to expose a versioned view of a log
69
*      based file system like this.  Shoehorning it into the framework
70
*      ends up dropping some information.  I looked at using resource
71
*      streams as versions, but the abstraction breaks quickly.
72
*
73
*/
74
75
static const int TWELVE_BITS_MASK = 0xFFF; // Only keep 12 bits
76
77
static uint8_t
78
    yaffsfs_read_header(YAFFSFS_INFO *yfs, YaffsHeader ** header, TSK_OFF_T offset);
79
static uint8_t
80
    yaffsfs_load_attrs(TSK_FS_FILE *file);
81
82
/**
83
 * Generate an inode number based on the file's object and version numbers
84
 */
85
static TSK_RETVAL_ENUM
86
0
    yaffscache_obj_id_and_version_to_inode(uint32_t obj_id, uint32_t version_num, TSK_INUM_T *inode) {
87
0
        if ((obj_id & ~YAFFS_OBJECT_ID_MASK) != 0) {
88
0
            tsk_error_reset();
89
0
            tsk_error_set_errno(TSK_ERR_FS);
90
0
            tsk_error_set_errstr(
91
0
                "yaffsfs_parse_image_load_cache: Max object ID %" PRIu32 " is invalid", obj_id);
92
0
            return TSK_ERR;
93
0
        }
94
95
0
        if ((version_num & ~YAFFS_VERSION_NUM_MASK) != 0) {
96
0
            tsk_error_reset();
97
0
            tsk_error_set_errno(TSK_ERR_FS);
98
0
            tsk_error_set_errstr(
99
0
                "yaffsfs_parse_image_load_cache: Max version number %" PRIu32 " is invalid", version_num);
100
0
            return TSK_ERR;
101
0
        }
102
103
0
        *inode = obj_id | (version_num << YAFFS_VERSION_NUM_SHIFT);
104
0
        return TSK_OK;
105
0
}
106
107
/**
108
 * Given the TSK-generated inode address, extract the object id and version number from it
109
 */
110
static TSK_RETVAL_ENUM
111
0
    yaffscache_inode_to_obj_id_and_version(TSK_INUM_T inode, uint32_t *obj_id, uint32_t *version_num) {
112
0
        *obj_id = inode & YAFFS_OBJECT_ID_MASK;
113
0
        *version_num = (inode >> YAFFS_VERSION_NUM_SHIFT) & YAFFS_VERSION_NUM_MASK;
114
115
0
        return TSK_OK;
116
0
}
117
118
/*
119
* Order it like yaffs2.git does -- sort by (seq_num, offset/block)
120
*/
121
static int
122
    yaffscache_chunk_compare(YaffsCacheChunk *curr, uint32_t addee_obj_id, TSK_OFF_T addee_offset, uint32_t addee_seq_number)
123
0
{
124
0
    if (curr->ycc_obj_id == addee_obj_id) {
125
0
        if (curr->ycc_seq_number == addee_seq_number) {
126
0
            if (curr->ycc_offset == addee_offset) {
127
0
                return 0;
128
0
            }
129
0
            else if (curr->ycc_offset < addee_offset) {
130
0
                return -1;
131
0
            }
132
0
            else {
133
0
                return 1;
134
0
            }
135
0
        }
136
0
        else if (curr->ycc_seq_number < addee_seq_number) {
137
0
            return -1;
138
0
        }
139
0
        else {
140
0
            return 1;
141
0
        }
142
0
    }
143
0
    else if (curr->ycc_obj_id < addee_obj_id) {
144
0
        return -1;
145
0
    }
146
0
    else {
147
0
        return 1;
148
0
    }
149
0
}
150
151
static TSK_RETVAL_ENUM
152
    yaffscache_chunk_find_insertion_point(YAFFSFS_INFO *yfs, uint32_t obj_id, TSK_OFF_T offset, uint32_t seq_number, YaffsCacheChunk **chunk)
153
0
{
154
0
    YaffsCacheChunk *curr, *prev;
155
156
    // Have we seen this obj_id? If not, add an entry for it
157
0
    if (yfs->chunkMap->find(obj_id) == yfs->chunkMap->end()) {
158
0
        fflush(stderr);
159
0
        YaffsCacheChunkGroup chunkGroup;
160
0
        chunkGroup.cache_chunks_head = NULL;
161
0
        chunkGroup.cache_chunks_tail = NULL;
162
0
        yfs->chunkMap->insert(std::make_pair(obj_id, chunkGroup));
163
0
    }
164
165
0
    curr = yfs->chunkMap->operator[](obj_id).cache_chunks_head;
166
0
    prev = NULL;
167
168
0
    if (chunk == NULL) {
169
0
        return TSK_ERR;
170
0
    }
171
172
0
    while(curr != NULL) {
173
        // Compares obj id, then seq num, then offset. -1 => current < new
174
0
        int cmp = yaffscache_chunk_compare(curr, obj_id, offset, seq_number);
175
176
0
        if (cmp == 0) {
177
0
            *chunk = curr;
178
0
            return TSK_OK;
179
0
        }
180
0
        else if (cmp == 1) {
181
0
            *chunk = prev;
182
0
            return TSK_STOP;
183
0
        }
184
185
0
        prev = curr;
186
0
        curr = curr->ycc_next;
187
0
    }
188
189
0
    *chunk = prev;
190
0
    return TSK_STOP;
191
0
}
192
193
/**
194
 * Add a chunk to the cache.
195
 * @param yfs
196
 * @param offset Byte offset this chunk was found in (in the disk image)
197
 * @param seq_number Sequence number of this chunk
198
 * @param obj_id Object Id this chunk is associated with
199
 * @param parent_id Parent object ID that this chunk/object is associated with
200
 */
201
static TSK_RETVAL_ENUM
202
    yaffscache_chunk_add(YAFFSFS_INFO *yfs, TSK_OFF_T offset, uint32_t seq_number,
203
    uint32_t obj_id, uint32_t chunk_id, uint32_t parent_id)
204
0
{
205
0
    TSK_RETVAL_ENUM result;
206
0
    YaffsCacheChunk *prev;
207
0
    YaffsCacheChunk *chunk;
208
0
    if ((chunk = (YaffsCacheChunk*)tsk_malloc(sizeof(YaffsCacheChunk))) == NULL) {
209
0
        return TSK_ERR;
210
0
    }
211
212
0
    chunk->ycc_offset = offset;
213
0
    chunk->ycc_seq_number = seq_number;
214
0
    chunk->ycc_obj_id = obj_id;
215
0
    chunk->ycc_chunk_id = chunk_id;
216
0
    chunk->ycc_parent_id = parent_id;
217
218
    // Bit of a hack here. In some images, the root directory (obj_id = 1) lists iself as its parent
219
    // directory, which can cause issues later when we get directory contents. To prevent this,
220
    // if a chunk comes in with obj_id = 1 and parent_id = 1, manually set the parent ID to zero.
221
0
    if ((obj_id == 1) && (parent_id == 1)) {
222
0
        chunk->ycc_parent_id = 0;
223
0
    }
224
225
    // Find the chunk that should go right before the new chunk
226
0
    result = yaffscache_chunk_find_insertion_point(yfs, obj_id, offset, seq_number, &prev);
227
228
0
    if (result == TSK_ERR) {
229
0
        return TSK_ERR;
230
0
    }
231
232
0
    if (prev == NULL) {
233
        // No previous chunk - new chunk is the lowest we've seen and the new start of the list
234
0
        chunk->ycc_prev = NULL;
235
0
        chunk->ycc_next = yfs->chunkMap->operator[](obj_id).cache_chunks_head;
236
0
    }
237
0
    else {
238
0
        chunk->ycc_prev = prev;
239
0
        chunk->ycc_next = prev->ycc_next;
240
0
    }
241
242
0
    if (chunk->ycc_next != NULL) {
243
        // If we're not at the end, set the prev pointer on the next chunk to point to our new one
244
0
        chunk->ycc_next->ycc_prev = chunk;
245
0
    }
246
0
    else {
247
0
        yfs->chunkMap->operator[](obj_id).cache_chunks_tail = chunk;
248
0
    }
249
250
0
    if (chunk->ycc_prev != NULL) {
251
        // If we're not at the beginning, set the next pointer on the previous chunk to point at our new one
252
0
        chunk->ycc_prev->ycc_next = chunk;
253
0
    }
254
0
    else {
255
0
        yfs->chunkMap->operator[](obj_id).cache_chunks_head = chunk;
256
0
    }
257
258
0
    return TSK_OK;
259
0
}
260
261
262
/**
263
 * Get the file object from the cache.
264
 * @returns TSK_OK if it was found and TSK_STOP if we did not find it
265
 */
266
static TSK_RETVAL_ENUM
267
    yaffscache_object_find(YAFFSFS_INFO *yfs, uint32_t obj_id, YaffsCacheObject **obj)
268
0
{
269
0
    YaffsCacheObject *curr, *prev;
270
0
    curr = yfs->cache_objects;
271
0
    prev = NULL;
272
273
0
    if (obj == NULL) {
274
0
        return TSK_ERR;
275
0
    }
276
277
0
    while(curr != NULL) {
278
0
        if (curr->yco_obj_id == obj_id) {
279
0
            *obj = curr;
280
0
            return TSK_OK;
281
0
        }
282
0
        else if (curr->yco_obj_id > obj_id) {
283
0
            *obj = prev;
284
0
            return TSK_STOP;
285
0
        }
286
287
0
        prev = curr;
288
0
        curr = curr->yco_next;
289
0
    }
290
291
0
    *obj = prev;
292
0
    return TSK_STOP;
293
0
}
294
295
/**
296
 * Add an object to the cache if it does not already exist in there.
297
 * @returns TSK_ERR  on error, TSK_OK otherwise.
298
 */
299
static TSK_RETVAL_ENUM
300
    yaffscache_object_find_or_add(YAFFSFS_INFO *yfs, uint32_t obj_id, YaffsCacheObject **obj)
301
0
{
302
0
    YaffsCacheObject *prev;
303
0
    TSK_RETVAL_ENUM result;
304
305
0
    if (obj == NULL) {
306
0
        return TSK_ERR;
307
0
    }
308
309
    // Look for this obj_id in yfs->cache_objects
310
    // If not found, add it in the correct spot
311
    // yaffscache_object_find returns the last object with obj_id less than the one
312
    // we were searching for, so use that to insert the new one in the list
313
0
    result = yaffscache_object_find(yfs, obj_id, &prev);
314
0
    if (result == TSK_OK) {
315
0
        *obj = prev;
316
0
        return TSK_OK;
317
0
    }
318
0
    else if (result == TSK_STOP) {
319
0
        *obj = (YaffsCacheObject *) tsk_malloc(sizeof(YaffsCacheObject));
320
0
        (*obj)->yco_obj_id = obj_id;
321
0
        if (prev == NULL) {
322
0
            (*obj)->yco_next = yfs->cache_objects;
323
0
            yfs->cache_objects = *obj;
324
0
        }
325
0
        else {
326
0
            (*obj)->yco_next = prev->yco_next;
327
0
            prev->yco_next = (*obj);
328
0
        }
329
0
        return TSK_OK;
330
0
    }
331
0
    else {
332
0
        *obj = NULL;
333
0
        return TSK_ERR;
334
0
    }
335
0
}
336
337
static TSK_RETVAL_ENUM
338
    yaffscache_object_add_version(YaffsCacheObject *obj, YaffsCacheChunk *chunk)
339
0
{
340
0
    uint32_t ver_number;
341
0
    YaffsCacheChunk *header_chunk = NULL;
342
0
    YaffsCacheVersion *version;
343
344
    // Going to try ignoring unlinked/deleted headers (objID 3 and 4)
345
0
    if ((chunk->ycc_chunk_id == 0) && (chunk->ycc_parent_id != YAFFS_OBJECT_UNLINKED)
346
0
        &&(chunk->ycc_parent_id != YAFFS_OBJECT_DELETED)) {
347
0
            header_chunk = chunk;
348
0
    }
349
350
    /* If this is the second version (since last header_chunk is not NULL) and no
351
    * header was added, get rid of this incomplete old version -- can't be
352
    * reasonably recovered.
353
    *
354
    * TODO: These chunks are still in the structure and can be walked,
355
    *       but I'm not sure how to represent this set of data chunks
356
    *       with no metadata under TSK. This is rare and we don't have
357
    *       a testcase for it now. Punting right now.
358
    *
359
    * Edit: Shouldn't get to this point anymore. Changes to
360
    *       yaffscache_versions_insert_chunk make a version continue until it
361
    *       has a header block.
362
    */
363
0
    if (obj->yco_latest != NULL) {
364
0
        if (obj->yco_latest->ycv_header_chunk == NULL) {
365
0
            YaffsCacheVersion *incomplete = obj->yco_latest;
366
367
0
            if (tsk_verbose)
368
0
                tsk_fprintf(stderr, "yaffscache_object_add_version: "
369
0
                "removed an incomplete first version (no header)\n");
370
371
0
            obj->yco_latest = obj->yco_latest->ycv_prior;
372
0
            free(incomplete);
373
0
        }
374
0
    }
375
376
0
    if (obj->yco_latest != NULL) {
377
0
        ver_number = obj->yco_latest->ycv_version + 1;
378
379
        /* Until a new header is given, use the last seen header. */
380
0
        if (header_chunk == NULL) {
381
0
            header_chunk = obj->yco_latest->ycv_header_chunk;
382
383
            // If we haven't seen a good header yet and we have a deleted/unlinked one, use it
384
0
            if ((header_chunk == NULL) && (chunk->ycc_chunk_id == 0)) {
385
0
                header_chunk = chunk;
386
0
            }
387
0
        }
388
0
    }
389
0
    else {
390
0
        ver_number = 1;
391
0
    }
392
393
0
    if ((version = (YaffsCacheVersion *) tsk_malloc(sizeof(YaffsCacheVersion))) == NULL) {
394
0
        return TSK_ERR;
395
0
    }
396
397
0
    version->ycv_prior = obj->yco_latest;
398
0
    version->ycv_version = ver_number;
399
0
    version->ycv_seq_number = chunk->ycc_seq_number;
400
0
    version->ycv_header_chunk = header_chunk;
401
0
    version->ycv_first_chunk = chunk;
402
0
    version->ycv_last_chunk = chunk;
403
404
0
    obj->yco_latest = version;
405
406
0
    return TSK_OK;
407
0
}
408
409
/**
410
 * Add a chunk to its corresponding object in the cache.
411
 */
412
static TSK_RETVAL_ENUM
413
    yaffscache_versions_insert_chunk(YAFFSFS_INFO *yfs, YaffsCacheChunk *chunk)
414
0
{
415
0
    YaffsCacheObject *obj;
416
0
    TSK_RETVAL_ENUM result;
417
0
    YaffsCacheVersion *version;
418
419
    // Building a list in yfs->cache_objects, sorted by obj_id
420
0
    result = yaffscache_object_find_or_add(yfs, chunk->ycc_obj_id, &obj);
421
0
    if (result != TSK_OK) {
422
0
        return TSK_ERR;
423
0
    }
424
0
    version = obj->yco_latest;
425
426
    /* First chunk in this object? */
427
0
    if (version == NULL) {
428
0
        yaffscache_object_add_version(obj, chunk);
429
0
    }
430
0
    else {
431
        /* Chunk in the same update? */
432
0
        if (chunk->ycc_seq_number == version->ycv_seq_number) {
433
0
            version->ycv_last_chunk = chunk;
434
0
            if ((chunk->ycc_chunk_id == 0) && (chunk->ycc_parent_id != YAFFS_OBJECT_UNLINKED)
435
0
                &&(chunk->ycc_parent_id != YAFFS_OBJECT_DELETED)) {
436
0
                    version->ycv_header_chunk = chunk;
437
0
            }
438
0
            else if ((chunk->ycc_chunk_id == 0) && (version->ycv_header_chunk == NULL)) {
439
0
                version->ycv_header_chunk = chunk;
440
0
            }
441
0
        }
442
        // If there was no header for the last version, continue adding to it instead
443
        // of starting a new version.
444
0
        else if (version->ycv_header_chunk == NULL) {
445
0
            version->ycv_seq_number = chunk->ycc_seq_number;
446
0
            version->ycv_last_chunk = chunk;
447
0
            if ((chunk->ycc_chunk_id == 0) && (chunk->ycc_parent_id != YAFFS_OBJECT_UNLINKED)
448
0
                &&(chunk->ycc_parent_id != YAFFS_OBJECT_DELETED)) {
449
0
                    version->ycv_header_chunk = chunk;
450
0
            }
451
0
            else if ((chunk->ycc_chunk_id == 0) && (version->ycv_header_chunk == NULL)) {
452
0
                version->ycv_header_chunk = chunk;
453
0
            }
454
0
        }
455
0
        else if (chunk->ycc_chunk_id == 0) {   // Directories only have a header block
456
            // If we're looking at a new version of a directory where the previous version had the same name,
457
            // leave everything in the same version. Multiple versions of the same directory aren't really giving us
458
            // any information.
459
0
            YaffsHeader * newHeader;
460
0
            yaffsfs_read_header(yfs, &newHeader, chunk->ycc_offset);
461
0
            if ((newHeader != NULL) && (newHeader->obj_type == YAFFS_TYPE_DIRECTORY)) {
462
                // Read in the old header
463
0
                YaffsHeader * oldHeader;
464
0
                yaffsfs_read_header(yfs, &oldHeader, version->ycv_header_chunk->ycc_offset);
465
0
                if ((oldHeader != NULL) && (oldHeader->obj_type == YAFFS_TYPE_DIRECTORY) &&
466
0
                    (0 == strncmp(oldHeader->name, newHeader->name, YAFFS_HEADER_NAME_LENGTH))) {
467
0
                        version->ycv_seq_number = chunk->ycc_seq_number;
468
0
                        version->ycv_last_chunk = chunk;
469
0
                        version->ycv_header_chunk = chunk;
470
0
                }
471
0
                else {
472
                    // The older header either isn't a directory or it doesn't have the same name, so leave it
473
                    // as its own version
474
0
                    yaffscache_object_add_version(obj, chunk);
475
0
                }
476
0
            }
477
0
            else {
478
                //  Not a directory
479
0
                yaffscache_object_add_version(obj, chunk);
480
0
            }
481
0
        }
482
0
        else {
483
            //  Otherwise, add this chunk as the start of a new version
484
0
            yaffscache_object_add_version(obj, chunk);
485
0
        }
486
0
    }
487
488
0
    return TSK_OK;
489
0
}
490
491
static TSK_RETVAL_ENUM
492
    yaffscache_versions_compute(YAFFSFS_INFO *yfs)
493
0
{
494
0
    std::map<unsigned int,YaffsCacheChunkGroup>::iterator iter;
495
0
    for( iter = yfs->chunkMap->begin(); iter != yfs->chunkMap->end(); ++iter ) {
496
0
        YaffsCacheChunk *chunk_curr = yfs->chunkMap->operator[](iter->first).cache_chunks_head;
497
498
0
        while(chunk_curr != NULL) {
499
0
            if (yaffscache_versions_insert_chunk(yfs, chunk_curr) != TSK_OK) {
500
0
                return TSK_ERR;
501
0
            }
502
503
0
            chunk_curr = chunk_curr->ycc_next;
504
0
        }
505
0
    }
506
507
0
    return TSK_OK;
508
0
}
509
510
/**
511
 * Callback for yaffscache_find_children()
512
 * @param obj Object that is a child
513
 * @param version Version of the object
514
 * @param args Pointer to what was passed into yaffscache_find_children
515
 */
516
typedef TSK_RETVAL_ENUM yc_find_children_cb(YaffsCacheObject *obj, YaffsCacheVersion *version, void *args);
517
518
/**
519
 * Search the cache for objects that are children of the given address.
520
 * @param yfs
521
 * @param parent_inode Inode of folder/directory
522
 * @param cb Call back to call for each found child
523
 * @param args Pointer to structure that will be passed to cb
524
 * @returns TSK_ERR on error
525
 */
526
static TSK_RETVAL_ENUM
527
    yaffscache_find_children(YAFFSFS_INFO *yfs, TSK_INUM_T parent_inode, yc_find_children_cb cb, void *args)
528
0
{
529
0
    YaffsCacheObject *obj;
530
531
0
    uint32_t parent_id, version_num;
532
0
    if (yaffscache_inode_to_obj_id_and_version(parent_inode, &parent_id, &version_num) != TSK_OK) {
533
0
        return TSK_ERR;
534
0
    }
535
536
    /* Iterate over all objects and all versions of the objects to see if one is the child
537
     * of the given parent. */
538
0
    for (obj = yfs->cache_objects; obj != NULL; obj = obj->yco_next) {
539
0
        YaffsCacheVersion *version;
540
0
        for (version = obj->yco_latest; version != NULL; version = version->ycv_prior) {
541
            /* Is this an incomplete version? */
542
0
            if (version->ycv_header_chunk == NULL) {
543
0
                continue;
544
0
            }
545
546
0
            if (version->ycv_header_chunk->ycc_parent_id == parent_id) {
547
0
                TSK_RETVAL_ENUM result = cb(obj, version, args);
548
0
                if (result != TSK_OK)
549
0
                    return result;
550
0
            }
551
0
        }
552
0
    }
553
554
0
    return TSK_OK;
555
0
}
556
557
/**
558
 * Lookup an object based on its inode.
559
 * @param yfs
560
 * @param inode
561
 * @param version [out] Pointer to store version of the object that was found (if inode had a version of 0)
562
 * @param obj_ret [out] Pointer to store found object into
563
 * @returns TSK_ERR on error.
564
 */
565
static TSK_RETVAL_ENUM
566
0
    yaffscache_version_find_by_inode(YAFFSFS_INFO *yfs, TSK_INUM_T inode, YaffsCacheVersion **version, YaffsCacheObject **obj_ret) {
567
0
        uint32_t obj_id, version_num;
568
0
        YaffsCacheObject *obj;
569
0
        YaffsCacheVersion *curr;
570
571
0
        if (version == NULL) {
572
0
            return TSK_ERR;
573
0
        }
574
575
        // convert inode to obj and version and find it in cache
576
0
        if (yaffscache_inode_to_obj_id_and_version(inode, &obj_id, &version_num) != TSK_OK) {
577
0
            *version = NULL;
578
0
            return TSK_ERR;
579
0
        }
580
581
0
        if (yaffscache_object_find(yfs, obj_id, &obj) != TSK_OK) {
582
0
            *version = NULL;
583
0
            return TSK_ERR;
584
0
        }
585
586
0
        if (version_num == 0) {
587
0
            if (obj_ret != NULL) {
588
0
                *obj_ret = obj;
589
0
            }
590
0
            *version = obj->yco_latest;
591
0
            return TSK_OK;
592
0
        }
593
594
        // Find the requested version in the list.
595
0
        for(curr = obj->yco_latest; curr != NULL; curr = curr->ycv_prior) {
596
0
            if (curr->ycv_version == version_num) {
597
0
                if (obj_ret != NULL) {
598
0
                    *obj_ret = obj;
599
0
                }
600
0
                *version = curr;
601
0
                return TSK_OK;
602
0
            }
603
0
        }
604
605
0
        if (obj_ret != NULL) {
606
0
            *obj_ret = NULL;
607
0
        }
608
0
        *version = NULL;
609
0
        return TSK_ERR;
610
0
}
611
612
static void
613
    yaffscache_object_dump(FILE *fp, YaffsCacheObject *obj)
614
0
{
615
0
    YaffsCacheVersion *next_version = obj->yco_latest;
616
0
    YaffsCacheChunk *chunk = next_version->ycv_last_chunk;
617
618
0
    fprintf(fp, "Object %d\n", obj->yco_obj_id);
619
0
    while(chunk != NULL && chunk->ycc_obj_id == obj->yco_obj_id) {
620
0
        if (next_version != NULL &&
621
0
            chunk == next_version->ycv_last_chunk) {
622
0
                fprintf(fp, "  @%d: %p %p %p\n",
623
0
                    next_version->ycv_version,
624
0
                    (void*) next_version->ycv_header_chunk,
625
0
                    (void*) next_version->ycv_first_chunk,
626
0
                    (void*)next_version->ycv_last_chunk);
627
0
                next_version = next_version->ycv_prior;
628
0
        }
629
630
0
        fprintf(fp, "    + %p %08x %08x %0" PRIxOFF "\n",
631
0
            (void*) chunk,
632
0
            chunk->ycc_chunk_id,
633
0
            chunk->ycc_seq_number,
634
0
            chunk->ycc_offset);
635
636
0
        chunk = chunk->ycc_prev;
637
0
    }
638
0
}
639
640
/*
641
static void
642
    yaffscache_objects_dump(FILE *fp, YAFFSFS_INFO *yfs)
643
{
644
    YaffsCacheObject *obj;
645
646
    for(obj = yfs->cache_objects; obj != NULL; obj = obj->yco_next)
647
        yaffscache_object_dump(fp, obj);
648
}
649
*/
650
651
static void
652
    yaffscache_objects_stats(YAFFSFS_INFO *yfs,
653
    unsigned int *obj_count,
654
    uint32_t *obj_first, uint32_t *obj_last,
655
    uint32_t *version_count,
656
    uint32_t *version_first, uint32_t *version_last)
657
0
{
658
0
    YaffsCacheObject *obj;
659
0
    YaffsCacheVersion *ver;
660
661
    /* deleted and unlinked special objects don't have headers */
662
0
    *obj_count = 2;
663
0
    *obj_first = 0xffffffff;
664
0
    *obj_last = 0;
665
666
0
    *version_count = 0;
667
0
    *version_first = 0xffffffff;
668
0
    *version_last = 0;
669
670
0
    for(obj = yfs->cache_objects; obj != NULL; obj = obj->yco_next) {
671
0
        *obj_count += 1;
672
0
        if (obj->yco_obj_id < *obj_first)
673
0
            *obj_first = obj->yco_obj_id;
674
0
        if (obj->yco_obj_id > *obj_last)
675
0
            *obj_last = obj->yco_obj_id;
676
677
0
        for(ver = obj->yco_latest; ver != NULL; ver = ver->ycv_prior) {
678
0
            *version_count += 1;
679
0
            if (ver->ycv_seq_number < *version_first)
680
0
                *version_first = ver->ycv_seq_number;
681
0
            if (ver->ycv_seq_number > *version_last)
682
0
                *version_last = ver->ycv_seq_number;
683
0
        }
684
0
    }
685
0
}
686
687
static void
688
    yaffscache_objects_free(YAFFSFS_INFO *yfs)
689
0
{
690
0
    if ((yfs != NULL) && (yfs->cache_objects != NULL)) {
691
0
        YaffsCacheObject *obj = yfs->cache_objects;
692
0
        while(obj != NULL) {
693
0
            YaffsCacheObject *to_free = obj;
694
695
0
            YaffsCacheVersion *ver = obj->yco_latest;
696
0
            while(ver != NULL) {
697
0
                YaffsCacheVersion *v_to_free = ver;
698
0
                ver = ver->ycv_prior;
699
0
                free(v_to_free);
700
0
            }
701
702
0
            obj = obj->yco_next;
703
0
            free(to_free);
704
0
        }
705
0
    }
706
0
}
707
708
static void
709
    yaffscache_chunks_free(YAFFSFS_INFO *yfs)
710
0
{
711
0
    if ((yfs != NULL) && (yfs->chunkMap != NULL)) {
712
        // Free the YaffsCacheChunks in each ChunkGroup
713
0
        std::map<unsigned int,YaffsCacheChunkGroup>::iterator iter;
714
0
        for( iter = yfs->chunkMap->begin(); iter != yfs->chunkMap->end(); ++iter ) {
715
0
            YaffsCacheChunk *chunk = yfs->chunkMap->operator[](iter->first).cache_chunks_head;
716
0
            while(chunk != NULL) {
717
0
                YaffsCacheChunk *to_free = chunk;
718
0
                chunk = chunk->ycc_next;
719
0
                free(to_free);
720
0
            }
721
0
        }
722
723
        // Free the map
724
0
        yfs->chunkMap->clear();
725
0
        delete yfs->chunkMap;
726
0
    }
727
728
0
}
729
730
731
732
/*
733
* Parsing and helper functions
734
*
735
*
736
*/
737
738
/* Function to parse config file
739
 *
740
 * @param img_info Image info for this image
741
 * @param map<string, int> Stores values from config file indexed on parameter name
742
 * @returns YAFFS_CONFIG_STATUS One of  YAFFS_CONFIG_OK, YAFFS_CONFIG_FILE_NOT_FOUND, or YAFFS_CONFIG_ERROR
743
 */
744
static YAFFS_CONFIG_STATUS
745
0
yaffs_load_config_file(TSK_IMG_INFO * a_img_info, std::map<std::string, std::string> & results) {
746
0
    size_t config_file_name_len;
747
0
    TSK_TCHAR * config_file_name;
748
0
    FILE* config_file;
749
0
    char buf[1001];
750
751
    // Ensure there is at least one image name
752
0
    if (a_img_info->num_img < 1) {
753
0
        return YAFFS_CONFIG_ERROR;
754
0
    }
755
756
    // Construct the name of the config file from the first image name
757
0
    config_file_name_len = TSTRLEN(a_img_info->images[0]);
758
0
    config_file_name_len += TSTRLEN(YAFFS_CONFIG_FILE_SUFFIX);
759
0
    config_file_name = (TSK_TCHAR *) tsk_malloc(sizeof(TSK_TCHAR) * (config_file_name_len + 1));
760
761
0
    TSTRNCPY(config_file_name, a_img_info->images[0], config_file_name_len + 1);
762
0
    TSTRNCAT(config_file_name, YAFFS_CONFIG_FILE_SUFFIX, TSTRLEN(YAFFS_CONFIG_FILE_SUFFIX) + 1);
763
764
#ifdef TSK_WIN32
765
    HANDLE hWin;
766
767
    if ((hWin = CreateFile(config_file_name, GENERIC_READ,
768
            FILE_SHARE_READ, 0, OPEN_EXISTING, 0,
769
            0)) == INVALID_HANDLE_VALUE) {
770
771
        // For the moment, assume that the file just doesn't exist, which isn't an error
772
        free(config_file_name);
773
        return YAFFS_CONFIG_FILE_NOT_FOUND;
774
    }
775
    config_file = _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r");
776
    if (config_file == NULL) {
777
        tsk_error_reset();
778
        tsk_error_set_errno(TSK_ERR_FS);
779
        tsk_error_set_errstr(
780
                    "yaffs_load_config: Error converting Windows handle to C handle");
781
        free(config_file_name);
782
        CloseHandle(hWin);
783
        return YAFFS_CONFIG_ERROR;
784
    }
785
#else
786
0
    if (NULL == (config_file = fopen(config_file_name, "r"))) {
787
0
        free(config_file_name);
788
0
        return YAFFS_CONFIG_FILE_NOT_FOUND;
789
0
    }
790
0
#endif
791
792
0
    while(fgets(buf, 1000, config_file) != NULL) {
793
794
        // Is it a comment?
795
0
        if ((buf[0] == '#') || (buf[0] == ';')) {
796
0
            continue;
797
0
        }
798
799
        // Is there a '=' ?
800
0
        if (strchr(buf, '=') == NULL) {
801
0
            continue;
802
0
        }
803
804
        // Copy to strings while removing whitespace and converting to lower case
805
0
        std::string paramName("");
806
0
        std::string paramVal("");
807
808
0
        const char * paramNamePtr = strtok(buf, "=");
809
0
        while(*paramNamePtr != '\0') {
810
0
            if (! isspace((char)(*paramNamePtr))) {
811
0
                paramName += tolower((char)(*paramNamePtr));
812
0
            }
813
0
            paramNamePtr++;
814
0
        }
815
816
0
        const char * paramValPtr = strtok(NULL, "=");
817
0
        while(*paramValPtr != '\0') {
818
0
            if (! isspace(*paramValPtr)) {
819
0
                paramVal += tolower((char)(*paramValPtr));
820
0
            }
821
0
            paramValPtr++;
822
0
        }
823
824
        // Make sure this parameter is not already in the map
825
0
        if (results.find(paramName) != results.end()) {
826
            // Duplicate parameter - return an error
827
0
            tsk_error_reset();
828
0
            tsk_error_set_errno(TSK_ERR_FS);
829
0
            tsk_error_set_errstr(
830
0
                        "yaffs_load_config: Duplicate parameter name in config file (\"%s\"). %s", paramName.c_str(), YAFFS_HELP_MESSAGE);
831
0
            fclose(config_file);
832
0
            free(config_file_name);
833
0
            return YAFFS_CONFIG_ERROR;
834
0
        }
835
836
        // Add this entry to the map
837
0
        results[paramName] = paramVal;
838
0
    }
839
840
0
    fclose(config_file);
841
0
    free(config_file_name);
842
0
    return YAFFS_CONFIG_OK;
843
0
}
844
845
/*
846
 * Helper function for yaffs_validate_config
847
 * Tests that a string consists only of digits and has at least one digit
848
 * (Can modify later if we want negative fields to be valid)
849
 *
850
 * @param numStr String to test
851
 * @returns 1 on error, 0 on success
852
 */
853
static int
854
0
yaffs_validate_integer_field(std::string numStr) {
855
0
    unsigned int i;
856
857
    // Test if empty
858
0
    if (numStr.length() == 0) {
859
0
        return 1;
860
0
    }
861
862
    // Test each character
863
0
    for(i = 0;i < numStr.length();i++) {
864
0
        if (isdigit(numStr[i]) == 0) {
865
0
            return 1;
866
0
        }
867
0
    }
868
869
0
    return 0;
870
0
}
871
872
/*
873
 * Function to validate the contents of the config file
874
 * Currently testing:
875
 *  All YAFFS_CONFIG fields should be integers (if they exist)
876
 *  Either need all three of YAFFS_CONFIG_SEQ_NUM_STR, YAFFS_CONFIG_OBJ_ID_STR, YAFFS_CONFIG_CHUNK_ID_STR
877
 *   or none of them
878
 *
879
 * @param paramMap Holds mapping of parameter name to parameter value
880
 * @returns 1 on error (invalid parameters), 0 on success
881
 */
882
static int
883
0
yaffs_validate_config_file(std::map<std::string, std::string> & paramMap) {
884
0
    int offset_field_count;
885
886
    // Make a list of all fields to test
887
0
    std::set<std::string> integerParams;
888
0
    integerParams.insert(YAFFS_CONFIG_SEQ_NUM_STR);
889
0
    integerParams.insert(YAFFS_CONFIG_OBJ_ID_STR);
890
0
    integerParams.insert(YAFFS_CONFIG_CHUNK_ID_STR);
891
0
    integerParams.insert(YAFFS_CONFIG_PAGE_SIZE_STR);
892
0
    integerParams.insert(YAFFS_CONFIG_SPARE_SIZE_STR);
893
0
    integerParams.insert(YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR);
894
895
    // If the parameter is set, verify that the value is an int
896
0
    for(std::set<std::string>::iterator it = integerParams.begin();it != integerParams.end();it++) {
897
0
        if ((paramMap.find(*it) != paramMap.end()) &&
898
0
            (0 != yaffs_validate_integer_field(paramMap[*it]))) {
899
0
            tsk_error_reset();
900
0
            tsk_error_set_errno(TSK_ERR_FS);
901
0
            tsk_error_set_errstr(
902
0
                        "yaffs_validate_config_file: Empty or non-integer value for Yaffs2 parameter \"%s\". %s", (*it).c_str(), YAFFS_HELP_MESSAGE);
903
0
            return 1;
904
0
        }
905
0
    }
906
907
    // Check that we have all three spare offset fields, or none of the three
908
0
    offset_field_count = 0;
909
0
    if (paramMap.find(YAFFS_CONFIG_SEQ_NUM_STR) != paramMap.end()) {
910
0
        offset_field_count++;
911
0
    }
912
0
    if (paramMap.find(YAFFS_CONFIG_OBJ_ID_STR) != paramMap.end()) {
913
0
        offset_field_count++;
914
0
    }
915
0
    if (paramMap.find(YAFFS_CONFIG_CHUNK_ID_STR) != paramMap.end()) {
916
0
        offset_field_count++;
917
0
    }
918
919
0
    if (! ((offset_field_count == 0) || (offset_field_count == 3))) {
920
0
            tsk_error_reset();
921
0
            tsk_error_set_errno(TSK_ERR_FS);
922
0
            tsk_error_set_errstr(
923
0
                        "yaffs_validate_config_file: Require either all three spare offset fields or none. %s", YAFFS_HELP_MESSAGE);
924
0
            return 1;
925
0
    }
926
927
    // Make sure there aren't any unexpected fields present
928
0
    for(std::map<std::string, std::string>::iterator it = paramMap.begin(); it != paramMap.end();it++) {
929
0
        if (integerParams.find(it->first) == integerParams.end()) {
930
0
            tsk_error_reset();
931
0
            tsk_error_set_errno(TSK_ERR_FS);
932
0
            tsk_error_set_errstr(
933
0
                        "yaffs_validate_config_file: Found unexpected field in config file (\"%s\"). %s", it->first.c_str(), YAFFS_HELP_MESSAGE);
934
0
            return 1;
935
0
        }
936
0
    }
937
938
0
    return 0;
939
0
}
940
941
/*
942
* Function to attempt to determine the layout of the yaffs spare area.
943
* Results of the analysis (if the format could be determined) will be stored
944
* in yfs variables.
945
*
946
* @param yfs File system being analyzed
947
* @param maxBlocksToTest Number of block groups to scan to detect spare area or 0 if there is no limit.
948
* @returns TSK_ERR if format could not be detected and TSK_OK if it could be.
949
*/
950
static TSK_RETVAL_ENUM
951
0
yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest) {
952
953
    // Testing parameters - can all be changed
954
0
    unsigned int blocksToTest = 10;  // Number of blocks (64 chunks) to test
955
0
    unsigned int chunksToTest = 10;  // Number of chunks to test in each block
956
0
    unsigned int minChunksRead = 10; // Minimum number of chunks we require to run the test (we might not get the full number we want to test for a very small file)
957
958
0
    unsigned int chunkSize = yfs->page_size + yfs->spare_size;
959
0
    unsigned int blockSize = yfs->chunks_per_block * chunkSize;
960
961
0
    TSK_FS_INFO *fs = &(yfs->fs_info);
962
0
    unsigned char *spareBuffer;
963
964
0
    unsigned int blockIndex;
965
0
    unsigned int chunkIndex;
966
967
0
    unsigned int currentOffset;
968
969
0
    unsigned char * allSpares;
970
0
    unsigned int allSparesLength;
971
972
0
    TSK_OFF_T maxBlocks;
973
974
0
    bool skipBlock;
975
0
    int goodOffset;
976
0
    unsigned int nGoodSpares;
977
0
    unsigned int nBlocksTested;
978
979
0
    int okOffsetFound = 0;   // Used as a flag for if we've found an offset that sort of works but doesn't seem great
980
0
    int goodOffsetFound = 0; // Flag to mark that we've found an offset that also passed secondary testing
981
0
    int bestOffset = 0;
982
983
0
    bool allSameByte; // Used in test that the spare area fields not be one repeated byte
984
985
0
    unsigned int i;
986
987
0
    int thisChunkBase;
988
0
    int lastChunkBase;
989
990
    // The spare area needs to be at least 16 bytes to run the test
991
0
    if (yfs->spare_size < 16) {
992
0
        if (tsk_verbose && (! yfs->autoDetect)) {
993
0
            tsk_fprintf(stderr,
994
0
                "yaffs_initialize_spare_format failed - given spare size (%d) is not large enough to contain needed fields\n", yfs->spare_size);
995
0
        }
996
0
        return TSK_ERR;
997
0
    }
998
999
0
    if ((spareBuffer = (unsigned char*) tsk_malloc(yfs->spare_size)) == NULL) {
1000
0
        return TSK_ERR;
1001
0
    }
1002
1003
0
    allSparesLength = yfs->spare_size * blocksToTest * chunksToTest;
1004
0
    if ((allSpares = (unsigned char*) tsk_malloc(allSparesLength)) == NULL) {
1005
0
        free(spareBuffer);
1006
0
        return TSK_ERR;
1007
0
    }
1008
1009
    // Initialize the pointers to one of the configurations we've seen (thought these defaults should not get used)
1010
0
    yfs->spare_seq_offset = 0;
1011
0
    yfs->spare_obj_id_offset = 4;
1012
0
    yfs->spare_chunk_id_offset = 8;
1013
0
    yfs->spare_nbytes_offset = 12;
1014
1015
    // Assume the data we want is 16 consecutive bytes in the order:
1016
    //  seq num, obj id, chunk id, byte count
1017
    //  (not sure we're guaranteed this but we wouldn't be able to deal with the alternative anyway)
1018
    // Seq num is the important one. This number is constant in each block (block = 64 chunks), meaning
1019
    //  all chunks in a block will share the same sequence number. The YAFFS2 descriptions would seem to
1020
    //  indicate it should be different for each block, but this doesn't seem to always be the case.
1021
    //  In particular we frequently see the 0x1000 seq number used over multiple blocks, but this isn't the only
1022
    //  observed exception.
1023
1024
    // Calculate the number of blocks in the image
1025
0
    maxBlocks = yfs->fs_info.img_info->size / (yfs->chunks_per_block * chunkSize);
1026
1027
    // If maxBlocksToTest = 0 (unlimited), set it to the total number of blocks
1028
    // Also reduce the number of blocks to test if it is larger than the total number of blocks
1029
0
    if ((maxBlocksToTest == 0) || (maxBlocksToTest > maxBlocks)) {
1030
0
        maxBlocksToTest = maxBlocks;
1031
0
    }
1032
1033
0
    nGoodSpares = 0;
1034
0
    nBlocksTested = 0;
1035
0
    for (TSK_OFF_T blockIndex = 0;blockIndex < maxBlocksToTest;blockIndex++) {
1036
1037
        // Read the last spare area that we want to test first
1038
0
        TSK_OFF_T offset = (TSK_OFF_T)blockIndex * blockSize + (chunksToTest - 1) * chunkSize + yfs->page_size;
1039
0
        ssize_t cnt = tsk_img_read(fs->img_info, offset, (char *) spareBuffer,
1040
0
            yfs->spare_size);
1041
0
        if ((cnt < 0) || ((unsigned int)cnt < yfs->spare_size)) {
1042
0
            break;
1043
0
        }
1044
1045
        // Is the spare all 0xff / 0x00?
1046
        // If not, we know we should have all allocated chunks since YAFFS2 writes sequentially in a block
1047
        //  - can't have an unallocated chunk followed by an allocated one
1048
        // We occasionally see almost all null spare area with a few 0xff, which is not a valid spare.
1049
0
        skipBlock = true;
1050
0
        for (i = 0;i < yfs->spare_size;i++) {
1051
0
            if ((spareBuffer[i] != 0xff) && (spareBuffer[i] != 0x00)) {
1052
0
                skipBlock = false;
1053
0
                break;
1054
0
            }
1055
0
        }
1056
1057
0
        if (skipBlock) {
1058
0
            continue;
1059
0
        }
1060
1061
        // If this block is potentialy valid (i.e., the spare contains something besides 0x00 and 0xff), copy all the spares into
1062
        // the big array of extracted spare areas
1063
1064
        // Copy this spare area
1065
0
        nGoodSpares++;
1066
0
        for (i = 0;i < yfs->spare_size;i++) {
1067
0
            allSpares[nBlocksTested * yfs->spare_size * chunksToTest + (chunksToTest - 1) * yfs->spare_size + i] = spareBuffer[i];
1068
0
        }
1069
1070
        // Copy all earlier spare areas in the block
1071
0
        for (chunkIndex = 0;chunkIndex < chunksToTest - 1;chunkIndex++) {
1072
0
            offset = blockIndex * blockSize + chunkIndex * chunkSize + yfs->page_size;
1073
0
            cnt = tsk_img_read(fs->img_info, offset, (char *) spareBuffer,
1074
0
                yfs->spare_size);
1075
0
            if ((cnt < 0) || ((unsigned int)cnt < yfs->spare_size)) {
1076
                // We really shouldn't run out of data here since we already read in the furthest entry
1077
0
                break; // Break out of chunksToTest loop
1078
0
            }
1079
1080
0
            nGoodSpares++;
1081
0
            for(i = 0;i < yfs->spare_size;i++) {
1082
0
                allSpares[nBlocksTested * yfs->spare_size * chunksToTest + chunkIndex * yfs->spare_size + i] = spareBuffer[i];
1083
0
            }
1084
0
        }
1085
1086
        // Record that we've found a potentially valid block
1087
0
        nBlocksTested++;
1088
1089
        // If we've found enough potentailly valid blocks, break
1090
0
        if (nBlocksTested >= blocksToTest) {
1091
0
            break;
1092
0
        }
1093
0
    }
1094
1095
    // Make sure we read enough data to reasonably perform the testing
1096
0
    if (nGoodSpares < minChunksRead) {
1097
1098
0
        if (tsk_verbose && (! yfs->autoDetect)) {
1099
0
            tsk_fprintf(stderr,
1100
0
                "yaffs_initialize_spare_format failed - not enough potentially valid data could be read\n");
1101
0
        }
1102
1103
0
        free(spareBuffer);
1104
0
        free(allSpares);
1105
0
        return TSK_ERR;
1106
0
    }
1107
1108
0
    if (tsk_verbose && (! yfs->autoDetect)) {
1109
0
        tsk_fprintf(stderr,
1110
0
            "yaffs_initialize_spare_format: Testing potential offsets for the sequence number in the spare area\n");
1111
0
    }
1112
1113
    // Print out the collected spare areas if we're in verbose mode
1114
0
    if (tsk_verbose && (! yfs->autoDetect)) {
1115
0
        for(blockIndex = 0;blockIndex < nBlocksTested;blockIndex++) {
1116
0
            for(chunkIndex = 0;chunkIndex < chunksToTest;chunkIndex++) {
1117
0
                for(i = 0;i < yfs->spare_size;i++) {
1118
0
                    fprintf(stderr, "%02x", allSpares[blockIndex * yfs->spare_size * chunksToTest + chunkIndex * yfs->spare_size + i]);
1119
0
                }
1120
0
                fprintf(stderr, "\n");
1121
0
            }
1122
0
        }
1123
0
    }
1124
1125
    // Test all indices into the spare area (that leave enough space for all 16 bytes)
1126
0
    for(currentOffset = 0;currentOffset <= yfs->spare_size - 16;currentOffset++) {
1127
0
        goodOffset = 1;
1128
0
        for(blockIndex = 0;blockIndex < nBlocksTested;blockIndex++) {
1129
0
            for(chunkIndex = 1;chunkIndex < chunksToTest;chunkIndex++) {
1130
1131
0
                lastChunkBase = blockIndex * yfs->spare_size * chunksToTest + (chunkIndex - 1) * yfs->spare_size;
1132
0
                thisChunkBase = lastChunkBase + yfs->spare_size;
1133
1134
                // Seq num should not be all 0xff (we tested earlier that the chunk has been initialized)
1135
0
                if ((0xff == allSpares[thisChunkBase + currentOffset]) &&
1136
0
                    (0xff == allSpares[thisChunkBase + currentOffset + 1]) &&
1137
0
                    (0xff == allSpares[thisChunkBase + currentOffset + 2]) &&
1138
0
                    (0xff == allSpares[thisChunkBase + currentOffset + 3])) {
1139
0
                        if (tsk_verbose && (! yfs->autoDetect)) {
1140
0
                            tsk_fprintf(stderr,
1141
0
                                "yaffs_initialize_spare_format: Eliminating offset %d - invalid sequence number 0xffffffff\n",
1142
0
                                currentOffset);
1143
0
                        }
1144
0
                        goodOffset = 0;
1145
0
                        break;
1146
0
                }
1147
1148
                // Seq num should not be zero
1149
0
                if ((0 == allSpares[thisChunkBase + currentOffset]) &&
1150
0
                    (0 == allSpares[thisChunkBase + currentOffset + 1]) &&
1151
0
                    (0 == allSpares[thisChunkBase + currentOffset + 2]) &&
1152
0
                    (0 == allSpares[thisChunkBase + currentOffset + 3])) {
1153
0
                        if (tsk_verbose && (! yfs->autoDetect)) {
1154
0
                            tsk_fprintf(stderr,
1155
0
                                "yaffs_initialize_spare_format: Eliminating offset %d - invalid sequence number 0\n",
1156
0
                                currentOffset);
1157
0
                        }
1158
0
                        goodOffset = 0;
1159
0
                        break;
1160
0
                }
1161
1162
                // Seq num should match the previous one in the block
1163
0
                if ((allSpares[lastChunkBase + currentOffset] != allSpares[thisChunkBase + currentOffset]) ||
1164
0
                    (allSpares[lastChunkBase + currentOffset + 1] != allSpares[thisChunkBase + currentOffset + 1]) ||
1165
0
                    (allSpares[lastChunkBase + currentOffset + 2] != allSpares[thisChunkBase + currentOffset + 2]) ||
1166
0
                    (allSpares[lastChunkBase + currentOffset + 3] != allSpares[thisChunkBase + currentOffset + 3])) {
1167
0
                        if (tsk_verbose && (! yfs->autoDetect)) {
1168
0
                            tsk_fprintf(stderr,
1169
0
                                "yaffs_initialize_spare_format: Eliminating offset %d - did not match previous chunk sequence number\n",
1170
0
                                currentOffset);
1171
0
                        }
1172
0
                        goodOffset = 0;
1173
0
                        break;
1174
0
                }
1175
1176
                // Obj id should not be zero
1177
0
                if ((0 == allSpares[thisChunkBase + currentOffset + 4]) &&
1178
0
                    (0 == allSpares[thisChunkBase + currentOffset + 5]) &&
1179
0
                    (0 == allSpares[thisChunkBase + currentOffset + 6]) &&
1180
0
                    (0 == allSpares[thisChunkBase + currentOffset + 7])) {
1181
0
                        if (tsk_verbose && (! yfs->autoDetect)) {
1182
0
                            tsk_fprintf(stderr,
1183
0
                                "yaffs_initialize_spare_format: Eliminating offset %d - invalid object id 0\n",
1184
0
                                currentOffset);
1185
0
                        }
1186
0
                        goodOffset = 0;
1187
0
                        break;
1188
0
                }
1189
1190
                // All 16 bytes should not be the same
1191
                // (It is theoretically possible that this could be valid, but incredibly unlikely)
1192
0
                allSameByte = true;
1193
0
                for(i = 1;i < 16;i++) {
1194
0
                    if (allSpares[thisChunkBase + currentOffset] != allSpares[thisChunkBase + currentOffset + i]) {
1195
0
                        allSameByte = false;
1196
0
                        break;
1197
0
                    }
1198
0
                }
1199
0
                if (allSameByte) {
1200
0
                    if (tsk_verbose && (! yfs->autoDetect)) {
1201
0
                        tsk_fprintf(stderr,
1202
0
                            "yaffs_initialize_spare_format: Eliminating offset %d - all repeated bytes\n",
1203
0
                            currentOffset);
1204
0
                    }
1205
0
                    goodOffset = 0;
1206
0
                    break;
1207
0
                }
1208
1209
0
            } // End of loop over chunks
1210
1211
0
            if (!goodOffset) { // Break out of loop over blocks
1212
0
                break;
1213
0
            }
1214
0
        }
1215
0
        if (goodOffset) {
1216
1217
            // Note that we've found an offset that is at least promising
1218
0
            if ((! goodOffsetFound) && (! okOffsetFound)) {
1219
0
                bestOffset = currentOffset;
1220
0
            }
1221
0
            okOffsetFound = 1;
1222
1223
0
            if (tsk_verbose && (! yfs->autoDetect)) {
1224
0
                tsk_fprintf(stderr,
1225
0
                    "yaffs_initialize_spare_format: Found potential spare offsets:  %d (sequence number), %d (object id), %d (chunk id), %d (n bytes)\n",
1226
0
                    currentOffset, currentOffset+4, currentOffset+8, currentOffset+12);
1227
0
            }
1228
1229
            // Now do some more tests
1230
            // Really need some more real-world test data to do this right.
1231
0
            int possibleError = 0;
1232
1233
            // We probably don't want the first byte to always be 0xff
1234
0
            int firstByteFF = 1;
1235
0
            for(blockIndex = 0;blockIndex < nBlocksTested;blockIndex++) {
1236
0
                for(chunkIndex = 1;chunkIndex < chunksToTest;chunkIndex++) {
1237
0
                    if (allSpares[blockIndex * yfs->spare_size * chunksToTest + chunkIndex * yfs->spare_size + currentOffset] != 0xff) {
1238
0
                        firstByteFF = 0;
1239
0
                    }
1240
0
                }
1241
0
            }
1242
1243
0
            if (firstByteFF) {
1244
0
                if (tsk_verbose && (! yfs->autoDetect)) {
1245
0
                    tsk_fprintf(stderr,
1246
0
                        "yaffs_initialize_spare_format:  Previous data starts with all 0xff bytes. Looking for better offsets.\n");
1247
0
                }
1248
0
                possibleError = 1;
1249
0
            }
1250
1251
0
            if (! possibleError) {
1252
1253
                // If we already have a good offset, print this one out but don't record it
1254
0
                if (! goodOffsetFound) {
1255
1256
0
                    goodOffsetFound = 1;
1257
0
                    bestOffset = currentOffset;
1258
1259
                    // Offset passed additional testing and we haven't seen an earlier good one, so go ahead and use it
1260
0
                    if (tsk_verbose && (! yfs->autoDetect)) {
1261
0
                        tsk_fprintf(stderr,
1262
0
                            "yaffs_initialize_spare_format:  Previous offsets appear good - will use as final offsets\n");
1263
0
                    }
1264
1265
0
                }
1266
0
                else {
1267
                    // Keep using the old one
1268
0
                    if (tsk_verbose && (! yfs->autoDetect)) {
1269
0
                        tsk_fprintf(stderr,
1270
0
                            "yaffs_initialize_spare_format:  Previous offsets appear good but staying with earlier valid ones\n");
1271
0
                    }
1272
0
                }
1273
0
            }
1274
0
        }
1275
0
    }
1276
1277
0
    free(spareBuffer);
1278
0
    free(allSpares);
1279
1280
0
    if (okOffsetFound || goodOffsetFound) {
1281
        // Record everything
1282
0
        yfs->spare_seq_offset = bestOffset;
1283
0
        yfs->spare_obj_id_offset = bestOffset + 4;
1284
0
        yfs->spare_chunk_id_offset = bestOffset + 8;
1285
0
        yfs->spare_nbytes_offset = bestOffset + 12;
1286
1287
0
        if (tsk_verbose && (! yfs->autoDetect)) {
1288
0
            tsk_fprintf(stderr,
1289
0
                "yaffs_initialize_spare_format: Final offsets: %d (sequence number), %d (object id), %d (chunk id), %d (n bytes)\n",
1290
0
                bestOffset, bestOffset+4, bestOffset+8, bestOffset+12);
1291
0
            tsk_fprintf(stderr,
1292
0
                "If these do not seem valid: %s\n", YAFFS_HELP_MESSAGE);
1293
0
        }
1294
0
        return TSK_OK;
1295
0
    }
1296
0
    else {
1297
0
        return TSK_ERR;
1298
0
    }
1299
0
}
1300
1301
/**
1302
* yaffsfs_read_header( ... )
1303
*
1304
*/
1305
static uint8_t
1306
    yaffsfs_read_header(YAFFSFS_INFO *yfs, YaffsHeader ** header, TSK_OFF_T offset)
1307
0
{
1308
0
    unsigned char *hdr;
1309
0
    ssize_t cnt;
1310
0
    YaffsHeader *head;
1311
0
    TSK_FS_INFO *fs = &(yfs->fs_info);
1312
1313
0
    if ((hdr = (unsigned char*) tsk_malloc(yfs->page_size)) == NULL) {
1314
0
        return 1;
1315
0
    }
1316
1317
0
    cnt = tsk_img_read(fs->img_info, offset, (char *) hdr,
1318
0
        yfs->page_size);
1319
0
    if ((cnt < 0) || ((unsigned int)cnt < yfs->page_size)) {
1320
0
        free(hdr);
1321
0
        return 1;
1322
0
    }
1323
1324
0
    if ((head = (YaffsHeader*) tsk_malloc( sizeof(YaffsHeader))) == NULL) {
1325
0
        free(hdr);
1326
0
        return 1;
1327
0
    }
1328
1329
0
    memcpy(&head->obj_type, hdr, 4);
1330
0
    memcpy(&head->parent_id, &hdr[4], 4);
1331
0
    memcpy(head->name, (char*) &hdr[0xA], YAFFS_HEADER_NAME_LENGTH);
1332
0
    memcpy(&head->file_mode, &hdr[0x10C], 4);
1333
0
    memcpy(&head->user_id, &hdr[0x110], 4);
1334
0
    memcpy(&head->group_id, &hdr[0x114], 4);
1335
0
    memcpy(&head->atime, &hdr[0x118], 4);
1336
0
    memcpy(&head->mtime, &hdr[0x11C], 4);
1337
0
    memcpy(&head->ctime, &hdr[0x120], 4);
1338
0
    memcpy(&head->file_size, &hdr[0x124], 4);
1339
0
    memcpy(&head->equivalent_id, &hdr[0x128], 4);
1340
0
    memcpy(head->alias, (char*) &hdr[0x12C], YAFFS_HEADER_ALIAS_LENGTH);
1341
1342
    //memcpy(&head->rdev_mode, &hdr[0x1CC], 4);
1343
    //memcpy(&head->win_ctime, &hdr[0x1D0], 8);
1344
    //memcpy(&head->win_atime, &hdr[0x1D8], 8);
1345
    //memcpy(&head->win_mtime, &hdr[0x1E0], 8);
1346
    //memcpy(&head->inband_obj_id, &hdr[0x1E8], 4);
1347
    //memcpy(&head->inband_is_shrink, &hdr[0x1EC], 4);
1348
1349
    // NOTE: This isn't in Android 3.3 kernel but is in YAFFS2 git
1350
    //memcpy(&head->file_size_high, &hdr[0x1F0], 4);
1351
1352
0
    free(hdr);
1353
1354
0
    *header = head;
1355
0
    return 0;
1356
0
}
1357
1358
/**
1359
* Read and parse the YAFFS2 tags in the NAND spare bytes.
1360
*
1361
* @param info is a YAFFS fs handle
1362
* @param spare YaffsSpare object to be populated
1363
* @param offset, offset to read from
1364
*
1365
* @returns 0 on success and 1 on error
1366
*/
1367
static uint8_t
1368
    yaffsfs_read_spare(YAFFSFS_INFO *yfs, YaffsSpare ** spare, TSK_OFF_T offset)
1369
0
{
1370
0
    unsigned char *spr;
1371
0
    ssize_t cnt;
1372
0
    YaffsSpare *sp;
1373
0
    TSK_FS_INFO *fs = &(yfs->fs_info);
1374
1375
0
    uint32_t seq_number;
1376
0
    uint32_t object_id;
1377
0
    uint32_t chunk_id;
1378
1379
    // Should have checked this by now, but just in case
1380
0
    if ((yfs->spare_seq_offset + 4 > yfs->spare_size) ||
1381
0
        (yfs->spare_obj_id_offset + 4 > yfs->spare_size) ||
1382
0
        (yfs->spare_chunk_id_offset + 4 > yfs->spare_size)) {
1383
0
            return 1;
1384
0
    }
1385
1386
0
    if ((spr = (unsigned char*) tsk_malloc(yfs->spare_size)) == NULL) {
1387
0
        return 1;
1388
0
    }
1389
1390
0
    if (yfs->spare_size < 46) { // Why is this 46?
1391
0
        tsk_error_reset();
1392
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1393
0
        tsk_error_set_errstr("yaffsfs_read_spare: spare size is too small");
1394
0
        free(spr);
1395
0
        return 1;
1396
0
    }
1397
1398
0
    cnt = tsk_img_read(fs->img_info, offset, (char*) spr, yfs->spare_size);
1399
0
    if ((cnt < 0) || ((unsigned int)cnt < yfs->spare_size)) {
1400
        // couldn't read sufficient bytes...
1401
0
        if (spare) {
1402
0
            free(spr);
1403
0
            *spare = NULL;
1404
0
        }
1405
0
        return 1;
1406
0
    }
1407
1408
0
    if ((sp = (YaffsSpare*) tsk_malloc(sizeof(YaffsSpare))) == NULL) {
1409
0
        return 1;
1410
0
    }
1411
1412
0
    memset(sp, 0, sizeof(YaffsSpare));
1413
1414
    /*
1415
    * Complete read of the YAFFS2 spare
1416
    */
1417
1418
1419
    // The format of the spare area should have been determined earlier
1420
0
    memcpy(&seq_number, &spr[yfs->spare_seq_offset], 4);
1421
0
    memcpy(&object_id, &spr[yfs->spare_obj_id_offset], 4);
1422
0
    memcpy(&chunk_id, &spr[yfs->spare_chunk_id_offset], 4);
1423
1424
0
    if ((YAFFS_SPARE_FLAGS_IS_HEADER & chunk_id) != 0) {
1425
1426
0
        sp->seq_number = seq_number;
1427
0
        sp->object_id = object_id & ~YAFFS_SPARE_OBJECT_TYPE_MASK;
1428
0
        sp->chunk_id = 0;
1429
1430
0
        sp->has_extra_fields = 1;
1431
0
        sp->extra_parent_id = chunk_id & YAFFS_SPARE_PARENT_ID_MASK;
1432
0
        sp->extra_object_type =
1433
0
            (object_id & YAFFS_SPARE_OBJECT_TYPE_MASK)
1434
0
            >> YAFFS_SPARE_OBJECT_TYPE_SHIFT;
1435
0
    }
1436
0
    else {
1437
0
        sp->seq_number = seq_number;
1438
0
        sp->object_id = object_id;
1439
0
        sp->chunk_id = chunk_id;
1440
1441
0
        sp->has_extra_fields = 0;
1442
0
    }
1443
1444
0
    free(spr);
1445
0
    *spare = sp;
1446
1447
0
    return 0;
1448
0
}
1449
1450
static uint8_t
1451
    yaffsfs_is_spare_valid(YAFFSFS_INFO * /*yfs*/, YaffsSpare *spare)
1452
0
{
1453
0
    if (spare == NULL) {
1454
0
        return 1;
1455
0
    }
1456
1457
0
    if ((spare->object_id > YAFFS_MAX_OBJECT_ID) ||
1458
0
        (spare->seq_number < YAFFS_LOWEST_SEQUENCE_NUMBER) ||
1459
0
        (spare->seq_number > YAFFS_HIGHEST_SEQUENCE_NUMBER)) {
1460
0
            return 1;
1461
0
    }
1462
1463
0
    return 0;
1464
0
}
1465
1466
static uint8_t
1467
    yaffsfs_read_chunk(YAFFSFS_INFO *yfs,
1468
    YaffsHeader **header, YaffsSpare **spare, TSK_OFF_T offset)
1469
0
{
1470
0
    TSK_OFF_T header_offset = offset;
1471
0
    TSK_OFF_T spare_offset = offset + yfs->page_size;
1472
1473
0
    if (header == NULL || spare == NULL) {
1474
0
        return 1;
1475
0
    }
1476
1477
0
    if (yaffsfs_read_header(yfs, header, header_offset) != 0) {
1478
0
        return 1;
1479
0
    }
1480
1481
0
    if (yaffsfs_read_spare(yfs, spare, spare_offset) != 0) {
1482
0
        free(*header);
1483
0
        *header = NULL;
1484
0
        return 1;
1485
0
    }
1486
1487
0
    return 0;
1488
0
}
1489
1490
/**
1491
 * Cycle through the entire image and populate the cache with objects as they are found.
1492
 */
1493
static uint8_t
1494
    yaffsfs_parse_image_load_cache(YAFFSFS_INFO * yfs)
1495
0
{
1496
0
    uint8_t status = TSK_OK;
1497
0
    uint32_t nentries = 0;
1498
0
    YaffsSpare *spare = NULL;
1499
1500
0
    uint8_t tempBuf[8];
1501
0
    uint32_t parentID;
1502
1503
0
    if (yfs->cache_objects)
1504
0
        return 0;
1505
1506
0
    for(TSK_OFF_T offset = 0;offset < yfs->fs_info.img_info->size;offset += yfs->page_size + yfs->spare_size) {
1507
0
        status = yaffsfs_read_spare( yfs, &spare, offset + yfs->page_size);
1508
0
        if (status != TSK_OK) {
1509
0
            break;
1510
0
        }
1511
1512
0
        if (yaffsfs_is_spare_valid(yfs, spare) == TSK_OK) {
1513
1514
1515
0
            if ((spare->has_extra_fields) || (spare->chunk_id != 0)) {
1516
0
                yaffscache_chunk_add(yfs,
1517
0
                    offset,
1518
0
                    spare->seq_number,
1519
0
                    spare->object_id,
1520
0
                    spare->chunk_id,
1521
0
                    spare->extra_parent_id);
1522
0
            }
1523
0
            else {
1524
                // If we have a header block and didn't extract it already from the spare, get the parent ID from
1525
                // the non-spare data
1526
0
                if (8 == tsk_img_read(yfs->fs_info.img_info, offset, (char*) tempBuf, 8)) {
1527
0
                    memcpy(&parentID, &tempBuf[4], 4);
1528
1529
0
                    yaffscache_chunk_add(yfs,
1530
0
                        offset,
1531
0
                        spare->seq_number,
1532
0
                        spare->object_id,
1533
0
                        spare->chunk_id,
1534
0
                        parentID);
1535
0
                }
1536
0
                else {
1537
                    // Really shouldn't happen
1538
0
                    fprintf(stderr, "Error reading header to get parent id at offset %" PRIxOFF "\n", offset);
1539
0
                    yaffscache_chunk_add(yfs,
1540
0
                        offset,
1541
0
                        spare->seq_number,
1542
0
                        spare->object_id,
1543
0
                        spare->chunk_id,
1544
0
                        0);
1545
0
                }
1546
0
            }
1547
0
        }
1548
1549
0
        free(spare);
1550
0
        spare = NULL;
1551
1552
0
        ++nentries;
1553
0
    }
1554
1555
0
    if (tsk_verbose)
1556
0
        fprintf(stderr, "yaffsfs_parse_image_load_cache: read %d entries\n", nentries);
1557
1558
0
    if (tsk_verbose)
1559
0
        fprintf(stderr, "yaffsfs_parse_image_load_cache: started processing chunks for version cache...\n");
1560
0
    fflush(stderr);
1561
1562
    // At this point, we have a list of chunks sorted by obj id, seq number, and offset
1563
    // This makes the list of objects in cache_objects, which link to different versions
1564
0
    yaffscache_versions_compute(yfs);
1565
1566
0
    if (tsk_verbose)
1567
0
        fprintf(stderr, "yaffsfs_parse_image_load_cache: done version cache!\n");
1568
0
    fflush(stderr);
1569
1570
1571
    // Having multiple inodes point to the same object seems to cause trouble in TSK, especially in orphan file detection,
1572
    //  so set the version number of the final one to zero.
1573
    // While we're at it, find the highest obj_id and the highest version (before resetting to zero)
1574
0
    YaffsCacheObject * currObj = yfs->cache_objects;
1575
0
    YaffsCacheVersion * currVer;
1576
0
    while(currObj != NULL) {
1577
0
        if (currObj->yco_obj_id > yfs->max_obj_id) {
1578
0
            yfs->max_obj_id = currObj->yco_obj_id;
1579
0
        }
1580
1581
0
        currVer = currObj->yco_latest;
1582
0
        if (currVer->ycv_version > yfs->max_version) {
1583
0
            yfs->max_version = currVer->ycv_version;
1584
0
        }
1585
1586
0
        currVer->ycv_version = 0;
1587
0
        currObj = currObj->yco_next;
1588
0
    }
1589
1590
    // Use the max object id and version number to construct an upper bound on the inode
1591
0
    TSK_INUM_T max_inum = 0;
1592
0
    if (TSK_OK != yaffscache_obj_id_and_version_to_inode(yfs->max_obj_id, yfs->max_version, &max_inum)) {
1593
0
        return TSK_ERR;
1594
0
    }
1595
0
    yfs->fs_info.last_inum = max_inum + 1; // One more for the orphan dir
1596
1597
    // Make sure the orphan dir is greater than the root dir
1598
0
    if (yfs->fs_info.last_inum <= yfs->fs_info.root_inum) {
1599
0
        tsk_error_reset();
1600
0
        tsk_error_set_errno(TSK_ERR_FS);
1601
0
        tsk_error_set_errstr(
1602
0
            "yaffsfs_parse_image_load_cache: Maximum inum %" PRIuINUM " is not greater than the root inum", yfs->fs_info.last_inum);
1603
0
        return TSK_ERR;
1604
0
    }
1605
1606
0
    return TSK_OK;
1607
0
}
1608
1609
// A version is allocated if:
1610
//   1. This version is pointed to by yco_latest
1611
//   2. This version didn't have a delete/unlinked header after the most recent copy of the normal header
1612
0
static uint8_t yaffs_is_version_allocated(YAFFSFS_INFO * yfs, TSK_INUM_T inode) {
1613
0
    YaffsCacheObject * obj;
1614
0
    YaffsCacheVersion * version;
1615
0
    YaffsCacheChunk * curr;
1616
1617
0
    TSK_RETVAL_ENUM result = yaffscache_version_find_by_inode(yfs, inode, &version, &obj);
1618
0
    if (result != TSK_OK) {
1619
0
        if (tsk_verbose)
1620
0
            tsk_fprintf(stderr, "yaffs_is_version_allocated: yaffscache_version_find_by_inode failed! (inode: %d)\n", inode);
1621
0
        return 0;
1622
0
    }
1623
1624
0
    if (obj->yco_latest == version) {
1625
0
        curr = obj->yco_latest->ycv_header_chunk;
1626
0
        while(curr != NULL) {
1627
            // We're looking for a newer unlinked or deleted header. If one exists, then this object should be considered unallocated
1628
0
            if ((curr->ycc_parent_id == YAFFS_OBJECT_UNLINKED) || (curr->ycc_parent_id == YAFFS_OBJECT_DELETED)) {
1629
0
                return 0;
1630
0
            }
1631
0
            curr = curr ->ycc_next;
1632
0
        }
1633
0
        return 1;
1634
0
    }
1635
0
    else {
1636
0
        return 0;
1637
0
    }
1638
1639
0
}
1640
1641
/*
1642
* TSK integration
1643
*
1644
*
1645
*/
1646
1647
static uint8_t
1648
    yaffs_make_directory(YAFFSFS_INFO *yaffsfs, TSK_FS_FILE *a_fs_file,
1649
    TSK_INUM_T inode, const char *name)
1650
0
{
1651
0
    TSK_FS_FILE *fs_file = a_fs_file;
1652
1653
1654
0
    fs_file->meta->type = TSK_FS_META_TYPE_DIR;
1655
0
    fs_file->meta->mode = (TSK_FS_META_MODE_ENUM)0;
1656
0
    fs_file->meta->nlink = 1;
1657
1658
0
    if ((inode == YAFFS_OBJECT_UNLINKED) || (inode == YAFFS_OBJECT_DELETED) ||
1659
0
        (inode == yaffsfs->fs_info.last_inum)) {
1660
0
            fs_file->meta->flags =
1661
0
                (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
1662
0
    }
1663
0
    else {
1664
0
        if (yaffs_is_version_allocated(yaffsfs, inode)) {
1665
0
            fs_file->meta->flags =
1666
0
                (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
1667
0
        }
1668
0
        else {
1669
0
            fs_file->meta->flags =
1670
0
                (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNALLOC);
1671
0
        }
1672
0
    }
1673
0
    fs_file->meta->uid = fs_file->meta->gid = 0;
1674
0
    fs_file->meta->mtime = fs_file->meta->atime = fs_file->meta->ctime =
1675
0
        fs_file->meta->crtime = 0;
1676
0
    fs_file->meta->mtime_nano = fs_file->meta->atime_nano =
1677
0
        fs_file->meta->ctime_nano = fs_file->meta->crtime_nano = 0;
1678
1679
0
    if (fs_file->meta->name2 == NULL) {
1680
0
        if ((fs_file->meta->name2 = (TSK_FS_META_NAME_LIST *)
1681
0
            tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) {
1682
0
            return 1;
1683
0
        }
1684
0
        fs_file->meta->name2->next = NULL;
1685
0
    }
1686
1687
0
    if (fs_file->meta->attr != NULL) {
1688
0
        tsk_fs_attrlist_markunused(fs_file->meta->attr);
1689
0
    }
1690
0
    else {
1691
0
        fs_file->meta->attr = tsk_fs_attrlist_alloc();
1692
0
    }
1693
1694
0
    strncpy(fs_file->meta->name2->name, name, TSK_FS_META_NAME_LIST_NSIZE - 1);
1695
1696
0
    fs_file->meta->size = 0;
1697
0
    fs_file->meta->attr_state = TSK_FS_META_ATTR_EMPTY;
1698
0
    fs_file->meta->addr = inode;
1699
0
    return 0;
1700
0
}
1701
1702
static uint8_t
1703
    yaffs_make_regularfile( YAFFSFS_INFO * yaffsfs, TSK_FS_FILE * a_fs_file,
1704
    TSK_INUM_T inode, const char * name )
1705
0
{
1706
0
    TSK_FS_FILE *fs_file = a_fs_file;
1707
1708
0
    fs_file->meta->type = TSK_FS_META_TYPE_REG;
1709
0
    fs_file->meta->mode = (TSK_FS_META_MODE_ENUM)0;
1710
0
    fs_file->meta->nlink =1;
1711
1712
0
    if (yaffs_is_version_allocated(yaffsfs, inode)) {
1713
0
        fs_file->meta->flags =
1714
0
            (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
1715
0
    }
1716
0
    else {
1717
0
        fs_file->meta->flags =
1718
0
            (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNALLOC);
1719
0
    }
1720
1721
0
    fs_file->meta->uid = fs_file->meta->gid = 0;
1722
0
    fs_file->meta->mtime = fs_file->meta->atime = fs_file->meta->ctime =
1723
0
        fs_file->meta->crtime = 0;
1724
0
    fs_file->meta->mtime_nano = fs_file->meta->atime_nano =
1725
0
        fs_file->meta->ctime_nano = fs_file->meta->crtime_nano = 0;
1726
1727
0
    if (fs_file->meta->name2 == NULL) {
1728
0
        if ((fs_file->meta->name2 = (TSK_FS_META_NAME_LIST *)
1729
0
            tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL)
1730
0
            return 1;
1731
0
        fs_file->meta->name2->next = NULL;
1732
0
    }
1733
1734
0
    if (fs_file->meta->attr != NULL) {
1735
0
        tsk_fs_attrlist_markunused(fs_file->meta->attr);
1736
0
    }
1737
0
    else {
1738
0
        fs_file->meta->attr = tsk_fs_attrlist_alloc();
1739
0
    }
1740
1741
0
    fs_file->meta->addr = inode;
1742
0
    strncpy(fs_file->meta->name2->name, name, TSK_FS_META_NAME_LIST_NSIZE - 1);
1743
1744
0
    fs_file->meta->size = 0;
1745
0
    fs_file->meta->attr_state = TSK_FS_META_ATTR_EMPTY;
1746
1747
0
    return 0;
1748
0
}
1749
1750
/**
1751
* \internal
1752
* Create YAFFS2 Deleted Object
1753
*
1754
* @ param yaffs file system
1755
* fs_file to copy file information to
1756
* return 1 on error, 0 on success
1757
*/
1758
static uint8_t
1759
    yaffs_make_deleted( YAFFSFS_INFO * yaffsfs, TSK_FS_FILE * a_fs_file )
1760
0
{
1761
0
    TSK_FS_FILE *fs_file = a_fs_file;
1762
1763
0
    if (tsk_verbose)
1764
0
        tsk_fprintf(stderr, "yaffs_make_deleted: Making virtual deleted node\n");
1765
0
    if (yaffs_make_directory(yaffsfs, fs_file, YAFFS_OBJECT_DELETED, YAFFS_OBJECT_DELETED_NAME))
1766
0
        return 1;
1767
1768
0
    return 0;
1769
0
}
1770
1771
/**
1772
* \internal
1773
* Create YAFFS2 Unlinked object
1774
*
1775
* @ param yaffs file system
1776
* fs_file to copy file information to
1777
* return 1 on error, 0 on success
1778
*/
1779
static uint8_t
1780
    yaffs_make_unlinked( YAFFSFS_INFO * yaffsfs, TSK_FS_FILE * a_fs_file )
1781
0
{
1782
0
    TSK_FS_FILE * fs_file = a_fs_file;
1783
1784
0
    if (tsk_verbose)
1785
0
        tsk_fprintf(stderr, "yaffs_make_unlinked: Making virtual unlinked node\n");
1786
1787
0
    if (yaffs_make_directory(yaffsfs, fs_file, YAFFS_OBJECT_UNLINKED, YAFFS_OBJECT_UNLINKED_NAME))
1788
0
        return 1;
1789
1790
0
    return 0;
1791
0
}
1792
1793
/**
1794
* \internal
1795
* Create YAFFS2 orphan object
1796
*
1797
* @ param yaffs file system
1798
* fs_file to copy file information to
1799
* return 1 on error, 0 on success
1800
*/
1801
static uint8_t
1802
    yaffs_make_orphan_dir( YAFFSFS_INFO * yaffsfs, TSK_FS_FILE * a_fs_file )
1803
0
{
1804
0
    TSK_FS_FILE * fs_file = a_fs_file;
1805
0
    TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0);
1806
0
    if (fs_name == NULL)
1807
0
        return TSK_ERR;
1808
1809
0
    if (tsk_verbose)
1810
0
        tsk_fprintf(stderr, "yaffs_make_orphan_dir: Making orphan dir node\n");
1811
1812
0
    if (tsk_fs_dir_make_orphan_dir_name(&(yaffsfs->fs_info), fs_name)) {
1813
0
        tsk_fs_name_free(fs_name);
1814
0
        return TSK_ERR;
1815
0
    }
1816
1817
0
    if (yaffs_make_directory(yaffsfs, fs_file, yaffsfs->fs_info.last_inum, (char *)fs_name)) {
1818
0
        tsk_fs_name_free(fs_name);
1819
0
        return 1;
1820
0
    }
1821
0
    tsk_fs_name_free(fs_name);
1822
0
    return 0;
1823
0
}
1824
1825
/* yaffsfs_inode_lookup - lookup inode, external interface
1826
*
1827
* Returns 1 on error and 0 on success
1828
*
1829
*/
1830
1831
static uint8_t
1832
    yaffs_inode_lookup(TSK_FS_INFO *a_fs, TSK_FS_FILE * a_fs_file,
1833
    TSK_INUM_T inum)
1834
0
{
1835
0
    YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)a_fs;
1836
0
    YaffsCacheObject *obj;
1837
0
    YaffsCacheVersion *version;
1838
0
    YaffsHeader *header = NULL;
1839
0
    YaffsSpare *spare = NULL;
1840
0
    TSK_RETVAL_ENUM result;
1841
0
    uint8_t type;
1842
0
    const char *real_name;
1843
1844
0
    if (a_fs_file == NULL) {
1845
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
1846
0
        tsk_error_set_errstr("yaffsfs_inode_lookup: fs_file is NULL");
1847
0
        return 1;
1848
0
    }
1849
1850
0
    if (a_fs_file->meta == NULL) {
1851
0
        if ((a_fs_file->meta =
1852
0
            tsk_fs_meta_alloc(YAFFS_FILE_CONTENT_LEN)) == NULL)
1853
0
            return 1;
1854
0
    }
1855
0
    else {
1856
0
        tsk_fs_meta_reset(a_fs_file->meta);
1857
0
    }
1858
1859
0
    if (tsk_verbose)
1860
0
        tsk_fprintf(stderr, "yaffs_inode_lookup: looking up %" PRIuINUM "\n",inum);
1861
1862
0
    switch(inum) {
1863
0
    case YAFFS_OBJECT_UNLINKED:
1864
0
        yaffs_make_unlinked(yfs, a_fs_file);
1865
0
        return 0;
1866
1867
0
    case YAFFS_OBJECT_DELETED:
1868
0
        yaffs_make_deleted(yfs, a_fs_file);
1869
0
        return 0;
1870
0
    }
1871
1872
0
    if (inum == yfs->fs_info.last_inum) {
1873
0
        yaffs_make_orphan_dir(yfs, a_fs_file);
1874
0
        return 0;
1875
0
    }
1876
1877
0
    result = yaffscache_version_find_by_inode(yfs, inum, &version, &obj);
1878
0
    if (result != TSK_OK) {
1879
0
        if (tsk_verbose)
1880
0
            tsk_fprintf(stderr, "yaffs_inode_lookup: yaffscache_version_find_by_inode failed! (inode = %d)\n", inum);
1881
0
        return 1;
1882
0
    }
1883
1884
0
    if (version->ycv_header_chunk == NULL) {
1885
0
        return 1;
1886
0
    }
1887
1888
0
    if (yaffsfs_read_chunk(yfs, &header, &spare, version->ycv_header_chunk->ycc_offset) != TSK_OK) {
1889
0
        if (tsk_verbose)
1890
0
            tsk_fprintf(stderr, "yaffs_inode_lookup: yaffsfs_read_chunk failed!\n");
1891
0
        return 1;
1892
0
    }
1893
1894
0
    type = header->obj_type;
1895
1896
0
    switch(inum) {
1897
0
    case YAFFS_OBJECT_LOSTNFOUND:
1898
0
        real_name = YAFFS_OBJECT_LOSTNFOUND_NAME;
1899
0
        break;
1900
0
    case YAFFS_OBJECT_UNLINKED:
1901
0
        real_name = YAFFS_OBJECT_UNLINKED_NAME;
1902
0
        break;
1903
0
    case YAFFS_OBJECT_DELETED:
1904
0
        real_name = YAFFS_OBJECT_DELETED_NAME;
1905
0
        break;
1906
0
    default:
1907
0
        real_name = header->name;
1908
0
        break;
1909
0
    }
1910
1911
0
    switch(type) {
1912
0
    case YAFFS_TYPE_FILE:
1913
0
        if (tsk_verbose)
1914
0
            tsk_fprintf(stderr, "yaffs_inode_lookup: is a file\n");
1915
0
        yaffs_make_regularfile(yfs, a_fs_file, inum, real_name);
1916
0
        break;
1917
1918
0
    case YAFFS_TYPE_DIRECTORY:
1919
0
        if (tsk_verbose)
1920
0
            tsk_fprintf(stderr, "yaffs_inode_lookup: is a directory\n");
1921
0
        yaffs_make_directory(yfs, a_fs_file, inum, real_name);
1922
0
        break;
1923
1924
0
    case YAFFS_TYPE_SOFTLINK:
1925
0
        if (tsk_verbose)
1926
0
            tsk_fprintf(stderr, "yaffs_inode_lookup: is a symbolic link\n");
1927
0
        yaffs_make_regularfile(yfs, a_fs_file, inum, real_name);
1928
0
        a_fs_file->meta->type = TSK_FS_META_TYPE_LNK;
1929
0
        break;
1930
1931
0
    case YAFFS_TYPE_HARDLINK:
1932
0
    case YAFFS_TYPE_UNKNOWN:
1933
0
        if (tsk_verbose)
1934
0
            tsk_fprintf(stderr, "yaffs_inode_lookup: is *** UNHANDLED *** (type %d, header at 0x%x)\n", type, version->ycv_header_chunk->ycc_offset);
1935
        // We can still set a few things
1936
0
        a_fs_file->meta->type = TSK_FS_META_TYPE_UNDEF;
1937
0
        a_fs_file->meta->addr = inum;
1938
0
        if (yaffs_is_version_allocated(yfs, inum)) {
1939
0
            a_fs_file->meta->flags =
1940
0
                (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
1941
0
        }
1942
0
        else {
1943
0
            a_fs_file->meta->flags =
1944
0
                (TSK_FS_META_FLAG_ENUM)(TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNALLOC);
1945
0
        }
1946
0
        if (a_fs_file->meta->name2 == NULL) {
1947
0
            if ((a_fs_file->meta->name2 = (TSK_FS_META_NAME_LIST *)
1948
0
                tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL) {
1949
0
                    return 1;
1950
0
            }
1951
0
            a_fs_file->meta->name2->next = NULL;
1952
0
        }
1953
0
        strncpy(a_fs_file->meta->name2->name, real_name,
1954
0
            TSK_FS_META_NAME_LIST_NSIZE);
1955
0
        break;
1956
0
    default:
1957
0
        if (tsk_verbose)
1958
0
            tsk_fprintf(stderr, "yaffs_inode_lookup: type is invalid (type %d, header at 0x%x)\n", type, version->ycv_header_chunk->ycc_offset);
1959
0
        return 1;
1960
0
    }
1961
1962
    /* Who owns this? I'm following the way FATFS does it by freeing + NULLing
1963
    * this and mallocing if used.
1964
    */
1965
0
    free(a_fs_file->meta->link);
1966
0
    a_fs_file->meta->link = NULL;
1967
1968
0
    if (type != YAFFS_TYPE_HARDLINK) {
1969
0
        a_fs_file->meta->mode = (TSK_FS_META_MODE_ENUM)(header->file_mode & TWELVE_BITS_MASK); // chop at 12 bits;
1970
0
        a_fs_file->meta->uid = header->user_id;
1971
0
        a_fs_file->meta->gid = header->group_id;
1972
0
        a_fs_file->meta->mtime = header->mtime;
1973
0
        a_fs_file->meta->atime = header->atime;
1974
0
        a_fs_file->meta->ctime = header->ctime;
1975
0
    }
1976
1977
0
    if (type == YAFFS_TYPE_FILE) {
1978
0
        a_fs_file->meta->size = header->file_size;
1979
        // NOTE: This isn't in Android 3.3 kernel but is in YAFFS2 git
1980
        //a_fs_file->meta->size |= ((TSK_OFF_T) header->file_size_high) << 32;
1981
0
    }
1982
1983
0
    if (type == YAFFS_TYPE_HARDLINK) {
1984
        // TODO: Store equivalent_id somewhere? */
1985
0
    }
1986
1987
0
    if (type == YAFFS_TYPE_SOFTLINK) {
1988
0
        a_fs_file->meta->link = (char*)tsk_malloc(YAFFS_HEADER_ALIAS_LENGTH);
1989
0
        if (a_fs_file->meta->link == NULL) {
1990
0
            free(header);
1991
0
            free(spare);
1992
0
            return 1;
1993
0
        }
1994
1995
0
        memcpy(a_fs_file->meta->link, header->alias, YAFFS_HEADER_ALIAS_LENGTH);
1996
0
    }
1997
1998
0
    free(header);
1999
0
    free(spare);
2000
0
    return 0;
2001
0
}
2002
2003
2004
2005
/* yaffsfs_inode_walk - inode iterator
2006
*
2007
* flags used: TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED,
2008
*  TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN
2009
*
2010
*  Return 1 on error and 0 on success
2011
*/
2012
2013
static uint8_t
2014
    yaffsfs_inode_walk(TSK_FS_INFO *fs, TSK_INUM_T start_inum,
2015
    TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM flags,
2016
    TSK_FS_META_WALK_CB a_action, void *a_ptr)
2017
0
{
2018
0
    YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)fs;
2019
0
    TSK_RETVAL_ENUM result;
2020
2021
0
    uint32_t start_obj_id;
2022
0
    uint32_t start_ver_number;
2023
0
    uint32_t end_obj_id;
2024
0
    uint32_t end_ver_number;
2025
2026
0
    uint32_t obj_id;
2027
2028
0
    YaffsCacheObject *curr_obj;
2029
0
    YaffsCacheVersion *curr_version;
2030
2031
0
    result = yaffscache_inode_to_obj_id_and_version(start_inum, &start_obj_id, &start_ver_number);
2032
2033
0
    result = yaffscache_inode_to_obj_id_and_version(end_inum, &end_obj_id, &end_ver_number);
2034
2035
0
    if (end_obj_id < start_obj_id) {
2036
0
        tsk_error_reset();
2037
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
2038
0
        tsk_error_set_errstr("yaffsfs_inode_walk: end object id must be >= start object id: "
2039
0
            "%" PRIx32 " must be >= %" PRIx32 "",
2040
0
            end_obj_id, start_obj_id);
2041
0
        return 1;
2042
0
    }
2043
2044
    /* The ORPHAN flag is unsupported for YAFFS2 */
2045
0
    if (flags & TSK_FS_META_FLAG_ORPHAN) {
2046
0
        if (tsk_verbose) {
2047
0
            tsk_fprintf(stderr, "yaffsfs_inode_walk: ORPHAN flag unsupported by YAFFS2");
2048
0
        }
2049
0
    }
2050
2051
0
    if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) &&
2052
0
        ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) {
2053
0
            flags = (TSK_FS_META_FLAG_ENUM)(flags | TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC);
2054
0
    }
2055
2056
    /* If neither of the USED or UNUSED flags are set, then set them
2057
    * both
2058
    */
2059
0
    if (((flags & TSK_FS_META_FLAG_USED) == 0) &&
2060
0
        ((flags & TSK_FS_META_FLAG_UNUSED) == 0)) {
2061
0
            flags = (TSK_FS_META_FLAG_ENUM)(flags | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED);
2062
0
    }
2063
2064
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
2065
0
        tsk_fs_file_alloc(fs),
2066
0
        tsk_fs_file_close
2067
0
    };
2068
2069
0
    if (!fs_file)
2070
0
        return 1;
2071
0
    if ((fs_file->meta =
2072
0
        tsk_fs_meta_alloc(YAFFS_FILE_CONTENT_LEN)) == NULL)
2073
0
        return 1;
2074
2075
0
    for (obj_id = start_obj_id; obj_id <= end_obj_id; obj_id++) {
2076
0
        int retval;
2077
2078
0
        result = yaffscache_version_find_by_inode(yfs, obj_id, &curr_version, &curr_obj);
2079
0
        if (result == TSK_OK) {
2080
2081
0
            TSK_INUM_T curr_inode;
2082
0
            YaffsCacheVersion *version;
2083
2084
            // ALLOC, UNALLOC, or both are set at this point
2085
0
            if (flags & TSK_FS_META_FLAG_ALLOC) {
2086
                // Allocated only - just look at current version
2087
0
                if (yaffscache_obj_id_and_version_to_inode(obj_id, curr_obj->yco_latest->ycv_version, &curr_inode) != TSK_OK) {
2088
0
                    return 1;
2089
0
                }
2090
2091
                // It's possible for the current version to be unallocated if the last header was a deleted or unlinked header
2092
0
                if (yaffs_is_version_allocated(yfs, curr_inode)) {
2093
0
                    if (yaffs_inode_lookup(fs, fs_file.get(), curr_inode) != TSK_OK) {
2094
0
                        return 1;
2095
0
                    }
2096
2097
0
                    retval = a_action(fs_file.get(), a_ptr);
2098
0
                    if (retval == TSK_WALK_STOP) {
2099
0
                        return 0;
2100
0
                    }
2101
0
                    else if (retval == TSK_WALK_ERROR) {
2102
0
                        return 1;
2103
0
                    }
2104
0
                }
2105
0
            }
2106
0
            if (flags & TSK_FS_META_FLAG_UNALLOC) {
2107
0
                for (version = curr_obj->yco_latest; version != NULL; version = version->ycv_prior) {
2108
0
                    if (yaffscache_obj_id_and_version_to_inode(obj_id, version->ycv_version, &curr_inode) != TSK_OK) {
2109
0
                        return 1;
2110
0
                    }
2111
2112
0
                    if (!yaffs_is_version_allocated(yfs, curr_inode)) {
2113
0
                        if (yaffs_inode_lookup(fs, fs_file.get(), curr_inode) != TSK_OK) {
2114
0
                            return 1;
2115
0
                        }
2116
2117
0
                        retval = a_action(fs_file.get(), a_ptr);
2118
0
                        if (retval == TSK_WALK_STOP) {
2119
0
                            return 0;
2120
0
                        }
2121
0
                        else if (retval == TSK_WALK_ERROR) {
2122
0
                            return 1;
2123
0
                        }
2124
0
                    }
2125
0
                }
2126
0
            }
2127
2128
0
            curr_obj = curr_obj->yco_next;
2129
0
        }
2130
0
    }
2131
2132
    /*
2133
    * Cleanup.
2134
    */
2135
0
    return 0;
2136
0
}
2137
2138
static TSK_FS_BLOCK_FLAG_ENUM
2139
    yaffsfs_block_getflags(TSK_FS_INFO *fs, TSK_DADDR_T a_addr)
2140
0
{
2141
0
    YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)fs;
2142
0
    TSK_FS_BLOCK_FLAG_ENUM flags = TSK_FS_BLOCK_FLAG_UNUSED;
2143
2144
0
    TSK_OFF_T offset = (a_addr * (fs->block_pre_size + fs->block_size + fs->block_post_size)) + yfs->page_size;
2145
0
    YaffsSpare *spare = NULL;
2146
0
    YaffsHeader *header = NULL;
2147
0
    if (yaffsfs_read_spare(yfs, &spare, offset) != TSK_OK) {
2148
        /* NOTE: Uh, how do we signal error? */
2149
0
        return flags;
2150
0
    }
2151
2152
0
    if (yaffsfs_is_spare_valid(yfs, spare) == TSK_OK) {
2153
        /* XXX: Do we count blocks of older versions unallocated?
2154
        *      If so, we need a smarter way to do this :/
2155
        *
2156
        *      Walk the object from this block and see if this
2157
        *      block is used in the latest version. Could pre-
2158
        *      calculate this at cache time as well.
2159
        */
2160
2161
2162
0
        if (spare->chunk_id == 0) {
2163
0
            flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_META);
2164
0
        } else {
2165
0
            flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_CONT);
2166
0
        }
2167
2168
        // Have obj id and offset
2169
        // 1. Is the current version of this object allocated?
2170
        // 2. If this is a header, is it the header of the current version?
2171
        // 3. Is the chunk id too big given the current header?
2172
        // 4. Is there a more recent version of this chunk id?
2173
0
        YaffsCacheObject * obj = NULL;
2174
0
        yaffscache_object_find(yfs, spare->object_id, &obj);
2175
2176
        // The result really shouldn't be NULL since we loaded every chunk
2177
0
        if (obj != NULL) {
2178
0
            if (! yaffs_is_version_allocated(yfs, spare->object_id)) {
2179
                // If the current version isn't allocated, then no chunks in it are
2180
0
                flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_UNALLOC);
2181
0
            }
2182
0
            else if (obj->yco_latest == NULL || obj->yco_latest->ycv_header_chunk == NULL) {
2183
0
                flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_UNALLOC);
2184
0
            }
2185
0
            else if (spare->chunk_id == 0) {
2186
0
                if (obj->yco_latest->ycv_header_chunk->ycc_offset == offset - yfs->page_size) {
2187
                    // Have header chunk and it's the most recent header chunk
2188
0
                    flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_ALLOC);
2189
0
                }
2190
0
                else {
2191
                    // Have header chunk but isn't the most recent
2192
0
                    flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_UNALLOC);
2193
0
                }
2194
0
            }
2195
0
            else {
2196
                // Read in the full header
2197
0
                yaffsfs_read_header(yfs, &header, obj->yco_latest->ycv_header_chunk->ycc_offset);
2198
2199
                // chunk_id is 1-based, so for example chunk id 2 would be too big for a file
2200
                //   500 bytes long
2201
0
                if (header->file_size <= ((spare->chunk_id - 1) * (fs->block_size))) {
2202
0
                    flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_UNALLOC);
2203
0
                }
2204
0
                else {
2205
                    // Since at this point we know there should be a chunk with this chunk id in the file, if
2206
                    // this is the most recent version of the chunk assume it's part of the current version of the object.
2207
0
                    YaffsCacheChunk * curr = obj->yco_latest->ycv_last_chunk;
2208
0
                    while(curr != NULL) { // curr should really never make it to the beginning of the list
2209
2210
                        // Did we find our chunk?
2211
0
                        if (curr->ycc_offset == offset - yfs->page_size) {
2212
0
                            flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_ALLOC);
2213
0
                            break;
2214
0
                        }
2215
2216
                        // Did we find a different chunk with our chunk id?
2217
0
                        if (curr->ycc_chunk_id == spare->chunk_id) {
2218
0
                            flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_UNALLOC);
2219
0
                            break;
2220
0
                        }
2221
0
                        curr = curr->ycc_prev;
2222
0
                    }
2223
0
                }
2224
0
            }
2225
0
        }
2226
2227
0
    } else {
2228
0
        flags = (TSK_FS_BLOCK_FLAG_ENUM)(flags | TSK_FS_BLOCK_FLAG_UNUSED | TSK_FS_BLOCK_FLAG_UNALLOC);
2229
0
    }
2230
2231
0
    free(spare);
2232
0
    free(header);
2233
0
    return flags;
2234
0
}
2235
2236
2237
/* yaffsfs_block_walk - block iterator
2238
*
2239
* flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_CONT,
2240
*  TSK_FS_BLOCK_FLAG_META
2241
*
2242
*  Return 1 on error and 0 on success
2243
*/
2244
static uint8_t
2245
    yaffsfs_block_walk(TSK_FS_INFO *a_fs, TSK_DADDR_T a_start_blk,
2246
    TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags,
2247
    TSK_FS_BLOCK_WALK_CB a_action, void *a_ptr)
2248
0
{
2249
0
    TSK_FS_BLOCK *fs_block;
2250
0
    TSK_DADDR_T addr;
2251
2252
    // clean up any error messages that are lying around
2253
0
    tsk_error_reset();
2254
2255
    /*
2256
    * Sanity checks.
2257
    */
2258
0
    if (a_start_blk < a_fs->first_block || a_start_blk > a_fs->last_block) {
2259
0
        tsk_error_reset();
2260
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
2261
0
        tsk_error_set_errstr("yaffsfs_block_walk: start block: %" PRIuDADDR,
2262
0
            a_start_blk);
2263
0
        return 1;
2264
0
    }
2265
0
    if (a_end_blk < a_fs->first_block || a_end_blk > a_fs->last_block
2266
0
        || a_end_blk < a_start_blk) {
2267
0
            tsk_error_reset();
2268
0
            tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
2269
0
            tsk_error_set_errstr("yaffsfs_block_walk: end block: %" PRIuDADDR ,
2270
0
                a_end_blk);
2271
0
            return 1;
2272
0
    }
2273
2274
    /* Sanity check on a_flags -- make sure at least one ALLOC is set */
2275
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) &&
2276
0
        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) {
2277
0
            a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
2278
0
                (a_flags | TSK_FS_BLOCK_WALK_FLAG_ALLOC |
2279
0
                TSK_FS_BLOCK_WALK_FLAG_UNALLOC);
2280
0
    }
2281
0
    if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) &&
2282
0
        ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) {
2283
0
            a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM)
2284
0
                (a_flags | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META);
2285
0
    }
2286
2287
2288
0
    if ((fs_block = tsk_fs_block_alloc(a_fs)) == NULL) {
2289
0
        return 1;
2290
0
    }
2291
2292
0
    for (addr = a_start_blk; addr <= a_end_blk; addr++) {
2293
0
        int retval;
2294
0
        int myflags;
2295
2296
0
        myflags = yaffsfs_block_getflags(a_fs, addr);
2297
2298
        // test if we should call the callback with this one
2299
0
        if ((myflags & TSK_FS_BLOCK_FLAG_META)
2300
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_META)))
2301
0
            continue;
2302
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_CONT)
2303
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT)))
2304
0
            continue;
2305
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC)
2306
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC)))
2307
0
            continue;
2308
0
        else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC)
2309
0
            && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC)))
2310
0
            continue;
2311
2312
0
        if (tsk_fs_block_get(a_fs, fs_block, addr) == NULL) {
2313
0
            tsk_error_set_errstr2("yaffsfs_block_walk: block %" PRIuDADDR,
2314
0
                addr);
2315
0
            tsk_fs_block_free(fs_block);
2316
0
            return 1;
2317
0
        }
2318
2319
0
        retval = a_action(fs_block, a_ptr);
2320
0
        if (retval == TSK_WALK_STOP) {
2321
0
            break;
2322
0
        }
2323
0
        else if (retval == TSK_WALK_ERROR) {
2324
0
            tsk_fs_block_free(fs_block);
2325
0
            return 1;
2326
0
        }
2327
0
    }
2328
2329
    /*
2330
    * Cleanup.
2331
    */
2332
0
    tsk_fs_block_free(fs_block);
2333
0
    return 0;
2334
0
}
2335
2336
static uint8_t
2337
    yaffsfs_fscheck(TSK_FS_INFO * /*fs*/, FILE * /*hFile*/)
2338
0
{
2339
0
    tsk_error_reset();
2340
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
2341
0
    tsk_error_set_errstr("fscheck not implemented yet for YAFFS");
2342
0
    return 1;
2343
0
}
2344
2345
2346
/**
2347
* Print details about the file system to a file handle.
2348
*
2349
* @param fs File system to print details on
2350
* @param hFile File handle to print text to
2351
*
2352
* @returns 1 on error and 0 on success
2353
*/
2354
static uint8_t
2355
    yaffsfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
2356
0
{
2357
0
    YAFFSFS_INFO *yfs = (YAFFSFS_INFO *) fs;
2358
0
    unsigned int obj_count, version_count;
2359
0
    uint32_t obj_first, obj_last, version_first, version_last;
2360
2361
    // clean up any error messages that are lying around
2362
0
    tsk_error_reset();
2363
2364
0
    tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
2365
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
2366
2367
0
    tsk_fprintf(hFile, "File System Type: YAFFS2\n");
2368
0
    tsk_fprintf(hFile, "Page Size: %u\n", yfs->page_size);
2369
0
    tsk_fprintf(hFile, "Spare Size: %u\n", yfs->spare_size);
2370
0
    tsk_fprintf(hFile, "Spare Offsets: Sequence number: %d, Object ID: %d, Chunk ID: %d, nBytes: %d\n",
2371
0
        yfs->spare_seq_offset, yfs->spare_obj_id_offset, yfs->spare_chunk_id_offset, yfs->spare_nbytes_offset);
2372
2373
0
    tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
2374
0
    tsk_fprintf(hFile, "--------------------------------------------\n");
2375
2376
2377
0
    yaffscache_objects_stats(yfs,
2378
0
        &obj_count, &obj_first, &obj_last,
2379
0
        &version_count, &version_first, &version_last);
2380
2381
0
    tsk_fprintf(hFile, "Number of Allocated Objects: %u\n", obj_count);
2382
0
    tsk_fprintf(hFile, "Object Id Range: %" PRIu32 " - %" PRIu32 "\n",
2383
0
        obj_first, obj_last);
2384
0
    tsk_fprintf(hFile, "Number of Total Object Versions: %u\n", version_count);
2385
0
    tsk_fprintf(hFile, "Object Version Range: %" PRIu32 " - %" PRIu32 "\n",
2386
0
        version_first, version_last);
2387
2388
0
    return 0;
2389
0
}
2390
2391
/************************* istat *******************************/
2392
2393
typedef struct {
2394
    FILE *hFile;
2395
    int idx;
2396
} YAFFSFS_PRINT_ADDR;
2397
2398
/* Callback for istat to print the block addresses */
2399
static TSK_WALK_RET_ENUM
2400
    print_addr_act(YAFFSFS_INFO * /*fs_file*/, TSK_OFF_T /*a_off*/,
2401
    TSK_DADDR_T addr, char * /*buf*/, size_t /*size*/,
2402
    TSK_FS_BLOCK_FLAG_ENUM flags, void *a_ptr)
2403
0
{
2404
0
    YAFFSFS_PRINT_ADDR *print = (YAFFSFS_PRINT_ADDR *) a_ptr;
2405
2406
0
    if (flags & TSK_FS_BLOCK_FLAG_CONT) {
2407
0
        tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr);
2408
2409
0
        if (++(print->idx) == 8) {
2410
0
            tsk_fprintf(print->hFile, "\n");
2411
0
            print->idx = 0;
2412
0
        }
2413
0
    }
2414
2415
0
    return TSK_WALK_CONT;
2416
0
}
2417
2418
/**
2419
* Print details on a specific file to a file handle.
2420
*
2421
* @param fs File system file is located in
2422
* @param hFile File handle to print text to
2423
* @param inum Address of file in file system
2424
* @param numblock The number of blocks in file to force print (can go beyond file size)
2425
* @param sec_skew Clock skew in seconds to also print times in
2426
*
2427
* @returns 1 on error and 0 on success
2428
*/
2429
static uint8_t
2430
    yaffsfs_istat(TSK_FS_INFO *fs, TSK_FS_ISTAT_FLAG_ENUM flags, FILE * hFile, TSK_INUM_T inum,
2431
    TSK_DADDR_T numblock, int32_t sec_skew)
2432
0
{
2433
0
    TSK_FS_META *fs_meta;
2434
0
    YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)fs;
2435
0
    char ls[12];
2436
0
    YAFFSFS_PRINT_ADDR print;
2437
0
    char timeBuf[128];
2438
0
    YaffsCacheObject * obj = NULL;
2439
0
    YaffsCacheVersion * version = NULL;
2440
0
    YaffsHeader * header = NULL;
2441
2442
0
    yaffscache_version_find_by_inode(yfs, inum, &version, &obj);
2443
2444
0
    std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{
2445
0
        tsk_fs_file_open_meta(fs, NULL, inum),
2446
0
        tsk_fs_file_close
2447
0
    };
2448
2449
0
    if (!fs_file) {
2450
0
        return 1;
2451
0
    }
2452
0
    fs_meta = fs_file->meta;
2453
2454
0
    tsk_fprintf(hFile, "inode: %" PRIuINUM "\n", inum);
2455
0
    tsk_fprintf(hFile, "%sAllocated\n",
2456
0
        (fs_meta->flags & TSK_FS_META_FLAG_ALLOC) ? "" : "Not ");
2457
2458
0
    if (fs_meta->link)
2459
0
        tsk_fprintf(hFile, "symbolic link to: %s\n", fs_meta->link);
2460
2461
0
    tsk_fprintf(hFile, "uid / gid: %" PRIuUID " / %" PRIuGID "\n",
2462
0
        fs_meta->uid, fs_meta->gid);
2463
2464
0
    tsk_fs_meta_make_ls(fs_meta, ls, sizeof(ls));
2465
0
    tsk_fprintf(hFile, "mode: %s\n", ls);
2466
2467
0
    tsk_fprintf(hFile, "size: %" PRIdOFF "\n", fs_meta->size);
2468
0
    tsk_fprintf(hFile, "num of links: %d\n", fs_meta->nlink);
2469
2470
0
    if (version != NULL) {
2471
0
        yaffsfs_read_header(yfs, &header, version->ycv_header_chunk->ycc_offset);
2472
0
        if (header != NULL) {
2473
0
            tsk_fprintf(hFile, "Name: %s\n", header->name);
2474
0
        }
2475
0
    }
2476
2477
0
    if (sec_skew != 0) {
2478
0
        tsk_fprintf(hFile, "\nAdjusted Inode Times:\n");
2479
0
        fs_meta->mtime -= sec_skew;
2480
0
        fs_meta->atime -= sec_skew;
2481
0
        fs_meta->ctime -= sec_skew;
2482
2483
0
        tsk_fprintf(hFile, "Accessed:\t%s\n",
2484
0
            tsk_fs_time_to_str(fs_meta->atime, timeBuf));
2485
0
        tsk_fprintf(hFile, "File Modified:\t%s\n",
2486
0
            tsk_fs_time_to_str(fs_meta->mtime, timeBuf));
2487
0
        tsk_fprintf(hFile, "Inode Modified:\t%s\n",
2488
0
            tsk_fs_time_to_str(fs_meta->ctime, timeBuf));
2489
2490
0
        fs_meta->mtime += sec_skew;
2491
0
        fs_meta->atime += sec_skew;
2492
0
        fs_meta->ctime += sec_skew;
2493
2494
0
        tsk_fprintf(hFile, "\nOriginal Inode Times:\n");
2495
0
    }
2496
0
    else {
2497
0
        tsk_fprintf(hFile, "\nInode Times:\n");
2498
0
    }
2499
2500
0
    tsk_fprintf(hFile, "Accessed:\t%s\n",
2501
0
        tsk_fs_time_to_str(fs_meta->atime, timeBuf));
2502
0
    tsk_fprintf(hFile, "File Modified:\t%s\n",
2503
0
        tsk_fs_time_to_str(fs_meta->mtime, timeBuf));
2504
0
    tsk_fprintf(hFile, "Inode Modified:\t%s\n",
2505
0
        tsk_fs_time_to_str(fs_meta->ctime, timeBuf));
2506
2507
0
    if (version != NULL) {
2508
0
        tsk_fprintf(hFile, "\nHeader Chunk:\n");
2509
0
        tsk_fprintf(hFile, "%" PRIuDADDR "\n", (version->ycv_header_chunk->ycc_offset / (yfs->page_size + yfs->spare_size)));
2510
0
    }
2511
2512
0
    if (numblock > 0) {
2513
0
        TSK_OFF_T lower_size = numblock * fs->block_size;
2514
0
        fs_meta->size = (lower_size < fs_meta->size)?(lower_size):(fs_meta->size);
2515
0
    }
2516
0
    tsk_fprintf(hFile, "\nData Chunks:\n");
2517
2518
2519
0
    if (flags & TSK_FS_ISTAT_RUNLIST) {
2520
0
        const TSK_FS_ATTR *fs_attr_default =
2521
0
            tsk_fs_file_attr_get_type(fs_file.get(),
2522
0
                TSK_FS_ATTR_TYPE_DEFAULT, 0, 0);
2523
0
        if (fs_attr_default && (fs_attr_default->flags & TSK_FS_ATTR_NONRES)) {
2524
0
            if (tsk_fs_attr_print(fs_attr_default, hFile)) {
2525
0
                tsk_fprintf(hFile, "\nError creating run lists  ");
2526
0
                tsk_error_print(hFile);
2527
0
                tsk_error_reset();
2528
0
            }
2529
0
        }
2530
0
    }
2531
0
    else {
2532
0
        print.idx = 0;
2533
0
        print.hFile = hFile;
2534
2535
0
        if (tsk_fs_file_walk(fs_file.get(), TSK_FS_FILE_WALK_FLAG_AONLY,
2536
0
            (TSK_FS_FILE_WALK_CB)print_addr_act, (void *)&print)) {
2537
0
            tsk_fprintf(hFile, "\nError reading file:  ");
2538
0
            tsk_error_print(hFile);
2539
0
            tsk_error_reset();
2540
0
        }
2541
0
        else if (print.idx != 0) {
2542
0
            tsk_fprintf(hFile, "\n");
2543
0
        }
2544
0
    }
2545
2546
0
    return 0;
2547
0
}
2548
2549
/* yaffsfs_close - close an yaffsfs file system */
2550
static void
2551
    yaffsfs_close(TSK_FS_INFO *fs)
2552
0
{
2553
0
    if (fs != NULL) {
2554
0
        YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)fs;
2555
2556
0
        fs->tag = 0;
2557
2558
        // Walk and free the cache structures
2559
0
        yaffscache_objects_free(yfs);
2560
0
        yaffscache_chunks_free(yfs);
2561
2562
        //tsk_deinit_lock(&yaffsfs->lock);
2563
0
        tsk_fs_free(fs);
2564
0
  }
2565
0
}
2566
2567
typedef struct _dir_open_cb_args {
2568
    YAFFSFS_INFO *yfs;
2569
    TSK_FS_DIR *dir;
2570
    TSK_INUM_T parent_addr;
2571
} dir_open_cb_args;
2572
2573
static TSK_RETVAL_ENUM
2574
0
    yaffs_dir_open_meta_cb(YaffsCacheObject * /*obj*/, YaffsCacheVersion *version, void *args) {
2575
0
        dir_open_cb_args *cb_args = (dir_open_cb_args *) args;
2576
0
        YaffsCacheChunk *chunk = version->ycv_header_chunk;
2577
0
        TSK_INUM_T curr_inode = 0;
2578
0
        uint32_t obj_id = chunk->ycc_obj_id;
2579
0
        uint32_t chunk_id = chunk->ycc_chunk_id;
2580
0
        uint32_t vnum = version->ycv_version;
2581
0
        YaffsHeader *header = NULL;
2582
0
        TSK_FS_NAME * fs_name;
2583
0
        char *file_ext;
2584
0
        char version_string[64]; // Allow a max of 64 bytes in the version string
2585
2586
0
        yaffscache_obj_id_and_version_to_inode(obj_id, vnum, &curr_inode);
2587
2588
0
        if (chunk_id != 0) {
2589
0
            return TSK_ERR;
2590
0
        }
2591
2592
0
        if (tsk_verbose)
2593
0
            fprintf(stderr, "dir_open_find_children_cb: %08" PRIxINUM " -> %08" PRIx32 ":%d\n", cb_args->parent_addr, obj_id, vnum);
2594
2595
2596
0
        if (yaffsfs_read_header(cb_args->yfs, &header, chunk->ycc_offset) != TSK_OK) {
2597
0
            return TSK_ERR;
2598
0
        }
2599
2600
0
        if ((fs_name = tsk_fs_name_alloc(YAFFSFS_MAXNAMLEN + 64, 0)) == NULL) {
2601
0
            free(header);
2602
0
            return TSK_ERR;
2603
0
        }
2604
2605
0
        switch (obj_id) {
2606
0
        case YAFFS_OBJECT_LOSTNFOUND:
2607
0
            strncpy(fs_name->name, YAFFS_OBJECT_LOSTNFOUND_NAME,
2608
0
                fs_name->name_size - 64);
2609
0
            break;
2610
0
        case YAFFS_OBJECT_UNLINKED:
2611
0
            strncpy(fs_name->name, YAFFS_OBJECT_UNLINKED_NAME,
2612
0
                fs_name->name_size - 64);
2613
0
            break;
2614
0
        case YAFFS_OBJECT_DELETED:
2615
0
            strncpy(fs_name->name, YAFFS_OBJECT_DELETED_NAME,
2616
0
                fs_name->name_size - 64);
2617
0
            break;
2618
0
        default:
2619
0
            strncpy(fs_name->name, header->name, fs_name->name_size - 64);
2620
0
            break;
2621
0
        }
2622
0
        fs_name->name[fs_name->name_size - 65] = 0;
2623
2624
        // Only put object/version string onto unallocated versions
2625
0
        if (! yaffs_is_version_allocated(cb_args->yfs, curr_inode)) {
2626
            // Also copy the extension so that it also shows up after the version string, which allows
2627
            // easier searching by file extension. Max extension length is 5 characters after the dot,
2628
            // and require at least one character before the dot
2629
0
            file_ext = strrchr(fs_name->name, '.');
2630
0
            if ((file_ext != NULL) && (file_ext != fs_name->name) && (strlen(file_ext) < 7)) {
2631
0
               snprintf(version_string, 64, "#%d,%d%s", obj_id, vnum, file_ext);
2632
0
            }
2633
0
            else {
2634
0
               snprintf(version_string, 64, "#%d,%d", obj_id, vnum);
2635
0
            }
2636
0
            strncat(fs_name->name, version_string, 64);
2637
0
            fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
2638
0
        }
2639
0
        else {
2640
0
            fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
2641
0
        }
2642
2643
0
        fs_name->meta_addr = curr_inode;
2644
2645
0
        switch (header->obj_type) {
2646
0
        case YAFFS_TYPE_FILE:
2647
0
            fs_name->type = TSK_FS_NAME_TYPE_REG;
2648
0
            break;
2649
2650
0
        case YAFFS_TYPE_DIRECTORY:
2651
0
            fs_name->type = TSK_FS_NAME_TYPE_DIR;
2652
0
            break;
2653
2654
0
        case YAFFS_TYPE_SOFTLINK:
2655
0
        case YAFFS_TYPE_HARDLINK:
2656
0
            fs_name->type = TSK_FS_NAME_TYPE_LNK;
2657
0
            break;
2658
2659
0
        case YAFFS_TYPE_SPECIAL:
2660
0
            fs_name->type = TSK_FS_NAME_TYPE_UNDEF; // Could be a socket
2661
0
            break;
2662
2663
0
        default:
2664
0
            if (tsk_verbose)
2665
0
                fprintf(stderr, "yaffs_dir_open_meta_cb: unhandled object type\n");
2666
0
            fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
2667
0
            break;
2668
0
        }
2669
2670
0
        free(header);
2671
2672
0
        if (tsk_fs_dir_add(cb_args->dir, fs_name)) {
2673
0
            tsk_fs_name_free(fs_name);
2674
0
            return TSK_ERR;
2675
0
        }
2676
2677
        /* A copy is made in tsk_fs_dir_add, so we can free this one */
2678
0
        tsk_fs_name_free(fs_name);
2679
2680
0
        return TSK_OK;
2681
0
}
2682
2683
static TSK_RETVAL_ENUM
2684
yaffsfs_dir_open_meta(
2685
  TSK_FS_INFO *a_fs,
2686
  TSK_FS_DIR ** a_fs_dir,
2687
  TSK_INUM_T a_addr,
2688
  [[maybe_unused]] int recursion_depth)
2689
0
{
2690
0
    TSK_FS_DIR *fs_dir;
2691
0
    TSK_FS_NAME *fs_name;
2692
0
    YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)a_fs;
2693
0
    int should_walk_children = 0;
2694
0
    uint32_t obj_id;
2695
0
    uint32_t ver_number;
2696
2697
0
    if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
2698
0
        tsk_error_reset();
2699
0
        tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
2700
0
        tsk_error_set_errstr("yaffs_dir_open_meta: Invalid inode value: %"
2701
0
            PRIuINUM, a_addr);
2702
0
        return TSK_ERR;
2703
0
    }
2704
0
    else if (a_fs_dir == NULL) {
2705
0
        tsk_error_reset();
2706
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
2707
0
        tsk_error_set_errstr("yaffs_dir_open_meta: NULL fs_dir argument given");
2708
0
        return TSK_ERR;
2709
0
    }
2710
2711
0
    fs_dir = *a_fs_dir;
2712
2713
0
    if (fs_dir) {
2714
0
        tsk_fs_dir_reset(fs_dir);
2715
0
        fs_dir->addr = a_addr;
2716
0
    }
2717
0
    else if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) {
2718
0
        return TSK_ERR;
2719
0
    }
2720
2721
0
    if (tsk_verbose)
2722
0
        fprintf(stderr,"yaffs_dir_open_meta: called for directory %" PRIu32 "\n", (uint32_t) a_addr);
2723
2724
    //  handle the orphan directory if its contents were requested
2725
0
    if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) {
2726
0
        return tsk_fs_dir_find_orphans(a_fs, fs_dir);
2727
0
    }
2728
2729
0
    if ((fs_name = tsk_fs_name_alloc(YAFFSFS_MAXNAMLEN, 0)) == NULL) {
2730
0
        return TSK_ERR;
2731
0
    }
2732
2733
2734
0
    if ((fs_dir->fs_file =
2735
0
        tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) {
2736
0
            tsk_error_errstr2_concat(" - yaffs_dir_open_meta");
2737
0
            tsk_fs_name_free(fs_name);
2738
0
            return TSK_ERR;
2739
0
    }
2740
2741
    // extract obj_id and ver_number from inum
2742
0
    yaffscache_inode_to_obj_id_and_version(a_addr, &obj_id, &ver_number);
2743
2744
    // Decide if we should walk the directory structure
2745
0
    if (obj_id == YAFFS_OBJECT_DELETED ||
2746
0
        obj_id == YAFFS_OBJECT_UNLINKED) {
2747
0
            should_walk_children = 1;
2748
0
    }
2749
0
    else {
2750
0
        YaffsCacheObject *obj;
2751
0
        YaffsCacheVersion *versionFound;
2752
0
        TSK_RETVAL_ENUM result = yaffscache_version_find_by_inode(yfs, a_addr, &versionFound, &obj);
2753
0
        if (result != TSK_OK) {
2754
0
            if (tsk_verbose)
2755
0
                tsk_fprintf(stderr, "yaffsfs_dir_open_meta: yaffscache_version_find_by_inode failed! (inode: %d\n", a_addr);
2756
0
            tsk_fs_name_free(fs_name);
2757
0
            return TSK_ERR;
2758
0
        }
2759
2760
        /* Only attach files onto the latest version of the directory */
2761
0
        should_walk_children = (obj->yco_latest == versionFound);
2762
0
    }
2763
2764
    // Search the cache for the children of this object and add them to fs_dir
2765
0
    if (should_walk_children) {
2766
0
        dir_open_cb_args args;
2767
0
        args.yfs = yfs;
2768
0
        args.dir = fs_dir;
2769
0
        args.parent_addr = a_addr;
2770
0
        yaffscache_find_children(yfs, a_addr, yaffs_dir_open_meta_cb, &args);
2771
0
    }
2772
2773
    // add special entries to root directory
2774
0
    if (obj_id == YAFFS_OBJECT_ROOT) {
2775
0
        strncpy(fs_name->name, YAFFS_OBJECT_UNLINKED_NAME, fs_name->name_size);
2776
0
        fs_name->meta_addr = YAFFS_OBJECT_UNLINKED;
2777
0
        fs_name->type = TSK_FS_NAME_TYPE_DIR;
2778
0
        fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
2779
0
        if (tsk_fs_dir_add(fs_dir, fs_name)) {
2780
0
            tsk_fs_name_free(fs_name);
2781
0
            return TSK_ERR;
2782
0
        }
2783
2784
0
        strncpy(fs_name->name, YAFFS_OBJECT_DELETED_NAME, fs_name->name_size);
2785
0
        fs_name->meta_addr = YAFFS_OBJECT_DELETED;
2786
0
        fs_name->type = TSK_FS_NAME_TYPE_DIR;
2787
0
        fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
2788
0
        if (tsk_fs_dir_add(fs_dir, fs_name)) {
2789
0
            tsk_fs_name_free(fs_name);
2790
0
            return TSK_ERR;
2791
0
        }
2792
2793
        // orphan directory
2794
0
        if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) {
2795
0
            tsk_fs_name_free(fs_name);
2796
0
            return TSK_ERR;
2797
0
        }
2798
0
        fs_name->meta_addr = yfs->fs_info.last_inum;
2799
0
        fs_name->type = TSK_FS_NAME_TYPE_DIR;
2800
0
        fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
2801
0
        if (tsk_fs_dir_add(fs_dir, fs_name)) {
2802
0
            tsk_fs_name_free(fs_name);
2803
0
            return TSK_ERR;
2804
0
        }
2805
0
    }
2806
2807
0
    tsk_fs_name_free(fs_name);
2808
0
    return TSK_OK;
2809
0
}
2810
2811
static TSK_FS_ATTR_TYPE_ENUM
2812
    yaffsfs_get_default_attr_type(const TSK_FS_FILE * /*a_file*/)
2813
0
{
2814
0
    return TSK_FS_ATTR_TYPE_DEFAULT;
2815
0
}
2816
2817
static uint8_t
2818
    yaffsfs_load_attrs(TSK_FS_FILE *file)
2819
0
{
2820
0
    TSK_FS_ATTR *attr;
2821
0
    TSK_FS_META *meta;
2822
0
    TSK_FS_INFO *fs;
2823
0
    YAFFSFS_INFO *yfs;
2824
0
    TSK_FS_ATTR_RUN *data_run;
2825
0
    TSK_DADDR_T file_block_count;
2826
0
    YaffsCacheObject *obj;
2827
0
    YaffsCacheVersion *version;
2828
0
    TSK_RETVAL_ENUM result;
2829
0
    TSK_LIST *chunks_seen = NULL;
2830
0
    YaffsCacheChunk *curr;
2831
0
    TSK_FS_ATTR_RUN *data_run_new;
2832
2833
2834
0
    if (file == NULL || file->meta == NULL || file->fs_info == NULL)
2835
0
    {
2836
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
2837
0
        tsk_error_set_errstr
2838
0
            ("yaffsfs_load_attrs: called with NULL pointers");
2839
0
        return 1;
2840
0
    }
2841
2842
0
    meta = file->meta;
2843
0
    yfs = (YAFFSFS_INFO *)file->fs_info;
2844
0
    fs = &yfs->fs_info;
2845
2846
    // see if we have already loaded the runs
2847
0
    if ((meta->attr != NULL)
2848
0
        && (meta->attr_state == TSK_FS_META_ATTR_STUDIED)) {
2849
0
            return 0;
2850
0
    }
2851
0
    else if (meta->attr_state == TSK_FS_META_ATTR_ERROR) {
2852
0
        return 1;
2853
0
    }
2854
    // not sure why this would ever happen, but...
2855
0
    else if (meta->attr != NULL) {
2856
0
        tsk_fs_attrlist_markunused(meta->attr);
2857
0
    }
2858
0
    else if (meta->attr == NULL) {
2859
0
        meta->attr = tsk_fs_attrlist_alloc();
2860
0
    }
2861
2862
0
    attr = tsk_fs_attrlist_getnew(meta->attr, TSK_FS_ATTR_NONRES);
2863
0
    if (attr == NULL) {
2864
0
        meta->attr_state = TSK_FS_META_ATTR_ERROR;
2865
0
        return 1;
2866
0
    }
2867
2868
0
    if (meta->size == 0) {
2869
0
        data_run = NULL;
2870
0
    }
2871
0
    else {
2872
        /* BC: I'm not entirely sure this is needed.  My guess is that
2873
         * this was done instead of maintaining the head of the list of
2874
         * runs.  In theory, the tsk_fs_attr_add_run() method should handle
2875
         * the fillers. */
2876
0
        data_run = tsk_fs_attr_run_alloc();
2877
0
        if (data_run == NULL) {
2878
0
            tsk_fs_attr_run_free(data_run);
2879
0
            meta->attr_state = TSK_FS_META_ATTR_ERROR;
2880
0
            return 1;
2881
0
        }
2882
2883
0
        data_run->offset = 0;
2884
0
        data_run->addr = 0;
2885
0
        data_run->len = (meta->size + fs->block_size - 1) / fs->block_size;
2886
0
        data_run->flags = TSK_FS_ATTR_RUN_FLAG_FILLER;
2887
0
    }
2888
2889
2890
    // initialize the data run
2891
0
    if (tsk_fs_attr_set_run(file, attr, data_run, NULL,
2892
0
        TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
2893
0
        meta->size, meta->size, roundup(meta->size, fs->block_size), (TSK_FS_ATTR_FLAG_ENUM)0, 0)) {
2894
0
            meta->attr_state = TSK_FS_META_ATTR_ERROR;
2895
0
            return 1;
2896
0
    }
2897
2898
    // If the file has size zero, return now
2899
0
    if (meta->size == 0) {
2900
0
        meta->attr_state = TSK_FS_META_ATTR_STUDIED;
2901
0
        return 0;
2902
0
    }
2903
2904
2905
    /* Get the version for the given object. */
2906
0
    result = yaffscache_version_find_by_inode(yfs, meta->addr, &version, &obj);
2907
0
    if (result != TSK_OK || version == NULL) {
2908
0
        if (tsk_verbose)
2909
0
            tsk_fprintf(stderr, "yaffsfs_load_attrs: yaffscache_version_find_by_inode failed!\n");
2910
0
        meta->attr_state = TSK_FS_META_ATTR_ERROR;
2911
0
        return 1;
2912
0
    }
2913
2914
0
    if (tsk_verbose)
2915
0
        yaffscache_object_dump(stderr, obj);
2916
2917
0
    file_block_count = data_run->len;
2918
    /* Cycle through the chunks for this version of this object */
2919
0
    curr = version->ycv_last_chunk;
2920
0
    while (curr != NULL && curr->ycc_obj_id == obj->yco_obj_id) {
2921
2922
0
        if (curr->ycc_chunk_id == 0) {
2923
0
            if (tsk_verbose)
2924
0
                tsk_fprintf(stderr, "yaffsfs_load_attrs: skipping header chunk\n");
2925
0
        }
2926
0
        else if (tsk_list_find(chunks_seen, curr->ycc_chunk_id)) {
2927
0
            if (tsk_verbose)
2928
0
                tsk_fprintf(stderr, "yaffsfs_load_attrs: skipping duplicate chunk\n");
2929
0
        }
2930
0
        else if (curr->ycc_chunk_id > file_block_count) {
2931
0
            if (tsk_verbose)
2932
0
                tsk_fprintf(stderr, "yaffsfs_load_attrs: skipping chunk past end\n");
2933
0
        }
2934
        /* We like this chunk */
2935
0
        else {
2936
            // add it to our internal list
2937
0
            if (tsk_list_add(&chunks_seen, curr->ycc_chunk_id)) {
2938
0
                meta->attr_state = TSK_FS_META_ATTR_ERROR;
2939
0
                tsk_list_free(chunks_seen);
2940
0
                chunks_seen = NULL;
2941
0
                return 1;
2942
0
            }
2943
2944
0
            data_run_new = tsk_fs_attr_run_alloc();
2945
0
            if (data_run_new == NULL) {
2946
0
                tsk_fs_attr_run_free(data_run_new);
2947
0
                meta->attr_state = TSK_FS_META_ATTR_ERROR;
2948
0
                return 1;
2949
0
            }
2950
2951
0
            data_run_new->offset = (curr->ycc_chunk_id - 1);
2952
0
            data_run_new->addr = curr->ycc_offset / (fs->block_pre_size + fs->block_size + fs->block_post_size);
2953
0
            data_run_new->len = 1;
2954
0
            data_run_new->flags = TSK_FS_ATTR_RUN_FLAG_NONE;
2955
2956
0
            if (tsk_verbose)
2957
0
                tsk_fprintf(stderr, "yaffsfs_load_attrs: @@@ Chunk %d : %08x is at offset 0x%016llx\n",
2958
0
                curr->ycc_chunk_id, curr->ycc_seq_number, curr->ycc_offset);
2959
2960
0
            tsk_fs_attr_add_run(fs, attr, data_run_new);
2961
0
        }
2962
2963
0
        curr = curr->ycc_prev;
2964
0
    }
2965
2966
0
    tsk_list_free(chunks_seen);
2967
0
    meta->attr_state = TSK_FS_META_ATTR_STUDIED;
2968
0
    return 0;
2969
0
}
2970
2971
static uint8_t
2972
    yaffsfs_jentry_walk(TSK_FS_INFO * /*info*/, int /*entry*/,
2973
    TSK_FS_JENTRY_WALK_CB /*cb*/, void * /*fn*/)
2974
0
{
2975
0
    tsk_error_reset();
2976
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
2977
0
    tsk_error_set_errstr("Journal support for YAFFS is not implemented");
2978
0
    return 1;
2979
0
}
2980
2981
static uint8_t
2982
    yaffsfs_jblk_walk(TSK_FS_INFO * /*info*/, TSK_DADDR_T /*daddr*/,
2983
    TSK_DADDR_T /*daddrt*/, int /*entry*/, TSK_FS_JBLK_WALK_CB /*cb*/,
2984
    void * /*fn*/)
2985
0
{
2986
0
    tsk_error_reset();
2987
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
2988
0
    tsk_error_set_errstr("Journal support for YAFFS is not implemented");
2989
0
    return 1;
2990
0
}
2991
2992
static uint8_t
2993
    yaffsfs_jopen(TSK_FS_INFO * /*info*/, TSK_INUM_T /*inum*/)
2994
0
{
2995
0
    tsk_error_reset();
2996
0
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
2997
0
    tsk_error_set_errstr("Journal support for YAFFS is not implemented");
2998
0
    return 1;
2999
0
}
3000
3001
/**
3002
* \internal
3003
* Open part of a disk image as a Yaffs/2 file system.
3004
*
3005
* @param img_info Disk image to analyze
3006
* @param offset Byte offset where file system starts
3007
* @param ftype Specific type of file system
3008
* @param a_pass NOT USED
3009
* @param test Going to use this - 1 if we're doing auto-detect, 0 if not (display more verbose messages if the user specified YAFFS2)
3010
* @returns NULL on error or if data is not an Yaffs/3 file system
3011
*/
3012
TSK_FS_INFO *
3013
yaffs2_open(
3014
  TSK_IMG_INFO * img_info,
3015
  TSK_OFF_T offset,
3016
  TSK_FS_TYPE_ENUM ftype,
3017
  [[maybe_unused]] const char* a_pass,
3018
  uint8_t test)
3019
0
{
3020
0
    const unsigned int psize = img_info->page_size;
3021
0
    const unsigned int ssize = img_info->spare_size;
3022
0
    YaffsHeader * first_header = NULL;
3023
0
    std::map<std::string, std::string> configParams;
3024
0
    YAFFS_CONFIG_STATUS config_file_status;
3025
3026
    // clean up any error messages that are lying around
3027
0
    tsk_error_reset();
3028
3029
0
    if (TSK_FS_TYPE_ISYAFFS2(ftype) == 0) {
3030
0
        tsk_error_reset();
3031
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
3032
0
        tsk_error_set_errstr("Invalid FS Type in yaffsfs_open");
3033
0
        return nullptr;
3034
0
    }
3035
3036
0
    if (img_info->sector_size == 0) {
3037
0
        tsk_error_reset();
3038
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
3039
0
        tsk_error_set_errstr("yaffs2_open: sector size is 0");
3040
0
        return nullptr;
3041
0
    }
3042
3043
0
    const auto deleter = [](YAFFSFS_INFO* yaffsfs) {
3044
0
        yaffsfs_close(&yaffsfs->fs_info);
3045
0
    };
3046
3047
0
    std::unique_ptr<YAFFSFS_INFO, decltype(deleter)> yaffsfs{
3048
0
        (YAFFSFS_INFO *) tsk_fs_malloc(sizeof(YAFFSFS_INFO)),
3049
0
        deleter
3050
0
    };
3051
3052
0
    if (!yaffsfs) {
3053
0
        return nullptr;
3054
0
    }
3055
3056
0
    yaffsfs->cache_objects = NULL;
3057
0
    yaffsfs->chunkMap = NULL;
3058
3059
0
    TSK_FS_INFO* fs = &(yaffsfs->fs_info);
3060
3061
0
    fs->tag = TSK_FS_INFO_TAG;
3062
0
    fs->ftype = ftype;
3063
0
    fs->flags = (TSK_FS_INFO_FLAG_ENUM)0;
3064
0
    fs->img_info = img_info;
3065
0
    fs->offset = offset;
3066
0
    fs->endian = TSK_LIT_ENDIAN;
3067
3068
    // Read config file (if it exists)
3069
0
    config_file_status = yaffs_load_config_file(img_info, configParams);
3070
    // BL-6929(JTS): When using external readers, this call will fail.
3071
    // Not having a config should not be a fatal error.
3072
  /*if (config_file_status == YAFFS_CONFIG_ERROR) {
3073
        // tsk_error was set by yaffs_load_config
3074
        goto on_error;
3075
    }
3076
0
    else*/ if (config_file_status == YAFFS_CONFIG_OK) {
3077
        // Validate the input
3078
        // If it fails validation, return (tsk_error will be set up already)
3079
0
        if (1 == yaffs_validate_config_file(configParams)) {
3080
0
            return nullptr;
3081
0
        }
3082
0
    }
3083
3084
    // If we read these fields from the config file, use those values. Otherwise use the defaults
3085
0
    if (configParams.find(YAFFS_CONFIG_PAGE_SIZE_STR) != configParams.end()) {
3086
0
        yaffsfs->page_size = atoi(configParams[YAFFS_CONFIG_PAGE_SIZE_STR].c_str());
3087
0
    }
3088
0
    else {
3089
0
        yaffsfs->page_size = psize == 0 ? YAFFS_DEFAULT_PAGE_SIZE : psize;
3090
0
    }
3091
3092
0
    if (configParams.find(YAFFS_CONFIG_SPARE_SIZE_STR) != configParams.end()) {
3093
0
        yaffsfs->spare_size = atoi(configParams[YAFFS_CONFIG_SPARE_SIZE_STR].c_str());
3094
0
    }
3095
0
    else {
3096
0
        yaffsfs->spare_size = ssize == 0 ? YAFFS_DEFAULT_SPARE_SIZE : ssize;
3097
0
    }
3098
3099
0
    if (configParams.find(YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR) != configParams.end()) {
3100
0
        yaffsfs->chunks_per_block = atoi(configParams[YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR].c_str());
3101
0
    }
3102
0
    else {
3103
0
        yaffsfs->chunks_per_block = 64;
3104
0
    }
3105
3106
    // TODO: Why are 2 different memory allocation methods used in the same code?
3107
    // This makes things unnecessary complex.
3108
0
    yaffsfs->max_obj_id = 1;
3109
0
    yaffsfs->max_version = 0;
3110
3111
    // Keep track of whether we're doing auto-detection of the file system
3112
0
    if (test) {
3113
0
        yaffsfs->autoDetect = 1;
3114
0
    }
3115
0
    else {
3116
0
        yaffsfs->autoDetect = 0;
3117
0
    }
3118
3119
    // Determine the layout of the spare area
3120
    // If it was specified in the config file, use those values. Otherwise do the auto-detection
3121
0
    if (configParams.find(YAFFS_CONFIG_SEQ_NUM_STR) != configParams.end()) {
3122
        // In the validation step, we ensured that if one of the offsets was set, we have all of them
3123
0
        yaffsfs->spare_seq_offset = atoi(configParams[YAFFS_CONFIG_SEQ_NUM_STR].c_str());
3124
0
        yaffsfs->spare_obj_id_offset = atoi(configParams[YAFFS_CONFIG_OBJ_ID_STR].c_str());
3125
0
        yaffsfs->spare_chunk_id_offset = atoi(configParams[YAFFS_CONFIG_CHUNK_ID_STR].c_str());
3126
3127
        // Check that the offsets are valid for the given spare area size (fields are 4 bytes long)
3128
0
        if ((yaffsfs->spare_seq_offset + 4 > yaffsfs->spare_size) ||
3129
0
            (yaffsfs->spare_obj_id_offset + 4 > yaffsfs->spare_size) ||
3130
0
            (yaffsfs->spare_chunk_id_offset + 4 > yaffsfs->spare_size)) {
3131
0
            tsk_error_reset();
3132
0
            tsk_error_set_errno(TSK_ERR_FS);
3133
0
            tsk_error_set_errstr("yaffs2_open: Offset(s) in config file too large for spare area (size %d). %s", yaffsfs->spare_size, YAFFS_HELP_MESSAGE);
3134
0
            return nullptr;
3135
0
        }
3136
3137
3138
        // nBytes isn't currently used, so just set to zero
3139
0
        yaffsfs->spare_nbytes_offset = 0;
3140
0
    }
3141
0
    else {
3142
        // Decide how many blocks to test. If we're not doing auto-detection, set to zero (no limit)
3143
0
        unsigned int maxBlocksToTest;
3144
0
        if (yaffsfs->autoDetect) {
3145
0
            maxBlocksToTest = YAFFS_DEFAULT_MAX_TEST_BLOCKS;
3146
0
        }
3147
0
        else {
3148
0
            maxBlocksToTest = 0;
3149
0
        }
3150
3151
0
        if (yaffs_initialize_spare_format(yaffsfs.get(), maxBlocksToTest) != TSK_OK) {
3152
0
            tsk_error_reset();
3153
0
            tsk_error_set_errno(TSK_ERR_FS_MAGIC);
3154
0
            tsk_error_set_errstr("not a YAFFS file system (bad spare format). %s", YAFFS_HELP_MESSAGE);
3155
0
            if (tsk_verbose)
3156
0
                fprintf(stderr, "yaffsfs_open: could not find valid spare area format\n%s\n", YAFFS_HELP_MESSAGE);
3157
0
            return nullptr;
3158
0
        }
3159
0
    }
3160
3161
    /*
3162
    * Read the first record, make sure it's a valid header...
3163
    *
3164
    * Used for verification and autodetection of
3165
    * the FS type.
3166
    */
3167
0
    if (yaffsfs_read_header(yaffsfs.get(), &first_header, 0)) {
3168
0
        tsk_error_reset();
3169
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
3170
0
        tsk_error_set_errstr("not a YAFFS file system (first record). %s", YAFFS_HELP_MESSAGE);
3171
0
        if (tsk_verbose)
3172
0
            fprintf(stderr, "yaffsfs_open: invalid first record\n%s\n", YAFFS_HELP_MESSAGE);
3173
0
        return nullptr;
3174
0
    }
3175
0
    free(first_header);
3176
0
    first_header = NULL;
3177
3178
0
    fs->duname = "Chunk";
3179
3180
    /*
3181
    * Calculate the meta data info
3182
    */
3183
    //fs->last_inum = 0xffffffff; // Will update this as we go
3184
0
    fs->last_inum = 0;
3185
0
    fs->root_inum = YAFFS_OBJECT_ROOT;
3186
0
    fs->first_inum = YAFFS_OBJECT_FIRST;
3187
    //fs->inum_count = fs->last_inum; // For now this will be the last_inum - 1 (after we calculate it)
3188
3189
    /*
3190
    * Calculate the block info
3191
    */
3192
0
    fs->dev_bsize = img_info->sector_size;
3193
0
    fs->block_size = yaffsfs->page_size;
3194
0
    fs->block_pre_size = 0;
3195
0
    fs->block_post_size = yaffsfs->spare_size;
3196
0
    fs->block_count = img_info->size / (fs->block_pre_size + fs->block_size + fs->block_post_size);
3197
0
    fs->first_block = 0;
3198
0
    fs->last_block_act = fs->last_block = fs->block_count ? fs->block_count - 1 : 0;
3199
3200
    /* Set the generic function pointers */
3201
0
    fs->inode_walk = yaffsfs_inode_walk;
3202
0
    fs->block_walk = yaffsfs_block_walk;
3203
0
    fs->block_getflags = yaffsfs_block_getflags;
3204
3205
0
    fs->get_default_attr_type = yaffsfs_get_default_attr_type;
3206
0
    fs->load_attrs = yaffsfs_load_attrs;
3207
3208
0
    fs->file_add_meta = yaffs_inode_lookup;
3209
0
    fs->dir_open_meta = yaffsfs_dir_open_meta;
3210
0
    fs->fsstat = yaffsfs_fsstat;
3211
0
    fs->fscheck = yaffsfs_fscheck;
3212
0
    fs->istat = yaffsfs_istat;
3213
0
    fs->name_cmp = tsk_fs_unix_name_cmp;
3214
3215
0
    fs->close = yaffsfs_close;
3216
3217
    /* Journal */
3218
0
    fs->jblk_walk = yaffsfs_jblk_walk;
3219
0
    fs->jentry_walk = yaffsfs_jentry_walk;
3220
0
    fs->jopen = yaffsfs_jopen;
3221
3222
    /* Initialize the caches */
3223
0
    if (tsk_verbose)
3224
0
        fprintf(stderr, "yaffsfs_open: building cache...\n");
3225
3226
    /* Build cache */
3227
    /* NOTE: The only modifications to the cache happen here, during at
3228
    *       the open. Should be fine with no lock, even if access to the
3229
    *       cache is shared among threads.
3230
    */
3231
    //tsk_init_lock(&yaffsfs->lock);
3232
0
    yaffsfs->chunkMap = new std::map<uint32_t, YaffsCacheChunkGroup>;
3233
0
    if (TSK_OK != yaffsfs_parse_image_load_cache(yaffsfs.get())) {
3234
0
        return nullptr;
3235
0
    }
3236
3237
0
    if (tsk_verbose) {
3238
0
        fprintf(stderr, "yaffsfs_open: done building cache!\n");
3239
        //yaffscache_objects_dump(yaffsfs, stderr);
3240
0
    }
3241
3242
    // Update the number of inums now that we've read in the file system
3243
0
    fs->inum_count = fs->last_inum - 1;
3244
3245
0
    std::unique_ptr<TSK_FS_DIR, decltype(&tsk_fs_dir_close)> test_dir{
3246
0
        tsk_fs_dir_open_meta(fs, fs->root_inum),
3247
0
        tsk_fs_dir_close
3248
0
    };
3249
3250
0
    if (!test_dir) {
3251
0
        tsk_error_reset();
3252
0
        tsk_error_set_errno(TSK_ERR_FS_MAGIC);
3253
0
        tsk_error_set_errstr("not a YAFFS file system (no root directory). %s", YAFFS_HELP_MESSAGE);
3254
0
        if (tsk_verbose)
3255
0
            fprintf(stderr, "yaffsfs_open: invalid file system\n%s\n", YAFFS_HELP_MESSAGE);
3256
0
        return nullptr;
3257
0
    }
3258
3259
0
    return fs;
3260
0
}