Demonstration release of the principles underpinning krsd.
[krsd] / src / handler / storage.c
index 1540835..d3579bd 100644 (file)
  *
  */
 
-static char *make_etag(struct stat *stat_buf);
-static char *make_disk_path(char *user, char *path, char **storage_root);
+static char *make_disk_path(char *dom_user, char *path, gss_buffer_t authuser, char **storage_root);
 static evhtp_res serve_directory(evhtp_request_t *request, char *disk_path,
                                  struct stat *stat_buf);
 static evhtp_res serve_file_head(evhtp_request_t *request_t, char *disk_path,
                            struct stat *stat_buf,const char *mime_type);
 static evhtp_res serve_file(evhtp_request_t *request, const char *disk_path,
                       struct stat *stat_buf);
-static evhtp_res handle_get_or_head(evhtp_request_t *request, int include_body);
-static int content_type_to_xattr(int fd, const char *content_type);
-static int compare_version(struct stat *stat_buf, const char *expected);
+static evhtp_res handle_get_or_head(evhtp_request_t *request, gss_buffer_t authuser, int include_body);
 
-evhtp_res storage_handle_head(evhtp_request_t *request) {
-  return handle_get_or_head(request, 0);
+evhtp_res storage_handle_head(evhtp_request_t *request, gss_buffer_t authuser) {
+  if(RS_EXPERIMENTAL) {
+    return handle_get_or_head(request, authuser, 0);
+  } else {
+    return EVHTP_RES_METHNALLOWED;
+  }
 }
 
-evhtp_res storage_handle_get(evhtp_request_t *request) {
+evhtp_res storage_handle_get(evhtp_request_t *request, gss_buffer_t authuser) {
   log_debug("storage_handle_get()");
-  return handle_get_or_head(request, 1);
+  return handle_get_or_head(request, authuser, 1);
 }
 
-evhtp_res storage_handle_put(evhtp_request_t *request) {
+evhtp_res storage_handle_put(evhtp_request_t *request, gss_buffer_t authuser) {
   log_debug("HANDLE PUT");
 
   if(request->uri->path->file == NULL) {
@@ -51,11 +52,12 @@ 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,
+  char *disk_path = make_disk_path(REQUEST_GET_USER(request),
+                                   REQUEST_GET_PATH(request),
+                                  authuser,
                                    &storage_root);
   if(disk_path == NULL) {
-    return 500;
+    return EVHTP_RES_SERVERR;
   }
 
   // check if file exists (needed for preconditions and response code)
@@ -71,7 +73,7 @@ evhtp_res storage_handle_put(evhtp_request_t *request) {
     // current version.
 
     evhtp_header_t *if_match = evhtp_headers_find_header(request->headers_in, "If-Match");
-    if(if_match && ((!exists) || compare_version(&stat_buf, if_match->val) != 0)) {
+    if(if_match && ((!exists) || strcmp(get_etag(disk_path), if_match->val) != 0)) {
       return 412;
     }
 
@@ -86,17 +88,38 @@ evhtp_res storage_handle_put(evhtp_request_t *request) {
 
   } while(0);
 
+#if 0
+  // look up uid and gid of current user, so we can chown() correctly.
+  uid_t uid;
+  gid_t gid;
+  do {
+    char *bufptr = NULL;
+    struct passwd *user_entry = user_get_entry(REQUEST_GET_USER(request), &bufptr);
+    if(user_entry > 0) {
+      uid = user_entry->pw_uid;
+      gid = user_entry->pw_gid;
+      free(user_entry);
+      free(bufptr);
+    } else {
+      return EVHTP_RES_SERVERR;
+    }
+  } while(0);
+#endif
+
   // create parent directories
   do {
 
-    char *path_copy = strdup(request->uri->path->match_end);
+    char *path_copy = strdup(REQUEST_GET_PATH(request));
     if(path_copy == NULL) {
       log_error("strdup() failed: %s", strerror(errno));
       free(disk_path);
       free(storage_root);
-      return 500;
+      return EVHTP_RES_SERVERR;
     }
     char *dir_path = dirname(path_copy);
+    if(strcmp(dir_path, ".") == 0) { // PUT to file below root directory
+      continue;
+    }
     char *saveptr = NULL;
     char *dir_name;
     int dirfd = open(storage_root, O_RDONLY), prevfd;
@@ -105,9 +128,10 @@ evhtp_res storage_handle_put(evhtp_request_t *request) {
       free(disk_path);
       free(path_copy);
       free(storage_root);
-      return 500;
+      return EVHTP_RES_SERVERR;
     }
     struct stat dir_stat;
+    log_debug("strtok_r(\"%s\", ...), (dir_path: %p, saveptr: %p)", dir_path, dir_path, saveptr);
     for(dir_name = strtok_r(dir_path, "/", &saveptr);
         dir_name != NULL;
         dir_name = strtok_r(NULL, "/", &saveptr)) {
@@ -130,8 +154,14 @@ evhtp_res storage_handle_put(evhtp_request_t *request) {
           free(disk_path);
           free(path_copy);
           free(storage_root);
-          return 500;
+          return EVHTP_RES_SERVERR;
         }
+
+#if 0
+        if(fchownat(dirfd, dir_name, uid, gid, AT_SYMLINK_NOFOLLOW) != 0) {
+          log_warn("failed to chown() newly created directory: %s", strerror(errno));
+        }
+#endif
       }
       prevfd = dirfd;
       dirfd = openat(prevfd, dir_name, O_RDONLY);
@@ -142,7 +172,7 @@ evhtp_res storage_handle_put(evhtp_request_t *request) {
         free(disk_path);
         free(path_copy);
         free(storage_root);
-        return 500;
+        return EVHTP_RES_SERVERR;
       }
     }
 
@@ -159,7 +189,15 @@ evhtp_res storage_handle_put(evhtp_request_t *request) {
   if(fd == -1) {
     log_error("open() failed to open file \"%s\": %s", disk_path, strerror(errno));
     free(disk_path);
-    return 500;
+    return EVHTP_RES_SERVERR;
+  }
+
+  if(! exists) {
+#if 0
+    if(fchown(fd, uid, gid) != 0) {
+      log_warn("Failed to chown() newly created file: %s", strerror(errno));
+    }
+#endif
   }
 
   // write buffered data
@@ -174,7 +212,7 @@ evhtp_res storage_handle_put(evhtp_request_t *request) {
   }
   
   // remember content type in extended attributes
-  if(content_type_to_xattr(fd, content_type) != 0) {
+  if(content_type_to_xattr(disk_path, content_type) != 0) {
     log_error("Setting xattr for content type failed. Ignoring.");
   }
 
@@ -185,10 +223,10 @@ evhtp_res storage_handle_put(evhtp_request_t *request) {
   if(stat(disk_path, &stat_buf) != 0) {
     log_error("failed to stat() file after writing: %s", strerror(errno));
     free(disk_path);
-    return 500;
+    return EVHTP_RES_SERVERR;
   }
 
-  char *etag_string = make_etag(&stat_buf);
+  char *etag_string = get_etag(disk_path);
 
   ADD_RESP_HEADER_CP(request, "Content-Type", content_type);
   ADD_RESP_HEADER_CP(request, "ETag", etag_string);
@@ -199,7 +237,7 @@ evhtp_res storage_handle_put(evhtp_request_t *request) {
   return exists ? EVHTP_RES_OK : EVHTP_RES_CREATED;
 }
 
-evhtp_res storage_handle_delete(evhtp_request_t *request) {
+evhtp_res storage_handle_delete(evhtp_request_t *request, gss_buffer_t authuser) {
 
   if(request->uri->path->file == NULL) {
     // DELETE to directories aren't allowed
@@ -207,11 +245,12 @@ evhtp_res storage_handle_delete(evhtp_request_t *request) {
   }
 
   char *storage_root = NULL;
-  char *disk_path = make_disk_path(request->uri->path->match_start,
-                                   request->uri->path->match_end,
+  char *disk_path = make_disk_path(REQUEST_GET_USER(request),
+                                   REQUEST_GET_PATH(request),
+                                  authuser,
                                    &storage_root);
   if(disk_path == NULL) {
-    return 500;
+    return EVHTP_RES_SERVERR;
   }
 
   struct stat stat_buf;
@@ -221,20 +260,29 @@ evhtp_res storage_handle_delete(evhtp_request_t *request) {
       return 400;
     }
 
+    char *etag_string = get_etag(disk_path);
+
+    evhtp_header_t *if_match = evhtp_headers_find_header(request->headers_in, "If-Match");
+    if(if_match && (strcmp(etag_string, if_match->val) != 0)) {
+      return 412;
+    }
+
+    ADD_RESP_HEADER_CP(request, "ETag", etag_string);
+
     // file exists, delete it.
     if(unlink(disk_path) == -1) {
       log_error("unlink() failed: %s", strerror(errno));
-      return 500;
+      return EVHTP_RES_SERVERR;
     }
     
     /* 
      * remove empty parents
      */
-    char *path_copy = strdup(request->uri->path->match_end);
+    char *path_copy = strdup(REQUEST_GET_PATH(request));
     if(path_copy == NULL) {
       log_error("strdup() failed to copy path: %s", strerror(errno));
       free(disk_path);
-      return 500;
+      return EVHTP_RES_SERVERR;
     }
     char *dir_path;
     int rootdirfd = open(storage_root, O_RDONLY);
@@ -242,7 +290,7 @@ evhtp_res storage_handle_delete(evhtp_request_t *request) {
       log_error("failed to open() storage root: %s", strerror(errno));
       free(path_copy);
       free(disk_path);
-      return 500;
+      return EVHTP_RES_SERVERR;
     }
     int result;
     // skip leading slash
@@ -262,14 +310,15 @@ evhtp_res storage_handle_delete(evhtp_request_t *request) {
           log_error("unlinkat() failed to remove parent directory: %s", strerror(errno));
           free(path_copy);
           free(disk_path);
-          return 500;
+          return EVHTP_RES_SERVERR;
         }
       }
     }
     close(rootdirfd);
     free(path_copy);
   } else {
-    // file doesn't exist, ignore it.
+    // file doesn't exist, return 404.
+    return 404;
   }
 
   free(storage_root);
@@ -277,16 +326,6 @@ evhtp_res storage_handle_delete(evhtp_request_t *request) {
   return 200;
 }
 
-static char *make_etag(struct stat *stat_buf) {
-  char *etag = malloc(21);
-  if(etag == NULL) {
-    log_error("malloc() failed: %s", strerror(errno));
-    return NULL;
-  }
-  snprintf(etag, 20, "%lld", ((long long int)stat_buf->st_mtime) * 1000);
-  return etag;
-}
-
 size_t json_buf_writer(char *buf, size_t count, void *arg) {
   return evbuffer_add((struct evbuffer*)arg, buf, count);
 }
@@ -298,7 +337,7 @@ static evhtp_res serve_directory(evhtp_request_t *request, char *disk_path, stru
   DIR *dir = opendir(disk_path);
   if(dir == NULL) {
     log_error("opendir() failed: %s", strerror(errno));
-    return 500;
+    return EVHTP_RES_SERVERR;
   }
   struct dirent *entryp = malloc(offsetof(struct dirent, d_name) +
                                  pathconf(disk_path, _PC_NAME_MAX) + 1);
@@ -306,7 +345,7 @@ static evhtp_res serve_directory(evhtp_request_t *request, char *disk_path, stru
   if(entryp == NULL) {
     log_error("malloc() failed while creating directory pointer: %s",
               strerror(errno));
-    return 500;
+    return EVHTP_RES_SERVERR;
   }
 
   struct json *json = new_json(json_buf_writer, buf);
@@ -334,7 +373,7 @@ static evhtp_res serve_directory(evhtp_request_t *request, char *disk_path, stru
     char key_string[entry_len + 2];
     sprintf(key_string, "%s%s", entryp->d_name,
             S_ISDIR(file_stat_buf.st_mode) ? "/": "");
-    char *val_string = make_etag(&file_stat_buf);
+    char *val_string = get_etag(full_path);
 
     json_write_key_val(json, key_string, val_string);
 
@@ -345,119 +384,70 @@ static evhtp_res serve_directory(evhtp_request_t *request, char *disk_path, stru
 
   free_json(json);
 
-  char *etag = make_etag(stat_buf);
+  char *etag = get_etag(disk_path);
   if(etag == NULL) {
-    log_error("make_etag() failed");
+    log_error("get_etag() failed");
     free(entryp);
     closedir(dir);
-    return 500;
+    return EVHTP_RES_SERVERR;
   }
 
   ADD_RESP_HEADER(request, "Content-Type", "application/json; charset=UTF-8");
   ADD_RESP_HEADER_CP(request, "ETag", etag);
 
-  evhtp_send_reply(request, EVHTP_RES_OK);
   free(etag);
   free(entryp);
   closedir(dir);
-  return 0;
+  return EVHTP_RES_OK;
 }
 
-static char *get_xattr(const char *path, const char *key, int maxlen) {
-  int len = 32;
-  char *value = malloc(32);
-  for(value = malloc(len);len<=maxlen;value = realloc(value, len+=16)) {
-    if(value == NULL) {
-      log_error("malloc() / realloc() failed: %s", strerror(errno));
-      return NULL;
+static evhtp_res serve_file_head(evhtp_request_t *request, char *disk_path, struct stat *stat_buf, const char *mime_type) {
+
+  log_debug("serve file head");
+
+  if(request->uri->path->file == NULL) {
+    log_debug("HEAD dir requested");
+    // directory was requested
+    if(! S_ISDIR(stat_buf->st_mode)) {
+      log_debug("HEAD file found");
+      // but is actually a file
+      return EVHTP_RES_NOTFOUND;
     }
-    int actual_len = getxattr(path, key, value, len);
-    if(actual_len > 0) {
-      return value;
-    } else {
-      if(errno == ERANGE) {
-        // buffer too small.
-        continue;
-      } else if(errno == ENOATTR) {
-        // attribute not set
-        free(value);
-        return NULL;
-      } else if(errno == ENOTSUP) {
-        // xattr not supported
-        log_error("File system doesn't support extended attributes! You may want to use another one.");
-        free(value);
-        return NULL;
-      } else {
-        log_error("Unexpected error while getting %s attribute: %s", key, strerror(errno));
-        free(value);
-        return NULL;
-      }
+    log_debug("HEAD directory found");
+  } else {
+    log_debug("HEAD file requested");
+    // file was requested
+    if(S_ISDIR(stat_buf->st_mode)) {
+      log_debug("HEAD directory found");
+      // but is actually a directory
+      return EVHTP_RES_NOTFOUND;
     }
+    log_debug("HEAD file found");
   }
-  log_error("%s attribute seems to be longer than %d bytes. That is simply unreasonable.", key, maxlen);
-  free(value);
-  return NULL;
-}
-
-static char *content_type_from_xattr(const char *path) {
-  char *mime_type = get_xattr(path, "user.mime_type", 128);
-  if(mime_type == NULL) {
-    return NULL;
-  }
-  char *charset = get_xattr(path, "user.charset", 64);
-  if(charset == NULL) {
-    return mime_type;
-  }
-  int mt_len = strlen(mime_type);
-  char *content_type = realloc(mime_type, mt_len + strlen(charset) + 10 + 1);
-  if(content_type == NULL) {
-    log_error("realloc() failed: %s", strerror(errno));
-    free(charset);
-    return mime_type;
+    
+  char *etag_string = get_etag(disk_path);
+  if(etag_string == NULL) {
+    log_error("get_etag() failed");
+    return EVHTP_RES_SERVERR;
   }
-  sprintf(content_type + mt_len, "; charset=%s", charset);
-  free(charset);
-  return content_type;
-}
 
-static int content_type_to_xattr(int fd, const char *content_type) {
-  char *content_type_copy = strdup(content_type), *saveptr = NULL;
-  if(content_type_copy == NULL) {
-    log_error("strdup() failed: %s", strerror(errno));
-    return -1;
-  }
-  char *mime_type = strtok_r(content_type_copy, ";", &saveptr);
-  log_debug("extracted mime type: %s", mime_type);
-  char *rest = strtok_r(NULL, "", &saveptr);
-  char *charset_begin = NULL, *charset = NULL;
-  if(rest) {
-    charset_begin = strstr(rest, "charset=");
-    if(charset_begin) {
-      charset = charset_begin + 8;
-      log_debug("extracted charset: %s", charset);
+  evhtp_header_t *if_none_match_header = evhtp_headers_find_header(request->headers_in, "If-None-Match");
+  if(if_none_match_header) {
+    // FIXME: support multiple comma-separated ETags in If-None-Match header
+    if(strcmp(if_none_match_header->val, etag_string) == 0) {
+      free(etag_string);
+      return EVHTP_RES_NOTMOD;
     }
   }
-  if(charset == NULL) {
-    // FIXME: should this rather be binary or us-ascii?
-    charset = "UTF-8";
-    log_debug("guessed charset: %s", charset);
-  }
-  if(fsetxattr(fd, "user.mime_type", mime_type, strlen(mime_type) + 1, 0) != 0) {
-    log_error("fsetxattr() failed: %s", strerror(errno));
-    free(content_type_copy);
-    return -1;
-  }
-  if(fsetxattr(fd, "user.charset", charset, strlen(charset) + 1, 0) != 0) {
-    log_error("fsetxattr() failed: %s", strerror(errno));
-    free(content_type_copy);
-    return -1;
-  }
-  free(content_type_copy);
-  return 0;
-}
 
+  char *length_string = malloc(24);
+  if(length_string == NULL) {
+    log_error("malloc() failed: %s", strerror(errno));
+    free(etag_string);
+    return EVHTP_RES_SERVERR;
+  }
+  snprintf(length_string, 24, "%ld", stat_buf->st_size);
 
-static evhtp_res serve_file_head(evhtp_request_t *request, char *disk_path, struct stat *stat_buf, const char *mime_type) {
   int free_mime_type = 0;
   // mime type is either passed in ... (such as for directory listings)
   if(mime_type == NULL) {
@@ -477,18 +467,6 @@ static evhtp_res serve_file_head(evhtp_request_t *request, char *disk_path, stru
       free_mime_type = 1;
     }
   }
-  char *length_string = malloc(24);
-  if(length_string == NULL) {
-    log_error("malloc() failed: %s", strerror(errno));
-    return 500;
-  }
-  snprintf(length_string, 24, "%ld", stat_buf->st_size);
-  char *etag_string = make_etag(stat_buf);
-  if(etag_string == NULL) {
-    log_error("make_etag() failed");
-    free(length_string);
-    return 500;
-  }
 
   log_info("setting Content-Type of %s: %s", request->uri->path->full, mime_type);
   ADD_RESP_HEADER_CP(request, "Content-Type", mime_type);
@@ -508,40 +486,78 @@ static evhtp_res serve_file(evhtp_request_t *request, const char *disk_path, str
   int fd = open(disk_path, O_RDONLY | O_NONBLOCK);
   if(fd < 0) {
     log_error("open() failed: %s", strerror(errno));
-    return 500;
+    return EVHTP_RES_SERVERR;
   }
   while(evbuffer_read(request->buffer_out, fd, 4096) != 0);
-  evhtp_send_reply(request, EVHTP_RES_OK);
-  return 0;
+  close(fd);
+  return EVHTP_RES_OK;
 }
 
-static char *make_disk_path(char *user, char *path, char **storage_root) {
+static char *make_disk_path(char *dom_user, char *path, gss_buffer_t authuser, char **storage_root) {
 
   // FIXME: use passwd->pwdir instead of /home/{user}/
 
   // calculate maximum length of path
-  int pathlen = ( strlen(user) + strlen(path) +
+  int pathlen = ( strlen(dom_user) + strlen(path) +
                   6 + // "/home/"
                   1 + // another slash
                   RS_HOME_SERVE_ROOT_LEN );
   char *disk_path = malloc(pathlen + 1);
+  char *xsfile = NULL;
+  FILE *xsf;
+  char principal [1026];
+  bool authorized;
   if(disk_path == NULL) {
     log_error("malloc() failed: %s", strerror(errno));
     return NULL;
   }
-  if(storage_root) {
-    *storage_root = malloc( 7 + RS_HOME_SERVE_ROOT_LEN + strlen(user) + 1);
-    if(*storage_root == NULL) {
-      log_error("malloc() failed: %s", strerror(errno));
-      free(disk_path);
-      return NULL;
+  log_debug("Constructing disk_path for dom_user = \"%s\"", dom_user);
+  xsfile = malloc( 7 + RS_HOME_SERVE_ROOT_LEN + strlen(dom_user) + 1 + 17);
+  if(xsfile == NULL) {
+    log_error("malloc() failed: %s", strerror(errno));
+    free(disk_path);
+    return NULL;
+  }
+  sprintf(xsfile, "/home/%s/%s/.k5remotestorage", dom_user, RS_HOME_SERVE_ROOT);
+  log_debug("Access control list = \"%s\"", xsfile);
+  xsf = fopen (xsfile, "r");
+  authorized = false;
+  if (xsf) {
+    while ((!authorized) && fgets (principal, sizeof (principal)-1, xsf)) {
+      int len = strlen (principal);
+      if ((len > 1) && (principal [len-1] == '\n')) {
+        principal [--len] = '\0';
+      }
+      log_debug("Considering acceptable principal \"%s\"", principal);
+      authorized = (len == authuser->length) && (0 == memcmp (principal, authuser->value, len));
     }
-    sprintf(*storage_root, "/home/%s/%s", user, RS_HOME_SERVE_ROOT);
+    fclose (xsf);
+  } else {
+    log_error ("Failed to open access control list");
+    free(xsfile);
+    free(disk_path);
+    return NULL;
+  }
+  if (!authorized) {
+    log_error ("Access control list does not contain authorized user");
+    free(xsfile);
+    free(disk_path);
+    return NULL;
+  }
+  log_debug ("xsfile = \"%s\"", xsfile);
+  if(storage_root) {
+    // Cut off .k5remotestorage and reuse for *storage_root
+    xsfile [7 + RS_HOME_SERVE_ROOT_LEN + strlen (dom_user)] = '\0';
+    *storage_root = xsfile;
+    log_debug ("storage_root = \"%s\"", storage_root);
+  } else {
+    free (xsfile);
+    xsfile = NULL;
   }
   // remove all /.. segments
   // (we don't try to resolve them, but instead treat them as garbage)
   char *pos = NULL;
-  while((pos = strstr(path, "/..")) != NULL) {
+  while((pos = strstr(path, "/..")) != NULL) { // FIXME: this would also filter out valid paths like /foo/..bar
     int restlen = strlen(pos + 3);
     memmove(pos, pos + 3, restlen);
     pos[restlen] = 0;
@@ -553,42 +569,36 @@ static char *make_disk_path(char *user, char *path, char **storage_root) {
     pos[restlen] = 0;
   }
   // build path
-  sprintf(disk_path, "/home/%s/%s%s", user, RS_HOME_SERVE_ROOT, path);
+  sprintf(disk_path, "/home/%s/%s%s", dom_user, RS_HOME_SERVE_ROOT, path);
+  log_debug ("disk_path = \"%s\"", disk_path);
   return disk_path;
 }
 
-static evhtp_res handle_get_or_head(evhtp_request_t *request, int include_body) {
+static evhtp_res handle_get_or_head(evhtp_request_t *request, gss_buffer_t authuser, int include_body) {
 
   log_debug("HANDLE GET / HEAD (body: %s)", include_body ? "true" : "false");
 
-  char *storage_root = NULL;
-  char *disk_path = make_disk_path(request->uri->path->match_start,
-                                   request->uri->path->match_end,
-                                   &storage_root);
+  char *disk_path = make_disk_path(REQUEST_GET_USER(request),
+                                   REQUEST_GET_PATH(request),
+                                  authuser,
+                                   NULL);
   if(disk_path == NULL) {
-    return 500;
+    return EVHTP_RES_SERVERR;
   }
 
-  free(storage_root);
-
   // stat
   struct stat stat_buf;
   if(stat(disk_path, &stat_buf) != 0) {
-    if(errno != ENOENT) {
+    if(errno != ENOENT && errno != ENOTDIR) {
       log_error("stat() failed for path \"%s\": %s", disk_path, strerror(errno));
-      return EVHTP_SERVERR;
+      return EVHTP_RES_SERVERR;
     } else {
-      return EVHTP_NOTFOUND;
+      return EVHTP_RES_NOTFOUND;
     }
   }
   // check for directory
   if(request->uri->path->file == NULL) {
     // directory requested
-    if(! S_ISDIR(stat_buf.st_mode)) {
-      // not a directory.
-      return EVHTP_RES_NOTFOUND;
-    }
-    // directory found
     if(include_body) {
       return serve_directory(request, disk_path, &stat_buf);
     } else {
@@ -597,12 +607,7 @@ static evhtp_res handle_get_or_head(evhtp_request_t *request, int include_body)
     }
   } else {
     // file requested
-    if(S_ISDIR(stat_buf.st_mode)) {
-      // found, but is a directory
-      return EVHTP_RES_OK;
-    }
-    // file found
-    int head_result = serve_file_head(request, disk_path, &stat_buf, NULL);
+    evhtp_res head_result = serve_file_head(request, disk_path, &stat_buf, NULL);
     if(head_result != 0) {
       return head_result;
     }
@@ -613,9 +618,3 @@ static evhtp_res handle_get_or_head(evhtp_request_t *request, int include_body)
     }
   }
 }
-
-static int compare_version(struct stat *stat_buf, const char *expected) {
-  char version_string[32];
-  sprintf(version_string, "%lld", ((long long int) stat_buf->st_mtime) * 1000);
-  return strcmp(version_string, expected);
-}