hfs_load_attrs(TSK_FS_FILE * fs_file)
{
    TSK_FS_INFO *fs;
    HFS_INFO *hfs;
    TSK_FS_ATTR *fs_attr;
    TSK_FS_ATTR_RUN *attr_run;
    hfs_fork *forkx;
    unsigned char resource_fork_has_contents = FALSE;
    unsigned char compression_flag = FALSE;
    unsigned char isCompressed = FALSE;
    unsigned char compDataInRSRCFork = FALSE;
    unsigned char cmpType = 0;
    uint64_t uncompressedSize;
    uint64_t logicalSize;       // of a fork

    // clean up any error messages that are lying around
    tsk_error_reset();

    if ((fs_file == NULL) || (fs_file->meta == NULL)
        || (fs_file->fs_info == NULL)) {
        error_detected(TSK_ERR_FS_ARG,
            "hfs_load_attrs: fs_file or meta is NULL");
        return 1;
    }

    fs = (TSK_FS_INFO *) fs_file->fs_info;
    hfs = (HFS_INFO *) fs;

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_load_attrs: Processing file %" PRIuINUM "\n",
            fs_file->meta->addr);


    // see if we have already loaded the runs
    if (fs_file->meta->attr_state == TSK_FS_META_ATTR_STUDIED) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_load_attrs: Attributes already loaded\n");
        return 0;
    }
    else if (fs_file->meta->attr_state == TSK_FS_META_ATTR_ERROR) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_load_attrs: Previous attempt to load attributes resulted in error\n");
        return 1;
    }

    // Now (re)-initialize the attrlist that will hold the list of attributes
    if (fs_file->meta->attr != NULL) {
        tsk_fs_attrlist_markunused(fs_file->meta->attr);
    }
    else if (fs_file->meta->attr == NULL) {
        fs_file->meta->attr = tsk_fs_attrlist_alloc();
    }

    /****************** EXTENDED ATTRIBUTES *******************************/
    // We do these first, so that we can detect the mode of compression, if
    // any.  We need to know that mode in order to handle the forks.

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_load_attrs: loading the HFS+ extended attributes\n");

    if (hfs_load_extended_attrs(fs_file, &isCompressed,
            &cmpType, &uncompressedSize)) {
        error_returned(" - hfs_load_attrs A");
        fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
        return 1;
    }

