[sheepdog] [PATCH stable-0.8 2/9] sheepfs: implement the http interface of sheepfs by using libcurl

Hitoshi Mitake mitake.hitoshi at lab.ntt.co.jp
Thu Mar 6 08:12:37 CET 2014


From: Robin Dong <sanbai at taobao.com>

Use libcurl to call the interface of http for object-storage and implement
the read/write operation.

Signed-off-by: Robin Dong <sanbai at taobao.com>
[ replace sd_err by sheepfs_pr ]
Signed-off-by: Liu Yuan <namei.unix at gmail.com>
---
 sheepfs/Makefile.am |    2 +-
 sheepfs/core.c      |    1 +
 sheepfs/http.c      |  269 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 sheepfs/sheepfs.h   |    5 +
 4 files changed, 275 insertions(+), 2 deletions(-)

diff --git a/sheepfs/Makefile.am b/sheepfs/Makefile.am
index 64f84f2..a5e84ba 100644
--- a/sheepfs/Makefile.am
+++ b/sheepfs/Makefile.am
@@ -26,7 +26,7 @@ sbin_PROGRAMS		= sheepfs
 sheepfs_SOURCES		= core.c cluster.c vdi.c shadow_file.c volume.c node.c \
 			  config.c http.c
 
-sheepfs_LDADD	  	= ../lib/libsheepdog.a $(fuse_LIBS) $(LIBS) -lpthread
+sheepfs_LDADD	  	= ../lib/libsheepdog.a $(fuse_LIBS) $(LIBS) -lpthread -lcurl
 sheepfs_DEPENDENCIES	= ../lib/libsheepdog.a
 
 noinst_HEADERS		= sheepfs.h
diff --git a/sheepfs/core.c b/sheepfs/core.c
index 2f900bc..f80270b 100644
--- a/sheepfs/core.c
+++ b/sheepfs/core.c
@@ -79,6 +79,7 @@ static struct sheepfs_file_operation {
 	[OP_HTTP_ADDRESS]   = { http_address_read, http_address_write,
 				http_address_get_size },
 	[OP_HTTP_OBJECT]    = { NULL, http_object_write },
+	[OP_OBJECT]         = { object_read, NULL, object_get_size },
 };
 
 __printf(3, 4)
diff --git a/sheepfs/http.c b/sheepfs/http.c
index e220e9c..9884cd0 100644
--- a/sheepfs/http.c
+++ b/sheepfs/http.c
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
+#include <curl/curl.h>
 
 #include "strbuf.h"
 #include "sheepfs.h"
@@ -27,6 +28,9 @@
 #define PATH_HTTP_ADDRESS	"/http/address"
 #define PATH_HTTP_OBJECT	"/http/object"
 
+#define HTTP_SIZE_NAME		"user.object.size"
+#define HTTP_SIZE_SIZE		sizeof(uint64_t)
+
 int create_http_layout(void)
 {
 	if (shadow_dir_create(PATH_HTTP) < 0)
@@ -64,8 +68,271 @@ size_t http_address_get_size(const char *path)
 	return 0;
 }
 
