From 34d4165f7b8a36419c417f164ccdebbe04808247 Mon Sep 17 00:00:00 2001 From: Antoine Cellerier Date: Tue, 10 Jun 2008 13:20:05 +0000 Subject: [PATCH] New md5sum plugin. Open a file, a directory or just launch it from the plugin menu to create an md5sum of the file, the directory's contents or the whole filesystem. If the file's extension is .md5 or .md5sum, it will check the md5 sums in the file instead. If the file's extension is .md5list it will compute md5 sums for all the files listed. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17709 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/CATEGORIES | 1 + apps/plugins/SOURCES | 2 + apps/plugins/lib/SOURCES | 1 + apps/plugins/lib/md5.c | 255 ++++++++++++++++++++++++++++++++++++ apps/plugins/lib/md5.h | 56 ++++++++ apps/plugins/md5sum.c | 206 +++++++++++++++++++++++++++++ apps/plugins/viewers.config | 1 + 7 files changed, 522 insertions(+) create mode 100644 apps/plugins/lib/md5.c create mode 100644 apps/plugins/lib/md5.h create mode 100644 apps/plugins/md5sum.c diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index 6992ca3ca1..337704dfa3 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES @@ -99,3 +99,4 @@ wormlet,games xobox,games zxbox,viewers lamp,apps +md5sum,apps diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 86d7055db2..0be7ccfaa8 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -150,3 +150,5 @@ invadrox.c superdom.c #endif #endif /* m:robe 500 */ + +md5sum.c diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES index c5c3a25e26..775db0587e 100644 --- a/apps/plugins/lib/SOURCES +++ b/apps/plugins/lib/SOURCES @@ -42,3 +42,4 @@ helper.c #ifdef HAVE_TOUCHPAD touchscreen.c #endif +md5.c diff --git a/apps/plugins/lib/md5.c b/apps/plugins/lib/md5.c new file mode 100644 index 0000000000..97156634e5 --- /dev/null +++ b/apps/plugins/lib/md5.c @@ -0,0 +1,255 @@ +/***************************************************************************** + * md5.c: not so strong MD5 hashing + ***************************************************************************** + * Copyright (C) 2004-2005 the VideoLAN team + * $Id: cb57f7b6522df0960a924193cfca438c03a92521 $ + * + * Authors: Jon Lech Johansen + * Sam Hocevar + * + * Adapted to Rockbox by: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "plugin.h" +#include "md5.h" + +static const struct plugin_api *rb; + +void md5_init( const struct plugin_api *api ) +{ + rb = api; +} + +#ifdef WORDS_BIGENDIAN +/***************************************************************************** + * Reverse: reverse byte order + *****************************************************************************/ +static inline void Reverse( uint32_t *p_buffer, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) + { + p_buffer[ i ] = GetDWLE(&p_buffer[ i ]); + } +} +# define REVERSE( p, n ) Reverse( p, n ) +#else +# define REVERSE( p, n ) +#endif + +#define F1( x, y, z ) ((z) ^ ((x) & ((y) ^ (z)))) +#define F2( x, y, z ) F1((z), (x), (y)) +#define F3( x, y, z ) ((x) ^ (y) ^ (z)) +#define F4( x, y, z ) ((y) ^ ((x) | ~(z))) + +#define MD5_DO( f, w, x, y, z, data, s ) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/***************************************************************************** + * DigestMD5: update the MD5 digest with 64 bytes of data + *****************************************************************************/ +static void DigestMD5( struct md5_s *p_md5, uint32_t *p_input ) +{ + uint32_t a, b, c, d; + + REVERSE( p_input, 16 ); + + a = p_md5->p_digest[ 0 ]; + b = p_md5->p_digest[ 1 ]; + c = p_md5->p_digest[ 2 ]; + d = p_md5->p_digest[ 3 ]; + + MD5_DO( F1, a, b, c, d, p_input[ 0 ] + 0xd76aa478, 7 ); + MD5_DO( F1, d, a, b, c, p_input[ 1 ] + 0xe8c7b756, 12 ); + MD5_DO( F1, c, d, a, b, p_input[ 2 ] + 0x242070db, 17 ); + MD5_DO( F1, b, c, d, a, p_input[ 3 ] + 0xc1bdceee, 22 ); + MD5_DO( F1, a, b, c, d, p_input[ 4 ] + 0xf57c0faf, 7 ); + MD5_DO( F1, d, a, b, c, p_input[ 5 ] + 0x4787c62a, 12 ); + MD5_DO( F1, c, d, a, b, p_input[ 6 ] + 0xa8304613, 17 ); + MD5_DO( F1, b, c, d, a, p_input[ 7 ] + 0xfd469501, 22 ); + MD5_DO( F1, a, b, c, d, p_input[ 8 ] + 0x698098d8, 7 ); + MD5_DO( F1, d, a, b, c, p_input[ 9 ] + 0x8b44f7af, 12 ); + MD5_DO( F1, c, d, a, b, p_input[ 10 ] + 0xffff5bb1, 17 ); + MD5_DO( F1, b, c, d, a, p_input[ 11 ] + 0x895cd7be, 22 ); + MD5_DO( F1, a, b, c, d, p_input[ 12 ] + 0x6b901122, 7 ); + MD5_DO( F1, d, a, b, c, p_input[ 13 ] + 0xfd987193, 12 ); + MD5_DO( F1, c, d, a, b, p_input[ 14 ] + 0xa679438e, 17 ); + MD5_DO( F1, b, c, d, a, p_input[ 15 ] + 0x49b40821, 22 ); + + MD5_DO( F2, a, b, c, d, p_input[ 1 ] + 0xf61e2562, 5 ); + MD5_DO( F2, d, a, b, c, p_input[ 6 ] + 0xc040b340, 9 ); + MD5_DO( F2, c, d, a, b, p_input[ 11 ] + 0x265e5a51, 14 ); + MD5_DO( F2, b, c, d, a, p_input[ 0 ] + 0xe9b6c7aa, 20 ); + MD5_DO( F2, a, b, c, d, p_input[ 5 ] + 0xd62f105d, 5 ); + MD5_DO( F2, d, a, b, c, p_input[ 10 ] + 0x02441453, 9 ); + MD5_DO( F2, c, d, a, b, p_input[ 15 ] + 0xd8a1e681, 14 ); + MD5_DO( F2, b, c, d, a, p_input[ 4 ] + 0xe7d3fbc8, 20 ); + MD5_DO( F2, a, b, c, d, p_input[ 9 ] + 0x21e1cde6, 5 ); + MD5_DO( F2, d, a, b, c, p_input[ 14 ] + 0xc33707d6, 9 ); + MD5_DO( F2, c, d, a, b, p_input[ 3 ] + 0xf4d50d87, 14 ); + MD5_DO( F2, b, c, d, a, p_input[ 8 ] + 0x455a14ed, 20 ); + MD5_DO( F2, a, b, c, d, p_input[ 13 ] + 0xa9e3e905, 5 ); + MD5_DO( F2, d, a, b, c, p_input[ 2 ] + 0xfcefa3f8, 9 ); + MD5_DO( F2, c, d, a, b, p_input[ 7 ] + 0x676f02d9, 14 ); + MD5_DO( F2, b, c, d, a, p_input[ 12 ] + 0x8d2a4c8a, 20 ); + + MD5_DO( F3, a, b, c, d, p_input[ 5 ] + 0xfffa3942, 4 ); + MD5_DO( F3, d, a, b, c, p_input[ 8 ] + 0x8771f681, 11 ); + MD5_DO( F3, c, d, a, b, p_input[ 11 ] + 0x6d9d6122, 16 ); + MD5_DO( F3, b, c, d, a, p_input[ 14 ] + 0xfde5380c, 23 ); + MD5_DO( F3, a, b, c, d, p_input[ 1 ] + 0xa4beea44, 4 ); + MD5_DO( F3, d, a, b, c, p_input[ 4 ] + 0x4bdecfa9, 11 ); + MD5_DO( F3, c, d, a, b, p_input[ 7 ] + 0xf6bb4b60, 16 ); + MD5_DO( F3, b, c, d, a, p_input[ 10 ] + 0xbebfbc70, 23 ); + MD5_DO( F3, a, b, c, d, p_input[ 13 ] + 0x289b7ec6, 4 ); + MD5_DO( F3, d, a, b, c, p_input[ 0 ] + 0xeaa127fa, 11 ); + MD5_DO( F3, c, d, a, b, p_input[ 3 ] + 0xd4ef3085, 16 ); + MD5_DO( F3, b, c, d, a, p_input[ 6 ] + 0x04881d05, 23 ); + MD5_DO( F3, a, b, c, d, p_input[ 9 ] + 0xd9d4d039, 4 ); + MD5_DO( F3, d, a, b, c, p_input[ 12 ] + 0xe6db99e5, 11 ); + MD5_DO( F3, c, d, a, b, p_input[ 15 ] + 0x1fa27cf8, 16 ); + MD5_DO( F3, b, c, d, a, p_input[ 2 ] + 0xc4ac5665, 23 ); + + MD5_DO( F4, a, b, c, d, p_input[ 0 ] + 0xf4292244, 6 ); + MD5_DO( F4, d, a, b, c, p_input[ 7 ] + 0x432aff97, 10 ); + MD5_DO( F4, c, d, a, b, p_input[ 14 ] + 0xab9423a7, 15 ); + MD5_DO( F4, b, c, d, a, p_input[ 5 ] + 0xfc93a039, 21 ); + MD5_DO( F4, a, b, c, d, p_input[ 12 ] + 0x655b59c3, 6 ); + MD5_DO( F4, d, a, b, c, p_input[ 3 ] + 0x8f0ccc92, 10 ); + MD5_DO( F4, c, d, a, b, p_input[ 10 ] + 0xffeff47d, 15 ); + MD5_DO( F4, b, c, d, a, p_input[ 1 ] + 0x85845dd1, 21 ); + MD5_DO( F4, a, b, c, d, p_input[ 8 ] + 0x6fa87e4f, 6 ); + MD5_DO( F4, d, a, b, c, p_input[ 15 ] + 0xfe2ce6e0, 10 ); + MD5_DO( F4, c, d, a, b, p_input[ 6 ] + 0xa3014314, 15 ); + MD5_DO( F4, b, c, d, a, p_input[ 13 ] + 0x4e0811a1, 21 ); + MD5_DO( F4, a, b, c, d, p_input[ 4 ] + 0xf7537e82, 6 ); + MD5_DO( F4, d, a, b, c, p_input[ 11 ] + 0xbd3af235, 10 ); + MD5_DO( F4, c, d, a, b, p_input[ 2 ] + 0x2ad7d2bb, 15 ); + MD5_DO( F4, b, c, d, a, p_input[ 9 ] + 0xeb86d391, 21 ); + + p_md5->p_digest[ 0 ] += a; + p_md5->p_digest[ 1 ] += b; + p_md5->p_digest[ 2 ] += c; + p_md5->p_digest[ 3 ] += d; +} + +/***************************************************************************** + * InitMD5: initialise an MD5 message + ***************************************************************************** + * The MD5 message-digest algorithm is described in RFC 1321 + *****************************************************************************/ +void InitMD5( struct md5_s *p_md5 ) +{ + p_md5->p_digest[ 0 ] = 0x67452301; + p_md5->p_digest[ 1 ] = 0xefcdab89; + p_md5->p_digest[ 2 ] = 0x98badcfe; + p_md5->p_digest[ 3 ] = 0x10325476; + + rb->memset( p_md5->p_data, 0, 64 ); + p_md5->i_bits = 0; +} + +/***************************************************************************** + * AddMD5: add i_len bytes to an MD5 message + *****************************************************************************/ +void AddMD5( struct md5_s *p_md5, const void *p_src, size_t i_len ) +{ + unsigned int i_current; /* Current bytes in the spare buffer */ + size_t i_offset = 0; + + i_current = (p_md5->i_bits / 8) & 63; + + p_md5->i_bits += 8 * i_len; + + /* If we can complete our spare buffer to 64 bytes, do it and add the + * resulting buffer to the MD5 message */ + if( i_len >= (64 - i_current) ) + { + rb->memcpy( ((uint8_t *)p_md5->p_data) + i_current, p_src, + (64 - i_current) ); + DigestMD5( p_md5, p_md5->p_data ); + + i_offset += (64 - i_current); + i_len -= (64 - i_current); + i_current = 0; + } + + /* Add as many entire 64 bytes blocks as we can to the MD5 message */ + while( i_len >= 64 ) + { + uint32_t p_tmp[ 16 ]; + rb->memcpy( p_tmp, ((const uint8_t *)p_src) + i_offset, 64 ); + DigestMD5( p_md5, p_tmp ); + i_offset += 64; + i_len -= 64; + } + + /* Copy our remaining data to the message's spare buffer */ + rb->memcpy( ((uint8_t *)p_md5->p_data) + i_current, + ((const uint8_t *)p_src) + i_offset, i_len ); +} + +/***************************************************************************** + * EndMD5: finish an MD5 message + ***************************************************************************** + * This function adds adequate padding to the end of the message, and appends + * the bit count so that we end at a block boundary. + *****************************************************************************/ +void EndMD5( struct md5_s *p_md5 ) +{ + unsigned int i_current; + + i_current = (p_md5->i_bits / 8) & 63; + + /* Append 0x80 to our buffer. No boundary check because the temporary + * buffer cannot be full, otherwise AddMD5 would have emptied it. */ + ((uint8_t *)p_md5->p_data)[ i_current++ ] = 0x80; + + /* If less than 8 bytes are available at the end of the block, complete + * this 64 bytes block with zeros and add it to the message. We'll add + * our length at the end of the next block. */ + if( i_current > 56 ) + { + rb->memset( ((uint8_t *)p_md5->p_data) + i_current, 0, (64 - i_current) ); + DigestMD5( p_md5, p_md5->p_data ); + i_current = 0; + } + + /* Fill the unused space in our last block with zeroes and put the + * message length at the end. */ + rb->memset( ((uint8_t *)p_md5->p_data) + i_current, 0, (56 - i_current) ); + p_md5->p_data[ 14 ] = p_md5->i_bits & 0xffffffff; + p_md5->p_data[ 15 ] = (p_md5->i_bits >> 32); + REVERSE( &p_md5->p_data[ 14 ], 2 ); + + DigestMD5( p_md5, p_md5->p_data ); +} + +void psz_md5_hash( char *psz, struct md5_s *md5_s ) +{ + int i; + for ( i = 0; i < 4; i++ ) + { + rb->snprintf( &psz[8*i], 9, "%02x%02x%02x%02x", + md5_s->p_digest[i] & 0xff, + ( md5_s->p_digest[i] >> 8 ) & 0xff, + ( md5_s->p_digest[i] >> 16 ) & 0xff, + md5_s->p_digest[i] >> 24 + ); + } +} diff --git a/apps/plugins/lib/md5.h b/apps/plugins/lib/md5.h new file mode 100644 index 0000000000..e19c749664 --- /dev/null +++ b/apps/plugins/lib/md5.h @@ -0,0 +1,56 @@ +/***************************************************************************** + * vlc_md5.h: MD5 hash + ***************************************************************************** + * Copyright (C) 2004-2005 the VideoLAN team + * $Id: 46f10f01439edbcc8bad45673cc302039b76dd5b $ + * + * Authors: Jon Lech Johansen + * Sam Hocevar + * + * Adapted to Rockbox by: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef _VLC_MD5_H +# define _VLC_MD5_H + +void md5_init( const struct plugin_api * ); + +/***************************************************************************** + * md5_s: MD5 message structure + ***************************************************************************** + * This structure stores the static information needed to compute an MD5 + * hash. It has an extra data buffer to allow non-aligned writes. + *****************************************************************************/ +struct md5_s +{ + uint64_t i_bits; /* Total written bits */ + uint32_t p_digest[4]; /* The MD5 digest */ + uint32_t p_data[16]; /* Buffer to cache non-aligned writes */ +}; + +void InitMD5( struct md5_s * ); +void AddMD5( struct md5_s *, const void *, size_t ); +void EndMD5( struct md5_s * ); + +/** + * Returns a char representation of the md5 hash, as shown by UNIX md5 or + * md5sum tools. + */ +#define MD5_STRING_LENGTH 32 +void psz_md5_hash( char *, struct md5_s * ); + +#endif diff --git a/apps/plugins/md5sum.c b/apps/plugins/md5sum.c new file mode 100644 index 0000000000..6845c4c473 --- /dev/null +++ b/apps/plugins/md5sum.c @@ -0,0 +1,206 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 2008 Antoine Cellerier + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "plugin.h" +#include "lib/md5.h" + +PLUGIN_HEADER + +static const struct plugin_api *rb; + +int hash( char *string, const char *path ) +{ + char *buffer[512]; + ssize_t len; + struct md5_s md5; + int in = rb->open( path, O_RDONLY ); + if( in < 0 ) return -1; + + InitMD5( &md5 ); + while( ( len = rb->read( in, buffer, 512 ) ) > 0 ) + AddMD5( &md5, buffer, len ); + EndMD5( &md5 ); + + psz_md5_hash( string, &md5 ); + + rb->close( in ); + return 0; +} + +void hash_file( int out, const char *path ) +{ + char string[MD5_STRING_LENGTH+1]; + if( hash( string, path ) ) + rb->write( out, "error", 5 ); + else + rb->write( out, string, MD5_STRING_LENGTH ); + rb->write( out, " ", 1 ); + rb->write( out, path, rb->strlen( path ) ); + rb->write( out, "\n", 1 ); +} + +void hash_dir( int out, const char *path ); +void hash_dir( int out, const char *path ) +{ + DIR *dir; + struct dirent *entry; + + dir = rb->opendir( path ); + if( dir ) + { + while( ( entry = rb->readdir( dir ) ) ) + { + char childpath[MAX_PATH]; + rb->snprintf( childpath, MAX_PATH, "%s/%s", + path, entry->d_name ); + if( entry->attribute & ATTR_DIRECTORY ) + { + if( rb->strcmp( entry->d_name, "." ) + && rb->strcmp( entry->d_name, ".." ) ) + { + /* Got a sub directory */ + hash_dir( out, childpath ); + } + } + else + { + /* Got a file */ + hash_file( out, childpath ); + } + } + rb->closedir( dir ); + } +} + +void hash_list( int out, const char *path ) +{ + int list = rb->open( path, O_RDONLY ); + char newpath[MAX_PATH]; + if( list < 0 ) return; + + while( rb->read_line( list, newpath, MAX_PATH ) > 0 ) + { + DIR *dir = rb->opendir( newpath ); + if( dir ) + { + rb->closedir( dir ); + hash_dir( out, newpath ); + } + else + { + hash_file( out, newpath ); + } + } + + rb->close( list ); +} + +void hash_check( int out, const char *path ) +{ + int list = rb->open( path, O_RDONLY ); + char line[MD5_STRING_LENGTH+1+MAX_PATH+1]; + int len; + if( list < 0 ) return; + + while( ( len = rb->read_line( list, line, MD5_STRING_LENGTH+1+MAX_PATH+1 ) ) > 0 ) + { + const char *filename = rb->strchr( line, ' ' ); + if( !filename || len < MD5_STRING_LENGTH + 2 ) + { + const char error[] = "Malformed input line ... skipping"; + rb->write( out, error, rb->strlen( error ) ); + } + else + { + char string[MD5_STRING_LENGTH+1]; + filename++; + rb->write( out, filename, rb->strlen( filename ) ); + rb->write( out, ": ", 2 ); + if( hash( string, filename ) ) + rb->write( out, "FAILED open or read", 19 ); + else if( rb->memcmp( line, string, MD5_STRING_LENGTH ) ) + rb->write( out, "FAILED", 6 ); + else + rb->write( out, "OK", 2 ); + } + rb->write( out, "\n", 1 ); + } +} + +enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter) +{ + const char *arg = (const char *)parameter; /* input file name, if any */ + int out = -1; /* output file descriptor */ + char filename[MAX_PATH]; /* output file name */ + + md5_init( api ); + rb = api; + + if( arg && *arg ) + { + const char *ext = rb->strrchr( arg, '.' ); + DIR *dir; + rb->snprintf( filename, MAX_PATH, "%s.md5sum", arg ); + out = rb->open( filename, O_WRONLY|O_CREAT ); + if( out < 0 ) return PLUGIN_ERROR; + + if( ext ) + { + if( !rb->strcmp( ext, ".md5" ) || !rb->strcmp( ext, ".md5sum" ) ) + { + /* Lets check the sums */ + hash_check( out, arg ); + goto exit; + } + else if( !rb->strcmp( ext, ".md5list" ) ) /* ugly */ + { + /* Hash listed files */ + hash_list( out, arg ); + goto exit; + } + } + + dir = rb->opendir( arg ); + if( dir ) + { + api->closedir( dir ); + + /* Hash the directory's content recursively */ + hash_dir( out, arg ); + } + else + { + /* Hash the file */ + hash_file( out, arg ); + } + } + else + { + rb->snprintf( filename, MAX_PATH, "/everything.md5sum" ); + out = rb->open( filename, O_WRONLY|O_CREAT ); + if( out < 0 ) return PLUGIN_ERROR; + + /* Hash the whole filesystem */ + hash_dir( out, "/" ); + } + + exit: + rb->close( out ); + return PLUGIN_OK; +} diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config index bfb408c397..c1df1b2e71 100644 --- a/apps/plugins/viewers.config +++ b/apps/plugins/viewers.config @@ -41,3 +41,4 @@ colours,apps/text_editor,11 ssg,games/superdom,- link,viewers/shortcuts_view,- *,viewers/shortcuts_append,- +*,apps/md5sum,-