// TODO: What about DECMPFS_TYPE_RAW_RSRC?
    switch (cmpType) {
    case DECMPFS_TYPE_ZLIB_RSRC:
    case DECMPFS_TYPE_LZVN_RSRC:
        compDataInRSRCFork = TRUE;
        break;
    default:
        compDataInRSRCFork = FALSE;
        break;
    }

    if (isCompressed) {
        fs_file->meta->size = uncompressedSize;
    }

    // This is the flag indicating compression, from the Catalog File record.
    compression_flag = (fs_file->meta->flags & TSK_FS_META_FLAG_COMP) != 0;

    if (compression_flag && !isCompressed) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_load_attrs: WARNING, HFS marks this as a"
                " compressed file, but no compression record was found.\n");
    }
    if (isCompressed && !compression_flag) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_load_attrs: WARNING, this file has a compression"
                " record, but the HFS compression flag is not set.\n");
    }

    /************* FORKS (both) ************************************/

    // Process the data and resource forks.  We only do this if the
    // fork data structures are non-null, so test that:
    if (fs_file->meta->content_ptr != NULL) {

        /**************  DATA FORK STUFF ***************************/

        // Get the data fork data-structure
        forkx = (hfs_fork *) fs_file->meta->content_ptr;

        // If this is a compressed file, then either this attribute is already loaded
        // because the data was in the compression record, OR
        // the compressed data is in the resource fork.  We will load those runs when
        // we handle the resource fork.
        if (!isCompressed) {
            // We only load this attribute if this fork has non-zero length
            // or if this is a REG or LNK file.  Otherwise, we skip
            logicalSize = tsk_getu64(fs->endian, forkx->logic_sz);

            if (logicalSize > 0 ||
                fs_file->meta->type == TSK_FS_META_TYPE_REG ||
                fs_file->meta->type == TSK_FS_META_TYPE_LNK) {


                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "hfs_load_attrs: loading the data fork attribute\n");

                // get an attribute structure to store the data in
                if ((fs_attr = tsk_fs_attrlist_getnew(fs_file->meta->attr,
                            TSK_FS_ATTR_NONRES)) == NULL) {
                    error_returned(" - hfs_load_attrs");
                    return 1;
                }
                /* NOTE that fs_attr is now tied to fs_file->meta->attr.
                 * that means that we do not need to free it if we abort in the
                 * following code (and doing so will cause double free errors). */

                if (logicalSize > 0) {

                    // Convert runs of blocks to the TSK internal form
                    if (((attr_run =
                                hfs_extents_to_attr(fs, forkx->extents,
                                    0)) == NULL)
                        && (tsk_error_get_errno() != 0)) {
                        error_returned(" - hfs_load_attrs");
                        return 1;
                    }



                    // add the runs to the attribute and the attribute to the file.
                    if (tsk_fs_attr_set_run(fs_file, fs_attr, attr_run,
                            "", TSK_FS_ATTR_TYPE_HFS_DATA,
                            HFS_FS_ATTR_ID_DATA, logicalSize, logicalSize,
                            (TSK_OFF_T) tsk_getu32(fs->endian,
                                forkx->total_blk) * fs->block_size, 0,
                            0)) {
                        error_returned(" - hfs_load_attrs (DATA)");
                        tsk_fs_attr_run_free(attr_run);
                        return 1;
                    }

                    // see if extents file has additional runs
                    if (hfs_ext_find_extent_record_attr(hfs,
                            (uint32_t) fs_file->meta->addr, fs_attr,
                            TRUE)) {
                        error_returned(" - hfs_load_attrs B");
                        fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
                        return 1;
                    }

                }
                else {
                    // logicalSize == 0, but this is either a REG or LNK file
                    // so, it should have a DATA fork attribute of zero length.
                    if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, "",
                            TSK_FS_ATTR_TYPE_HFS_DATA, HFS_FS_ATTR_ID_DATA,
                            0, 0, 0, 0, 0)) {
                        error_returned(" - hfs_load_attrs (non-file)");
                        return 1;
                    }
                }

            }                   // END  logicalSize>0 or REG or LNK file type
        }                       // END if not Compressed



        /**************  RESOURCE FORK STUFF ************************************/

        // Get the resource fork.
        //Note that content_ptr points to an array of two
        // hfs_fork data structures, the second of which
        // describes the blocks of the resource fork.

        forkx = &((hfs_fork *) fs_file->meta->content_ptr)[1];

        logicalSize = tsk_getu64(fs->endian, forkx->logic_sz);

        // Skip if the length of the resource fork is zero
        if (logicalSize > 0) {

            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "hfs_load_attrs: loading the resource fork\n");

            resource_fork_has_contents = TRUE;

            // get an attribute structure to store the resource fork data in.  We will
            // reuse the fs_attr variable, since we are done with the data fork.
            if ((fs_attr =
                    tsk_fs_attrlist_getnew(fs_file->meta->attr,
                        TSK_FS_ATTR_NONRES)) == NULL) {
                error_returned(" - hfs_load_attrs (RSRC)");
                return 1;
            }
            /* NOTE that fs_attr is now tied to fs_file->meta->attr.
             * that means that we do not need to free it if we abort in the
             * following code (and doing so will cause double free errors). */


            // convert the resource fork to the TSK format
            if (((attr_run =
                        hfs_extents_to_attr(fs, forkx->extents,
                            0)) == NULL)
                && (tsk_error_get_errno() != 0)) {
                error_returned(" - hfs_load_attrs");
                return 1;
            }

            // add the runs to the attribute and the attribute to the file.
            if (tsk_fs_attr_set_run(fs_file, fs_attr, attr_run, "RSRC",
                    TSK_FS_ATTR_TYPE_HFS_RSRC, HFS_FS_ATTR_ID_RSRC,
                    tsk_getu64(fs->endian, forkx->logic_sz),
                    tsk_getu64(fs->endian, forkx->logic_sz),
                    (TSK_OFF_T) tsk_getu32(fs->endian,
                        forkx->total_blk) * fs->block_size, 0, 0)) {
                error_returned(" - hfs_load_attrs (RSRC)");
                tsk_fs_attr_run_free(attr_run);
                return 1;
            }

            // see if extents file has additional runs for the resource fork.
            if (hfs_ext_find_extent_record_attr(hfs,
                    (uint32_t) fs_file->meta->addr, fs_attr, FALSE)) {
                error_returned(" - hfs_load_attrs C");
                fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
                return 1;
            }

            if (isCompressed && compDataInRSRCFork) {
                // OK, we are going to load those same resource fork blocks as the "DATA"
                // attribute, but will mark it as compressed.
                // get an attribute structure to store the resource fork data in.  We will
                // reuse the fs_attr variable, since we are done with the data fork.
                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "File is compressed with data in the resource fork. "
                        "Loading the default DATA attribute.\n");
                if ((fs_attr =
                        tsk_fs_attrlist_getnew(fs_file->meta->attr,
                            TSK_FS_ATTR_NONRES)) == NULL) {
                    error_returned
                        (" - hfs_load_attrs (RSRC loading as DATA)");
                    return 1;
                }
                /* NOTE that fs_attr is now tied to fs_file->meta->attr.
                 * that means that we do not need to free it if we abort in the
                 * following code (and doing so will cause double free errors). */

                switch (cmpType) {
                case DECMPFS_TYPE_ZLIB_RSRC:
#ifdef HAVE_LIBZ
                    fs_attr->w = hfs_attr_walk_zlib_rsrc;
                    fs_attr->r = hfs_file_read_zlib_rsrc;
#else
                    // We don't have zlib, so the uncompressed data is not
                    // available to us; however, we must have a default DATA
                    // attribute, or icat will misbehave.
                    if (tsk_verbose)
                        tsk_fprintf(stderr,
                            "hfs_load_attrs: No zlib compression library, so setting a zero-length default DATA attribute.\n");

                    if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, "DATA",
                            TSK_FS_ATTR_TYPE_HFS_DATA, HFS_FS_ATTR_ID_DATA, 0,
                            0, 0, 0, 0)) {
                        error_returned(" - hfs_load_attrs (non-file)");
                        return 1;
                    }
