Example #1
0
File: curl.c Project: calind/nbdkit
static size_t
header_cb (void *ptr, size_t size, size_t nmemb, void *opaque)
{
  struct curl_handle *h = opaque;
  size_t realsize = size * nmemb;
  size_t len;
  const char *accept_line = "Accept-Ranges: bytes";
  const char *line = ptr;

  if (realsize >= strlen (accept_line) &&
      strncmp (line, accept_line, strlen (accept_line)) == 0)
    h->accept_range = 1;

  /* Useful to print the server headers when debugging.  However we
   * must strip off trailing \r?\n from each line.
   */
  len = realsize;
  if (len > 0 && line[len-1] == '\n')
    len--;
  if (len > 0 && line[len-1] == '\r')
    len--;
  if (len > 0)
    nbdkit_debug ("S: %.*s", (int) len, line);

  return realsize;
}
Example #2
0
/* Convert a list of extents into NBD_REPLY_TYPE_BLOCK_STATUS blocks.
 * The rules here are very complicated.  Read the spec carefully!
 */
static struct block_descriptor *
extents_to_block_descriptors (struct nbdkit_extents *extents,
                              uint16_t flags,
                              uint32_t count, uint64_t offset,
                              size_t *nr_blocks)
{
  const bool req_one = flags & NBD_CMD_FLAG_REQ_ONE;
  const size_t nr_extents = nbdkit_extents_count (extents);
  size_t i;
  struct block_descriptor *blocks;

  /* This is checked in server/plugins.c. */
  assert (nr_extents >= 1);

  /* We may send fewer than nr_extents blocks, but never more. */
  blocks = calloc (req_one ? 1 : nr_extents, sizeof (struct block_descriptor));
  if (blocks == NULL) {
    nbdkit_error ("calloc: %m");
    return NULL;
  }

  if (req_one) {
    const struct nbdkit_extent e = nbdkit_get_extent (extents, 0);

    /* Checked as a side effect of how the extent list is created. */
    assert (e.length > 0);

    *nr_blocks = 1;

    /* Must not exceed count of the original request. */
    blocks[0].length = MIN (e.length, (uint64_t) count);
    blocks[0].status_flags = e.type & 3;
  }
  else {
    uint64_t pos = offset;

    *nr_blocks = 0;
    for (i = 0; i < nr_extents; ++i) {
      const struct nbdkit_extent e = nbdkit_get_extent (extents, i);
      uint64_t length;

      if (i == 0)
        assert (e.offset == offset);

      /* Must not exceed UINT32_MAX. */
      blocks[i].length = length = MIN (e.length, UINT32_MAX);
      blocks[i].status_flags = e.type & 3;
      (*nr_blocks)++;

      pos += length;
      if (pos > offset + count) /* this must be the last block */
        break;

      /* If we reach here then we must have consumed this whole
       * extent.  This is currently true because the server only sends
       * 32 bit requests, but if we move to 64 bit requests we will
       * need to revisit this code so it can split extents into
       * multiple blocks.  XXX
       */
      assert (e.length <= length);
    }
  }

#if 0
  for (i = 0; i < *nr_blocks; ++i)
    nbdkit_debug ("block status: sending block %" PRIu32 " type %" PRIu32,
                  blocks[i].length, blocks[i].status_flags);
#endif

  /* Convert to big endian for the protocol. */
  for (i = 0; i < *nr_blocks; ++i) {
    blocks[i].length = htobe32 (blocks[i].length);
    blocks[i].status_flags = htobe32 (blocks[i].status_flags);
  }

  return blocks;
}
Example #3
0
File: curl.c Project: calind/nbdkit
/* Create the per-connection handle. */
static void *
curl_open (int readonly)
{
  struct curl_handle *h;
  CURLcode r;
  double d;

  h = calloc (1, sizeof *h);
  if (h == NULL) {
    nbdkit_error ("calloc: %m");
    return NULL;
  }

  h->c = curl_easy_init ();
  if (h->c == NULL) {
    nbdkit_error ("curl_easy_init: failed: %m");
    goto err;
  }

  nbdkit_debug ("opened libcurl easy handle");

  curl_easy_setopt (h->c, CURLOPT_ERRORBUFFER, h->errbuf);

  r = curl_easy_setopt (h->c, CURLOPT_URL, url);
  if (r != CURLE_OK) {
    display_curl_error (h, r, "curl_easy_setopt: CURLOPT_URL [%s]", url);
    goto err;
  }

  nbdkit_debug ("set libcurl URL: %s", url);

  /* Other possible settings, which could be specified on the command line:
CURLOPT_PROXY
  */
  curl_easy_setopt (h->c, CURLOPT_AUTOREFERER, 1);
  curl_easy_setopt (h->c, CURLOPT_FOLLOWLOCATION, 1);
  curl_easy_setopt (h->c, CURLOPT_FAILONERROR, 1);
  if (timeout > 0)
    curl_easy_setopt (h->c, CURLOPT_TIMEOUT, timeout);
  if (sslverify == 0)
    curl_easy_setopt (h->c, CURLOPT_SSL_VERIFYPEER, sslverify);
  if (user)
    curl_easy_setopt (h->c, CURLOPT_USERNAME, user);
  if (password)
    curl_easy_setopt (h->c, CURLOPT_USERPWD, password);

  /* Get the file size and also whether the remote HTTP server
   * supports byte ranges.
   */
  h->accept_range = 0;
  curl_easy_setopt (h->c, CURLOPT_NOBODY, 1); /* No Body, not nobody! */
  curl_easy_setopt (h->c, CURLOPT_HEADERFUNCTION, header_cb);
  curl_easy_setopt (h->c, CURLOPT_HEADERDATA, h);
  r = curl_easy_perform (h->c);
  if (r != CURLE_OK) {
    display_curl_error (h, r, "problem doing HEAD request to fetch size of URL [%s]", url);
    goto err;
  }

  r = curl_easy_getinfo (h->c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
  if (r != CURLE_OK) {
    display_curl_error (h, r, "could not get length of remote file [%s]", url);
    goto err;
  }

  if (d == -1) {
    nbdkit_error ("could not get length of remote file [%s], is the URL correct?", url);
    goto err;
  }

  h->exportsize = (size_t) d;
  nbdkit_debug ("content length: %" PRIi64, h->exportsize);

  if (strncasecmp (url, "http://", strlen ("http://")) == 0 ||
      strncasecmp (url, "https://", strlen ("https://")) == 0) {
    if (!h->accept_range) {
      nbdkit_error ("server does not support 'range' (byte range) requests");
      goto err;
    }

    nbdkit_debug ("accept range supported (for HTTP/HTTPS)");
  }

  /* Get set up for reading and writing. */
  curl_easy_setopt (h->c, CURLOPT_HEADERFUNCTION, NULL);
  curl_easy_setopt (h->c, CURLOPT_HEADERDATA, NULL);
  curl_easy_setopt (h->c, CURLOPT_WRITEFUNCTION, write_cb);
  curl_easy_setopt (h->c, CURLOPT_WRITEDATA, h);
  if (!readonly) {
    curl_easy_setopt (h->c, CURLOPT_READFUNCTION, read_cb);
    curl_easy_setopt (h->c, CURLOPT_READDATA, h);
  }

  nbdkit_debug ("returning new handle %p", h);

  return h;

 err:
  if (h->c)
    curl_easy_cleanup (h->c);
  free (h);
  return NULL;
}
Example #4
0
/* Create the per-connection handle. */
static void *
split_open (int readonly)
{
  struct handle *h;
  int flags;
  size_t i;
  uint64_t offset;
  struct stat statbuf;

  h = malloc (sizeof *h);
  if (h == NULL) {
    nbdkit_error ("malloc: %m");
    return NULL;
  }

  h->files = malloc (nr_files * sizeof (struct file));
  if (h->files == NULL) {
    nbdkit_error ("malloc: %m");
    free (h);
    return NULL;
  }
  for (i = 0; i < nr_files; ++i)
    h->files[i].fd = -1;

  /* Open the files. */
  flags = O_CLOEXEC|O_NOCTTY;
  if (readonly)
    flags |= O_RDONLY;
  else
    flags |= O_RDWR;

  for (i = 0; i < nr_files; ++i) {
    h->files[i].fd = open (filenames[i], flags);
    if (h->files[i].fd == -1) {
      nbdkit_error ("open: %s: %m", filenames[i]);
      goto err;
    }
  }

  offset = 0;
  for (i = 0; i < nr_files; ++i) {
    h->files[i].offset = offset;

    if (fstat (h->files[i].fd, &statbuf) == -1) {
      nbdkit_error ("stat: %s: %m", filenames[i]);
      goto err;
    }
    h->files[i].size = statbuf.st_size;
    offset += statbuf.st_size;

    nbdkit_debug ("file[%zu]=%s: offset=%" PRIu64 ", size=%" PRIu64,
                  i, filenames[i], h->files[i].offset, h->files[i].size);
  }
  h->size = offset;
  nbdkit_debug ("total size=%" PRIu64, h->size);

  return h;

 err:
  for (i = 0; i < nr_files; ++i) {
    if (h->files[i].fd >= 0)
      close (h->files[i].fd);
  }
  free (h->files);
  free (h);
  return NULL;
}