Permalink
Browse files

- Storage of the entries and its navigation were reformulated. The pr…

…evious array with raw entries was substituted with an array with unique entries that also stores the directory depth and length of the entry name. The hash table used for direct access (url stater mainly) was substituted for a sorted array that makes directory navigation more efficient. The penalty of losing constant time access to entries by name (now it takes log n) was mitigated by a cache that is optimized for stating every element while a directory or the whole file are being traversed.

- RAR navigation and indexing were moved to rar_navigation.c.
- RAR archives that contain entries with the same name are correctly handled.
- Changed the way properties are accessed in RarEntry (does not require building the properties table in trunk).
- Fixed memory leak in silent url stat.
- Fixed handling of optional passwords. Now giving no password, NULL or '' result in the same behavior.

git-svn-id: http://svn.php.net/repository/pecl/rar/trunk@299926 c90b9560-bf6c-de11-be94-00142212c4b1
  • Loading branch information...
1 parent 79b78f8 commit 9a7227a9e05947c6ac07b4b50b87a1331ae23924 cataphract committed May 29, 2010
Showing with 1,157 additions and 458 deletions.
  1. +46 −27 php_rar.h
  2. +72 −4 rar.c
  3. +577 −0 rar_navigation.c
  4. +54 −17 rar_stream.c
  5. +12 −322 rararch.c
  6. +69 −48 rarentry.c
  7. +8 −0 tests/002.phpt
  8. +4 −0 tests/003.phpt
  9. +2 −0 tests/004.phpt
  10. +4 −0 tests/011.phpt
  11. +4 −4 tests/060.phpt
  12. +3 −3 tests/064.phpt
  13. +7 −9 tests/065.phpt
  14. +12 −12 tests/068.phpt
  15. +12 −12 tests/069.phpt
  16. +17 −0 tests/070.phpt
  17. +21 −0 tests/071.phpt
  18. +21 −0 tests/072.phpt
  19. +25 −0 tests/073.phpt
  20. +33 −0 tests/074.phpt
  21. +52 −0 tests/075.phpt
  22. +60 −0 tests/076.phpt
  23. +42 −0 tests/077.phpt
  24. BIN tests/repeated_name.rar