#endif
                    break;

                case DECMPFS_TYPE_LZVN_RSRC:

                    fs_attr->w = hfs_attr_walk_lzvn_rsrc;
                    fs_attr->r = hfs_file_read_lzvn_rsrc;

                    break;
                }

                // convert the resource fork to the TSK format
                if (((attr_run =
                            hfs_extents_to_attr(fs, forkx->extents,
                                0)) == NULL)
                    && (tsk_error_get_errno() != 0)) {
                    error_returned
                        (" - hfs_load_attrs, RSRC fork as DATA fork");
                    return 1;
                }

                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "hfs_load_attrs:  Loading RSRC fork block runs as the default DATA attribute.\n");

                // add the runs to the attribute and the attribute to the file.
                if (tsk_fs_attr_set_run(fs_file, fs_attr, attr_run, "DECOMP",
                        TSK_FS_ATTR_TYPE_HFS_DATA, HFS_FS_ATTR_ID_DATA,
                        logicalSize,
                        logicalSize,
                        (TSK_OFF_T) tsk_getu32(fs->endian,
                            forkx->total_blk) * fs->block_size,
                        TSK_FS_ATTR_COMP | TSK_FS_ATTR_NONRES, 0)) {
                    error_returned
                        (" - hfs_load_attrs (RSRC loading as DATA)");
                    tsk_fs_attr_run_free(attr_run);
                    return 1;
                }

                // see if extents file has additional runs for the resource fork.
                if (hfs_ext_find_extent_record_attr(hfs,
                        (uint32_t) fs_file->meta->addr, fs_attr, FALSE)) {
                    error_returned
                        (" - hfs_load_attrs (RSRC loading as DATA");
                    fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
                    return 1;
                }

                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "hfs_load_attrs: setting the \"special\" function pointers to inflate compressed data.\n");
            }

        }                       // END resource fork size > 0

    }                           // END the fork data structures are non-NULL

    if (isCompressed && compDataInRSRCFork && !resource_fork_has_contents) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_load_attrs: WARNING, compression record claims that compressed data"
                " is in the Resource Fork, but that fork is empty or non-existent.\n");
    }

    // Finish up.
    fs_file->meta->attr_state = TSK_FS_META_ATTR_STUDIED;

    return 0;
}