/** writev against a gridfs file */
ssize_t
mongoc_gridfs_file_writev (mongoc_gridfs_file_t *file,
                           mongoc_iovec_t       *iov,
                           size_t                iovcnt,
                           uint32_t              timeout_msec)
{
   uint32_t bytes_written = 0;
   int32_t r;
   size_t i;
   uint32_t iov_pos;

   ENTRY;

   BSON_ASSERT (file);
   BSON_ASSERT (iov);
   BSON_ASSERT (iovcnt);
   BSON_ASSERT (timeout_msec <= INT_MAX);

   /* TODO: we should probably do something about timeout_msec here */

   for (i = 0; i < iovcnt; i++) {
      iov_pos = 0;

      for (;; ) {
         if (!file->page) {
            _mongoc_gridfs_file_refresh_page (file);
         }

         r = _mongoc_gridfs_file_page_write (file->page,
                                            (uint8_t *)iov[i].iov_base + iov_pos,
                                            (uint32_t)(iov[i].iov_len - iov_pos));
         BSON_ASSERT (r >= 0);

         iov_pos += r;
         file->pos += r;
         bytes_written += r;

         file->length = BSON_MAX (file->length, (int64_t)file->pos);

         if (iov_pos == iov[i].iov_len) {
            /** filled a bucket, keep going */
            break;
         } else {
            /** flush the buffer, the next pass through will bring in a new page
             *
             * Our file pointer is now on the new page, so push it back one so
             * that flush knows to flush the old page rather than a new one.
             * This is a little hacky
             */
            file->pos--;
            _mongoc_gridfs_file_flush_page (file);
            file->pos++;
         }
      }
   }

   file->is_dirty = 1;

   RETURN (bytes_written);
}
Exemple #2
0
/**
 * mongoc_gridfs_file_seek:
 *
 *    Adjust the file position pointer in `file` by `delta`, starting from the
 *    position `whence`. The `whence` argument is interpreted as in fseek(2):
 *
 *       SEEK_SET    Set the position relative to the start of the file.
 *       SEEK_CUR    Move `delta` from the current file position.
 *       SEEK_END    Move `delta` from the end-of-file.
 *
 * Parameters:
 *
 *    @file: A mongoc_gridfs_file_t.
 *    @delta: The amount to move. May be positive or negative.
 *    @whence: One of SEEK_SET, SEEK_CUR or SEEK_END.
 *
 * Errors:
 *
 *    [EINVAL] `whence` is not one of SEEK_SET, SEEK_CUR or SEEK_END.
 *    [EINVAL] Resulting file position would be negative.
 *
 * Side Effects:
 *
 *    On success, the file's underlying position pointer is set appropriately.
 *    On failure, the file position is NOT changed and errno is set.
 *
 * Returns:
 *
 *    0 on success.
 *    -1 on error, and errno set to indicate the error.
 */
int
mongoc_gridfs_file_seek (mongoc_gridfs_file_t *file, int64_t delta, int whence)
{
   int64_t offset;

   BSON_ASSERT (file);

   switch (whence) {
   case SEEK_SET:
      offset = delta;
      break;
   case SEEK_CUR:
      offset = file->pos + delta;
      break;
   case SEEK_END:
      offset = file->length + delta;
      break;
   default:
      errno = EINVAL;
      return -1;

      break;
   }

   if (offset < 0) {
      errno = EINVAL;
      return -1;
   }

   if (offset / file->chunk_size != file->n) {
      /** no longer on the same page */

      if (file->page) {
         if (_mongoc_gridfs_file_page_is_dirty (file->page)) {
            if (!_mongoc_gridfs_file_flush_page (file)) {
               return -1;
            }
         } else {
            _mongoc_gridfs_file_page_destroy (file->page);
            file->page = NULL;
         }
      }

      /** we'll pick up the seek when we fetch a page on the next action.  We
       * lazily load */
   } else if (file->page) {
      BSON_ASSERT (
         _mongoc_gridfs_file_page_seek (file->page, offset % file->chunk_size));
   }

   file->pos = offset;
   file->n = file->pos / file->chunk_size;

   return 0;
}
/** Seek in a gridfs file to a given location
 *
 * @param whence is regular fseek whence.  I.e. SEEK_SET, SEEK_CUR or SEEK_END
 *
 */
int
mongoc_gridfs_file_seek (mongoc_gridfs_file_t *file,
                         bson_uint64_t         delta,
                         int                   whence)
{
   bson_uint64_t offset;

   BSON_ASSERT(file);

   switch (whence) {
   case SEEK_SET:
      offset = delta;
      break;
   case SEEK_CUR:
      offset = file->pos + delta;
      break;
   case SEEK_END:
      offset = (file->length - 1) + delta;
      break;
   default:
      errno = EINVAL;
      return -1;

      break;
   }

   BSON_ASSERT (file->length > offset);

   if (offset % file->chunk_size != file->pos % file->chunk_size) {
      /** no longer on the same page */

      if (file->page) {
         if (_mongoc_gridfs_file_page_is_dirty (file->page)) {
            _mongoc_gridfs_file_flush_page (file);
         } else {
            _mongoc_gridfs_file_page_destroy (file->page);
         }
      }

      /** we'll pick up the seek when we fetch a page on the next action.  We lazily load */
   } else {
      _mongoc_gridfs_file_page_seek (file->page, offset % file->chunk_size);
   }

   file->pos = offset;

   return 0;
}
Exemple #4
0
/**
 * _mongoc_gridfs_file_extend:
 *
 *      Extend a GridFS file to the current position pointer. Zeros will be
 *      appended to the end of the file until file->length is even with
 * file->pos.
 *
 *      If file->length >= file->pos, the function exits successfully with no
 *      operation performed.
 *
 * Parameters:
 *      @file: A mongoc_gridfs_file_t.
 *
 * Returns:
 *      The number of zero bytes written, or -1 on failure.
 */
