From 3aaf6848f2025c3688b3bf240f8bbf18fcb125f3 Mon Sep 17 00:00:00 2001 From: Andrew McNab Date: Sun, 28 May 2006 15:39:32 +0000 Subject: [PATCH] Add slashgrid client --- org.gridsite.core/src/Makefile | 8 + org.gridsite.core/src/slashgrid.c | 1673 ++++++++++++++++++++++++++++++++++ org.gridsite.core/src/slashgrid.init | 66 ++ 3 files changed, 1747 insertions(+) create mode 100644 org.gridsite.core/src/slashgrid.c create mode 100755 org.gridsite.core/src/slashgrid.init diff --git a/org.gridsite.core/src/Makefile b/org.gridsite.core/src/Makefile index 3af684b..b01edab 100644 --- a/org.gridsite.core/src/Makefile +++ b/org.gridsite.core/src/Makefile @@ -201,6 +201,12 @@ showx509exts: showx509exts.c libgridsite.a -lgridsite \ -lssl -lcrypto -lxml2 -lz -lm +slashgrid: slashgrid.c + gcc -g -o slashgrid -L/usr/local/lib -lfuse -lpthread slashgrid.c \ + -D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=22 \ + -I/usr/kerberos/include `curl-config --cflags` \ + `curl-config --libs` -lgridsite -lxml2 + apidoc: date doxygen Doxyfile @@ -340,3 +346,5 @@ wtf: ls -l ls -lR /usr/local/ ls -lR $(GSOAPDIR) + + diff --git a/org.gridsite.core/src/slashgrid.c b/org.gridsite.core/src/slashgrid.c new file mode 100644 index 0000000..4fb2ce7 --- /dev/null +++ b/org.gridsite.core/src/slashgrid.c @@ -0,0 +1,1673 @@ +/*5~ + SlashGrid2 (slashgrid for GridSite version 1.3.x) + + As root, start with: + + mkdir /grid + slashgrid /grid -d -o allow_other +*/ + +#define _XOPEN_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define GRST_SLASH_PIDFILE "/var/run/slashgrid.pid" + +#define GRST_SLASH_HEADERS "/var/spool/slashgrid/headers" +#define GRST_SLASH_BLOCKS "/var/spool/slashgrid/blocks" +#define GRST_SLASH_TMP "/var/spool/slashgrid/tmp" +#define GRST_SLASH_DIRFILE "::DIR::" + +#define GRST_SLASH_HEAD 0 +#define GRST_SLASH_GET 1 +#define GRST_SLASH_PUT 2 +#define GRST_SLASH_DELETE 3 +#define GRST_SLASH_MOVE 4 +#define GRST_SLASH_TRUNC 5 + +#define GRST_SLASH_HEADERS_EXPIRE 60 +#define GRST_SLASH_BLOCK_SIZE 4096 +#define GRST_SLASH_MAX_HANDLES 16 + +#ifndef CURLOPT_WRITEDATA +#define CURLOPT_WRITEDATA CURLOPT_FILE +#endif + +#ifndef CURLOPT_READDATA +#define CURLOPT_READDATA CURLOPT_FILE +#endif + +#ifndef CURLE_HTTP_RETURNED_ERROR +#define CURLE_HTTP_RETURNED_ERROR CURLE_HTTP_NOT_FOUND +#endif + +struct grst_body_text { char *text; + size_t used; + size_t allocated; } ; + +struct grst_read_data { const char *buf; + off_t sent; + off_t maxsent; }; + +struct grst_dir_list { char *filename; + off_t length; + int length_set; + time_t modified; + int modified_set; } ; + +struct grst_request { int retcode; + char *location; + size_t length; + int length_set; + time_t modified; + int modified_set; + void *readfunction; + void *readdata; + void *writefunction; + void *writedata; + size_t infilesize; + char *errorbuffer; + char *url; + int method; + char *destination; + off_t start; + off_t finish; } ; + +struct grst_handle { pthread_mutex_t mutex; + CURL *curl_handle; + uid_t uid; + char *proxyfile; + time_t last_used; + } handles[GRST_SLASH_MAX_HANDLES]; + +int debugmode = 0; + +size_t headers_callback(void *ptr, size_t size, size_t nmemb, void *p) +/* Find the values of the return code, Content-Length, Last-Modified + and Location headers */ +{ + float f; + char *s, *q; + size_t realsize; + struct tm modified_tm; + struct grst_request *request_data = (struct grst_request *) p; + + realsize = size * nmemb; + s = malloc(realsize + 1); + memcpy(s, ptr, realsize); + s[realsize] = '\0'; + + if (sscanf(s, "Content-Length: %d", &(request_data->length)) == 1) + request_data->length_set = 1; + else if (sscanf(s, "HTTP/%f %d ", &f, &(request_data->retcode)) == 2) ; + else if (strncmp(s, "Location: ", 10) == 0) + { + request_data->location = strdup(&s[10]); + + for (q=request_data->location; *q != '\0'; ++q) + if ((*q == '\r') || (*q == '\n')) *q = '\0'; + } + else if (strncmp(s, "Last-Modified: ", 15) == 0) + { + /* follow RFC 2616: first try RFC 822 (kosher), then RFC 850 and + asctime() formats too. Must be GMT whatever the format. */ + + if (strptime(&s[15], "%a, %d %b %Y %T GMT", &modified_tm) != NULL) + { + request_data->modified = mktime(&modified_tm); + request_data->modified_set = 1; + } + else if (strptime(&s[15], "%a, %d-%b-%y %T GMT", &modified_tm) != NULL) + { + request_data->modified = mktime(&modified_tm); + request_data->modified_set = 1; + } + else if (strptime(&s[15], "%a %b %d %T %Y", &modified_tm) != NULL) + { + request_data->modified = mktime(&modified_tm); + request_data->modified_set = 1; + } + } + + free(s); + return realsize; +} + +int debug_callback(CURL *handle, curl_infotype infotype, + char *rawmesg, size_t size, void *i) +{ + int n; + char *mesg; + + if ((infotype == CURLINFO_DATA_IN) || + (infotype == CURLINFO_DATA_OUT)) return 0; + + mesg = malloc(size + 1); + + for (n=0; n < size; ++n) + { + if ((rawmesg[n] == '\r') && (n >= size - 2)) mesg[n] = '\0'; + else if (((rawmesg[n] == '\r') || (rawmesg[n] == '\n')) && + (infotype == CURLINFO_HEADER_IN)) mesg[n] = '<'; + else if (((rawmesg[n] == '\r') || (rawmesg[n] == '\n')) && + (infotype == CURLINFO_HEADER_OUT)) mesg[n] = '>'; + else if ((rawmesg[n] < ' ') || (rawmesg[n] >= 127)) mesg[n] = '.'; + else mesg[n] = rawmesg[n]; + } + + mesg[n] = '\0'; + + syslog(LOG_DEBUG, "%d %s%s%s%s", + *((int *) i), + (infotype == CURLINFO_HEADER_IN ) ? "<<" : "", + (infotype == CURLINFO_HEADER_OUT) ? ">>" : "", + (infotype == CURLINFO_TEXT ) ? "**" : "", + mesg); + + free(mesg); + return 0; +} + +int perform_request(struct grst_request *request_data, + struct fuse_context *fuse_ctx) +{ + int ret, i, j; + char *proxyfile = NULL, *range_header = NULL; + struct stat statbuf; + struct curl_slist *headers_list = NULL; + + if (strncmp(request_data->url, "https://", 8) == 0) /* HTTPS options */ + { +// check for X509_USER_PROXY in that PID's environ too + + asprintf(&proxyfile, "/tmp/x509up_u%d", fuse_ctx->uid); + /* if proxyfile is used, it will be referenced by handles[].proxyfile + and freed when this handle is eventually freed */ + + if ((stat(proxyfile, &statbuf) != 0) || + (statbuf.st_uid != fuse_ctx->uid)) + { + free(proxyfile); + proxyfile = NULL; + } + } + + /* try to find an existing handle for this uid/proxyfile */ + + for (i=0; i < GRST_SLASH_MAX_HANDLES; ++i) + { + if ((handles[i].curl_handle != NULL) && + (handles[i].uid == fuse_ctx->uid) && + (((handles[i].proxyfile == NULL) && (proxyfile == NULL)) || + ((handles[i].proxyfile != NULL) && (proxyfile != NULL) && + (strcmp(handles[i].proxyfile, proxyfile) == 0)))) + { + break; + } + } + + if (i >= GRST_SLASH_MAX_HANDLES) /* no existing match found */ + { + i=0; + + for (j=0; j < GRST_SLASH_MAX_HANDLES; ++j) + { + if (handles[j].curl_handle == NULL) /* unused slot */ + { + i = j; + break; + } + + if (handles[j].last_used < handles[i].last_used) i = j; + } + } + + /* now lock this handle and recheck settings inside the mutex lock */ + + pthread_mutex_lock(&(handles[i].mutex)); + + if ((handles[i].curl_handle == NULL) || + (handles[i].uid != fuse_ctx->uid) || + (((handles[i].proxyfile != NULL) || (proxyfile != NULL)) && + ((handles[i].proxyfile == NULL) || (proxyfile == NULL) || + (strcmp(handles[i].proxyfile, proxyfile) != 0)))) + { + /* we do need to initialise this handle */ + + handles[i].uid = fuse_ctx->uid; + + if (handles[i].curl_handle != NULL) + curl_easy_cleanup(handles[i].curl_handle); + handles[i].curl_handle = curl_easy_init(); + + if (handles[i].proxyfile != NULL) free(handles[i].proxyfile); + handles[i].proxyfile = proxyfile; /* proxyfile might be NULL itself */ + + if (handles[i].proxyfile != NULL) + { + curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSLCERTTYPE, "PEM"); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSLCERT, + handles[i].proxyfile); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSLKEYTYPE, "PEM"); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSLKEY, + handles[i].proxyfile); + } + else + { + curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSLKEYTYPE, "ENG"); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSLCERTTYPE, "ENG"); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSLCERT, NULL); + } + + if (debugmode) + { + curl_easy_setopt(handles[i].curl_handle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_DEBUGFUNCTION, + debug_callback); + } + + curl_easy_setopt(handles[i].curl_handle, CURLOPT_USERAGENT, + "SlashGrid http://www.gridsite.org/slashgrid/"); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_FOLLOWLOCATION, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_HEADERFUNCTION, headers_callback); + + curl_easy_setopt(handles[i].curl_handle, CURLOPT_CAPATH, + "/etc/grid-security/certificates"); + + curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSL_VERIFYPEER, 2); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSL_VERIFYHOST, 2); + } + + if (request_data->method == GRST_SLASH_HEAD) + { + curl_easy_setopt(handles[i].curl_handle, CURLOPT_CUSTOMREQUEST, NULL); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_NOBODY, 1); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPGET, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_UPLOAD, 0); + } + else if (request_data->method == GRST_SLASH_GET) + { + curl_easy_setopt(handles[i].curl_handle, CURLOPT_CUSTOMREQUEST, NULL); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_NOBODY, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPGET, 1); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_UPLOAD, 0); + } + else if ((request_data->method == GRST_SLASH_PUT) || + (request_data->method == GRST_SLASH_TRUNC)) + { + curl_easy_setopt(handles[i].curl_handle, CURLOPT_CUSTOMREQUEST, NULL); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_NOBODY, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPGET, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_UPLOAD, 1); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_INFILESIZE, + (long) request_data->infilesize); + } + else if (request_data->method == GRST_SLASH_DELETE) + { + curl_easy_setopt(handles[i].curl_handle, CURLOPT_NOBODY, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPGET, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_UPLOAD, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_CUSTOMREQUEST, "DELETE"); + } + else if (request_data->method == GRST_SLASH_MOVE) + { + curl_easy_setopt(handles[i].curl_handle, CURLOPT_NOBODY, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPGET, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_UPLOAD, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_CUSTOMREQUEST, "MOVE"); + } + else return CURLE_UNSUPPORTED_PROTOCOL; + + curl_easy_setopt(handles[i].curl_handle, CURLOPT_WRITEHEADER, request_data); + + if (request_data->errorbuffer != NULL) + curl_easy_setopt(handles[i].curl_handle, CURLOPT_ERRORBUFFER, + request_data->errorbuffer); + + if (debugmode) + curl_easy_setopt(handles[i].curl_handle, CURLOPT_DEBUGDATA, &i); + + curl_easy_setopt(handles[i].curl_handle, CURLOPT_READFUNCTION, request_data->readfunction); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_READDATA, request_data->readdata); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_WRITEFUNCTION, request_data->writefunction); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_WRITEDATA, request_data->writedata); + + curl_easy_setopt(handles[i].curl_handle, CURLOPT_URL, request_data->url); + + if ((request_data->start >= 0) && + (request_data->finish >= request_data->start)) + { + if (request_data->method == GRST_SLASH_PUT) + asprintf(&range_header, "Content-Range: bytes %ld-%ld/*", + (long) request_data->start, (long) request_data->finish); + else if (request_data->method == GRST_SLASH_TRUNC) + asprintf(&range_header, "Content-Range: bytes *-*/%ld", + (long) request_data->finish); + else asprintf(&range_header, "Range: bytes=%ld-%ld", + (long) request_data->start, (long) request_data->finish); + + headers_list = curl_slist_append(headers_list, range_header); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPHEADER, headers_list); + } + else curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPHEADER, NULL); + + ret = curl_easy_perform(handles[i].curl_handle); + + if (headers_list != NULL) curl_slist_free_all(headers_list); + if (range_header != NULL) free(range_header); + + pthread_mutex_unlock(&(handles[i].mutex)); + + return ret; +} + +size_t rawbody_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + if ( ((struct grst_body_text *) data)->used + size * nmemb >= + ((struct grst_body_text *) data)->allocated ) + { + ((struct grst_body_text *) data)->allocated = + ((struct grst_body_text *) data)->used + size * nmemb + 4096; + + ((struct grst_body_text *) data)->text = + realloc( ((struct grst_body_text *) data)->text, + ((struct grst_body_text *) data)->allocated ); + } + + memcpy( &( ((struct grst_body_text *) + data)->text[((struct grst_body_text *) data)->used] ), + ptr, size * nmemb); + + ((struct grst_body_text *) data)->used += size * nmemb; + + return size * nmemb; +} + +size_t null_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + return size * nmemb; +} + +size_t read_data_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t sent; + + if (((struct grst_read_data *) data)->sent + >= ((struct grst_read_data *) data)->maxsent) return 0; + + if (size * nmemb + ((struct grst_read_data *) data)->sent + >= ((struct grst_read_data *) data)->maxsent) + { + sent = ((struct grst_read_data *) data)->maxsent + - ((struct grst_read_data *) data)->sent; + } + else sent = size * nmemb; + + memcpy(ptr, + ((struct grst_read_data *) data)->buf + + ((struct grst_read_data *) data)->sent, + sent); + + ((struct grst_read_data *) data)->sent += sent; + + return sent; +} + +char *canonicalise(char *link, char *source) +{ + int i, j, srclen; + char *s; + + srclen = strlen(source); + + if ((strncmp(link, "https://", 8) == 0) || + (strncmp(link, "http://", 7) == 0)) + { + if (strncmp(link, source, srclen) != 0) return NULL; /* other site */ + + if (link[srclen] == '\0') return NULL; /* we dont self-link! */ + + for (i=0; link[srclen + i] != '\0'; ++i) + if (link[srclen + i] == '/') + { + if (link[srclen + i + 1] != '\0') return NULL; /* no subdirs */ + else return strdup(&link[srclen]); /* resolves to this dir */ + } + } + else if (link[0] != '/') /* relative link - need to check for subsubdirs */ + { + for (i=0; link[i] != '\0'; ++i) + if ((link[i] == '/') && (link[i+1] != '\0')) return NULL; + + s = strdup(link); + + for (i=0; s[i] != '\0'; ++i) + if (s[i] == '#') + { + s[i] = '\0'; + break; + } + + return s; + } + + /* absolute link on this server, starting / */ + + for (i=8; source[i] != '\0'; ++i) if (source[i] == '/') break; + + if (strncmp(link, &source[i], srclen - i) != 0) return NULL; + + for (j = srclen - i; link[j] != '\0'; ++j) + if ((link[j] == '/') && (link[j+1] != '\0')) return NULL; + + s = strdup(&link[srclen - i]); + + for (i=0; s[i] != '\0'; ++i) + if (s[i] == '#') + { + s[i] = '\0'; + break; + } + + if (s[0] == '\0') /* on second thoughts... */ + { + free(s); + return NULL; + } + + return s; +} + +int grst_dir_list_cmp(const void *a, const void *b) +{ + return strcmp( ((struct grst_dir_list *) a)->filename, + ((struct grst_dir_list *) b)->filename); +} + +struct grst_dir_list *index_to_dir_list(char *text, char *source) +{ + int taglevel = 0, wordnew = 1, i, namestart, used = 0, + allocated = 256; + char *p, *s; + struct grst_dir_list *list; + + list = (struct grst_dir_list *) + malloc(allocated * sizeof(struct grst_dir_list)); + + list[0].filename = NULL; + list[0].length = 0; + list[0].length_set = 0; + list[0].modified = 0; + list[0].modified_set = 0; + + for (p=text; *p != '\0'; ++p) + { + if (*p == '<') + { + ++taglevel; + + if ((taglevel == 1) && (list[used].filename != NULL)) + { + ++used; + if (used >= allocated) + { + allocated += 256; + list = (struct grst_dir_list *) + realloc((void *) list, + allocated * sizeof(struct grst_dir_list)); + } + + list[used].filename = NULL; + list[used].length = 0; + list[used].length_set = 0; + list[used].modified = 0; + list[used].modified_set = 0; + } + + wordnew = 1; + continue; + } + + if (*p == '>') + { + --taglevel; + wordnew = 1; + continue; + } + + if (isspace(*p)) + { + wordnew = 1; + continue; + } + + if ((wordnew) && (taglevel == 1)) + { + if (((*p == 'h') || (*p == 'H')) && + (strncasecmp(p, "href=", 5) == 0)) + { + if (p[5] == '"') { namestart = 6; + for (i=namestart; (p[i] != '\0') && + (p[i] != '"' ) && + (p[i] != '\n') && + (p[i] != '\t') && + (p[i] != '>' ) ; ++i) ; } + else { namestart = 5; + for (i=namestart; (p[i] != '\0') && + (p[i] != '"' ) && + (p[i] != ' ' ) && + (p[i] != '\n') && + (p[i] != '\t') && + (p[i] != ')' ) && + (p[i] != '>' ) ; ++i) ; } + if (i > namestart) + { + s = malloc(1 + i - namestart); + memcpy(s, &p[namestart], i - namestart); + s[i - namestart] = '\0'; + + list[used].filename = canonicalise(s, source); + free(s); + } + + p = &p[i-1]; /* -1 since continue results in ++i */ + continue; + } + + if (((*p == 'c') || (*p == 'C')) && + (strncasecmp(p, "content-length=", 15) == 0)) + { + list[used].length = 0; + list[used].length_set = 1; + + if (p[15] == '"') list[used].length = atoi(&p[16]); + else list[used].length = atoi(&p[15]); + + p = &p[15]; + continue; + } + + if (((*p == 'l') || (*p == 'L')) && + (strncasecmp(p, "last-modified=", 14) == 0)) + { + list[used].modified = 0; + list[used].modified_set = 1; + + if (p[14] == '"') list[used].modified = atoi(&p[15]); + else list[used].modified = atoi(&p[14]); + + p = &p[14]; + continue; + } + } + + wordnew = 0; + } + + qsort((void *) list, used, sizeof(struct grst_dir_list), grst_dir_list_cmp); + + return list; +} + +static char *GRSThttpUrlMildencode(char *in) +/* Return a pointer to a malloc'd string holding a partially URL-encoded + version of *in. "Partially" means that A-Z a-z 0-9 . = - _ @ and / + are passed through unmodified. (DN's processed by GRSThttpUrlMildencode() + can be used as valid Unix paths+filenames if you are prepared to + create or simulate the resulting /X=xyz directories.) */ +{ + char *out, *p, *q; + + out = malloc(3*strlen(in) + 1); + + p = in; + q = out; + + while (*p != '\0') + { + if (isalnum(*p) || (*p == '.') || (*p == '=') || (*p == '-') + || (*p == '/') || (*p == '@') || (*p == '_')) + { + *q = *p; + ++q; + } + else if (*p == ' ') + { + *q = '+'; + ++q; + } + else + { + sprintf(q, "%%%2X", *p); + q = &q[3]; + } + + ++p; + } + + *q = '\0'; + return out; +} + +int read_headers_from_cache(struct fuse_context *fuse_ctx, char *filename, + off_t *length, time_t *modified) +{ + char *encoded_filename, *disk_filename; + int len; + long content_length, last_modified; + FILE *fp; + struct stat statbuf; + time_t now; + + encoded_filename = GRSThttpUrlMildencode(filename); + + len = strlen(encoded_filename); + + if (encoded_filename[len - 1] == '/') /* a directory */ + asprintf(&disk_filename, "%s/%d%s%s", + GRST_SLASH_HEADERS, fuse_ctx->uid, encoded_filename, GRST_SLASH_DIRFILE); + else asprintf(&disk_filename, "%s/%d%s%s", + GRST_SLASH_HEADERS, fuse_ctx->uid, encoded_filename); + + free(encoded_filename); + +// Change to fstat for the benefit of multiple threads: + + if (stat(disk_filename, &statbuf) != 0) /* no cache file to read */ + { + free(disk_filename); + return 0; + } + + time(&now); + + if (statbuf.st_mtime < now - GRST_SLASH_HEADERS_EXPIRE) + { + unlink(disk_filename); /* tidy up expired cache file */ + free(disk_filename); + return 0; + } + + last_modified = 0; + content_length = 0; + + if (debugmode) syslog(LOG_DEBUG, "Opening %s from cache", disk_filename); + + fp = fopen(disk_filename, "r"); + free(disk_filename); + + if (fp != NULL) + { + fscanf(fp, "content-length=%ld last-modified=%ld ", + &content_length, &last_modified); + fclose(fp); + + if (debugmode) syslog(LOG_DEBUG, "content-length=%ld last-modified=%ld", + content_length, last_modified); + + *length = (off_t) content_length; + *modified = (time_t) last_modified; + + return 1; + } + + return 0; +} + +int write_headers_to_cache(struct fuse_context *fuse_ctx, char *filename, + off_t length, time_t modified) +{ + int fd, len; + char *tempfile, *headline, *encoded_filename, *p, *newdir, + *new_filename; + struct stat statbuf; + + asprintf(&tempfile, "%s/headers-XXXXXX", GRST_SLASH_TMP); + fd = mkstemp(tempfile); + + if (fd == -1) + { + free(tempfile); + return 0; + } + + asprintf(&headline, "content-length=%ld last-modified=%ld \n", + (long) length, (long) modified); + + if ((write(fd, headline, strlen(headline)) == -1) || + (close(fd) == -1)) + { + free(tempfile); + free(headline); + return 0; + } + + free(headline); + + encoded_filename = GRSThttpUrlMildencode(filename); + +// need to protect against .. ? + + for (p = encoded_filename; *p != '\0'; ++p) + { + if (*p != '/') continue; + + *p = '\0'; + asprintf(&newdir, "%s/%d%s", GRST_SLASH_HEADERS, fuse_ctx->uid, encoded_filename); + *p = '/'; + + if (stat(newdir, &statbuf) == 0) + { + if (!S_ISDIR(statbuf.st_mode)) /* exists already - not a directory! */ + { + unlink(newdir); + mkdir(newdir, S_IRUSR | S_IWUSR | S_IXUSR); + } + /* else it already exists as a directory - so ok */ + } + else mkdir(newdir, S_IRUSR | S_IWUSR | S_IXUSR); + + free(newdir); + } + + len = strlen(encoded_filename); + + if (encoded_filename[len - 1] == '/') /* a directory */ + asprintf(&new_filename, "%s/%d%s%s", + GRST_SLASH_HEADERS, fuse_ctx->uid, encoded_filename, GRST_SLASH_DIRFILE); + else asprintf(&new_filename, "%s/%d%s", + GRST_SLASH_HEADERS, fuse_ctx->uid, encoded_filename); + + free(encoded_filename); + + if ((stat(new_filename, &statbuf) == 0) && S_ISDIR(statbuf.st_mode)) + { +// need change this to do it recursively in case any files/subdirs too + rmdir(new_filename); + } + + rename(tempfile, new_filename); + + if (debugmode) syslog(LOG_DEBUG, "Added %s to cache (%ld %ld)\n", + new_filename, length, modified); + + free(tempfile); + free(new_filename); + + return 1; +} + +static int slashgrid_readdir(const char *path, void *buf, + fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + (void) offset; + (void) fi; + + int anyerror = 0, thiserror, i, ilast; + const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + char *s, *url, errorbuffer[CURL_ERROR_SIZE+1] = ""; + struct grst_body_text rawindex; + struct grst_dir_list *list; + struct grst_request request_data; + struct tm modified_tm; + struct stat stat_tmp; + time_t now; + struct fuse_context fuse_ctx; + + memcpy(&fuse_ctx, fuse_get_context(), sizeof(struct fuse_context)); + + if (debugmode) syslog(LOG_DEBUG, "in slashgrid_readdir"); + + if (strncmp(path, "/http/", 6) == 0) + asprintf(&url, "http://%s/", &path[6]); + else if (strncmp(path, "/https/", 7) == 0) + asprintf(&url, "https://%s/", &path[7]); + else return -ENOENT; + + rawindex.text = NULL; + rawindex.used = 0; + rawindex.allocated = 0; + + bzero(&request_data, sizeof(struct grst_request)); + request_data.writefunction = rawbody_callback; + request_data.writedata = (void *) &rawindex; + request_data.errorbuffer = errorbuffer; + request_data.url = url; + request_data.method = GRST_SLASH_GET; + request_data.start = -1; + request_data.finish = -1; + + if (debugmode) syslog(LOG_DEBUG, "Get directory listing from URL %s", url); + + thiserror = perform_request(&request_data, &fuse_ctx); + + if ((thiserror != 0) || + (request_data.retcode < 200) || + (request_data.retcode >= 300)) + { + if (debugmode) + syslog(LOG_DEBUG, "... curl error: %s (%d), HTTP error: %d\n", + errorbuffer, thiserror, request_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = request_data.retcode; + + if (request_data.retcode == 403) return -EACCES; + else return -ENOENT; +/* memory clean up still needed here!!!!!! */ + } + else + { + time(&now); + + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + + rawindex.text[rawindex.used] = '\0'; + + list = index_to_dir_list(rawindex.text, url); + ilast = -1; + + for (i=0; list[i].filename != NULL; ++i) + { + if (strncmp(list[i].filename, "mailto:", 7) == 0) continue; + + /* skip over duplicates */ + + if ((ilast >= 0) && + (strcmp(list[i].filename, list[ilast].filename) == 0)) + continue; + ilast=i; /* last distinct entry */ + + asprintf(&s, "%s/%s", path, list[i].filename); + write_headers_to_cache(&fuse_ctx, s, list[i].length, + list[i].modified); + free(s); + + bzero(&stat_tmp, sizeof(struct stat)); + stat_tmp.st_size = list[i].length; + stat_tmp.st_mtime = list[i].modified; + stat_tmp.st_ctime = list[i].modified; + stat_tmp.st_atime = now; + stat_tmp.st_mode = 0666; + + filler(buf, list[i].filename, &stat_tmp, 0); + } + } + + return 0; +} + +static int slashgrid_getattr(const char *rawpath, struct stat *stbuf) +{ + int anyerror = 0, thiserror, i, ilast, len; + char *s, *url, *path, errorbuffer[CURL_ERROR_SIZE+1] = ""; + struct grst_dir_list *list; + struct grst_request request_data; + struct tm modified_tm; + struct stat stat_tmp; + time_t now; + + struct fuse_context fuse_ctx; + + memcpy(&fuse_ctx, fuse_get_context(), sizeof(struct fuse_context)); + + if (debugmode) syslog(LOG_DEBUG, + "in slashgrid_getattr, rawpath=%s, UID=%d\n", + rawpath, fuse_ctx.uid); + + memset(stbuf, 0, sizeof(struct stat)); + stbuf->st_mode = S_IFREG | 0755; + stbuf->st_nlink = 1; + + if ((strcmp(rawpath, "/") == 0) || + (strcmp(rawpath, "/http") == 0) || + (strcmp(rawpath, "/https") == 0)) + { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + + return 0; /* The empty top level directory: OK */ + } + else if (strncmp(rawpath, "/http/", 6) == 0) + { + if (index(&rawpath[6], '/') == NULL) /* top directory for remote server */ + { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + + asprintf(&url, "http://%s/", &rawpath[6]); + asprintf(&path, "%s/", rawpath); + } + else + { + asprintf(&url, "http://%s", &rawpath[6]); + path = strdup(rawpath); + } + } + else if (strncmp(rawpath, "/https/", 7) == 0) + { + if (index(&rawpath[7], '/') == NULL) /* top directory for remote server */ + { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + + asprintf(&url, "https://%s/", &rawpath[7]); + asprintf(&path, "%s/", rawpath); + } + else + { + asprintf(&url, "https://%s", &rawpath[7]); + path = strdup(rawpath); + } + } + else return -ENOENT; + + time(&now); + + if (read_headers_from_cache(&fuse_ctx, path, + &(stbuf->st_size), &(stbuf->st_mtime))) + { + if (debugmode) syslog(LOG_DEBUG, + "Retrieving details for %s from cache (%ld %ld)\n", url, + (long) stbuf->st_mtime, (long) stbuf->st_size); + + stbuf->st_ctime = stbuf->st_mtime; + stbuf->st_atime = now; + + free(url); + free(path); + return 0; + } + + bzero(&request_data, sizeof(struct grst_request)); + request_data.writefunction = null_callback; + request_data.writedata = NULL; + request_data.errorbuffer = errorbuffer; + request_data.url = url; + request_data.method = GRST_SLASH_HEAD; + request_data.start = -1; + request_data.finish = -1; + + thiserror = perform_request(&request_data, &fuse_ctx); + + if ((thiserror != 0) || + (request_data.retcode < 200) || + (request_data.retcode > 301)) + { + if (debugmode) + syslog(LOG_DEBUG, "... curl error: %s (%d), HTTP error: %d\n", + errorbuffer, thiserror, request_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = request_data.retcode; + + free(url); + free(path); + + if (request_data.retcode == 403) return -EACCES; + else return -ENOENT; +/* memory clean up still needed here!!!!!! */ + } + + if (request_data.retcode == 301) + { + if (debugmode) syslog(LOG_DEBUG, "301 detected"); + + len = strlen(url); + + if ((request_data.location != NULL) && + (len + 1 == strlen(request_data.location)) && + (request_data.location[len] == '/') && + (strncmp(url, request_data.location, len) == 0)) + { + request_data.length_set = 0; + request_data.modified_set = 0; + request_data.retcode = 0; + request_data.url = request_data.location; + + thiserror = perform_request(&request_data, &fuse_ctx); + + if ((thiserror != 0) || + (request_data.retcode < 200) || + ((request_data.retcode > 299) && (request_data.retcode != 403))) + { + if (debugmode) + syslog(LOG_DEBUG, "... curl error: %s (%d), HTTP error: %d\n", + errorbuffer, thiserror, request_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = request_data.retcode; + + free(url); + free(path); + return -ENOENT; +/* memory clean up still needed here!!!!!! */ + } + + stbuf->st_mode = S_IFDIR | 0755; /* this is a directory */ + stbuf->st_nlink = 2; + + free(path); + asprintf(&path, "%s/", rawpath); + } + else + { + free(url); + free(path); + return -ENOENT; + } + } + + if (request_data.length_set) stbuf->st_size = request_data.length; + else stbuf->st_size = 0; + + if (request_data.modified_set) + { + stbuf->st_mtime = request_data.modified; + stbuf->st_ctime = request_data.modified; + } + + stbuf->st_atime = now; + + write_headers_to_cache(&fuse_ctx, path, stbuf->st_size, stbuf->st_mtime); + + free(url); + free(path); + return 0; +} + + +int write_block_to_cache(struct fuse_context *fuse_ctx, char *filename, + off_t start, off_t finish) +{ + int anyerror = 0, thiserror, i, fd; + char *s, *url, *tempfile, *encoded_filename, *p, + *newdir, *new_filename, errorbuffer[CURL_ERROR_SIZE+1] = ""; + struct stat statbuf; + struct grst_request request_data; + FILE *fp; + + asprintf(&tempfile, "%s/blocks-XXXXXX", GRST_SLASH_TMP); + fd = mkstemp(tempfile); + + if (fd == -1) + { + free(tempfile); + return -EIO; + } + + fp = fdopen(fd, "w"); + + if (strncmp(filename, "/http/", 6) == 0) + asprintf(&url, "http://%s", &filename[6]); + else if (strncmp(filename, "/https/", 7) == 0) + asprintf(&url, "https://%s", &filename[7]); + else return -ENOENT; + + bzero(&request_data, sizeof(struct grst_request)); + request_data.writefunction = fwrite; + request_data.writedata = (void *) fp; + request_data.errorbuffer = errorbuffer; + request_data.url = url; + request_data.method = GRST_SLASH_GET; + request_data.start = start; + request_data.finish = finish; + + if (debugmode) syslog(LOG_DEBUG, "Get block %ld-%ld from URL %s\n", + (long) start, (long) finish, url); + + thiserror = perform_request(&request_data, fuse_ctx); + + free(url); + + fclose(fp); + + if ((thiserror != 0) || + (request_data.retcode < 200) || + (request_data.retcode >= 300)) + { + if (debugmode) + syslog(LOG_DEBUG, "... curl error: %s (%d), HTTP error: %d\n", + errorbuffer, thiserror, request_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = request_data.retcode; + + if (request_data.retcode == 403) return -EACCES; + else return -ENOENT; +/* memory clean up still needed here!!!!!! */ + } + + encoded_filename = GRSThttpUrlMildencode(filename); + +// need to protect against .. ? +// can optimise by checking for existing of filename as a dir at the start + + for (p = encoded_filename; ; ++p) + { + if ((*p != '/') && (*p != '\0')) continue; + + if (*p == '/') + { + *p = '\0'; + asprintf(&newdir, "%s/%d%s", + GRST_SLASH_BLOCKS, fuse_ctx->uid, encoded_filename); + *p = '/'; + } + else asprintf(&newdir, "%s/%d%s", + GRST_SLASH_BLOCKS, fuse_ctx->uid, encoded_filename); + + if (stat(newdir, &statbuf) != 0) + mkdir(newdir, S_IRUSR | S_IWUSR | S_IXUSR); + free(newdir); + + if (*p == '\0') break; + } + + asprintf(&new_filename, "%s/%d%s/%ld-%ld", + GRST_SLASH_BLOCKS, fuse_ctx->uid, encoded_filename, (long) start, (long) finish); + + free(encoded_filename); + + rename(tempfile, new_filename); + + if (debugmode) syslog(LOG_DEBUG, "Added %s to block cache", new_filename); + + free(tempfile); + free(new_filename); + + return 0; +} + +int drop_cache_blocks(struct fuse_context *fuse_ctx, char *filename) +/* drop ALL the blocks cached for this file, and delete the directory in + the blocks cache for this file */ +{ + int ret; + char *encoded_filename, *dirname, *blockname; + DIR *blocksDIR; + struct dirent *blocks_ent; + + encoded_filename = GRSThttpUrlMildencode(filename); + + asprintf(&dirname, "%s/%d%s", + GRST_SLASH_BLOCKS, fuse_ctx->uid, encoded_filename); + + free(encoded_filename); + + blocksDIR = opendir(dirname); + + if (blocksDIR == NULL) /* no directory to delete (probably) */ + { + free(dirname); + return 1; + } + + while ((blocks_ent = readdir(blocksDIR)) != NULL) + { + asprintf(&blockname, "%s/%s", dirname, blocks_ent->d_name); + remove(blockname); + free(blockname); + } + + closedir(blocksDIR); + + ret = rmdir(dirname); + free(dirname); + + return ret ? 1 : 0; /* return 1 on error, 0 on rmdir() success */ +} + +static int slashgrid_read(const char *path, char *buf, + size_t size, off_t offset, + struct fuse_file_info *fi) +{ + (void) offset; + (void) fi; + + int anyerror = 0, thiserror, i, ilast, fd; + char *s, *url, *disk_filename, *encoded_filename; + off_t block_start, block_finish, block_i, len; + struct grst_body_text rawbody; + struct grst_request request_data; + struct tm modified_tm; + struct stat statbuf; + time_t now; + struct fuse_context fuse_ctx; + + memcpy(&fuse_ctx, fuse_get_context(), sizeof(struct fuse_context)); + + if (debugmode) syslog(LOG_DEBUG, "in slashgrid_read size=%ld offset=%ld", + (long) size, (long) offset); + + if ((strncmp(path, "/http/", 6) != 0) && + (strncmp(path, "/https/", 7) != 0)) return -ENOENT; + + block_start = GRST_SLASH_BLOCK_SIZE * (offset / GRST_SLASH_BLOCK_SIZE); + block_finish = GRST_SLASH_BLOCK_SIZE * + ((offset + size - 1) / GRST_SLASH_BLOCK_SIZE); + + encoded_filename = GRSThttpUrlMildencode((char *) path); + time(&now); + + for (block_i = block_start; block_i <= block_finish; block_i += GRST_SLASH_BLOCK_SIZE) + { + asprintf(&disk_filename, "%s/%d%s/%ld-%ld", + GRST_SLASH_BLOCKS, fuse_ctx.uid, encoded_filename, + (long) block_i, (long) (block_i + GRST_SLASH_BLOCK_SIZE - 1)); + + if (debugmode) syslog(LOG_DEBUG, "disk_filename=%s", disk_filename); + + if ((stat(disk_filename, &statbuf) != 0) || + (statbuf.st_mtime < now - GRST_SLASH_HEADERS_EXPIRE)) + { + write_block_to_cache(&fuse_ctx, (char *) path, + block_i, block_i + GRST_SLASH_BLOCK_SIZE - 1); + } + +// need to worry about cached copy being deleted (invalidated by a writing +// thread?) between write_block_to_cache() and these reads? +// maybe return fd from write_block_to_cache() itself??? +// the initial stat() needs to be part of this too + + if ((fd = open(disk_filename, O_RDONLY)) != -1) + { + if (block_i == block_start) + { + lseek(fd, offset - block_start, SEEK_SET); + read(fd, buf, + (offset - block_start + size < GRST_SLASH_BLOCK_SIZE) + ? size : GRST_SLASH_BLOCK_SIZE - offset + block_start); + } + else if (block_i == block_finish) + { + read(fd, buf + (block_i - block_start), + offset + size - block_i); + } + else + { + read(fd, buf + (block_i - block_start), + GRST_SLASH_BLOCK_SIZE); + } + + close(fd); + } + else syslog(LOG_ERR, "Failed to open %s in cache", disk_filename); + } + + free(disk_filename); + free(encoded_filename); + + return size; +} + +static int slashgrid_write(const char *path, const char *buf, + size_t size, off_t offset, + struct fuse_file_info *fi) +{ + int anyerror = 0, thiserror, i, fd; + char *s, *url, *p, errorbuffer[CURL_ERROR_SIZE+1] = ""; + + struct grst_read_data read_data; + struct grst_request request_data; + struct fuse_context fuse_ctx; + + memcpy(&fuse_ctx, fuse_get_context(), sizeof(struct fuse_context)); + + if (strncmp(path, "/http/", 6) == 0) + asprintf(&url, "http://%s", &path[6]); + else if (strncmp(path, "/https/", 7) == 0) + asprintf(&url, "https://%s", &path[7]); + else return -ENOENT; + + read_data.buf = buf; + read_data.sent = 0; + read_data.maxsent = size; + + if (debugmode) syslog(LOG_DEBUG, "Put block %ld-%ld to URL %s", + (long) offset, (long) offset+size-1, url); + + drop_cache_blocks(&fuse_ctx, (char *) path); /* we drop all read-cache blocks first */ + + bzero(&request_data, sizeof(struct grst_request)); + request_data.writefunction = null_callback; + request_data.readfunction = read_data_callback; + request_data.readdata = &read_data; + request_data.infilesize = size; + request_data.errorbuffer = errorbuffer; + request_data.url = url; + request_data.method = GRST_SLASH_PUT; + request_data.start = offset; + request_data.finish = (off_t) (offset + size - 1); + + thiserror = perform_request(&request_data, &fuse_ctx); + + free(url); + + if ((thiserror != 0) || + (request_data.retcode < 200) || + (request_data.retcode >= 300)) + { + if (debugmode) + syslog(LOG_DEBUG, "... curl error: %s (%d), HTTP error: %d\n", + errorbuffer, thiserror, request_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = request_data.retcode; + + if (request_data.retcode == 403) return -EACCES; + else return -ENOENT; +/* memory clean up still needed here!!!!!! */ + } + + return size; +} + +int slashgrid_rename(const char *oldpath, const char *newpath) +{ + int anyerror = 0, thiserror, i, fd; + char *s, *url, *p, *destination, errorbuffer[CURL_ERROR_SIZE+1] = ""; + + struct grst_read_data read_data; + struct fuse_context fuse_ctx; + struct grst_request request_data; + + memcpy(&fuse_ctx, fuse_get_context(), sizeof(struct fuse_context)); + + if (strncmp(oldpath, "/http/", 6) == 0) + { + if (strncmp(newpath, "/http/", 6) != 0) return -EXDEV; + + asprintf(&url, "http://%s", &oldpath[6]); + asprintf(&destination, "http://%s", &newpath[6]); + } + else if (strncmp(oldpath, "/https/", 7) == 0) + { + if (strncmp(newpath, "/https/", 7) != 0) return -EXDEV; + + asprintf(&url, "https://%s", &oldpath[7]); + asprintf(&destination, "https://%s", &newpath[7]); + } + else return -ENOENT; + + read_data.buf = ""; + read_data.sent = 0; + read_data.maxsent = 0; + + if (debugmode) syslog(LOG_DEBUG, "MOVE URL %s to %s", url, destination); + + bzero(&request_data, sizeof(struct grst_request)); + request_data.writefunction = null_callback; + request_data.readfunction = read_data_callback; + request_data.readdata = &read_data; + request_data.errorbuffer = errorbuffer; + request_data.url = url; + request_data.method = GRST_SLASH_MOVE; + request_data.destination = destination; + + thiserror = perform_request(&request_data, &fuse_ctx); + + free(url); + free(destination); + + if ((thiserror != 0) || + (request_data.retcode < 200) || + (request_data.retcode >= 300)) + { + if (debugmode) + syslog(LOG_DEBUG, "... curl error: %s (%d), HTTP error: %d\n", + errorbuffer, thiserror, request_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = request_data.retcode; + + if (request_data.retcode == 403) return -EACCES; + else return -ENOENT; +/* memory clean up still needed here!!!!!! */ + } + + return 0; +} + +int slashgrid_unlink(const char *path) +{ + int anyerror = 0, thiserror, i, fd; + char *s, *url, *p, errorbuffer[CURL_ERROR_SIZE+1] = ""; + + struct grst_read_data read_data; + struct fuse_context fuse_ctx; + struct grst_request request_data; + + memcpy(&fuse_ctx, fuse_get_context(), sizeof(struct fuse_context)); + + if (strncmp(path, "/http/", 6) == 0) + asprintf(&url, "http://%s", &path[6]); + else if (strncmp(path, "/https/", 7) == 0) + asprintf(&url, "https://%s", &path[7]); + else return -ENOENT; + + read_data.buf = ""; + read_data.sent = 0; + read_data.maxsent = 0; + + if (debugmode) syslog(LOG_DEBUG, "DELETE URL %s", url); + + bzero(&request_data, sizeof(struct grst_request)); + request_data.writefunction = null_callback; + request_data.readfunction = read_data_callback; + request_data.readdata = &read_data; + request_data.errorbuffer = errorbuffer; + request_data.url = url; + request_data.method = GRST_SLASH_DELETE; + + thiserror = perform_request(&request_data, &fuse_ctx); + + free(url); + + if ((thiserror != 0) || + (request_data.retcode < 200) || + (request_data.retcode >= 300)) + { + if (debugmode) + syslog(LOG_DEBUG, "... curl error: %s (%d), HTTP error: %d\n", + errorbuffer, thiserror, request_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = request_data.retcode; + + if (request_data.retcode == 403) return -EACCES; + else return -ENOENT; +/* memory clean up still needed here!!!!!! */ + } + + return 0; +} + +int slashgrid_rmdir(const char *path) +{ + int ret; + char *pathwithslash; + + asprintf(&pathwithslash, "%s/", path); + ret = slashgrid_unlink(pathwithslash); + free(pathwithslash); + +/* error on GridSite side still??? */ + + return ret; +} + +int slashgrid_mknod(const char *path, mode_t mode, dev_t dev) +{ + int ret; + + ret = slashgrid_write(path, "", 0, 0, NULL); + + return (ret < 0) ? ret : 0; +} + +int slashgrid_mkdir(const char *path, mode_t mode) +{ + int ret; + char *pathwithslash; + + asprintf(&pathwithslash, "%s/", path); + ret = slashgrid_write(pathwithslash, "", 0, 0, NULL); + free(pathwithslash); + + return (ret < 0) ? ret : 0; +} + +int slashgrid_chown(const char *path, uid_t uid, gid_t gid) +{ + puts("slashgrid_chown - NOP"); + return 0; +} + +int slashgrid_chmod(const char *path, mode_t mode) +{ + puts("slashgrid_chmod - NOP"); + return 0; +} + +int slashgrid_truncate(const char *path, off_t offset) +{ + int anyerror = 0, thiserror, i, fd; + char *s, *url, *p, errorbuffer[CURL_ERROR_SIZE+1] = ""; + + struct grst_read_data read_data; + struct fuse_context fuse_ctx; + struct grst_request request_data; + + memcpy(&fuse_ctx, fuse_get_context(), sizeof(struct fuse_context)); + + if (strncmp(path, "/http/", 6) == 0) + asprintf(&url, "http://%s", &path[6]); + else if (strncmp(path, "/https/", 7) == 0) + asprintf(&url, "https://%s", &path[7]); + else return -ENOENT; + + read_data.buf = ""; + read_data.sent = 0; + read_data.maxsent = 0; + + if (debugmode) syslog(LOG_DEBUG, "Truncate URL %s to %ld\n", + url, (long) offset); + + bzero(&request_data, sizeof(struct grst_request)); + request_data.writefunction = null_callback; + request_data.readfunction = read_data_callback; + request_data.readdata = &read_data; + request_data.errorbuffer = errorbuffer; + request_data.url = url; + request_data.method = GRST_SLASH_TRUNC; + request_data.finish = offset; + + thiserror = perform_request(&request_data, &fuse_ctx); + + free(url); + + if ((thiserror != 0) || + (request_data.retcode < 200) || + (request_data.retcode >= 300)) + { + if (debugmode) + syslog(LOG_DEBUG, "... curl error: %s (%d), HTTP error: %d\n", + errorbuffer, thiserror, request_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = request_data.retcode; + + if (request_data.retcode == 403) return -EACCES; + else return -ENOENT; +/* memory clean up still needed here!!!!!! */ + } + + return 0; +} + +int slashgrid_statfs(const char *path, struct statfs *fs) +{ + return statfs(GRST_SLASH_BLOCKS, fs); +} + +void *slashgrid_init(void) +{ + FILE *fp; + + if ((fp = fopen(GRST_SLASH_PIDFILE, "w")) != NULL) + { + fprintf(fp, "%d\n", (int) getpid()); + fclose(fp); + } + + return NULL; +} + +void slashgrid_destroy(void *p) +{ + unlink(GRST_SLASH_PIDFILE); +} + +static struct fuse_operations slashgrid_oper = { + .getattr = slashgrid_getattr, + .chown = slashgrid_chown, + .chmod = slashgrid_chmod, + .truncate = slashgrid_truncate, + .readdir = slashgrid_readdir, + .write = slashgrid_write, + .read = slashgrid_read, + .mknod = slashgrid_mknod, + .mkdir = slashgrid_mkdir, + .unlink = slashgrid_unlink, + .rmdir = slashgrid_rmdir, + .rename = slashgrid_rename, + .statfs = slashgrid_statfs, + .init = slashgrid_init, + .destroy = slashgrid_destroy +}; + +int main(int argc, char *argv[]) +{ +#define FUSE_ARGC 4 + char *fuse_argv[FUSE_ARGC] = { "slashgrid", "/grid", "-o", "allow_other" }; + int i, ret; + + for (i=1; i < argc; ++i) + if (strcmp(argv[i], "--debug") == 0) debugmode = 1; + + openlog("slashgrid", 0, LOG_DAEMON); + + umount("/grid"); /* in case of a crash, but will fail if still busy */ + + for (i=0; i < GRST_SLASH_MAX_HANDLES; ++i) + { + pthread_mutex_init(&(handles[i].mutex), NULL); + handles[i].curl_handle = NULL; + handles[i].proxyfile = NULL; + handles[i].last_used = 0; + } + + ret = fuse_main(FUSE_ARGC, fuse_argv, &slashgrid_oper); + + return ret; +} diff --git a/org.gridsite.core/src/slashgrid.init b/org.gridsite.core/src/slashgrid.init new file mode 100755 index 0000000..44a12ad --- /dev/null +++ b/org.gridsite.core/src/slashgrid.init @@ -0,0 +1,66 @@ +#!/bin/bash +# +# slashgrid Startup script for the SlashGrid client-side server +# +# chkconfig: - 90 10 +# description: Filesystem extension for access to HTTP(S) file servers +# processname: slashgrid +# config: /etc/sysconfig/slashgrid +# pidfile: /var/run/slashgrid.pid + +# Source function library. +. /etc/rc.d/init.d/functions + +# Examples of useful overrides of default settings: +# +# OPTIONS=--debug # to turn on LOG_DEBUG in syslog +# SLASHGRID=/usr/local/sbin/slashgrid # if slashgrid not in /usr/sbin +# PATH=$PATH:/usr/local/bin # if fusermount not on old $PATH +# +if [ -f /etc/sysconfig/slashgrid ] ; then + . /etc/sysconfig/slashgrid +fi + +slashgrid=${SLASHGRID-/usr/sbin/slashgrid} +pidfile=${PIDFILE-/var/run/slashgrid.pid} +lockfile=${LOCKFILE-/var/lock/subsys/slashgrid} +prog=slashgrid +RETVAL=0 + +start() { + echo -n $"Starting $prog: " + modprobe fuse + daemon $slashgrid $OPTIONS + RETVAL=$? + echo + [ $RETVAL = 0 ] && touch ${lockfile} + return $RETVAL +} + +stop() { + echo -n $"Stopping $prog: " + killproc $slashgrid + RETVAL=$? + echo + [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile} +} + +# Invoke the right function + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + stop + start + ;; + *) + echo $"Usage: $prog {start|stop|restart|help}" + exit 1 +esac + +exit $RETVAL -- 1.8.2.3