/***************************************************************************/ /* */ /* ftrfork.c */ /* */ /* Embedded resource forks accessor (body). */ /* */ /* Copyright 2004-2016 by */ /* Masatake YAMATO and Redhat K.K. */ /* */ /* FT_Raccess_Get_HeaderInfo() and raccess_guess_darwin_hfsplus() are */ /* derived from ftobjs.c. */ /* */ /* This file is part of the FreeType project, and may only be used, */ /* modified, and distributed under the terms of the FreeType project */ /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ /* this file you indicate that you have read the license and */ /* understand and accept it fully. */ /* */ /***************************************************************************/ /***************************************************************************/ /* Development of the code in this file is support of */ /* Information-technology Promotion Agency, Japan. */ /***************************************************************************/ #include #include FT_INTERNAL_DEBUG_H #include FT_INTERNAL_STREAM_H #include FT_INTERNAL_RFORK_H #include "basepic.h" #include "ftbase.h" #undef FT_COMPONENT #define FT_COMPONENT trace_raccess /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /**** ****/ /**** ****/ /**** Resource fork directory access ****/ /**** ****/ /**** ****/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ FT_BASE_DEF( FT_Error ) FT_Raccess_Get_HeaderInfo( FT_Library library, FT_Stream stream, FT_Long rfork_offset, FT_Long *map_offset, FT_Long *rdata_pos ) { FT_Error error; unsigned char head[16], head2[16]; FT_Long map_pos, rdata_len; int allzeros, allmatch, i; FT_Long type_list; FT_UNUSED( library ); error = FT_Stream_Seek( stream, (FT_ULong)rfork_offset ); if ( error ) return error; error = FT_Stream_Read( stream, (FT_Byte *)head, 16 ); if ( error ) return error; /* ensure positive values */ if ( head[0] >= 0x80 || head[4] >= 0x80 || head[8] >= 0x80 ) return FT_THROW( Unknown_File_Format ); *rdata_pos = ( head[ 0] << 24 ) | ( head[ 1] << 16 ) | ( head[ 2] << 8 ) | head[ 3]; map_pos = ( head[ 4] << 24 ) | ( head[ 5] << 16 ) | ( head[ 6] << 8 ) | head[ 7]; rdata_len = ( head[ 8] << 24 ) | ( head[ 9] << 16 ) | ( head[10] << 8 ) | head[11]; /* map_len = head[12] .. head[15] */ if ( *rdata_pos != map_pos - rdata_len || map_pos == 0 ) return FT_THROW( Unknown_File_Format ); if ( FT_LONG_MAX - rfork_offset < *rdata_pos || FT_LONG_MAX - rfork_offset < map_pos ) return FT_THROW( Unknown_File_Format ); *rdata_pos += rfork_offset; map_pos += rfork_offset; error = FT_Stream_Seek( stream, (FT_ULong)map_pos ); if ( error ) return error; head2[15] = (FT_Byte)( head[15] + 1 ); /* make it be different */ error = FT_Stream_Read( stream, (FT_Byte*)head2, 16 ); if ( error ) return error; allzeros = 1; allmatch = 1; for ( i = 0; i < 16; ++i ) { if ( head2[i] != 0 ) allzeros = 0; if ( head2[i] != head[i] ) allmatch = 0; } if ( !allzeros && !allmatch ) return FT_THROW( Unknown_File_Format ); /* If we have reached this point then it is probably a mac resource */ /* file. Now, does it contain any interesting resources? */ /* Skip handle to next resource map, the file resource number, and */ /* attributes. */ (void)FT_STREAM_SKIP( 4 /* skip handle to next resource map */ + 2 /* skip file resource number */ + 2 ); /* skip attributes */ if ( FT_READ_USHORT( type_list ) ) return error; if ( type_list == -1 ) return FT_THROW( Unknown_File_Format ); error = FT_Stream_Seek( stream, (FT_ULong)( map_pos + type_list ) ); if ( error ) return error; *map_offset = map_pos + type_list; return FT_Err_Ok; } static int ft_raccess_sort_ref_by_id( FT_RFork_Ref* a, FT_RFork_Ref* b ) { if ( a->res_id < b->res_id ) return -1; else if ( a->res_id > b->res_id ) return 1; else return 0; } FT_BASE_DEF( FT_Error ) FT_Raccess_Get_DataOffsets( FT_Library library, FT_Stream stream, FT_Long map_offset, FT_Long rdata_pos, FT_Long tag, FT_Bool sort_by_res_id, FT_Long **offsets, FT_Long *count ) { FT_Error error; int i, j, cnt, subcnt; FT_Long tag_internal, rpos; FT_Memory memory = library->memory; FT_Long temp; FT_Long *offsets_internal = NULL; FT_RFork_Ref *ref = NULL; FT_TRACE3(( "\n" )); error = FT_Stream_Seek( stream, (FT_ULong)map_offset ); if ( error ) return error; if ( FT_READ_USHORT( cnt ) ) return error; cnt++; for ( i = 0; i < cnt; ++i ) { if ( FT_READ_LONG( tag_internal ) || FT_READ_USHORT( subcnt ) || FT_READ_USHORT( rpos ) ) return error; FT_TRACE2(( "Resource tags: %c%c%c%c\n", (char)( 0xFF & ( tag_internal >> 24 ) ), (char)( 0xFF & ( tag_internal >> 16 ) ), (char)( 0xFF & ( tag_internal >> 8 ) ), (char)( 0xFF & ( tag_internal >> 0 ) ) )); FT_TRACE3(( " : subcount=%d, suboffset=0x%04x\n", subcnt, rpos )); if ( tag_internal == tag ) { *count = subcnt + 1; rpos += map_offset; error = FT_Stream_Seek( stream, (FT_ULong)rpos ); if ( error ) return error; if ( FT_NEW_ARRAY( ref, *count ) ) return error; for ( j = 0; j < *count; ++j ) { if ( FT_READ_USHORT( ref[j].res_id ) ) goto Exit; if ( FT_STREAM_SKIP( 2 ) ) /* resource name */ goto Exit; if ( FT_READ_LONG( temp ) ) goto Exit; if ( FT_STREAM_SKIP( 4 ) ) /* mbz */ goto Exit; ref[j].offset = temp & 0xFFFFFFL; FT_TRACE3(( " [%d]:" " resource_id=0x%04x, offset=0x%08x\n", j, ref[j].res_id, ref[j].offset )); } if (sort_by_res_id) { ft_qsort( ref, (size_t)*count, sizeof ( FT_RFork_Ref ), ( int(*)(const void*, const void*) ) ft_raccess_sort_ref_by_id ); FT_TRACE3(( " -- sort resources by their ids --\n" )); for ( j = 0; j < *count; ++ j ) { FT_TRACE3(( " [%d]:" " resource_id=0x%04x, offset=0x%08x\n", j, ref[j].res_id, ref[j].offset )); } } if ( FT_NEW_ARRAY( offsets_internal, *count ) ) goto Exit; /* XXX: duplicated reference ID, * gap between reference IDs are acceptable? * further investigation on Apple implementation is needed. */ for ( j = 0; j < *count; ++j ) offsets_internal[j] = rdata_pos + ref[j].offset; *offsets = offsets_internal; error = FT_Err_Ok; Exit: FT_FREE( ref ); return error; } } return FT_THROW( Cannot_Open_Resource ); } #ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /**** ****/ /**** ****/ /**** Guessing functions ****/ /**** ****/ /**** When you add a new guessing function, ****/ /**** update FT_RACCESS_N_RULES in ftrfork.h. ****/ /**** ****/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ static FT_Error raccess_guess_apple_double( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ); static FT_Error raccess_guess_apple_single( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ); static FT_Error raccess_guess_darwin_ufs_export( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ); static FT_Error raccess_guess_darwin_newvfs( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ); static FT_Error raccess_guess_darwin_hfsplus( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ); static FT_Error raccess_guess_vfat( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ); static FT_Error raccess_guess_linux_cap( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ); static FT_Error raccess_guess_linux_double( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ); static FT_Error raccess_guess_linux_netatalk( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ); CONST_FT_RFORK_RULE_ARRAY_BEGIN(ft_raccess_guess_table, ft_raccess_guess_rec) CONST_FT_RFORK_RULE_ARRAY_ENTRY(apple_double, apple_double) CONST_FT_RFORK_RULE_ARRAY_ENTRY(apple_single, apple_single) CONST_FT_RFORK_RULE_ARRAY_ENTRY(darwin_ufs_export, darwin_ufs_export) CONST_FT_RFORK_RULE_ARRAY_ENTRY(darwin_newvfs, darwin_newvfs) CONST_FT_RFORK_RULE_ARRAY_ENTRY(darwin_hfsplus, darwin_hfsplus) CONST_FT_RFORK_RULE_ARRAY_ENTRY(vfat, vfat) CONST_FT_RFORK_RULE_ARRAY_ENTRY(linux_cap, linux_cap) CONST_FT_RFORK_RULE_ARRAY_ENTRY(linux_double, linux_double) CONST_FT_RFORK_RULE_ARRAY_ENTRY(linux_netatalk, linux_netatalk) CONST_FT_RFORK_RULE_ARRAY_END /*************************************************************************/ /**** ****/ /**** Helper functions ****/ /**** ****/ /*************************************************************************/ static FT_Error raccess_guess_apple_generic( FT_Library library, FT_Stream stream, char *base_file_name, FT_Int32 magic, FT_Long *result_offset ); static FT_Error raccess_guess_linux_double_from_file_name( FT_Library library, char * file_name, FT_Long *result_offset ); static char * raccess_make_file_name( FT_Memory memory, const char *original_name, const char *insertion ); FT_BASE_DEF( void ) FT_Raccess_Guess( FT_Library library, FT_Stream stream, char* base_name, char **new_names, FT_Long *offsets, FT_Error *errors ) { FT_Int i; for ( i = 0; i < FT_RACCESS_N_RULES; i++ ) { new_names[i] = NULL; if ( NULL != stream ) errors[i] = FT_Stream_Seek( stream, 0 ); else errors[i] = FT_Err_Ok; if ( errors[i] ) continue; errors[i] = (FT_RACCESS_GUESS_TABLE_GET[i].func)( library, stream, base_name, &(new_names[i]), &(offsets[i]) ); } return; } #ifndef FT_MACINTOSH static FT_RFork_Rule raccess_get_rule_type_from_rule_index( FT_Library library, FT_UInt rule_index ) { FT_UNUSED( library ); if ( rule_index >= FT_RACCESS_N_RULES ) return FT_RFork_Rule_invalid; return FT_RACCESS_GUESS_TABLE_GET[rule_index].type; } /* * For this function, refer ftbase.h. */ FT_LOCAL_DEF( FT_Bool ) ft_raccess_rule_by_darwin_vfs( FT_Library library, FT_UInt rule_index ) { switch( raccess_get_rule_type_from_rule_index( library, rule_index ) ) { case FT_RFork_Rule_darwin_newvfs: case FT_RFork_Rule_darwin_hfsplus: return TRUE; default: return FALSE; } } #endif static FT_Error raccess_guess_apple_double( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ) { FT_Int32 magic = ( 0x00 << 24 ) | ( 0x05 << 16 ) | ( 0x16 << 8 ) | 0x07; *result_file_name = NULL; if ( NULL == stream ) return FT_THROW( Cannot_Open_Stream ); return raccess_guess_apple_generic( library, stream, base_file_name, magic, result_offset ); } static FT_Error raccess_guess_apple_single( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ) { FT_Int32 magic = ( 0x00 << 24 ) | ( 0x05 << 16 ) | ( 0x16 << 8 ) | 0x00; *result_file_name = NULL; if ( NULL == stream ) return FT_THROW( Cannot_Open_Stream ); return raccess_guess_apple_generic( library, stream, base_file_name, magic, result_offset ); } static FT_Error raccess_guess_darwin_ufs_export( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ) { char* newpath; FT_Error error; FT_Memory memory; FT_UNUSED( stream ); memory = library->memory; newpath = raccess_make_file_name( memory, base_file_name, "._" ); if ( !newpath ) return FT_THROW( Out_Of_Memory ); error = raccess_guess_linux_double_from_file_name( library, newpath, result_offset ); if ( !error ) *result_file_name = newpath; else FT_FREE( newpath ); return error; } static FT_Error raccess_guess_darwin_hfsplus( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ) { /* Only meaningful on systems with hfs+ drivers (or Macs). */ FT_Error error; char* newpath = NULL; FT_Memory memory; FT_Long base_file_len = (FT_Long)ft_strlen( base_file_name ); FT_UNUSED( stream ); memory = library->memory; if ( base_file_len + 6 > FT_INT_MAX ) return FT_THROW( Array_Too_Large ); if ( FT_ALLOC( newpath, base_file_len + 6 ) ) return error; FT_MEM_COPY( newpath, base_file_name, base_file_len ); FT_MEM_COPY( newpath + base_file_len, "/rsrc", 6 ); *result_file_name = newpath; *result_offset = 0; return FT_Err_Ok; } static FT_Error raccess_guess_darwin_newvfs( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ) { /* Only meaningful on systems with Mac OS X (> 10.1). */ FT_Error error; char* newpath = NULL; FT_Memory memory; FT_Long base_file_len = (FT_Long)ft_strlen( base_file_name ); FT_UNUSED( stream ); memory = library->memory; if ( base_file_len + 18 > FT_INT_MAX ) return FT_THROW( Array_Too_Large ); if ( FT_ALLOC( newpath, base_file_len + 18 ) ) return error; FT_MEM_COPY( newpath, base_file_name, base_file_len ); FT_MEM_COPY( newpath + base_file_len, "/..namedfork/rsrc", 18 ); *result_file_name = newpath; *result_offset = 0; return FT_Err_Ok; } static FT_Error raccess_guess_vfat( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ) { char* newpath; FT_Memory memory; FT_UNUSED( stream ); memory = library->memory; newpath = raccess_make_file_name( memory, base_file_name, "resource.frk/" ); if ( !newpath ) return FT_THROW( Out_Of_Memory ); *result_file_name = newpath; *result_offset = 0; return FT_Err_Ok; } static FT_Error raccess_guess_linux_cap( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ) { char* newpath; FT_Memory memory; FT_UNUSED( stream ); memory = library->memory; newpath = raccess_make_file_name( memory, base_file_name, ".resource/" ); if ( !newpath ) return FT_THROW( Out_Of_Memory ); *result_file_name = newpath; *result_offset = 0; return FT_Err_Ok; } static FT_Error raccess_guess_linux_double( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ) { char* newpath; FT_Error error; FT_Memory memory; FT_UNUSED( stream ); memory = library->memory; newpath = raccess_make_file_name( memory, base_file_name, "%" ); if ( !newpath ) return FT_THROW( Out_Of_Memory ); error = raccess_guess_linux_double_from_file_name( library, newpath, result_offset ); if ( !error ) *result_file_name = newpath; else FT_FREE( newpath ); return error; } static FT_Error raccess_guess_linux_netatalk( FT_Library library, FT_Stream stream, char *base_file_name, char **result_file_name, FT_Long *result_offset ) { char* newpath; FT_Error error; FT_Memory memory; FT_UNUSED( stream ); memory = library->memory; newpath = raccess_make_file_name( memory, base_file_name, ".AppleDouble/" ); if ( !newpath ) return FT_THROW( Out_Of_Memory ); error = raccess_guess_linux_double_from_file_name( library, newpath, result_offset ); if ( !error ) *result_file_name = newpath; else FT_FREE( newpath ); return error; } static FT_Error raccess_guess_apple_generic( FT_Library library, FT_Stream stream, char *base_file_name, FT_Int32 magic, FT_Long *result_offset ) { FT_Int32 magic_from_stream; FT_Error error; FT_Int32 version_number = 0; FT_UShort n_of_entries; int i; FT_Int32 entry_id, entry_offset, entry_length = 0; const FT_Int32 resource_fork_entry_id = 0x2; FT_UNUSED( library ); FT_UNUSED( base_file_name ); FT_UNUSED( version_number ); FT_UNUSED( entry_length ); if ( FT_READ_LONG( magic_from_stream ) ) return error; if ( magic_from_stream != magic ) return FT_THROW( Unknown_File_Format ); if ( FT_READ_LONG( version_number ) ) return error; /* filler */ error = FT_Stream_Skip( stream, 16 ); if ( error ) return error; if ( FT_READ_USHORT( n_of_entries ) ) return error; if ( n_of_entries == 0 ) return FT_THROW( Unknown_File_Format ); for ( i = 0; i < n_of_entries; i++ ) { if ( FT_READ_LONG( entry_id ) ) return error; if ( entry_id == resource_fork_entry_id ) { if ( FT_READ_LONG( entry_offset ) || FT_READ_LONG( entry_length ) ) continue; *result_offset = entry_offset; return FT_Err_Ok; } else { error = FT_Stream_Skip( stream, 4 + 4 ); /* offset + length */ if ( error ) return error; } } return FT_THROW( Unknown_File_Format ); } static FT_Error raccess_guess_linux_double_from_file_name( FT_Library library, char *file_name, FT_Long *result_offset ) { FT_Open_Args args2; FT_Stream stream2; char * nouse = NULL; FT_Error error; args2.flags = FT_OPEN_PATHNAME; args2.pathname = file_name; error = FT_Stream_New( library, &args2, &stream2 ); if ( error ) return error; error = raccess_guess_apple_double( library, stream2, file_name, &nouse, result_offset ); FT_Stream_Free( stream2, 0 ); return error; } static char* raccess_make_file_name( FT_Memory memory, const char *original_name, const char *insertion ) { char* new_name = NULL; const char* tmp; const char* slash; size_t new_length; FT_Error error = FT_Err_Ok; FT_UNUSED( error ); new_length = ft_strlen( original_name ) + ft_strlen( insertion ); if ( FT_ALLOC( new_name, new_length + 1 ) ) return NULL; tmp = ft_strrchr( original_name, '/' ); if ( tmp ) { ft_strncpy( new_name, original_name, (size_t)( tmp - original_name + 1 ) ); new_name[tmp - original_name + 1] = '\0'; slash = tmp + 1; } else { slash = original_name; new_name[0] = '\0'; } ft_strcat( new_name, insertion ); ft_strcat( new_name, slash ); return new_name; } #else /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */ /*************************************************************************/ /* Dummy function; just sets errors */ /*************************************************************************/ FT_BASE_DEF( void ) FT_Raccess_Guess( FT_Library library, FT_Stream stream, char *base_name, char **new_names, FT_Long *offsets, FT_Error *errors ) { FT_Int i; FT_UNUSED( library ); FT_UNUSED( stream ); FT_UNUSED( base_name ); for ( i = 0; i < FT_RACCESS_N_RULES; i++ ) { new_names[i] = NULL; offsets[i] = 0; errors[i] = FT_ERR( Unimplemented_Feature ); } } #endif /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */ /* END */