/* ** The main function to do directory entry walking ** ** action is called for each entry with flags set to TSK_FS_DENT_FLAG_ALLOC for ** active entries ** ** this calls ext2fs_dent_parse_block to do the actual analysis ** ** Use the following flags: TSK_FS_DENT_FLAG_ALLOC, TSK_FS_DENT_FLAG_UNALLOC, ** TSK_FS_DENT_FLAG_RECURSE ** ** returns 0 on success and 1 on error */ uint8_t ext2fs_dent_walk(TSK_FS_INFO * fs, INUM_T inode, TSK_FS_DENT_FLAG_ENUM flags, TSK_FS_DENT_TYPE_WALK_CB action, void *ptr) { EXT2FS_DINFO dinfo; TSK_LIST *list_seen = NULL; uint8_t retval; // clean up any error messages that are lying around tsk_error_reset(); memset(&dinfo, 0, sizeof(EXT2FS_DINFO)); /* Sanity check on flags -- make sure at least one ALLOC is set */ if (((flags & TSK_FS_DENT_FLAG_ALLOC) == 0) && ((flags & TSK_FS_DENT_FLAG_UNALLOC) == 0)) { flags |= (TSK_FS_DENT_FLAG_ALLOC | TSK_FS_DENT_FLAG_UNALLOC); } retval = ext2fs_dent_walk_lcl(fs, &dinfo, &list_seen, inode, flags, action, ptr); tsk_list_free(list_seen); list_seen = NULL; return retval; }
/* ** The main function to do directory entry walking ** ** action is called for each entry with flags set to FS_FLAG_NAME_ALLOC for ** active entries ** ** this calls ext2fs_dent_parse_block to do the actual analysis ** ** Use the following flags: FS_FLAG_NAME_ALLOC, FS_FLAG_NAME_UNALLOC, ** FS_FLAG_NAME_RECURSE ** ** returns 0 on success and 1 on error */ uint8_t ext2fs_dent_walk(FS_INFO * fs, INUM_T inode, int flags, FS_DENT_WALK_FN action, void *ptr) { EXT2FS_DINFO dinfo; memset(&dinfo, 0, sizeof(EXT2FS_DINFO)); return ext2fs_dent_walk_lcl(fs, &dinfo, inode, flags, action, ptr); }
/* ** ** Read contents of directory block ** ** if entry is active call action with myflags set to TSK_FS_DENT_FLAG_ALLOC, if ** it is deleted then call action with TSK_FS_DENT_FLAG_UNALLOC. ** len is the size of buf ** ** return 1 to stop, 0 on success, and -1 on error */ static int ext2fs_dent_parse_block(EXT2FS_INFO * ext2fs, EXT2FS_DINFO * dinfo, TSK_LIST ** list_seen, char *buf, int len, int flags, TSK_FS_DENT_TYPE_WALK_CB action, void *ptr) { TSK_FS_INFO *fs = &(ext2fs->fs_info); int dellen = 0; int idx; uint16_t reclen; uint32_t inode; char *dirPtr; TSK_FS_DENT *fs_dent; int minreclen = 4; if ((fs_dent = tsk_fs_dent_alloc(EXT2FS_MAXNAMLEN + 1, 0)) == NULL) return -1; /* update each time by the actual length instead of the ** recorded length so we can view the deleted entries */ for (idx = 0; idx <= len - EXT2FS_DIRSIZ_lcl(1); idx += minreclen) { unsigned int namelen; dirPtr = &buf[idx]; if (ext2fs->deentry_type == EXT2_DE_V1) { ext2fs_dentry1 *dir = (ext2fs_dentry1 *) dirPtr; inode = tsk_getu32(fs->endian, dir->inode); namelen = tsk_getu16(fs->endian, dir->name_len); reclen = tsk_getu16(fs->endian, dir->rec_len); } else { ext2fs_dentry2 *dir = (ext2fs_dentry2 *) dirPtr; inode = tsk_getu32(fs->endian, dir->inode); namelen = dir->name_len; reclen = tsk_getu16(fs->endian, dir->rec_len); } minreclen = EXT2FS_DIRSIZ_lcl(namelen); /* ** Check if we may have a valid directory entry. If we don't, ** then increment to the next word and try again. */ if ((inode > fs->last_inum) || (inode < 0) || (namelen > EXT2FS_MAXNAMLEN) || (namelen <= 0) || (reclen < minreclen) || (reclen % 4) || (idx + reclen > len)) { minreclen = 4; if (dellen > 0) dellen -= 4; continue; } /* Before we process an entry in unallocated space, make * sure that it also ends in the unalloc space */ if ((dellen) && (dellen < minreclen)) { minreclen = 4; if (dellen > 0) dellen -= 4; continue; } if (ext2fs_dent_copy(ext2fs, dinfo, dirPtr, fs_dent)) { tsk_fs_dent_free(fs_dent); return -1; } /* Do we have a deleted entry? */ if ((dellen > 0) || (inode == 0)) { fs_dent->flags = TSK_FS_DENT_FLAG_UNALLOC; if (dellen > 0) dellen -= minreclen; if (flags & TSK_FS_DENT_FLAG_UNALLOC) { int retval; retval = action(fs, fs_dent, ptr); if (retval == TSK_WALK_STOP) { tsk_fs_dent_free(fs_dent); return 1; } else if (retval == TSK_WALK_ERROR) { tsk_fs_dent_free(fs_dent); return -1; } } } /* We have a non-deleted entry */ else { fs_dent->flags = TSK_FS_DENT_FLAG_ALLOC; if (flags & TSK_FS_DENT_FLAG_ALLOC) { int retval; retval = action(fs, fs_dent, ptr); if (retval == TSK_WALK_STOP) { tsk_fs_dent_free(fs_dent); return 1; } else if (retval == TSK_WALK_ERROR) { tsk_fs_dent_free(fs_dent); return -1; } } } /* If the actual length is shorter then the ** recorded length, then the next entry(ies) have been ** deleted. Set dellen to the length of data that ** has been deleted ** ** Because we aren't guaranteed with Ext2FS that the next ** entry begins right after this one, we will check to ** see if the difference is less than a possible entry ** before we waste time searching it */ if ((reclen - minreclen >= EXT2FS_DIRSIZ_lcl(1)) && (dellen <= 0)) dellen = reclen - minreclen; /* we will be recursing directories */ if ((fs_dent->flags & TSK_FS_DENT_FLAG_ALLOC) && (flags & TSK_FS_DENT_FLAG_RECURSE) && (!TSK_FS_ISDOT(fs_dent->name)) && ((fs_dent->fsi->mode & TSK_FS_INODE_MODE_FMT) == TSK_FS_INODE_MODE_DIR)) { int depth_added = 0; /* Make sure we do not get into an infinite loop */ if (0 == tsk_list_find(*list_seen, fs_dent->inode)) { if (tsk_list_add(list_seen, fs_dent->inode)) { tsk_fs_dent_free(fs_dent); return -1; } if ((dinfo->depth < MAX_DEPTH) && (DIR_STRSZ > strlen(dinfo->dirs) + strlen(fs_dent->name))) { dinfo->didx[dinfo->depth] = &dinfo->dirs[strlen(dinfo->dirs)]; strncpy(dinfo->didx[dinfo->depth], fs_dent->name, DIR_STRSZ - strlen(dinfo->dirs)); strncat(dinfo->dirs, "/", DIR_STRSZ); depth_added = 1; } dinfo->depth++; if (ext2fs_dent_walk_lcl(&(ext2fs->fs_info), dinfo, list_seen, fs_dent->inode, flags, action, ptr)) { /* If this fails because the directory could not be * loaded, then we still continue */ if (tsk_verbose) { tsk_fprintf(stderr, "ffs_dent_parse_block: error reading directory: %" PRIuINUM "\n", fs_dent->inode); tsk_error_print(stderr); } tsk_error_reset(); } dinfo->depth--; if (depth_added) *dinfo->didx[dinfo->depth] = '\0'; } } } tsk_fs_dent_free(fs_dent); return 0; } /* end ext2fs_dent_parse_block() */