PUT works again
author() <nilclass@riseup.net>
Sun, 16 Jun 2013 14:08:05 +0000 (16:08 +0200)
committer() <nilclass@riseup.net>
Sun, 16 Jun 2013 14:08:05 +0000 (16:08 +0200)
Makefile
src/handler/dispatch.c
src/handler/storage.c
src/handler/storage.h
src/process/main.c

index 8540c2a..f65b830 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,8 +4,8 @@ INCLUDES=-Isrc -Ilib/evhtp/ -Ilib/evhtp/htparse -Ilib/evhtp/evthr -Ilib/evhtp/on
 
 BASE_OBJECTS=src/config.o
 COMMON_OBJECTS=src/common/log.o src/common/process.o src/common/request_response.o src/common/user.o src/common/auth.o
-HANDLER_OBJECTS=src/handler/dispatch.o src/handler/storage.o src/handler/response.o src/handler/request.o src/handler/auth.o
-PROCESS_OBJECTS=src/process/main.o src/process/storage.o
+HANDLER_OBJECTS=src/handler/dispatch.o src/handler/storage.o src/handler/auth.o
+PROCESS_OBJECTS=src/process/main.o
 OBJECTS=$(BASE_OBJECTS) $(COMMON_OBJECTS) $(PROCESS_OBJECTS) $(HANDLER_OBJECTS)
 
 STATIC_LIBS=lib/evhtp/build/libevhtp.a
index 8d0c834..01ed9b4 100644 (file)
 
 #include "rs-serve.h"
 
