blob: fe80b482b6f55e993347a8e0894f471d454be31f [file] [log] [blame] [edit]
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