|
Rockbox mail archiveSubject: Windows sym-link code (standalone) for you to testWindows sym-link code (standalone) for you to test
From: <rockbox_at_diffenbach.org>
Date: Fri, 31 Jan 2003 23:33:03 -0500 There was a request on this list to have rockbox process Microsoft Windows sym-links (*.lnk) files. A poster posted a link to "The Windows Shortcut File Format as reverse-engineered by Jesse Hager jessehager_at_iname.com", Perusing this document, I've come up with the following code (see below). This code isn't integrated into Rockbox; it's a standalone test (see below for why). A few notes are in order. The .lnk file contains the absolute path to a resource, and some data about what that object is. It may or may not contain a relative path. My .lnk files differ from those described by Mr. Hager, in that the relative path is interspersed with nuls ((char) 0). I don't know if this is an oversight on his part, or a peculiarity of my system (Windows 2000 Service Pack 3). For this reason, I'd like to ask any other windows users to compile the code below and test it against some population of their .lnk files. The relative path is important, I think, because the way I get my mp3s onto my Archos is to utility called TreeComp. TreeComp compares files from two directory trees, and copies missing files from one to the other. TreeComp does not "fix" .lnk files to "correct" the absolute path. (Nor would I want it to; that wouldn't be a bit for bit copy.) As I suspect others may use similar utilities, I think it's preferable to find the .lnk's relative path entry, rather than the absolute path. What I'm most interested in is if you get a correct relative path, and what fraction of your sym-links that point to a file or directory do not have a relative path. I'm also interested in having my code critiqued for compliance with "best-practice" Rockbox code; it's been a while since I've coded any straight C, or for small memory devices (contrary to a previous post, I have coded for small memory devices, if the Palm OS counts). I'm especially interested in what you might think of my substitute for C++ exceptions/RAII with the read_lnk/do_read_lnk nested call construct. Note that I haven't divided the code into a header and a .c file, for your immediate convenience. This code compiles under gcc (GCC) 3.2 20020927 (prerelease), and runs under Windows 2000 SP3/cygwin. Thanks, --Tom Please find the code below // lnk.c // begin header #define LNK_VERBOSE 1 typedef long dword ; typedef dword qword[ 2 ] ; #define GUID_LENGTH 16 // const int GUID_LENGTH = 16 typedef unsigned char GUID[ GUID_LENGTH ] ; struct lnk_header { dword indicator ; GUID guid ; dword flags ; dword file_attr ; qword time1 ; qword time2 ; qword time3 ; dword file_length ; dword icon_number ; dword showWnd ; dword hot_key ; dword unknown1 ; dword unknown2 ; } ; struct file_location_info { dword total_length ; dword first_following_offset ; dword flags ; dword local_volume_info_offset ; dword base_pathname_offset ; dword network_volume_info_offset ; dword remaining_pathname_offset ; } ; struct lnk_header_and_next_length { struct lnk_header header ; unsigned short next_length ; } ; #define lnk_header_indicator 0x4C GUID lnk_GUID_1 = { 0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } ; //The flags #define HAS_SHELL_ITEM_LIST 0x01 #define POINTS_TO_FILE 0x02 #define HAS_DESCRIPTION_STRING 0x04 #define HAS_RELATIVE_PATH 0x08 #define HAS_WORKING_DIRECTORY 0x10 #define HAS_COMMAND_LINE_ARGS 0x20 #define HAS_CUSTOM_ICON 0x40 #ifdef LNK_VERBOSE static /*const*/ char* flag_message[ 7][ 2 ] = { { "The shell item id list is absent", "The shell item id list is present" }, { "Points to something else", "Points to a file or directory" }, { "No description string", "Has a description string" }, { "No relative path", "Has a relative path string" }, { "No working directory", "Has a working directory" }, { "No command line arguments", "Has command line arguments" }, { "Has the default icon", "Has a custom icon" }, } ; #endif #define TARGET_READ_ONLY 0x01 #define TARGET_HIDDEN 0x02 #define TARGET_SYSTEM 0x04 #define TARGET_VOLUME_LABEL 0x08 #define TARGET_DIRECTORY 0x10 #define TARGET_ARCHIVE 0x20 #define TARGET_ENCRYPTED 0x40 #define TARGET_NORMAL 0x80 #define TARGET_TEMPORARY 0x100 #define TARGET_SPARSE 0x200 #define TARGET_REPARSE 0x400 #define TARGET_COMPRESSED 0x800 #define TARGET_OFFLINE 0x1000 #define VOLUME_LOCAL 0x1 #define VOLUME_NETWORK 0x2 #ifdef LNK_VERBOSE static /*const*/ char* target_message[] = { "Target is read only.", "Target is hidden.", "Target is a system file.", "Target is a volume label. (Not possible)", "Target is a directory.", "Target has been modified since last backup. (archive)", "Target is encrypted (NTFS EFS)", "Target is Normal??", "Target is temporary.", "Target is a sparse file.", "Target has reparse point data.", "Target is compressed.", "Target is offline." } ; #endif #define LNK_OK_RELATIVE_PATH 3 #define LNK_OK_ABSOLUTE_PATH 2 #define LNK_OK_NOT_A_FILE_OR_DIRECTORY 1 #define LNK_NOT_A_LNK_FILE 0 #define LNK_BAD_GUID -1 #define LNK_PATH_BUFFER_TOO_SHORT -2 #define LNK_SEEK_ERROR -3 #define LNK_READ_ERROR -4 #ifdef LNK_VERBOSE #define LNK_ERROR_ADJUST 4 char* lnk_err_message[] = { "LNK_READ_ERROR", "LNK_SEEK_ERROR", "LNK_PATH_BUFFER_TOO_SHORT", "LNK_BAD_GUID", "LNK_NOT_A_LNK_FILE", "LNK_OK_NOT_A_FILE_OR_DIRECTORY", "LNK_OK_ABSOLUTE_PATH", "LNK_OK_RELATIVE_PATH" } ; #endif // end header #include <fcntl.h> #include<io.h> #include <stdio.h> #include <string.h> int read_lnk( int file_handle, char* path_buffer, unsigned int* path_buffer_length ) { int ret ; ret = do_read_lnk( file_handle, path_buffer, path_buffer_length ) ; close( file_handle ) ; return ret ; } int do_read_lnk( int file_handle, char* path_buffer, unsigned int* path_buffer_length ) { int ret ; struct lnk_header_and_next_length header ; int GUID_BAD = 0 ; int i ; long m ; off_t file_offset ; long read_len ; long read_len_2 ; char* path_ptr = path_buffer ; unsigned short string_len ; dword base_path_offset ; char buf2[ 256 ] ; if( ( ret = read( file_handle, &header, sizeof( header ) ) ) != sizeof( header ) ) return LNK_READ_ERROR ; if( header.header.indicator != lnk_header_indicator ) return LNK_NOT_A_LNK_FILE ; if( memcmp( (void*) header.header.guid, (void*) lnk_GUID_1, sizeof( GUID ) ) ) { return LNK_BAD_GUID ; } for( i = 0, m = HAS_SHELL_ITEM_LIST ; m <= HAS_CUSTOM_ICON ; m <<= 1, ++i ) { printf( "%s\n", flag_message[ i ][ ( header.header.flags & m ) == m ] ) ; } for( i = 0, m = TARGET_READ_ONLY ; m <= TARGET_OFFLINE ; m <<= 1, ++i ) { if( ( header.header.file_attr & m ) == m ) { printf( "%s\n", target_message[ i ] ) ; } } if( ! ( header.header.flags & POINTS_TO_FILE ) ) return LNK_OK_NOT_A_FILE_OR_DIRECTORY ; file_offset = ( header.header.flags & HAS_SHELL_ITEM_LIST ? header.next_length : 0 ) - sizeof( unsigned short ) ; // we'll skip the ITEMIDLIST if it's present if( lseek( file_handle, file_offset, SEEK_CUR ) == -1 ) return LNK_SEEK_ERROR ; struct file_location_info loc_info ; if( ( ret = read( file_handle, &loc_info, sizeof( loc_info ) ) ) != sizeof( loc_info ) ) return LNK_READ_ERROR ; printf( "Total length %u, next %u, flags %u, vol offset %u, base offset %u, network offset %u, remaining %u\n", loc_info.total_length , loc_info.first_following_offset , loc_info.flags , loc_info.local_volume_info_offset , loc_info.base_pathname_offset , loc_info.network_volume_info_offset , loc_info.remaining_pathname_offset ) ; if( loc_info.flags & VOLUME_LOCAL ) { base_path_offset = loc_info.base_pathname_offset ; } else { base_path_offset = loc_info.network_volume_info_offset + 0x14 ; } if( ( read_len = loc_info.remaining_pathname_offset - base_path_offset ) + ( read_len_2 = loc_info.total_length - loc_info.remaining_pathname_offset ) > *path_buffer_length ) return LNK_PATH_BUFFER_TOO_SHORT ; file_offset = base_path_offset - sizeof( loc_info ) ; if( (ret = lseek( file_handle, file_offset, SEEK_CUR ) ) == -1 ) return LNK_SEEK_ERROR ; if( ( ret = read( file_handle, path_buffer, read_len ) ) != read_len ) return LNK_READ_ERROR ; path_ptr = path_buffer + ret ; // position path_ptr to point to first (char)0 in path_buffer while( ( ! *( path_ptr - 1 ) ) && path_ptr > path_buffer ) { --path_ptr ; } // read remaining path file_offset = loc_info.remaining_pathname_offset - base_path_offset - ret ; if( file_offset ) { if( (ret = lseek( file_handle, file_offset, SEEK_CUR ) ) == -1 ) { return LNK_SEEK_ERROR ; } } if( ( ret = read( file_handle, path_ptr, read_len_2 ) ) != read_len_2 ) return LNK_READ_ERROR ; // this should already be \0, unless we got a pathologically short buffer, but set it anyway. *( path_ptr += ret ) = 0 ; // position path_ptr to point to first (char)0 in path_buffer while( ( ! *( path_ptr - 1 ) ) && path_ptr > path_buffer ) { --path_ptr ; } *path_buffer_length = path_ptr - path_buffer ; printf( "absolute path, length %d: <%s>\n", *path_buffer_length, path_buffer ) ; // skip the description string, if any if( header.header.flags & HAS_DESCRIPTION_STRING ) { if( read( file_handle, &string_len, sizeof( unsigned short ) ) != sizeof( unsigned short ) ) return LNK_READ_ERROR ; printf( "description string length = %d\n", string_len ) ; if( ( ret = lseek( file_handle, string_len + string_len, SEEK_CUR ) ) == -1 ) return LNK_SEEK_ERROR ; } // read relative path, if any if( header.header.flags & HAS_RELATIVE_PATH ) { if( ( ret = read( file_handle, &string_len, sizeof( unsigned short ) ) ) != sizeof( unsigned short ) ) return LNK_READ_ERROR ; read_len = string_len + string_len ; if( ( ret = read( file_handle, path_buffer, read_len ) ) != read_len ) return LNK_READ_ERROR ; // copy over the nuls for( i = 1 ; i < string_len ; ++i ) { path_buffer[ i ] = path_buffer[ i * 2 ] ; } path_buffer[ i ] = 0 ; *path_buffer_length = string_len ; printf( "relative path, length %d: <%s>\n", *path_buffer_length, path_buffer ) ; return LNK_OK_RELATIVE_PATH ; } return LNK_OK_ABSOLUTE_PATH ; } int main( int argc, char** argv ) { int file ; char buf[ 256 ] ; unsigned int len = sizeof( buf ) ; int ret ; if( argc >= 2 && ( file = open( argv[ 1 ], O_RDONLY | O_BINARY ) ) > 0 ) { ret = read_lnk( file, buf, &len ) ; printf( "Result: %s\n", lnk_err_message[ ret + LNK_ERROR_ADJUST ] ) ; } } // CODE ENDS Received on 2003-02-01 Page template was last modified "Tue Sep 7 00:00:02 2021" The Rockbox Crew -- Privacy Policy |