Commit 79ca2edcd4e7a801400fa33c2c705b8f03a5d7f5

Ramsay Jones 2009-03-20T19:51:48

win32: Add routines to abstract memory-mapped file functions In particular, the git__mmap() and git__munmap() routines provide the interface to platform specific memory-mapped file facilities. We provide implementations for unix and win32, which can be found in their own sub-directories. Signed-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

diff --git a/Makefile b/Makefile
index ca88eb6..6d681ba 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@ all::
 DOXYGEN = doxygen
 INSTALL = install
 RANLIB  = ranlib
+AR      = ar cr
 
 prefix=/usr/local
 libdir=$(prefix)/lib
@@ -21,6 +22,8 @@ CFLAGS = -g -O2 -Wall
 LIBS   = -L. -lgit2 -lz
 OS     = unix
 
+CRYPTO_LIB = -lcrypto
+
 EXTRA_LIBS =
 EXTRA_SRC =
 EXTRA_OBJ =
@@ -32,6 +35,8 @@ ifneq (,$(findstring CYGWIN,$(uname_S)))
 endif
 
 SRC_C = $(wildcard src/*.c)
+OS_SRC = $(wildcard src/$(OS)/*.c)
+SRC_C += $(OS_SRC)
 OBJS = $(patsubst %.c,%.o,$(SRC_C))
 HDRS = $(wildcard src/*.h)
 PUBLIC_HEADERS = $(wildcard src/git/*.h)
@@ -46,7 +51,7 @@ TEST_RUN = $(patsubst %.exe,%.run,$(TEST_EXE))
 
 ifndef NO_OPENSSL
 	SHA1_HEADER = <openssl/sha.h>
-	EXTRA_LIBS += -lcrypto
+	EXTRA_LIBS += $(CRYPTO_LIB)
 else
 	SHA1_HEADER = "sha1/sha1.h"
 	EXTRA_SRC += src/sha1/sha1.c
@@ -68,8 +73,7 @@ all:: $(GIT_LIB)
 clean:
 	rm -f $(GIT_LIB)
 	rm -f libgit2.pc
-	rm -f src/*.o
-	rm -f src/sha1/*.o
+	rm -f src/*.o src/sha1/*.o src/unix/*.o
 	rm -f tests/*.o tests/*.exe tests/*.toc
 	rm -rf trash-*.exe
 	rm -rf apidocs
@@ -111,7 +115,7 @@ uninstall:
 $(OBJS): $(HDRS)
 $(GIT_LIB): $(OBJS)
 	rm -f $(GIT_LIB)
-	$(AR) cr $(GIT_LIB) $(OBJS)
+	$(AR) $(GIT_LIB) $(OBJS)
 	$(RANLIB) $(GIT_LIB)
 
 T_HDR         = tests/test_lib.h tests/test_helpers.h
diff --git a/src/common.h b/src/common.h
index 14dc301..221c73c 100644
--- a/src/common.h
+++ b/src/common.h
@@ -23,14 +23,15 @@
 #ifdef GIT_WIN32
 
 # include <io.h>
-# include <winsock2.h>
+# include <windows.h>
+
+#define snprintf _snprintf
 
 typedef int ssize_t;
 
 #else
 
 # include <unistd.h>
-# include <sys/mman.h>
 # include <arpa/inet.h>
 
 #endif
diff --git a/src/fileops.c b/src/fileops.c
index e2ec615..09e1e12 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -106,18 +106,16 @@ void gitfo_free_buf(gitfo_buf *obj)
 	obj->data = NULL;
 }
 
-int gitfo_map_ro(gitfo_map *out, git_file fd, off_t begin, size_t len)
+int gitfo_map_ro(git_map *out, git_file fd, off_t begin, size_t len)
 {
-	out->data = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, begin);
-	if (out->data == (void*)-1)
+	if (git__mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin) < 0)
 		return git_os_error();
-	out->len = len;
 	return GIT_SUCCESS;
 }
 
-void gitfo_free_map(gitfo_map *out)
+void gitfo_free_map(git_map *out)
 {
-	munmap(out->data, out->len);
+	git__munmap(out);
 }
 
 /* cached diskio */
diff --git a/src/fileops.h b/src/fileops.h
index 3cc1e16..39e0181 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -10,6 +10,7 @@
 #define _FILE_OFFSET_BITS 64
 
 #include "common.h"
+#include "map.h"
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <time.h>
@@ -29,11 +30,6 @@ typedef struct {  /* file io buffer  */
 	size_t len;  /* data length  */
 } gitfo_buf;
 
-typedef struct {  /* memory mapped buffer */
-	void *data;   /* data bytes */
-	size_t len;   /* data length */
-} gitfo_map;
-
 extern int gitfo_exists(const char *path);
 extern int gitfo_open(const char *path, int flags);
 extern int gitfo_creat(const char *path, int mode);
@@ -66,7 +62,7 @@ extern void gitfo_free_buf(gitfo_buf *obj);
  * - GIT_EOSERR on an unspecified OS related error.
  */
 extern int gitfo_map_ro(
-	gitfo_map *out,
+	git_map *out,
 	git_file fd,
 	off_t begin,
 	size_t len);
@@ -75,7 +71,7 @@ extern int gitfo_map_ro(
  * Release the memory associated with a previous memory mapping.
  * @param map the mapping description previously configured.
  */
-extern void gitfo_free_map(gitfo_map *map);
+extern void gitfo_free_map(git_map *map);
 
 /**
  * Walk each directory entry, except '.' and '..', calling fn(state).
diff --git a/src/map.h b/src/map.h
new file mode 100644
index 0000000..3188ffd
--- /dev/null
+++ b/src/map.h
@@ -0,0 +1,31 @@
+#ifndef INCLUDE_map_h__
+#define INCLUDE_map_h__
+
+#include "common.h"
+
+
+/* git__mmap() prot values */
+#define GIT_PROT_NONE  0x0
+#define GIT_PROT_READ  0x1
+#define GIT_PROT_WRITE 0x2
+#define GIT_PROT_EXEC  0x4
+
+/* git__mmmap() flags values */
+#define GIT_MAP_FILE    0
+#define GIT_MAP_SHARED  1
+#define GIT_MAP_PRIVATE 2
+#define GIT_MAP_TYPE    0xf
+#define GIT_MAP_FIXED   0x10
+
+typedef struct {  /* memory mapped buffer   */
+	void *data;  /* data bytes          */
+	size_t len;  /* data length         */
+#ifdef GIT_WIN32
+	HANDLE fmh;  /* file mapping handle */
+#endif
+} git_map;
+
+extern int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, off_t offset);
+extern int git__munmap(git_map *map);
+
+#endif /* INCLUDE_map_h__ */
diff --git a/src/odb.c b/src/odb.c
index 11fd982..bd65f47 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -43,7 +43,7 @@ struct git_pack {
 
 	/** The .idx file, mapped into memory. */
 	git_file idx_fd;
-	gitfo_map idx_map;
+	git_map idx_map;
 	uint32_t *im_fanout;
 	unsigned char *im_oid;
 	uint32_t *im_crc;
diff --git a/src/unix/map.c b/src/unix/map.c
new file mode 100644
index 0000000..a41bae0
--- /dev/null
+++ b/src/unix/map.c
@@ -0,0 +1,61 @@
+
+#include "map.h"
+#include <sys/mman.h>
+#include <errno.h>
+
+
+int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, off_t offset)
+{
+	int mprot = 0;
+	int mflag = 0;
+
+	assert((out != NULL) && (len > 0));
+
+	if ((out == NULL) || (len == 0)) {
+		errno = EINVAL;
+		return GIT_ERROR;
+	}
+
+	out->data = NULL;
+	out->len = 0;
+
+	if (prot & GIT_PROT_WRITE)
+		mprot = PROT_WRITE;
+	else if (prot & GIT_PROT_READ)
+		mprot = PROT_READ;
+	else {
+		errno = EINVAL;
+		return GIT_ERROR;
+	}
+
+	if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)
+		mflag = MAP_SHARED;
+	else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE)
+		mflag = MAP_PRIVATE;
+
+	if (flags & GIT_MAP_FIXED) {
+		errno = EINVAL;
+		return GIT_ERROR;
+	}
+
+	out->data = mmap(NULL, len, mprot, mflag, fd, offset);
+	if (!out->data || out->data == MAP_FAILED)
+		return git_os_error();
+	out->len = len;
+
+	return GIT_SUCCESS;
+}
+
+int git__munmap(git_map *map)
+{
+	assert(map != NULL);
+
+	if (!map)
+		return GIT_ERROR;
+
+	munmap(map->data, map->len);
+
+	return GIT_SUCCESS;
+}
+
+
diff --git a/src/win32/map.c b/src/win32/map.c
new file mode 100644
index 0000000..7d685c7
--- /dev/null
+++ b/src/win32/map.c
@@ -0,0 +1,125 @@
+
+#include "map.h"
+#include <errno.h>
+
+
+static DWORD get_page_size(void)
+{
+	static DWORD page_size;
+	SYSTEM_INFO sys;
+
+	if (!page_size) {
+		GetSystemInfo(&sys);
+		page_size = sys.dwAllocationGranularity;
+	}
+
+	return page_size;
+}
+
+int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, off_t offset)
+{
+	HANDLE fh = (HANDLE)_get_osfhandle(fd);
+	DWORD page_size = get_page_size();
+	DWORD fmap_prot = 0;
+	DWORD view_prot = 0;
+	DWORD off_low = 0;
+	DWORD off_hi  = 0;
+	off_t page_start;
+	off_t page_offset;
+
+	assert((out != NULL) && (len > 0));
+
+	if ((out == NULL) || (len == 0)) {
+		errno = EINVAL;
+		return GIT_ERROR;
+	}
+
+	out->data = NULL;
+	out->len = 0;
+	out->fmh = NULL;
+
+	if (fh == INVALID_HANDLE_VALUE) {
+		errno = EBADF;
+		return GIT_ERROR;
+	}
+
+	if (prot & GIT_PROT_WRITE)
+		fmap_prot |= PAGE_READWRITE;
+	else if (prot & GIT_PROT_READ)
+		fmap_prot |= PAGE_READONLY;
+	else {
+		errno = EINVAL;
+		return GIT_ERROR;
+	}
+
+	if (prot & GIT_PROT_WRITE)
+		view_prot |= FILE_MAP_WRITE;
+	if (prot & GIT_PROT_READ)
+		view_prot |= FILE_MAP_READ;
+
+	if (flags & GIT_MAP_FIXED) {
+		errno = EINVAL;
+		return GIT_ERROR;
+	}
+
+	page_start = (offset / page_size) * page_size;
+	page_offset = offset - page_start;
+
+	if (page_offset != 0) {  /* offset must be multiple of page size */
+		errno = EINVAL;
+		return GIT_ERROR;
+	}
+
+	out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL);
+	if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) {
+		/* errno = ? */
+		out->fmh = NULL;
+		return GIT_ERROR;
+	}
+
+	off_low = (DWORD)(page_start);
+	if (sizeof(off_t) > 4)
+		off_hi = (DWORD)(page_start >> 32);
+	out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len);
+	if (!out->data) {
+		/* errno = ? */
+		CloseHandle(out->fmh);
+		out->fmh = NULL;
+		return GIT_ERROR;
+	}
+	out->len = len;
+
+	return GIT_SUCCESS;
+}
+
+int git__munmap(git_map *map)
+{
+	assert(map != NULL);
+
+	if (!map)
+		return GIT_ERROR;
+
+	if (map->data) {
+		if (!UnmapViewOfFile(map->data)) {
+			/* errno = ? */
+			CloseHandle(map->fmh);
+			map->data = NULL;
+			map->fmh = NULL;
+			return GIT_ERROR;
+		}
+		map->data = NULL;
+	}
+
+	if (map->fmh) {
+		if (!CloseHandle(map->fmh)) {
+			/* errno = ? */
+			map->fmh = NULL;
+			return GIT_ERROR;
+		}
+		map->fmh = NULL;
+	}
+
+	return GIT_SUCCESS;
+}
+
+