+static uint64_t curl_get_object_size(const char *url)
+{
+	CURL *curl;
+	CURLcode res;
+	double content_length;
+
+	curl = curl_easy_init();
+	if (!curl) {
+		sheepfs_pr("Failed to init curl");
+		goto out;
+	}
+
+	curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
+	curl_easy_setopt(curl, CURLOPT_NOBODY, true);
+	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "HEAD");
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+	res = curl_easy_perform(curl);
+	if (res == CURLE_OK) {
+		res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD,
+					&content_length);
+		if (res != CURLE_OK) {
+			sheepfs_pr("Failed to get size of object %s",
+			       curl_easy_strerror(res));
+			content_length = 0;
+		}
+	}
+out:
+	curl_easy_cleanup(curl);
+	return (uint64_t)content_length;
+}
+
+struct buffer_s {
+	char *mem;
+	size_t current_size;
+	size_t total_size;
+};
+
+static size_t write_cb(void *contents, size_t size, size_t nmemb, void *userp)
+{
+	size_t real_size = size * nmemb;
+	struct buffer_s *buff = (struct buffer_s *)userp;
+
+	if ((buff->current_size + real_size) > buff->total_size)
+		real_size = buff->total_size - buff->current_size;
+	memcpy(buff->mem + buff->current_size, contents, real_size);
+	buff->current_size += real_size;
+	return real_size;
+}
+
+static size_t curl_read_object(const char *url, char *buf, size_t size,
+			       off_t offset)
+{
+	CURL *curl;
+	CURLcode res;
+	char header[PATH_MAX];
+	double content_length;
+	struct buffer_s buffer = { 0 };
+	struct curl_slist *headers = NULL;
+
+	curl = curl_easy_init();
+	if (!curl) {
+		sheepfs_pr("Failed to init curl");
+		goto out;
+	}
+
+	snprintf(header, sizeof(header), "Range: bytes=%"PRIu64"-%"PRIu64,
+		 offset, offset + size - 1);
+	headers = curl_slist_append(headers, header);
+	curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
+	buffer.mem = buf;
+	buffer.total_size = size;
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
+	res = curl_easy_perform(curl);
+	if (res == CURLE_OK) {
+		res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD,
+					&content_length);
+		if (res != CURLE_OK) {
+			sheepfs_pr("Failed to getinfo res: %s",
+			       curl_easy_strerror(res));
+			size = 0;
+			goto out;
+		}
+		if ((size_t)content_length > size) {
+			sheepfs_pr("Failed to get correct CONTENT_LENGTH, "
+			       "content_length: %"PRIu64", get_size: %"PRIu64,
+			       (size_t)content_length, size);
+			size = 0;
+		} else
+			sd_debug("Read out %"PRIu64" data from %s", size, url);
+	} else {
+		sheepfs_pr("Failed to call libcurl res: %s, url: %s",
+		       curl_easy_strerror(res), url);
+		size = 0;
+	}
+out:
+	curl_slist_free_all(headers);
+	curl_easy_cleanup(curl);
+	return size;
+}
+
+static bool curl_object_exists(const char *url)
+{
+	CURL *curl;
+	CURLcode res;
+	bool ret = false;
+
+	curl = curl_easy_init();
+	if (!curl) {
+		sheepfs_pr("Failed to init curl");
+		goto out;
+	}
+
+	curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
+	curl_easy_setopt(curl, CURLOPT_NOBODY, true);
+	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "HEAD");
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+	res = curl_easy_perform(curl);
+	if (res == CURLE_OK)
+		ret = true;
+	else
+		sheepfs_pr("Failed to call libcurl res: %s, url: %s",
+		       curl_easy_strerror(res), url);
+
+out:
+	curl_easy_cleanup(curl);
+	return ret;
+}
+
+struct object_cache {
+	uint64_t offset;
+	uint64_t length;
+	unsigned char *buffer;
+};
+
+static int generate_url(const char *buff, int size, char *url, int url_len)
+{
+	char address[PATH_MAX], *ch;
+	int len, ret = 0;
+
+	len = shadow_file_read(PATH_HTTP_ADDRESS, address, sizeof(address), 0);
+	if (len <= 0) {
+		sheepfs_pr("Can't get address of http");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* remove last '\n' of address */
+	ch = strchr(address, '\n');
+	if (ch != NULL)
+		*ch = '\0';
+
+	snprintf(url, url_len, "http://%s/v1%.*s", address, size, buff);
+out:
+	return ret;
+}
+
+int object_read(const char *path, char *buf, size_t size, off_t offset)
+{
+	char url[PATH_MAX];
+	char *pos;
+	int ret;
+
+	pos = strstr(path, PATH_HTTP);
+	if (!pos) {
+		sheepfs_pr("Invalid Path %s", path);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	pos += strlen(PATH_HTTP);
+	/* don't need '\n' at the end of 'path' */
+	ret = generate_url(pos, strlen(path) - strlen(PATH_HTTP),
+			   url, PATH_MAX);
+	if (ret)
+		goto out;
+
+	ret = curl_read_object(url, buf, size, offset);
+out:
+	return ret;
+}
+
+size_t object_get_size(const char *path)
+{
+	uint64_t object_size;
+	if (shadow_file_getxattr(path, HTTP_SIZE_NAME, &object_size,
+				 HTTP_SIZE_SIZE) < 0)
+		return 0;
+
+	return object_size;
+}
+
+static int object_create_entry(const char *entry, const char *url)
+{
+	struct strbuf buf = STRBUF_INIT;
+	char *args[3], path[PATH_MAX];
+	int nr, ret = -EINVAL;
+	uint64_t size;
+
+	nr = split_path(entry, ARRAY_SIZE(args), args);
+	if (nr != ARRAY_SIZE(args)) {
+		sheepfs_pr("Invalid argument %d, %s", nr, entry);
+		goto out;
+	}
+
+	strbuf_addf(&buf, "%s", PATH_HTTP);
+	for (int i = 0; i < nr; i++) {
+		strbuf_addf(&buf, "/%s", args[i]);
+		snprintf(path, sizeof(path), "%.*s", (int)buf.len, buf.buf);
+		if (i == (nr - 1)) {
+			if (shadow_file_create(path) < 0) {
+				sheepfs_pr("Create file %s fail", path);
+				goto out;
+			}
+			size = curl_get_object_size(url);
+			if (size <= 0) {
+				sheepfs_pr("Failed to get size of object");
+				shadow_file_delete(path);
+				goto out;
+			}
+			if (shadow_file_setxattr(path, HTTP_SIZE_NAME, &size,
+						 HTTP_SIZE_SIZE) < 0) {
+				sheepfs_pr("Failed to setxattr for %s",
+				       HTTP_SIZE_NAME);
+				shadow_file_delete(path);
+				goto out;
+			}
+			if (sheepfs_set_op(path, OP_OBJECT) < 0) {
+				sheepfs_pr("Set_op %s fail", path);
+				shadow_file_delete(path);
+				goto out;
+			}
+		} else {
+			if (shadow_dir_create(path) < 0) {
+				sheepfs_pr("Create dir %s fail", path);
+				goto out;
+			}
+		}
+	}
+	ret = 0;
+out:
+	strbuf_release(&buf);
+	return ret;
+}
+
 int http_object_write(const char *path, const char *buf, size_t size,
 		      off_t ignore)
 {
-	return size;
+	char entry[PATH_MAX], url[PATH_MAX];
+	int ret = -EINVAL;
+
+	/* don't need '\n' at the end of 'buf' */
+	ret = generate_url(buf, size - 1, url, PATH_MAX);
+	if (ret)
+		goto out;
+
+	if (curl_object_exists(url)) {
+		snprintf(entry, size, "%s", buf);
+		ret = object_create_entry(entry, url);
+		if (ret >= 0)
+			ret = size;
+	}
+out:
+	return ret;
 }
diff --git a/sheepfs/sheepfs.h b/sheepfs/sheepfs.h
index 64e560a..b426f66 100644
--- a/sheepfs/sheepfs.h
+++ b/sheepfs/sheepfs.h
@@ -17,6 +17,7 @@ enum sheepfs_opcode {
 	OP_VOLUME,
 	OP_HTTP_ADDRESS,
 	OP_HTTP_OBJECT,
+	OP_OBJECT,
 };
 
 #define COMMAND_LEN  512
@@ -107,4 +108,8 @@ int http_address_write(const char *path, const char *buf, size_t size,
 size_t http_address_get_size(const char *path);
 int http_object_write(const char *path, const char *buf, size_t size,
 		      off_t ignore);
+
+int object_read(const char *path, char *buf, size_t size, off_t ignore);
+size_t object_get_size(const char *path);
+int object_open(const char *path, struct fuse_file_info *);
 #endif
-- 
1.7.10.4




More information about the sheepdog mailing list