static ssize_t
_mongoc_gridfs_file_extend (mongoc_gridfs_file_t *file)
{
   int64_t target_length;
   ssize_t diff;

   ENTRY;

   BSON_ASSERT (file);

   if (file->length >= file->pos) {
      RETURN (0);
   }

   diff = (ssize_t) (file->pos - file->length);
   target_length = file->pos;
   if (-1 == mongoc_gridfs_file_seek (file, 0, SEEK_END)) {
      RETURN (-1);
   }

   while (true) {
      if (!file->page && !_mongoc_gridfs_file_refresh_page (file)) {
         RETURN (-1);
      }

      /* Set bytes until we reach the limit or fill a page */
      file->pos += _mongoc_gridfs_file_page_memset0 (file->page,
                                                     target_length - file->pos);

      if (file->pos == target_length) {
         /* We're done */
         break;
      } else if (!_mongoc_gridfs_file_flush_page (file)) {
         /* We tried to flush a full buffer, but an error occurred */
         RETURN (-1);
      }
   }

   file->length = target_length;
   file->is_dirty = true;

   RETURN (diff);
}
/** save a gridfs file */
bool
mongoc_gridfs_file_save (mongoc_gridfs_file_t *file)
{
   bson_t *selector, *update, child;
   const char *md5;
   const char *filename;
   const char *content_type;
   const bson_t *aliases;
   const bson_t *metadata;
   bool r;

   ENTRY;

   if (!file->is_dirty) {
      return 1;
   }

   if (file->page && _mongoc_gridfs_file_page_is_dirty (file->page)) {
      _mongoc_gridfs_file_flush_page (file);
   }

   md5 = mongoc_gridfs_file_get_md5 (file);
   filename = mongoc_gridfs_file_get_filename (file);
   content_type = mongoc_gridfs_file_get_content_type (file);
   aliases = mongoc_gridfs_file_get_aliases (file);
   metadata = mongoc_gridfs_file_get_metadata (file);

   selector = bson_new ();
   bson_append_value (selector, "_id", -1, &file->files_id);

   update = bson_new ();
   bson_append_document_begin (update, "$set", -1, &child);
   bson_append_int64 (&child, "length", -1, file->length);
   bson_append_int32 (&child, "chunkSize", -1, file->chunk_size);
   bson_append_date_time (&child, "uploadDate", -1, file->upload_date);

   if (md5) {
      bson_append_utf8 (&child, "md5", -1, md5, -1);
   }

   if (filename) {
      bson_append_utf8 (&child, "filename", -1, filename, -1);
   }

   if (content_type) {
      bson_append_utf8 (&child, "contentType", -1, content_type, -1);
   }

   if (aliases) {
      bson_append_array (&child, "aliases", -1, aliases);
   }

   if (metadata) {
      bson_append_document (&child, "metadata", -1, metadata);
   }

   bson_append_document_end (update, &child);

   r = mongoc_collection_update (file->gridfs->files, MONGOC_UPDATE_UPSERT,
                                 selector, update, NULL, &file->error);

   file->failed = !r;


   bson_destroy (selector);
   bson_destroy (update);

   file->is_dirty = 0;

   RETURN (r);
}
Exemple #6
0
/** writev against a gridfs file
 *  timeout_msec is unused */
ssize_t
mongoc_gridfs_file_writev (mongoc_gridfs_file_t *file,
                           const mongoc_iovec_t *iov,
                           size_t iovcnt,
                           uint32_t timeout_msec)
{
   uint32_t bytes_written = 0;
   int32_t r;
   size_t i;
   uint32_t iov_pos;

   ENTRY;

   BSON_ASSERT (file);
   BSON_ASSERT (iov);
   BSON_ASSERT (iovcnt);

   /* Pull in the correct page */
   if (!file->page && !_mongoc_gridfs_file_refresh_page (file)) {
      return -1;
   }

   /* When writing past the end-of-file, fill the gap with zeros */
   if (file->pos > file->length && !_mongoc_gridfs_file_extend (file)) {
      return -1;
   }

   for (i = 0; i < iovcnt; i++) {
      iov_pos = 0;

      for (;;) {
         if (!file->page && !_mongoc_gridfs_file_refresh_page (file)) {
            return -1;
         }

         /* write bytes until an iov is exhausted or the page is full */
         r = _mongoc_gridfs_file_page_write (
            file->page,
            (uint8_t *) iov[i].iov_base + iov_pos,
            (uint32_t) (iov[i].iov_len - iov_pos));
         BSON_ASSERT (r >= 0);

         iov_pos += r;
         file->pos += r;
         bytes_written += r;

         file->length = BSON_MAX (file->length, (int64_t) file->pos);

         if (iov_pos == iov[i].iov_len) {
            /** filled a bucket, keep going */
            break;
         } else {
            /** flush the buffer, the next pass through will bring in a new page
             */
            if (!_mongoc_gridfs_file_flush_page (file)) {
               return -1;
            }
         }
      }
   }

   file->is_dirty = 1;

   RETURN (bytes_written);
}