-static struct rs_process_info *start_storage_process(uid_t uid) {
-  struct rs_process_info *storage_process = malloc(sizeof(struct rs_process_info));
-  if(storage_process == NULL) {
-    log_error("malloc() failed: %s", strerror(errno));
-    return NULL;
-  }
-  storage_process->uid = uid;
-  if(start_process(storage_process, storage_main) != 0) {
-    free(storage_process);
-    return NULL;
-  }
-  return storage_process;
-}
+/* static struct rs_process_info *start_storage_process(uid_t uid) { */
+/*   struct rs_process_info *storage_process = malloc(sizeof(struct rs_process_info)); */
+/*   if(storage_process == NULL) { */
+/*     log_error("malloc() failed: %s", strerror(errno)); */
+/*     return NULL; */
+/*   } */
+/*   storage_process->uid = uid; */
+/*   if(start_process(storage_process, storage_main) != 0) { */
+/*     free(storage_process); */
+/*     return NULL; */
+/*   } */
+/*   return storage_process; */
+/* } */
 
 static char *my_strtok(char *string, char *delim, char **saveptr) {
   char *ptr = string ? string : *saveptr, *dp;
@@ -48,84 +48,84 @@ static char *my_strtok(char *string, char *delim, char **saveptr) {
   return NULL;
 }
 
-int dispatch_request(struct evbuffer *buffer, int fd) {
-  size_t buf_len = evbuffer_get_length(buffer);
-  char *buf = malloc(buf_len + 1);
-  if(buf == NULL) {
-    log_error("malloc() failed: %s", strerror(errno));
-    return -1;
-  }
-  evbuffer_remove(buffer, buf, buf_len);
-  buf[buf_len] = 0; // terminate string.
-
-  // parse everything
-
-  log_debug("Got data buf (%d): %s", buf_len, buf);
-
-  char *saveptr;
-  char *verb = my_strtok(buf, " ", &saveptr);
-  saveptr++; // skip initial slash
-  char *username = my_strtok(NULL, "/ ", &saveptr);
-  char *file_path = my_strtok(NULL, " ", &saveptr);
-  char *http_version = my_strtok(NULL, "\r", &saveptr);
-  saveptr++; // skip \n
-  char *rest = my_strtok(NULL, "", &saveptr);
-
-  log_debug("verb: %s\nusername: %s\nfile_path: %s\nhttp_version: %s\nrest: %s", verb, username, file_path, http_version, rest);
-  if(verb == NULL || username == NULL || file_path == NULL ||
-     http_version == NULL) {
-    //log_debug("verb: %s, username: %s, file_path: %s, http_version: %s, rest: %s", verb, username, file_path, http_version);
-    return -1;
-  }
-
-  if(strncmp(http_version, "HTTP/1.", 7) != 0) {
-    log_error("Invalid request (HTTP version specified as \"%s\")", http_version);
-    return -1;
-  }
-
-  /* log_debug("Parsed something. Let's see:\n  verb: %s\n  username: %s\n  path: %s\n  http version: %s\n  rest: %s", */
-  /*           verb, username, file_path, http_version, rest); */
-
-  uid_t uid = user_get_uid(username);
-
-  if(uid == -1) {
-    // TODO: send 404 response here
-    log_error("User not found: %s", username);
-    free(buf);
-    return -1;
-  } else if(uid == -2) {
-    // TODO: send 500 response here
-    log_error("Failed to get uid for user: %s", username);
-    free(buf);
-    return -1;
-  }
-
-  // TODO: add check for UID > MIN_UID
-  //   (we don't want to fork storage workers for system users)
-
-  // rewrite first line to only include file path
-
-  int new_len = sprintf(buf, "%s /%s %s\r\n%s", verb, file_path, http_version, rest);
-  log_debug("reallocating to %d bytes", new_len + 1);
-  buf = realloc(buf, ++new_len);
-  if(buf == NULL) {
-    log_error("BUG: realloc() failed to shrink buffer from %d to %d bytes: %s",
-              buf_len + 1, new_len + 1, strerror(errno));
-  }
-  buf_len = new_len;
-
-  struct rs_process_info *storage_process = process_find_uid(uid);
-  if(storage_process == NULL) {
-    storage_process = start_storage_process(uid);
-    if(storage_process == NULL) {
-      log_error("Failed to start storage process!");
-      free(buf);
-      return -1;
-    }
-  }
-  forward_to_process(storage_process, fd, buf, buf_len);
-
-  free(buf);
-
-  return 0;
-}
+/* int dispatch_request(struct evbuffer *buffer, int fd) { */
+/*   size_t buf_len = evbuffer_get_length(buffer); */
+/*   char *buf = malloc(buf_len + 1); */
+/*   if(buf == NULL) { */
+/*     log_error("malloc() failed: %s", strerror(errno)); */
+/*     return -1; */
+/*   } */
+/*   evbuffer_remove(buffer, buf, buf_len); */
+/*   buf[buf_len] = 0; // terminate string. */
+
+/*   // parse everything */
+
+/*   log_debug("Got data buf (%d): %s", buf_len, buf); */
+
+/*   char *saveptr; */
+/*   char *verb = my_strtok(buf, " ", &saveptr); */
+/*   saveptr++; // skip initial slash */
+/*   char *username = my_strtok(NULL, "/ ", &saveptr); */
+/*   char *file_path = my_strtok(NULL, " ", &saveptr); */
+/*   char *http_version = my_strtok(NULL, "\r", &saveptr); */
+/*   saveptr++; // skip \n */
+/*   char *rest = my_strtok(NULL, "", &saveptr); */
+
+/*   log_debug("verb: %s\nusername: %s\nfile_path: %s\nhttp_version: %s\nrest: %s", verb, username, file_path, http_version, rest); */
+/*   if(verb == NULL || username == NULL || file_path == NULL || */
+/*      http_version == NULL) { */
+/*     //log_debug("verb: %s, username: %s, file_path: %s, http_version: %s, rest: %s", verb, username, file_path, http_version); */
+/*     return -1; */
+/*   } */
+
+/*   if(strncmp(http_version, "HTTP/1.", 7) != 0) { */
+/*     log_error("Invalid request (HTTP version specified as \"%s\")", http_version); */
+/*     return -1; */
+/*   } */
+
+/*   /\* log_debug("Parsed something. Let's see:\n  verb: %s\n  username: %s\n  path: %s\n  http version: %s\n  rest: %s", *\/ */
+/*   /\*           verb, username, file_path, http_version, rest); *\/ */
+
+/*   uid_t uid = user_get_uid(username); */
+
+/*   if(uid == -1) { */
+/*     // TODO: send 404 response here */
+/*     log_error("User not found: %s", username); */
+/*     free(buf); */
+/*     return -1; */
+/*   } else if(uid == -2) { */
+/*     // TODO: send 500 response here */
+/*     log_error("Failed to get uid for user: %s", username); */
+/*     free(buf); */
+/*     return -1; */
+/*   } */
+
+/*   // TODO: add check for UID > MIN_UID */
+/*   //   (we don't want to fork storage workers for system users) */
+
+/*   // rewrite first line to only include file path */
+
+/*   int new_len = sprintf(buf, "%s /%s %s\r\n%s", verb, file_path, http_version, rest); */
+/*   log_debug("reallocating to %d bytes", new_len + 1); */
+/*   buf = realloc(buf, ++new_len); */
+/*   if(buf == NULL) { */
+/*     log_error("BUG: realloc() failed to shrink buffer from %d to %d bytes: %s", */
+/*               buf_len + 1, new_len + 1, strerror(errno)); */
+/*   } */
+/*   buf_len = new_len; */
+
+/*   struct rs_process_info *storage_process = process_find_uid(uid); */
+/*   if(storage_process == NULL) { */
+/*     storage_process = start_storage_process(uid); */
+/*     if(storage_process == NULL) { */
+/*       log_error("Failed to start storage process!"); */
+/*       free(buf); */
+/*       return -1; */
+/*     } */
+/*   } */
+/*   forward_to_process(storage_process, fd, buf, buf_len); */
+
+/*   free(buf); */
+
+/*   return 0; */
+/* } */
index 1eebc92..d7f8baa 100644 (file)
  * Gets parsed requests and performs the requested actions / sends the requested
  * response.
  *
- * These functions are only called from storage processes.
  */
 
 static char *escape_name(const char *name);
 static char *make_etag(struct stat *stat_buf);
-static char *make_disk_path(char *user, char *path);
+static char *make_disk_path(char *user, char *path, char **storage_root);
 static evhtp_res serve_directory(evhtp_request_t *request, char *disk_path,
                                  struct stat *stat_buf);
 static int serve_file_head(evhtp_request_t *request_t, char *disk_path,
@@ -43,19 +42,67 @@ evhtp_res storage_handle_get(evhtp_request_t *request) {
   return handle_get_or_head(request, 1);
 }
 
-int storage_begin_put(struct rs_request *request) {
-  char *path_copy = strdup(request->path);
-  if(path_copy == NULL) {
+/* struct rs_put_context { */
+/*   int fd; */
+/*   struct evbuffer *buf; */
+/*   evhtp_request_t *request; */
+/* }; */
+
+/* static void finalize_put(struct rs_put_context *context) { */
+/*   close(context->fd); */
+/*   evbuffer_free(context->buf); */
+/*   evhtp_send_reply(context->request, 200); */
+/*   free(context); */
+/* } */
+
+/* static void write_from_put(struct bufferevent *bev, void *arg) { */
+/*   log_debug("write_from_put()"); */
+/*   struct rs_put_context *context = arg; */
+/*   bufferevent_read_buffer(bev, context->buf); */
+/*   size_t count = evbuffer_get_length(context->buf); */
+/*   if(count == 0) { */
+/*     log_debug("got 0 bytes, finalizing"); */
+/*     finalize_put(context); */
+/*   } else { */
+/*     log_debug("got %d bytes, writing", count); */
+/*     evbuffer_write(context->buf, context->fd); */
+/*   } */
+/* } */
+
+/* static void put_event(struct bufferevent *bev, short what, void *arg) { */
+/*   log_debug("PUT event:"); */
+/*   log_debug(" - BEV_EVENT_READING: %d", (what & BEV_EVENT_READING) ? 1 : 0); */
+/*   log_debug(" - BEV_EVENT_WRITING: %d", (what & BEV_EVENT_WRITING) ? 1 : 0); */
+/*   log_debug(" - BEV_EVENT_EOF: %d", (what & BEV_EVENT_EOF) ? 1 : 0); */
+/*   log_debug(" - BEV_EVENT_ERROR: %d", (what & BEV_EVENT_ERROR) ? 1 : 0); */
+/*   log_debug(" - BEV_EVENT_TIMEOUT: %d", (what & BEV_EVENT_TIMEOUT) ? 1 : 0); */
+/*   log_debug(" - BEV_EVENT_CONNECTED: %d", (what & BEV_EVENT_CONNECTED) ? 1 : 0); */
+/* } */
+
+evhtp_res storage_handle_put(evhtp_request_t *request) {
+  char *storage_root = NULL;
+  char *disk_path = make_disk_path(request->uri->path->match_start,
+                                   request->uri->path->match_end,
+                                   &storage_root);
+  if(disk_path == NULL) {
+    return 500;
+  }
+  char *disk_path_copy = strdup(disk_path);
+  if(disk_path_copy == NULL) {
     log_error("strdup() failed: %s", strerror(errno));
+    free(disk_path);
+    free(storage_root);
     return 500;
   }
-  char *dir_path = dirname(path_copy);
+  char *dir_path = dirname(disk_path_copy);
   char *saveptr = NULL;
   char *dir_name;
-  int dirfd = open("/", O_RDONLY), prevfd;
+  int dirfd = open(storage_root, O_RDONLY), prevfd;
   if(dirfd == -1) {
-    log_error("failed to open() root directory: %s", strerror(errno));
-    free(path_copy);
+    log_error("failed to open() storage path (\"%s\"): %s", storage_root, strerror(errno));
+    free(disk_path);
+    free(disk_path_copy);
+    free(storage_root);
     return 500;
   }
   struct stat dir_stat;
@@ -65,9 +112,11 @@ int storage_begin_put(struct rs_request *request) {
     if(fstatat(dirfd, dir_name, &dir_stat, 0) == 0) {
       if(! S_ISDIR(dir_stat.st_mode)) {
         // exists, but not a directory
-        log_error("Can't PUT to %s, found a non-directory parent.", request->path);
+        log_error("Can't PUT to %s, found a non-directory parent.", request->uri->path->full);
         close(dirfd);
-        free(path_copy);
+        free(disk_path);
+        free(disk_path_copy);
+        free(storage_root);
         return 400;
       } else {
         // directory exists
@@ -76,7 +125,9 @@ int storage_begin_put(struct rs_request *request) {
       if(mkdirat(dirfd, dir_name, S_IRWXU | S_IRWXG) != 0) {
         log_error("mkdirat() failed: %s", strerror(errno));
         close(dirfd);
-        free(path_copy);
+        free(disk_path);
+        free(disk_path_copy);
+        free(storage_root);
         return 500;
       }
     }
@@ -86,81 +137,65 @@ int storage_begin_put(struct rs_request *request) {
     if(dirfd == -1) {
       log_error("failed to openat() next directory (\"%s\"): %s",
                 dir_name, strerror(errno));
-      free(path_copy);
+      free(disk_path);
+      free(disk_path_copy);
+      free(storage_root);
       return 500;
     }
   }
 
-  char *expectation = NULL;
-  struct rs_header *header;
-  for(header = request->headers;
-      header != NULL;
-      header = header->next) {
-    if(strcmp(header->key, "Expect") == 0) {
-      expectation = header->value;
-      break;
-    }
-  }
-
-  // dirname() possibly made previous copy unusable
-  strcpy(path_copy, request->path);
-  char *file_name = basename(path_copy);
+  free(disk_path_copy);
+  free(storage_root);
+  close(dirfd);
 
   // open (and possibly create) file
-  int fd = openat(dirfd, file_name,
-                  O_NONBLOCK | O_CREAT | O_WRONLY | O_TRUNC,
-                  RS_FILE_CREATE_MODE);
-  free(path_copy);
-  close(dirfd);
+  int fd = open(disk_path, O_NONBLOCK | O_CREAT | O_WRONLY | O_TRUNC,
+                RS_FILE_CREATE_MODE);
 
   if(fd == -1) {
-    log_error("openat() failed to open file \"%s\": %s", file_name, strerror(errno));
+    log_error("open() failed to open file \"%s\": %s", disk_path, strerror(errno));
+    free(disk_path);
     return 500;
   }
 
-  request->file_fd = fd;
+  /* struct rs_put_context *context = malloc(sizeof(struct rs_put_context)); */
+  /* if(context == NULL) { */
+  /*   log_error("malloc() failed: %s", strerror(errno)); */
+  /*   return 500; */
+  /* } */
 
-  if(expectation != NULL) {
-    if(strcasecmp(expectation, "100-continue") == 0) {
-      send_response_head(request, 100, NULL);
-    } else {
-      log_error("Expectation \"%s\" cannot be met.", expectation);
-      return 417;
-    }
-  }
+  evbuffer_write(request->buffer_in, fd);
 
-  return 0;
-}
-
-int storage_end_put(struct rs_request *request) {
-  struct stat stat_buf;
-  log_debug("fstatting now");
-  if(fstat(request->file_fd, &stat_buf) != 0) {
-    log_error("fstat() after PUT failed: %s", strerror(errno));
-    return 500;
-  }
-  log_debug("trying to find content-type header");
-  struct rs_header *header;
   char *content_type = "application/octet-stream; charset=binary";
-  for(header = request->headers;
-      header != NULL;
-      header = header->next) {
-    if(strcmp(header->key, "Content-Type") == 0) {
-      log_debug("got content type header value: %s", header->value);
-      content_type = header->value;
-      break;
-    }
+  evhtp_kv_t *content_type_header = evhtp_headers_find_header(request->headers_in, "Content-Type");
+
+  if(content_type_header != NULL) {
+    content_type = content_type_header->val;
   }
-  if(content_type_to_xattr(request->file_fd, content_type) != 0) {
+  
+  if(content_type_to_xattr(fd, content_type) != 0) {
     log_error("Setting xattr for content type failed. Ignoring.");
   }
-  close(request->file_fd);
-  int head_result = serve_file_head(request, request->path, &stat_buf, content_type);
-  if(head_result != 0) {
-    return head_result;
+
+  close(fd);
+
+  struct stat stat_buf;
+  memset(&stat_buf, 0, sizeof(struct stat));
+  if(stat(disk_path, &stat_buf) != 0) {
+    log_error("failed to stat() file after writing: %s", strerror(errno));
+    free(disk_path);
+    return 500;
   }
-  send_response_empty(request);
-  return 0;
+
+  char *etag_string = make_etag(&stat_buf);
+
+  ADD_RESP_HEADER_CP(request, "Content-Type", content_type);
+  ADD_RESP_HEADER_CP(request, "ETag", etag_string);
+
+  free(etag_string);
+  free(disk_path);
+
+  return EVHTP_RES_OK;
 }
 
 int storage_handle_delete(struct rs_request *request) {
@@ -456,7 +491,7 @@ static int serve_file(evhtp_request_t *request, const char *disk_path, struct st
   return 0;
 }
 
-static char *make_disk_path(char *user, char *path) {
+static char *make_disk_path(char *user, char *path, char **storage_root) {
 
   // FIXME: use passwd->pwdir instead of /home/{user}/
 
@@ -470,6 +505,15 @@ static char *make_disk_path(char *user, char *path) {
     log_error("malloc() failed: %s", strerror(errno));
     return NULL;
   }
+  if(storage_root) {
+    *storage_root = malloc( 6 + RS_HOME_SERVE_ROOT_LEN + 1 + strlen(user) );
+    if(*storage_root == NULL) {
+      log_error("malloc() failed: %s", strerror(errno));
+      free(disk_path);
+      return NULL;
+    }
+    sprintf(*storage_root, "/home/%s/%s", user, RS_HOME_SERVE_ROOT);
+  }
   // remove all /.. segments
   // (we don't try to resolve them, but instead treat them as garbage)
   char *traverse_pos = NULL;
@@ -486,7 +530,7 @@ static char *make_disk_path(char *user, char *path) {
 
 static evhtp_res handle_get_or_head(evhtp_request_t *request, int include_body) {
   char *disk_path = make_disk_path(request->uri->path->match_start,
-                                   request->uri->path->match_end);
+                                   request->uri->path->match_end, NULL);
   if(disk_path == NULL) {
     return 500;
   }
index b8933c6..763ee82 100644 (file)
@@ -15,8 +15,8 @@
 
 evhtp_res storage_handle_head(evhtp_request_t *request);
 evhtp_res storage_handle_get(evhtp_request_t *request);
-int storage_begin_put(struct rs_request *request);
-int storage_end_put(struct rs_request *request);
+evhtp_res storage_begin_put(evhtp_request_t *request);
+//evhtp_res storage_end_put(struct rs_request *request);
 int storage_handle_delete(struct rs_request *request);
 
 #endif /* !RS_HANDLER_STORAGE_H */
index d4f5a0f..6737fc0 100644 (file)
@@ -151,25 +151,24 @@ static evhtp_res receive_headers(evhtp_request_t *req, evhtp_headers_t *hdr, voi
 }
 
 static void handle_storage(evhtp_request_t *req, void *arg) {
-  evhtp_res status = 0;
   switch(req->method) {
   case htp_method_GET:
-    status = storage_handle_get(req);
+    req->status = storage_handle_get(req);
     break;
   case htp_method_HEAD:
-    status = storage_handle_head(req);
+    req->status = storage_handle_head(req);
     break;
-  //case htp_method_PUT:
-  //  status = storage_handle_put(req);
-  //  break;
+  case htp_method_PUT:
+    req->status = storage_handle_put(req);
+   break;
   //case htp_method_DELETE:
-  //  status = storage_handle_delete(req);
+  //  req->status = storage_handle_delete(req);
   //  break;
   default:
-    status = EVHTP_RES_METHNALLOWED;
+    req->status = EVHTP_RES_METHNALLOWED;
   }
-  if(status != 0) {
-    evhtp_send_reply(req, status);
+  if(req->status != 0) {
+    evhtp_send_reply(req, req->status);
   }
 }