/** 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); }
/** * 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; }
/** * _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); }
/** 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); }