| diff --git a/CMakeLists.txt b/CMakeLists.txt |
| --- a/CMakeLists.txt |
| +++ b/CMakeLists.txt |
| @@ -91,6 +91,10 @@ FUNCTION(TARGET_OS_LIBRARIES target) |
| TARGET_LINK_LIBRARIES(${target} rt) |
| SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lrt" PARENT_SCOPE) |
| ENDIF() |
| + IF("$ENV{NACL_LIBC}" STREQUAL "newlib") |
| + TARGET_LINK_LIBRARIES(${target} glibc-compat) |
| + SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lglibc-compat" PARENT_SCOPE) |
| + ENDIF() |
| |
| IF(THREADSAFE) |
| TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT}) |
| @@ -364,6 +368,8 @@ FILE(GLOB SRC_H include/git2.h include/git2/*.h include/git2/sys/*.h) |
| IF (WIN32 AND NOT CYGWIN) |
| ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501) |
| FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h) |
| +ELSEIF (NACL) |
| + ADD_DEFINITIONS(-DNO_MMAP) |
| ELSEIF (AMIGA) |
| ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP) |
| ELSE() |
| @@ -385,8 +391,8 @@ ENDIF() |
| |
| # Compile and link libgit2 |
| ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC}) |
| -TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES}) |
| TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES}) |
| +TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES}) |
| TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES}) |
| TARGET_OS_LIBRARIES(git2) |
| |
| @@ -451,8 +457,8 @@ IF (BUILD_CLAR) |
| |
| ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1}) |
| |
| - TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES}) |
| TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES}) |
| + TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES}) |
| TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES}) |
| TARGET_OS_LIBRARIES(libgit2_clar) |
| MSVC_SPLIT_SOURCES(libgit2_clar) |
| diff --git a/include/git2/transport.h b/include/git2/transport.h |
| --- a/include/git2/transport.h |
| +++ b/include/git2/transport.h |
| @@ -11,6 +11,11 @@ |
| #include "net.h" |
| #include "types.h" |
| |
| +#ifdef __native_client__ |
| +#include <ppapi/c/pp_instance.h> |
| +#include <ppapi/c/ppb.h> |
| +#endif |
| + |
| /** |
| * @file git2/transport.h |
| * @brief Git transport interfaces and functions |
| @@ -514,6 +519,33 @@ GIT_EXTERN(int) git_smart_subtransport_http( |
| git_smart_subtransport **out, |
| git_transport* owner); |
| |
| +#ifdef __native_client__ |
| +/** |
| + * This function must be called before using git_smart_subtransport_pepper_http. |
| + * It will initialize |
| + */ |
| +GIT_EXTERN(int) git_smart_subtransport_pepper_http_init( |
| + PP_Instance instance, |
| + PPB_GetInterface get_interface); |
| + |
| +/** |
| + * Create an instance of the pepper http subtransport. This subtransport |
| + * also supports https. |
| + * |
| + * This will use the Pepper URLLoader interface rather than sockets. This will |
| + * allow libgit2 to be used without socket permission, though it requires the |
| + * git server to be on the same origin, or to respond with the correct CORS |
| + * headers (most git servers don't). |
| + * |
| + * @param out The newly created subtransport |
| + * @param owner The smart transport to own this subtransport |
| + * @return 0 or an error code |
| + */ |
| +GIT_EXTERN(int) git_smart_subtransport_pepper_http( |
| + git_smart_subtransport **out, |
| + git_transport* owner); |
| +#endif |
| + |
| /** |
| * Create an instance of the git subtransport. |
| * |
| diff --git a/src/indexer.c b/src/indexer.c |
| --- a/src/indexer.c |
| +++ b/src/indexer.c |
| @@ -437,12 +437,17 @@ static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t |
| page_start = (offset / page_size) * page_size; |
| page_offset = offset - page_start; |
| |
| +#ifdef NO_MMAP |
| + if ((error = pwrite(fd, data, size, offset)) < 0) |
| + return error; |
| +#else |
| if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0) |
| return error; |
| |
| map_data = (unsigned char *)map.data; |
| memcpy(map_data + page_offset, data, size); |
| p_munmap(&map); |
| +#endif |
| |
| return 0; |
| } |
| diff --git a/src/pool.c b/src/pool.c |
| --- a/src/pool.c |
| +++ b/src/pool.c |
| @@ -312,7 +312,7 @@ uint32_t git_pool__system_page_size(void) |
| #elif defined(__amigaos4__) |
| size = (uint32_t)4096; /* 4K as there is no global value we can query */ |
| #else |
| - size = (uint32_t)sysconf(_SC_PAGE_SIZE); |
| + size = (uint32_t)sysconf(_SC_PAGESIZE); |
| #endif |
| |
| size -= 2 * sizeof(void *); /* allow space for malloc overhead */ |
| diff --git a/src/posix.h b/src/posix.h |
| --- a/src/posix.h |
| +++ b/src/posix.h |
| @@ -12,6 +12,10 @@ |
| #include <time.h> |
| #include "fnmatch.h" |
| |
| +#if defined(__native_client__) && defined(_NEWLIB_VERSION) |
| +mode_t umask(mode_t cmask); |
| +#endif |
| + |
| #ifndef S_IFGITLINK |
| #define S_IFGITLINK 0160000 |
| #define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK) |
| diff --git a/src/transports/pepperhttp.c b/src/transports/pepperhttp.c |
| new file mode 100644 |
| --- /dev/null |
| +++ b/src/transports/pepperhttp.c |
| @@ -0,0 +1,621 @@ |
| +/* Copyright 2014 The Native Client Authors. All rights reserved. |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. */ |
| + |
| +#ifdef __native_client__ |
| + |
| +#include "git2.h" |
| +#include "http_parser.h" |
| +#include "buffer.h" |
| +#include "netops.h" |
| +#include "smart.h" |
| + |
| +#include <ppapi/c/pp_errors.h> |
| +#include <ppapi/c/pp_resource.h> |
| +#include <ppapi/c/pp_var.h> |
| +#include <ppapi/c/ppb_core.h> |
| +#include <ppapi/c/ppb_url_loader.h> |
| +#include <ppapi/c/ppb_url_request_info.h> |
| +#include <ppapi/c/ppb_url_response_info.h> |
| +#include <ppapi/c/ppb_var.h> |
| +#include <ppapi_simple/ps.h> |
| + |
| +static PP_Instance pp_instance; |
| +static const struct PPB_Core_1_0* core_iface; |
| +static const struct PPB_Var_1_2* var_iface; |
| +static const struct PPB_URLLoader_1_0* url_loader_iface; |
| +static const struct PPB_URLRequestInfo_1_0* url_request_info_iface; |
| +static const struct PPB_URLResponseInfo_1_0* url_response_info_iface; |
| + |
| +static const char* upload_pack_service = "upload-pack"; |
| +static const char* upload_pack_ls_service_url = |
| + "/info/refs?service=git-upload-pack"; |
| +static const char* upload_pack_service_url = "/git-upload-pack"; |
| +static const char* receive_pack_service = "receive-pack"; |
| +static const char* receive_pack_ls_service_url = |
| + "/info/refs?service=git-receive-pack"; |
| +static const char* receive_pack_service_url = "/git-receive-pack"; |
| +static const char* get_verb = "GET"; |
| +static const char* post_verb = "POST"; |
| + |
| +#define OWNING_SUBTRANSPORT(s) \ |
| + ((pepper_http_subtransport*)(s)->parent.subtransport) |
| + |
| +enum last_cb { |
| + NONE, |
| + FIELD, |
| + VALUE |
| +}; |
| + |
| +typedef struct { |
| + git_smart_subtransport_stream parent; |
| + PP_Resource url_request_info; |
| + PP_Resource url_loader; |
| + const char* service; |
| + const char* service_url; |
| + const char* verb; |
| + unsigned sent_request : 1; |
| + unsigned received_response : 1; |
| + unsigned chunked : 1; |
| +} pepper_http_stream; |
| + |
| +typedef struct { |
| + git_smart_subtransport parent; |
| + void* owner; |
| + char* url; |
| + git_cred* cred; |
| + git_cred* url_cred; |
| +} pepper_http_subtransport; |
| + |
| +typedef struct { |
| + pepper_http_stream* s; |
| + pepper_http_subtransport* t; |
| + |
| + git_buf parse_header_name; |
| + git_buf parse_header_value; |
| + char* content_type; |
| + enum last_cb last_cb; |
| + int parse_error; |
| +} parser_context; |
| + |
| +static int pepper_http_connect(pepper_http_subtransport* t) { |
| + return 0; |
| +} |
| + |
| +static int pepper_http_stream_connect(pepper_http_stream* s) { |
| + int error = -1; |
| + struct PP_Var url_var = PP_MakeUndefined(); |
| + struct PP_Var method_var = PP_MakeUndefined(); |
| + struct PP_Var headers_var = PP_MakeUndefined(); |
| + pepper_http_subtransport* t = OWNING_SUBTRANSPORT(s); |
| + git_buf buf = GIT_BUF_INIT; |
| + git_buf headers = GIT_BUF_INIT; |
| + char* url_colon; |
| + const char* scheme; |
| + |
| + if (s->url_loader != 0) |
| + return 0; |
| + |
| + s->url_loader = url_loader_iface->Create(pp_instance); |
| + if (s->url_loader == 0) |
| + return -1; |
| + |
| + s->url_request_info = url_request_info_iface->Create(pp_instance); |
| + if (s->url_request_info == 0) { |
| + goto on_error; |
| + } |
| + |
| + /* Allow the user to register the stream with a different scheme name. We |
| + * assume that if it contains https somewhere it should be an https stream, |
| + * otherwise it will be an http stream. */ |
| + url_colon = strchr(t->url, ':'); |
| + if (url_colon) { |
| + char* https = strstr(t->url, "https"); |
| + if (https == NULL || https > url_colon) { |
| + /* Not HTTPS, replace the scheme with http */ |
| + scheme = "http"; |
| + } else { |
| + scheme = "https"; |
| + } |
| + git_buf_printf(&buf, "%s%s%s", scheme, url_colon, s->service_url); |
| + } else { |
| + /* If there is no colon it must be a relative URL, so no modification is |
| + * necessary. */ |
| + git_buf_printf(&buf, "%s%s", t->url, s->service_url); |
| + } |
| + |
| + if (git_buf_oom(&buf)) { |
| + goto on_error; |
| + } |
| + |
| + url_var = var_iface->VarFromUtf8(git_buf_cstr(&buf), git_buf_len(&buf)); |
| + if (!url_request_info_iface->SetProperty( |
| + s->url_request_info, PP_URLREQUESTPROPERTY_URL, url_var)) { |
| + giterr_set(GITERR_OS, "Failed to set url property"); |
| + goto on_error; |
| + } |
| + |
| + method_var = var_iface->VarFromUtf8(s->verb, strlen(s->verb)); |
| + if (!url_request_info_iface->SetProperty( |
| + s->url_request_info, PP_URLREQUESTPROPERTY_METHOD, method_var)) { |
| + giterr_set(GITERR_OS, "Failed to set method property"); |
| + goto on_error; |
| + } |
| + |
| + url_request_info_iface->SetProperty( |
| + s->url_request_info, PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, |
| + PP_MakeBool(PP_TRUE)); |
| + |
| + if (post_verb == s->verb) { |
| + git_buf_printf(&headers, "Content-Type: application/x-git-%s-request\n", |
| + s->service); |
| + git_buf_printf(&headers, "Accept: application/x-git-%s-result\n", |
| + s->service); |
| + |
| + if (git_buf_oom(&headers)) { |
| + goto on_error; |
| + } |
| + |
| + headers_var = |
| + var_iface->VarFromUtf8(git_buf_cstr(&headers), git_buf_len(&headers)); |
| + if (!url_request_info_iface->SetProperty( |
| + s->url_request_info, PP_URLREQUESTPROPERTY_HEADERS, headers_var)) { |
| + giterr_set(GITERR_OS, "Failed to set headers property"); |
| + goto on_error; |
| + } |
| + } |
| + |
| + error = 0; |
| + |
| +on_error: |
| + git_buf_free(&headers); |
| + git_buf_free(&buf); |
| + var_iface->Release(headers_var); |
| + var_iface->Release(method_var); |
| + var_iface->Release(url_var); |
| + return error; |
| +} |
| + |
| +static int on_header_ready(parser_context* ctx) { |
| + git_buf* name = &ctx->parse_header_name; |
| + git_buf* value = &ctx->parse_header_value; |
| + |
| + if (!strcasecmp("Content-Type", git_buf_cstr(name))) { |
| + if (!ctx->content_type) { |
| + ctx->content_type = git__strdup(git_buf_cstr(value)); |
| + GITERR_CHECK_ALLOC(ctx->content_type); |
| + } |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int on_header_field(http_parser* parser, const char* str, size_t len) { |
| + parser_context* ctx = (parser_context*)parser->data; |
| + |
| + /* Both parse_header_name and parse_header_value are populated |
| + * and ready for consumption */ |
| + if (VALUE == ctx->last_cb) |
| + if (on_header_ready(ctx) < 0) |
| + return ctx->parse_error = -1; |
| + |
| + if (NONE == ctx->last_cb || VALUE == ctx->last_cb) |
| + git_buf_clear(&ctx->parse_header_name); |
| + |
| + if (git_buf_put(&ctx->parse_header_name, str, len) < 0) |
| + return ctx->parse_error = -1; |
| + |
| + ctx->last_cb = FIELD; |
| + return 0; |
| +} |
| + |
| +static int on_header_value(http_parser* parser, const char* str, size_t len) { |
| + parser_context* ctx = (parser_context*)parser->data; |
| + |
| + assert(NONE != ctx->last_cb); |
| + |
| + if (FIELD == ctx->last_cb) |
| + git_buf_clear(&ctx->parse_header_value); |
| + |
| + if (git_buf_put(&ctx->parse_header_value, str, len) < 0) |
| + return ctx->parse_error = -1; |
| + |
| + ctx->last_cb = VALUE; |
| + return 0; |
| +} |
| + |
| +static int pepper_http_stream_read_send_request(pepper_http_stream* s) { |
| + int32_t result; |
| + int rtn = -1; |
| + PP_Resource response; |
| + struct PP_Var statuscode_var; |
| + struct PP_Var headers_var; |
| + const char* var_str; |
| + uint32_t var_len; |
| + parser_context ctx; |
| + http_parser parser; |
| + http_parser_settings settings; |
| + size_t bytes_parsed; |
| + git_buf headers_buf = GIT_BUF_INIT; |
| + const char* headers_str; |
| + size_t headers_len; |
| + git_buf buf = GIT_BUF_INIT; |
| + |
| + result = url_loader_iface->Open(s->url_loader, s->url_request_info, |
| + PP_BlockUntilComplete()); |
| + if (result != PP_OK) { |
| + giterr_set(GITERR_OS, "Failed to send request"); |
| + goto on_error0; |
| + } |
| + |
| + response = url_loader_iface->GetResponseInfo(s->url_loader); |
| + |
| + /* Check statuscode */ |
| + statuscode_var = url_response_info_iface->GetProperty( |
| + response, PP_URLRESPONSEPROPERTY_STATUSCODE); |
| + if (statuscode_var.type != PP_VARTYPE_INT32) { |
| + giterr_set(GITERR_NET, "Invalid statuscode var type: %d", |
| + statuscode_var.type); |
| + goto on_error1; |
| + } |
| + |
| + if (statuscode_var.value.as_int != 200) { |
| + giterr_set(GITERR_NET, "Unexpected HTTP status code: %d", |
| + statuscode_var.value.as_int); |
| + goto on_error1; |
| + } |
| + |
| + /* Check content-type header */ |
| + headers_var = url_response_info_iface->GetProperty( |
| + response, PP_URLRESPONSEPROPERTY_HEADERS); |
| + if (headers_var.type != PP_VARTYPE_STRING) { |
| + giterr_set(GITERR_NET, "Invalid headers var type: %d", headers_var.type); |
| + goto on_error2; |
| + } |
| + |
| + var_str = var_iface->VarToUtf8(headers_var, &var_len); |
| + if (var_str == NULL) { |
| + giterr_set(GITERR_NET, "Unable to convert headers str to utf8"); |
| + goto on_error2; |
| + } |
| + |
| + git_buf_printf(&headers_buf, "HTTP/1.0 %d\n", statuscode_var.value.as_int); |
| + git_buf_put(&headers_buf, var_str, var_len); |
| + |
| + headers_str = git_buf_cstr(&headers_buf); |
| + headers_len = git_buf_len(&headers_buf); |
| + |
| + http_parser_init(&parser, HTTP_RESPONSE); |
| + memset(&ctx, 0, sizeof(ctx)); |
| + git_buf_init(&ctx.parse_header_name, 0); |
| + git_buf_init(&ctx.parse_header_value, 0); |
| + ctx.last_cb = NONE; |
| + parser.data = &ctx; |
| + |
| + memset(&settings, 0, sizeof(settings)); |
| + settings.on_header_field = on_header_field; |
| + settings.on_header_value = on_header_value; |
| + bytes_parsed = |
| + http_parser_execute(&parser, &settings, headers_str, headers_len); |
| + |
| + if (bytes_parsed != headers_len) { |
| + giterr_set(GITERR_NET, "HTTP parser error: %s.\nHeaders=%.*s", |
| + http_errno_description((enum http_errno)parser.http_errno), |
| + headers_len, headers_str); |
| + goto on_error3; |
| + } |
| + |
| + if (!ctx.content_type) { |
| + giterr_set(GITERR_NET, "No Content-Type header in response"); |
| + goto on_error3; |
| + } |
| + |
| + /* The Content-Type header must match our expectation. */ |
| + if (get_verb == s->verb) { |
| + git_buf_printf(&buf, "application/x-git-%s-advertisement", s->service); |
| + } else { |
| + git_buf_printf(&buf, "application/x-git-%s-result", s->service); |
| + } |
| + |
| + if (git_buf_oom(&buf)) { |
| + goto on_error4; |
| + } |
| + |
| + if (strcmp(ctx.content_type, git_buf_cstr(&buf))) { |
| + giterr_set(GITERR_NET, "Invalid Content-Type: %s", ctx.content_type); |
| + goto on_error4; |
| + } |
| + |
| + s->sent_request = 1; |
| + rtn = 0; |
| + |
| +on_error4: |
| + git_buf_free(&buf); |
| +on_error3: |
| + git_buf_free(&ctx.parse_header_name); |
| + git_buf_free(&ctx.parse_header_value); |
| + git__free(ctx.content_type); |
| + git_buf_free(&headers_buf); |
| +on_error2: |
| + var_iface->Release(headers_var); |
| +on_error1: |
| + var_iface->Release(statuscode_var); |
| +on_error0: |
| + if (rtn) { |
| + ctx.parse_error = rtn; |
| + } |
| + |
| + return rtn; |
| +} |
| + |
| +static int pepper_http_stream_read(git_smart_subtransport_stream* stream, |
| + char* buffer, size_t buf_size, |
| + size_t* bytes_read) { |
| + int32_t result; |
| + pepper_http_stream* s = (pepper_http_stream*)stream; |
| + |
| + if (!s->url_request_info && pepper_http_stream_connect(s) < 0) |
| + return -1; |
| + |
| + if (!s->received_response) { |
| + if (!s->sent_request) { |
| + int rtn = pepper_http_stream_read_send_request(s); |
| + if (rtn) { |
| + return rtn; |
| + } |
| + } |
| + |
| + s->received_response = 1; |
| + } |
| + |
| + result = url_loader_iface->ReadResponseBody(s->url_loader, buffer, buf_size, |
| + PP_BlockUntilComplete()); |
| + if (result < 0) { |
| + giterr_set(GITERR_OS, "Failed to read data"); |
| + return -1; |
| + } |
| + |
| + *bytes_read = result; |
| + |
| + return 0; |
| +} |
| + |
| +static int pepper_http_stream_write_buffered( |
| + git_smart_subtransport_stream* stream, const char* buffer, size_t len) { |
| + pepper_http_stream* s = (pepper_http_stream*)stream; |
| + |
| + if (!s->url_request_info && pepper_http_stream_connect(s) < 0) |
| + return -1; |
| + |
| + if (url_request_info_iface->AppendDataToBody(s->url_request_info, buffer, |
| + len) != PP_TRUE) { |
| + giterr_set(GITERR_OS, "Failed to write buffered data"); |
| + return -1; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int pepper_http_stream_write_single( |
| + git_smart_subtransport_stream* stream, const char* buffer, size_t len) { |
| + int32_t result; |
| + pepper_http_stream *s = (pepper_http_stream *)stream; |
| + |
| + if (!s->url_request_info && pepper_http_stream_connect(s) < 0) |
| + return -1; |
| + |
| + if (s->sent_request) { |
| + giterr_set(GITERR_NET, "Subtransport configured for only one write"); |
| + return -1; |
| + } |
| + |
| + if (url_request_info_iface->AppendDataToBody(s->url_request_info, buffer, |
| + len) != PP_TRUE) { |
| + giterr_set(GITERR_OS, "Failed to write data"); |
| + return -1; |
| + } |
| + |
| + result = url_loader_iface->Open(s->url_loader, s->url_request_info, |
| + PP_BlockUntilComplete()); |
| + if (result != PP_OK) { |
| + giterr_set(GITERR_OS, "Failed to send request"); |
| + return -1; |
| + } |
| + |
| + s->sent_request = 1; |
| + |
| + return 0; |
| +} |
| + |
| +static void pepper_http_stream_free(git_smart_subtransport_stream* stream) { |
| + pepper_http_stream* s = (pepper_http_stream*)stream; |
| + |
| + if (s->url_request_info) { |
| + core_iface->ReleaseResource(s->url_request_info); |
| + s->url_request_info = 0; |
| + } |
| + |
| + if (s->url_loader) { |
| + core_iface->ReleaseResource(s->url_loader); |
| + s->url_loader = 0; |
| + } |
| + |
| + free(s); |
| +} |
| + |
| +static int pepper_http_stream_alloc(pepper_http_subtransport* t, |
| + git_smart_subtransport_stream** stream) { |
| + pepper_http_stream* s; |
| + |
| + if (!stream) return -1; |
| + |
| + s = calloc(sizeof(pepper_http_stream), 1); |
| + |
| + s->parent.subtransport = &t->parent; |
| + s->parent.read = pepper_http_stream_read; |
| + s->parent.write = pepper_http_stream_write_single; |
| + s->parent.free = pepper_http_stream_free; |
| + |
| + *stream = (git_smart_subtransport_stream*)s; |
| + return 0; |
| +} |
| + |
| +static int pepper_http_uploadpack_ls(pepper_http_subtransport* t, |
| + git_smart_subtransport_stream** stream) { |
| + pepper_http_stream* s; |
| + |
| + if (pepper_http_stream_alloc(t, stream) < 0) return -1; |
| + |
| + s = (pepper_http_stream*)*stream; |
| + |
| + s->service = upload_pack_service; |
| + s->service_url = upload_pack_ls_service_url; |
| + s->verb = get_verb; |
| + |
| + return 0; |
| +} |
| + |
| +static int pepper_http_uploadpack(pepper_http_subtransport* t, |
| + git_smart_subtransport_stream** stream) { |
| + pepper_http_stream* s; |
| + |
| + if (pepper_http_stream_alloc(t, stream) < 0) return -1; |
| + |
| + s = (pepper_http_stream*)*stream; |
| + |
| + s->service = upload_pack_service; |
| + s->service_url = upload_pack_service_url; |
| + s->verb = post_verb; |
| + |
| + return 0; |
| +} |
| + |
| +static int pepper_http_receivepack_ls(pepper_http_subtransport* t, |
| + git_smart_subtransport_stream** stream) { |
| + pepper_http_stream* s; |
| + |
| + if (pepper_http_stream_alloc(t, stream) < 0) return -1; |
| + |
| + s = (pepper_http_stream*)*stream; |
| + |
| + s->service = receive_pack_service; |
| + s->service_url = receive_pack_ls_service_url; |
| + s->verb = get_verb; |
| + |
| + return 0; |
| +} |
| + |
| +static int pepper_http_receivepack(pepper_http_subtransport* t, |
| + git_smart_subtransport_stream** stream) { |
| + pepper_http_stream* s; |
| + |
| + if (pepper_http_stream_alloc(t, stream) < 0) return -1; |
| + |
| + s = (pepper_http_stream*)*stream; |
| + |
| + s->parent.write = pepper_http_stream_write_buffered; |
| + |
| + s->service = receive_pack_service; |
| + s->service_url = receive_pack_service_url; |
| + s->verb = post_verb; |
| + |
| + return 0; |
| +} |
| + |
| +static int pepper_http_action(git_smart_subtransport_stream** stream, |
| + git_smart_subtransport* subtransport, |
| + const char* url, git_smart_service_t action) { |
| + pepper_http_subtransport* t = (pepper_http_subtransport*)subtransport; |
| + |
| + if (!stream) return -1; |
| + |
| + t->url = strdup(url); |
| + |
| + if (pepper_http_connect(t) < 0) return -1; |
| + |
| + switch (action) { |
| + case GIT_SERVICE_UPLOADPACK_LS: |
| + return pepper_http_uploadpack_ls(t, stream); |
| + |
| + case GIT_SERVICE_UPLOADPACK: |
| + return pepper_http_uploadpack(t, stream); |
| + |
| + case GIT_SERVICE_RECEIVEPACK_LS: |
| + return pepper_http_receivepack_ls(t, stream); |
| + |
| + case GIT_SERVICE_RECEIVEPACK: |
| + return pepper_http_receivepack(t, stream); |
| + } |
| + |
| + giterr_set(GITERR_NET, "Invalid action received in pepper_http_action: %d\n", |
| + action); |
| + *stream = NULL; |
| + return -1; |
| +} |
| + |
| +static int pepper_http_close(git_smart_subtransport* subtransport) { |
| + pepper_http_subtransport *t = (pepper_http_subtransport *) subtransport; |
| + if (t->url) { |
| + free(t->url); |
| + t->url = 0; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static void pepper_http_free(git_smart_subtransport* subtransport) { |
| + pepper_http_subtransport *t = (pepper_http_subtransport *) subtransport; |
| + pepper_http_close(subtransport); |
| + free(t); |
| +} |
| + |
| +int git_smart_subtransport_pepper_http(git_smart_subtransport** out, |
| + git_transport* owner) { |
| + pepper_http_subtransport* t; |
| + |
| + if (!out) |
| + return -1; |
| + |
| + if (!core_iface || !var_iface || !url_loader_iface || |
| + !url_request_info_iface || !url_response_info_iface) { |
| + giterr_set(GITERR_INVALID, |
| + "You must call " |
| + "git_smart_subtransport_pepper_http_init() before creating a " |
| + "pepper_http git subtransport."); |
| + return -1; |
| + } |
| + |
| + t = calloc(sizeof(pepper_http_subtransport), 1); |
| + |
| + t->owner = (void*)owner; |
| + t->parent.action = pepper_http_action; |
| + t->parent.close = pepper_http_close; |
| + t->parent.free = pepper_http_free; |
| + |
| + *out = (git_smart_subtransport*)t; |
| + return 0; |
| +} |
| + |
| +#define GET_INTERFACE(var, name) \ |
| + do { \ |
| + var = get_interface(name); \ |
| + if (!var) { \ |
| + giterr_set(GITERR_OS, "Unable to get interface: " name); \ |
| + return -1; \ |
| + } \ |
| + } while (0) |
| + |
| +int git_smart_subtransport_pepper_http_init(PP_Instance instance, |
| + PPB_GetInterface get_interface) { |
| + pp_instance = instance; |
| + GET_INTERFACE(core_iface, PPB_CORE_INTERFACE_1_0); |
| + GET_INTERFACE(var_iface, PPB_VAR_INTERFACE_1_1); |
| + GET_INTERFACE(url_loader_iface, PPB_URLLOADER_INTERFACE_1_0); |
| + GET_INTERFACE(url_request_info_iface, PPB_URLREQUESTINFO_INTERFACE_1_0); |
| + GET_INTERFACE(url_response_info_iface, PPB_URLRESPONSEINFO_INTERFACE_1_0); |
| + |
| + return 0; |
| +} |
| + |
| +#endif // native_client |
| + |
| diff --git a/tests/clar.c b/tests/clar.c |
| --- a/tests/clar.c |
| +++ b/tests/clar.c |
| @@ -222,7 +222,7 @@ static void |
| clar_run_suite(const struct clar_suite *suite, const char *filter) |
| { |
| const struct clar_func *test = suite->tests; |
| - size_t i, matchlen; |
| + size_t i, matchlen = 0; |
| |
| if (!suite->enabled) |
| return; |
| diff --git a/tests/clar/fs.h b/tests/clar/fs.h |
| --- a/tests/clar/fs.h |
| +++ b/tests/clar/fs.h |
| @@ -250,6 +250,249 @@ cl_fs_cleanup(void) |
| fs_rm(fixture_path(_clar_path, "*")); |
| } |
| |
| +#elif defined(__native_client__) |
| + |
| +#include <dirent.h> |
| +#include <errno.h> |
| +#include <limits.h> |
| +#include <sys/stat.h> |
| + |
| +static int fs_copy_helper(const char* source, const char* dest); |
| + |
| +static int fs_copy_dir(const char* source, const char* dest) { |
| + DIR* src_dir = opendir(source); |
| + struct dirent* dirent_buf = malloc(sizeof(struct dirent)); |
| + struct dirent* dir_entry = NULL; |
| + int result = 1; |
| + |
| + if (src_dir == NULL) { |
| + fprintf(stderr, "Error opening directory %s: %s\n", |
| + source, strerror(errno)); |
| + goto error; |
| + } |
| + |
| + // Create the destination directory. |
| + if (mkdir(dest, 0700) != 0) { |
| + if (errno != EEXIST) { |
| + fprintf(stderr, "Error creating directory %s: %s\n", |
| + dest, strerror(errno)); |
| + goto error; |
| + } |
| + } |
| + |
| + while (1) { |
| + char src_entry_path[PATH_MAX]; |
| + char dst_entry_path[PATH_MAX]; |
| + const char* entry_name; |
| + |
| + if (readdir_r(src_dir, dirent_buf, &dir_entry) != 0) { |
| + fprintf(stderr, "Error reading directory %s: %s\n", |
| + source, strerror(errno)); |
| + goto error; |
| + } |
| + |
| + if (dir_entry == NULL) { |
| + break; |
| + } |
| + |
| + entry_name = dir_entry->d_name; |
| + |
| + if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) |
| + continue; |
| + |
| + snprintf(&src_entry_path[0], PATH_MAX, "%s/%s", source, entry_name); |
| + snprintf(&dst_entry_path[0], PATH_MAX, "%s/%s", dest, entry_name); |
| + |
| + if (!fs_copy_helper(src_entry_path, dst_entry_path)) { |
| + goto error; |
| + } |
| + } |
| + |
| + goto cleanup; |
| + |
| +error: |
| + result = 0; |
| + |
| +cleanup: |
| + closedir(src_dir); |
| + return result; |
| +} |
| + |
| +static int fs_copy_file(const char* source, const char* dest) { |
| + const size_t buffer_size = 8192; |
| + char buffer[buffer_size]; |
| + int result = 1; |
| + FILE* dst_file = NULL; |
| + |
| + FILE* src_file = fopen(source, "r"); |
| + if (src_file == NULL) { |
| + fprintf(stderr, "Error opening file %s for reading: %s\n", |
| + source, strerror(errno)); |
| + goto error; |
| + } |
| + |
| + dst_file = fopen(dest, "w"); |
| + if (dst_file == NULL) { |
| + fprintf(stderr, "Error opening file %s for writing: %s\n", |
| + dest, strerror(errno)); |
| + goto error; |
| + } |
| + |
| + while (!feof(src_file)) { |
| + ssize_t bytes_read = fread(&buffer[0], 1, buffer_size, src_file); |
| + ssize_t bytes_written; |
| + if (bytes_read < 0) { |
| + fprintf(stderr, "Unable to read from %s: %s\n", source, strerror(errno)); |
| + goto error; |
| + } |
| + |
| + bytes_written = fwrite(&buffer[0], 1, bytes_read, dst_file); |
| + if (bytes_written != bytes_read) { |
| + fprintf(stderr, "Unable to write %d bytes of %s to %s: %s\n", |
| + bytes_read, source, dest, strerror(errno)); |
| + goto error; |
| + } |
| + } |
| + |
| + goto cleanup; |
| + |
| +error: |
| + result = 0; |
| + |
| +cleanup: |
| + if (src_file) |
| + fclose(src_file); |
| + |
| + if (dst_file) |
| + fclose(dst_file); |
| + |
| + return result; |
| +} |
| + |
| +static int fs_copy_helper(const char *source, const char *dest) { |
| + struct stat statbuf; |
| + if (stat(source, &statbuf) != 0) { |
| + fprintf(stderr, "Error stat'ing file %s: %s\n", source, strerror(errno)); |
| + return 0; |
| + } |
| + |
| + if (S_ISDIR(statbuf.st_mode)) { |
| + if (!fs_copy_dir(source, dest)) |
| + return 0; |
| + } else { |
| + if (!fs_copy_file(source, dest)) |
| + return 0; |
| + } |
| + |
| + return 1; |
| +} |
| + |
| + |
| +static void fs_copy(const char *source, const char *dest) { |
| + char real_dest[PATH_MAX]; |
| + char* last_slash = NULL; |
| + |
| + // Copy the base directory name of source to dest. |
| + // e.g. fs_copy("foo/bar/baz", "/tmp/blah") creates "/tmp/blah/baz". |
| + |
| + last_slash = strrchr(source, '/'); |
| + if (last_slash != NULL) { |
| + snprintf(real_dest, PATH_MAX, "%s/%s", dest, last_slash + 1); |
| + } else { |
| + strncpy(real_dest, dest, PATH_MAX); |
| + } |
| + |
| + cl_must_pass_(fs_copy_helper(source, real_dest), |
| + "Failed to copy test fixtures to sandbox"); |
| +} |
| + |
| +static int fs_rm_helper(const char* source); |
| + |
| +static int fs_rm_dir(const char* source) { |
| + DIR* src_dir = opendir(source); |
| + struct dirent* dir_entry = NULL; |
| + int result = 1; |
| + |
| + if (src_dir == NULL) { |
| + fprintf(stderr, "Error opening directory %s: %s\n", |
| + source, strerror(errno)); |
| + goto error; |
| + } |
| + |
| + for (dir_entry = readdir(src_dir); dir_entry; dir_entry = readdir(src_dir)) { |
| + char src_entry_path[PATH_MAX]; |
| + const char* entry_name = dir_entry->d_name; |
| + |
| + if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) |
| + continue; |
| + |
| + snprintf(&src_entry_path[0], PATH_MAX, "%s/%s", source, entry_name); |
| + |
| + if (!fs_rm_helper(src_entry_path)) { |
| + goto error; |
| + } |
| + } |
| + |
| + // Finally, remove source. |
| + if (rmdir(source) != 0) { |
| + fprintf(stderr, "Error removing directory %s: %s\n", |
| + source, strerror(errno)); |
| + goto error; |
| + } |
| + |
| + goto cleanup; |
| + |
| +error: |
| + result = 0; |
| + |
| +cleanup: |
| + closedir(src_dir); |
| + return result; |
| +} |
| + |
| +static int fs_rm_file(const char* source) { |
| + if (unlink(source) != 0) { |
| + fprintf(stderr, "Error removing file %s: %s\n", source, strerror(errno)); |
| + return 0; |
| + } |
| + |
| + return 1; |
| +} |
| + |
| +static int fs_rm_helper(const char* source) { |
| + struct stat statbuf; |
| + if (lstat(source, &statbuf) != 0) { |
| + fprintf(stderr, "Error stat'ing file %s: %s\n", source, strerror(errno)); |
| + return 0; |
| + } |
| + |
| + if (S_ISDIR(statbuf.st_mode)) { |
| + if (!fs_rm_dir(source)) |
| + return 0; |
| + } else { |
| + if (!fs_rm_file(source)) |
| + return 0; |
| + } |
| + |
| + return 1; |
| +} |
| + |
| +static void |
| +fs_rm(const char *source) |
| +{ |
| + cl_must_pass_( |
| + fs_rm_helper(source), |
| + "Failed to cleanup the sandbox" |
| + ); |
| +} |
| + |
| +void |
| +cl_fs_cleanup(void) |
| +{ |
| + clar_unsandbox(); |
| + clar_sandbox(); |
| +} |
| + |
| #else |
| |
| #include <errno.h> |
| diff --git a/tests/clar/sandbox.h b/tests/clar/sandbox.h |
| --- a/tests/clar/sandbox.h |
| +++ b/tests/clar/sandbox.h |
| @@ -110,8 +110,21 @@ static int build_sandbox_path(void) |
| return -1; |
| #else |
| if (mkdtemp(_clar_path) == NULL) |
| +#if defined(__native_client__) |
| + { |
| + // Under sel_ldr mkdtemp currently always fails. For now |
| + // fake it. |
| + struct stat buf; |
| + strcpy(_clar_path + strlen(_clar_path) - 6, "123456"); |
| + if (stat(_clar_path, &buf) == 0) |
| + fs_rm(_clar_path); |
| + if (mkdir(_clar_path, 0700) != 0) |
| + return -1; |
| + } |
| +#else |
| return -1; |
| #endif |
| +#endif |
| |
| return 0; |
| } |
| diff --git a/tests/commit/parse.c b/tests/commit/parse.c |
| --- a/tests/commit/parse.c |
| +++ b/tests/commit/parse.c |
| @@ -119,8 +119,8 @@ passing_signature_test_case passing_signature_cases[] = { |
| {"author Vicent Marti <tanoku@gmail.com> 1234567890 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 1234567890, 0}, |
| {"author Vicent Marti <tanoku@gmail.com> 2147483647 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0x7fffffff, 0}, |
| {"author Vicent Marti <tanoku@gmail.com> 4294967295 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0xffffffff, 0}, |
| - {"author Vicent Marti <tanoku@gmail.com> 4294967296 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 4294967296, 0}, |
| - {"author Vicent Marti <tanoku@gmail.com> 8589934592 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 8589934592, 0}, |
| + {"author Vicent Marti <tanoku@gmail.com> 4294967296 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 4294967296ll, 0}, |
| + {"author Vicent Marti <tanoku@gmail.com> 8589934592 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 8589934592ll, 0}, |
| |
| {NULL,NULL,NULL,NULL,0,0} |
| }; |
| diff --git a/tests/config/read.c b/tests/config/read.c |
| --- a/tests/config/read.c |
| +++ b/tests/config/read.c |
| @@ -477,7 +477,7 @@ void test_config_read__simple_read_from_specific_level(void) |
| { |
| git_config *cfg, *cfg_specific; |
| int i; |
| - int64_t l, expected = +9223372036854775803; |
| + int64_t l, expected = +9223372036854775803ll; |
| const char *s; |
| |
| cl_git_pass(git_config_new(&cfg)); |
| diff --git a/tests/config/write.c b/tests/config/write.c |
| --- a/tests/config/write.c |
| +++ b/tests/config/write.c |
| @@ -18,7 +18,7 @@ void test_config_write__replace_value(void) |
| { |
| git_config *cfg; |
| int i; |
| - int64_t l, expected = +9223372036854775803; |
| + int64_t l, expected = +9223372036854775803ll; |
| |
| /* By freeing the config, we make sure we flush the values */ |
| cl_git_pass(git_config_open_ondisk(&cfg, "config9")); |
| @@ -179,7 +179,7 @@ void test_config_write__add_value_at_specific_level(void) |
| { |
| git_config *cfg, *cfg_specific; |
| int i; |
| - int64_t l, expected = +9223372036854775803; |
| + int64_t l, expected = +9223372036854775803ll; |
| const char *s; |
| |
| // open config15 as global level config file |