View
@@ -27,7 +27,6 @@
/* $Id$ */
-/* TODO: correct handling of archives with entries with the same name */
/* TODO: metadata block reading */
/* TODO: correct support for symlinks inside RAR files. This includes:
* - Respecting PHP_STREAM_URL_STAT_LINK in the url_stater
@@ -36,12 +35,14 @@
/* TODO: add support for opening RAR files in a persisten fashion */
/* TODO: consider making struct rar opaque, outside rararch.c only
* RarEntry::extract/getStream access the fields */
-/* TODO: merge rar_file_t.entries_idx and rar_file_t.entries */
/* TODO: consider using a php memory/tmpfile stream to serve as buffer for
* rar file streams */
/* TODO: improve RAR archive cache key for url_stater/dir_opener, so that it
* can detect file modification */
-/* TODO: make configurable the capacity of the usr_stater/dir_opener cache */
+/* TODO: make configurable the capacity of the url_stater/dir_opener cache */
+/* TODO: test url_stater/dir_opener cache exaustion */
+/* TODO: tests with empty rar file */
+/* TODO: optimize _rar_nav_directory_match with the depth */
#ifndef PHP_RAR_H
#define PHP_RAR_H
@@ -90,10 +91,7 @@ typedef struct _rar_cb_user_data {
typedef struct rar {
zend_object_handle id;
- int entry_count; //>= number of files
- struct RARHeaderDataEx **entries;
- //key: entry name, value: index in entries
- HashTable *entries_idx; /* TODO: merge into entries */
+ struct _rar_entries *entries;
struct RAROpenArchiveDataEx *list_open_data;
struct RAROpenArchiveDataEx *extract_open_data;
//archive handle opened with RAR_OM_LIST_INCSPLIT open mode
@@ -113,8 +111,9 @@ typedef struct rar {
* persistently allocated buffers since the RarArchive objects cannot be made
* persistent themselves.
*
- * I'll go with per-request and store zval pointers together with modification
- * time.
+ * I'll go with per-request and store zval pointers with a cache key that
+ * considers filename, modificaion time and stream context (currently only
+ * filename).
* I'll also go with a FIFO eviction policy because it's simpler to implement
* (just delete the first element of the HashTable).
*/
@@ -160,13 +159,6 @@ ZEND_EXTERN_MODULE_GLOBALS(rar);
{ NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args },
#endif
-#if !defined(HAVE_STRNLEN)
-size_t _rar_strnlen(const char *s, size_t maxlen);
-# define rar_strnlen _rar_strnlen
-#else
-# define rar_strnlen strnlen
-#endif
-
/* rar.c */
PHP_MINIT_FUNCTION(rar);
PHP_MSHUTDOWN_FUNCTION(rar);
@@ -176,6 +168,13 @@ PHP_MINFO_FUNCTION(rar);
PHP_FUNCTION(rar_bogus_ctor);
+#ifndef HAVE_STRNLEN
+size_t _rar_strnlen(const char *s, size_t maxlen);
+# define strnlen _rar_strnlen
+#else
+# define _rar_strnlen strnlen
+#endif
+
void _rar_wide_to_utf(const wchar_t *src, char *dest, size_t dest_size);
void _rar_utf_to_wide(const char *src, wchar_t *dest, size_t dest_size);
void _rar_destroy_userdata(rar_cb_user_data *udata);
@@ -193,6 +192,13 @@ int _rar_find_file_w(struct RAROpenArchiveDataEx *open_data, /* IN */
int *found, /* OUT */
struct RARHeaderDataEx *header_data /* OUT, can be null */
);
+int _rar_find_file_p(struct RAROpenArchiveDataEx *open_data, /* IN */
+ size_t position, /* IN */
+ rar_cb_user_data *cb_udata, /* IN, must be managed outside */
+ void **arc_handle, /* OUT: where to store rar archive handle */
+ int *found, /* OUT */
+ struct RARHeaderDataEx *header_data /* OUT, can be null */
+ );
int CALLBACK _rar_unrar_callback(UINT msg, LPARAM UserData, LPARAM P1, LPARAM P2);
/* rar_error.c */
@@ -204,31 +210,43 @@ int _rar_using_exceptions(TSRMLS_D);
const char * _rar_error_to_string(int errcode);
void minit_rarerror(TSRMLS_D);
-/* rararch.c */
-int _rar_create_rararch_obj(const char* resolved_path,
- const char* open_password,
- zval *volume_callback, //must be callable or NULL
- zval *object,
- int *err_code TSRMLS_DC);
-void _rar_close_file_resource(rar_file_t *rar);
-int _rar_index_entries(rar_file_t *rar_file TSRMLS_DC);
+/* rar_navigation.c */
+
+int _rar_list_files(rar_file_t *rar TSRMLS_DC);
+void _rar_delete_entries(rar_file_t *rar TSRMLS_DC);
/* entry search API {{{ */
typedef struct _rar_find_output {
int found;
+ size_t position;
struct RARHeaderDataEx * header;
unsigned long packed_size;
int eof;
} rar_find_output;
-void _rar_entry_search_start(rar_file_t *rar, rar_find_output **state);
+#define RAR_SEARCH_INDEX 0x01U
+#define RAR_SEARCH_TRAVERSE 0x01U
+#define RAR_SEARCH_DIRECTORY 0x02U
+#define RAR_SEARCH_NAME 0x02U
+void _rar_entry_search_start(rar_file_t *rar,
+ unsigned mode,
+ rar_find_output **state TSRMLS_DC);
void _rar_entry_search_end(rar_find_output *state);
+void _rar_entry_search_seek(rar_find_output *state, size_t pos);
void _rar_entry_search_rewind(rar_find_output *state);
void _rar_entry_search_advance(rar_find_output *state,
const wchar_t * const file, //NULL = give next
- size_t file_size,
+ size_t file_size, //length + 1
int directory_match);
/* end entry search API }}} */
+/* rararch.c */
+int _rar_create_rararch_obj(const char* resolved_path,
+ const char* open_password,
+ zval *volume_callback, //must be callable or NULL
+ zval *object,
+ int *err_code TSRMLS_DC);
+void _rar_close_file_resource(rar_file_t *rar);
+
/* Fetches the rar_file_t part of the RarArchive object in order to use the
* operations above and (discouraged) to have direct access to the fields
* RarEntry::extract/getStream access extract_open_dat and cb_userdata */
@@ -249,11 +267,12 @@ void minit_rarentry(TSRMLS_D);
void _rar_entry_to_zval(zval *parent,
struct RARHeaderDataEx *entry,
unsigned long packed_size,
+ size_t index,
zval *entry_object TSRMLS_DC);
/* rar_stream.c */
php_stream *php_stream_rar_open(char *arc_name,
- char *utf_file_name,
+ size_t position,
rar_cb_user_data *cb_udata_ptr, /* will be copied */
char *mode STREAMS_DC TSRMLS_DC);
extern php_stream_wrapper php_stream_rar_wrapper;
View
@@ -66,6 +66,15 @@ static int _rar_make_userdata_fcall(zval *callable,
/* }}} */
/* {{{ Functions with external linkage */
+#if !HAVE_STRNLEN
+size_t _rar_strnlen(const char *s, size_t maxlen) /* {{{ */
+{
+ char *r = memchr(s, '\0', maxlen);
+ return r ? r-s : maxlen;
+}
+/* }}} */
+#endif
+
/* From unicode.cpp
* I can't use that one directy because it takes a const wchar, not wchar_t.
* And I shouldn't because it's not a public API.
@@ -254,6 +263,63 @@ int _rar_find_file_w(struct RAROpenArchiveDataEx *open_data, /* IN */
}
/* }}} */
+int _rar_find_file_p(struct RAROpenArchiveDataEx *open_data, /* IN */
+ size_t position, /* IN */
+ rar_cb_user_data *cb_udata, /* IN, must be managed outside */
+ void **arc_handle, /* OUT: where to store rar archive handle */
+ int *found, /* OUT */
+ struct RARHeaderDataEx *header_data /* OUT, can be null */
+ ) /* {{{ */
+{
+ int result,
+ process_result;
+ struct RARHeaderDataEx *used_header_data;
+ int retval = 0; /* success in rar parlance */
+ size_t curpos = 0;
+
+ assert(open_data != NULL);
+ assert(arc_handle != NULL);
+ assert(found != NULL);
+ *found = FALSE;
+ *arc_handle = NULL;
+ used_header_data = header_data != NULL ?
+ header_data :
+ ecalloc(1, sizeof *used_header_data);
+
+ *arc_handle = RAROpenArchiveEx(open_data);
+ if (*arc_handle == NULL) {
+ retval = open_data->OpenResult;
+ goto cleanup;
+ }
+ RARSetCallback(*arc_handle, _rar_unrar_callback, (LPARAM) cb_udata);
+
+ while ((result = RARReadHeaderEx(*arc_handle, used_header_data)) == 0) {
+ /* skip entries that were split before with incrementing current pos */
+ if ((used_header_data->Flags & 0x01U) || (curpos++ != position)) {
+ process_result = RARProcessFile(*arc_handle, RAR_SKIP, NULL, NULL);
+ } else {
+ *found = TRUE;
+ goto cleanup;
+ }
+ if (process_result != 0) {
+ retval = process_result;
+ goto cleanup;
+ }
+ }
+
+ if (result != 0 && result != 1) {
+ //0 indicates success, 1 indicates normal end of file
+ retval = result;
+ goto cleanup;
+ }
+
+cleanup:
+ if (header_data == NULL)
+ efree(used_header_data);
+
+ return retval;
+}
+
/* An unRAR callback.
* Processes requests for passwords and missing volumes
* If there is (userland) volume find callback specified, try to use that
@@ -268,13 +334,15 @@ int CALLBACK _rar_unrar_callback(UINT msg, LPARAM UserData, LPARAM P1, LPARAM P2
//user data is the password or null if none
char *password = userdata->password;
- if (password == NULL) {
+ if (password == NULL || password[0] == '\0') {
/*php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Password needed, but it has not been specified");*/
return -1;
}
else {
- strncpy((char*) P1, password, (size_t) P2);
+ strncpy((char *) P1, password, (size_t) P2);
+ assert((size_t) P2 > 0);
+ ((char *) P1)[(size_t) P2 - 1] = '\0';
}
}
else if (msg == UCM_CHANGEVOLUME) {
@@ -402,7 +470,7 @@ static int _rar_unrar_volume_user_callback(char* dst_buffer,
goto cleanup;
}
- resolved_len = rar_strnlen(resolved_path, MAXPATHLEN);
+ resolved_len = _rar_strnlen(resolved_path, MAXPATHLEN);
/* dst_buffer size is NM; first condition won't happen short of a bug
* in expand_filepath */
if (resolved_len == MAXPATHLEN || resolved_len > NM - 1) {
@@ -529,7 +597,7 @@ static int _rar_array_apply_remove_first(void *pDest TSRMLS_DC)
return (ZEND_HASH_APPLY_STOP | ZEND_HASH_APPLY_REMOVE);
}
-/* caller should increment zval before calling this */
+/* caller should increment zval refcount before calling this */
static void _rar_contents_cache_put(const char *key,
uint key_len,
zval *zv TSRMLS_DC)
Oops, something went wrong.

0 comments on commit 9a7227a

Please sign in to comment.