/* sync-gridfs-chunk.c - libmongo-client GridFS chunk access implementation * Copyright 2011, 2012 Gergely Nagy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** @file src/sync-gridfs-chunk.c * MongoDB GridFS chunk access implementation. */ #include "sync-gridfs-chunk.h" #include "libmongo-private.h" #include #include void mongo_sync_gridfs_chunked_file_free (mongo_sync_gridfs_chunked_file *gfile) { if (!gfile) { errno = ENOTCONN; return; } bson_free (gfile->meta.metadata); g_free (gfile); errno = 0; } mongo_sync_gridfs_chunked_file * mongo_sync_gridfs_chunked_find (mongo_sync_gridfs *gfs, const bson *query) { mongo_sync_gridfs_chunked_file *f; mongo_packet *p; bson_cursor *c; if (!gfs) { errno = ENOTCONN; return NULL; } if (!query) { errno = EINVAL; return NULL; } p = mongo_sync_cmd_query (gfs->conn, gfs->ns.files, 0, 0, 1, query, NULL); if (!p) return NULL; f = g_new0 (mongo_sync_gridfs_chunked_file, 1); f->gfs = gfs; f->meta.type = LMC_GRIDFS_FILE_CHUNKED; mongo_wire_reply_packet_get_nth_document (p, 1, &f->meta.metadata); bson_finish (f->meta.metadata); mongo_wire_packet_free (p); c = bson_find (f->meta.metadata, "_id"); if (!bson_cursor_get_oid (c, &f->meta.oid)) { mongo_sync_gridfs_chunked_file_free (f); bson_cursor_free (c); errno = EPROTO; return NULL; } bson_cursor_find (c, "length"); bson_cursor_get_int64 (c, &f->meta.length); if (f->meta.length == 0) { gint32 i = 0; bson_cursor_get_int32 (c, &i); f->meta.length = i; } bson_cursor_find (c, "chunkSize"); bson_cursor_get_int32 (c, &f->meta.chunk_size); if (f->meta.length == 0 || f->meta.chunk_size == 0) { bson_cursor_free (c); mongo_sync_gridfs_chunked_file_free (f); errno = EPROTO; return NULL; } bson_cursor_find (c, "uploadDate"); if (!bson_cursor_get_utc_datetime (c, &f->meta.date)) { mongo_sync_gridfs_chunked_file_free (f); bson_cursor_free (c); errno = EPROTO; return NULL; } bson_cursor_find (c, "md5"); if (!bson_cursor_get_string (c, &f->meta.md5)) { mongo_sync_gridfs_chunked_file_free (f); bson_cursor_free (c); errno = EPROTO; return NULL; } bson_cursor_free (c); return f; } mongo_sync_cursor * mongo_sync_gridfs_chunked_file_cursor_new (mongo_sync_gridfs_chunked_file *gfile, gint start, gint num) { bson *q; mongo_sync_cursor *cursor; mongo_packet *p; if (!gfile) { errno = ENOTCONN; return NULL; } if (start < 0 || num < 0) { errno = EINVAL; return NULL; } q = bson_build_full (BSON_TYPE_DOCUMENT, "$query", TRUE, bson_build (BSON_TYPE_OID, "files_id", gfile->meta.oid, BSON_TYPE_NONE), BSON_TYPE_DOCUMENT, "$orderby", TRUE, bson_build (BSON_TYPE_INT32, "n", 1, BSON_TYPE_NONE), BSON_TYPE_NONE); bson_finish (q); p = mongo_sync_cmd_query (gfile->gfs->conn, gfile->gfs->ns.chunks, 0, start, num, q, NULL); cursor = mongo_sync_cursor_new (gfile->gfs->conn, gfile->gfs->ns.chunks, p); bson_free (q); return cursor; } guint8 * mongo_sync_gridfs_chunked_file_cursor_get_chunk (mongo_sync_cursor *cursor, gint32 *size) { bson *b; bson_cursor *c; const guint8 *d; guint8 *data; gint32 s; bson_binary_subtype sub = BSON_BINARY_SUBTYPE_USER_DEFINED; gboolean r; if (!cursor) { errno = ENOTCONN; return NULL; } b = mongo_sync_cursor_get_data (cursor); c = bson_find (b, "data"); r = bson_cursor_get_binary (c, &sub, &d, &s); if (!r || (sub != BSON_BINARY_SUBTYPE_GENERIC && sub != BSON_BINARY_SUBTYPE_BINARY)) { bson_cursor_free (c); errno = EPROTO; return NULL; } bson_cursor_free (c); if (sub == BSON_BINARY_SUBTYPE_BINARY) { s -= 4; data = g_malloc (s); memcpy (data, d + 4, s); } else { data = g_malloc (s); memcpy (data, d, s); } if (size) *size = s; bson_free (b); return data; } mongo_sync_gridfs_chunked_file * mongo_sync_gridfs_chunked_file_new_from_buffer (mongo_sync_gridfs *gfs, const bson *metadata, const guint8 *data, gint64 size) { mongo_sync_gridfs_chunked_file *gfile; bson *meta; bson_cursor *c; guint8 *oid; gint64 pos = 0, chunk_n = 0, upload_date; GTimeVal tv; GChecksum *chk; if (!gfs) { errno = ENOTCONN; return NULL; } if (!data || size <= 0) { errno = EINVAL; return NULL; } oid = mongo_util_oid_new (mongo_connection_get_requestid ((mongo_connection *)gfs->conn)); if (!oid) { errno = EFAULT; return NULL; } chk = g_checksum_new (G_CHECKSUM_MD5); /* Insert chunks first */ while (pos < size) { bson *chunk; gint32 csize = gfs->chunk_size; if (size - pos < csize) csize = size - pos; chunk = bson_new_sized (gfs->chunk_size + 128); bson_append_oid (chunk, "files_id", oid); bson_append_int64 (chunk, "n", (gint64)chunk_n); bson_append_binary (chunk, "data", BSON_BINARY_SUBTYPE_GENERIC, data + pos, csize); bson_finish (chunk); g_checksum_update (chk, data + pos, csize); if (!mongo_sync_cmd_insert (gfs->conn, gfs->ns.chunks, chunk, NULL)) { int e = errno; bson_free (chunk); g_free (oid); errno = e; return NULL; } bson_free (chunk); pos += csize; chunk_n++; } /* Insert metadata */ if (metadata) meta = bson_new_from_data (bson_data (metadata), bson_size (metadata) - 1); else meta = bson_new_sized (128); g_get_current_time (&tv); upload_date = (((gint64) tv.tv_sec) * 1000) + (gint64)(tv.tv_usec / 1000); bson_append_int64 (meta, "length", size); bson_append_int32 (meta, "chunkSize", gfs->chunk_size); bson_append_utc_datetime (meta, "uploadDate", upload_date); bson_append_string (meta, "md5", g_checksum_get_string (chk), -1); bson_append_oid (meta, "_id", oid); bson_finish (meta); g_checksum_free (chk); if (!mongo_sync_cmd_insert (gfs->conn, gfs->ns.files, meta, NULL)) { int e = errno; bson_free (meta); g_free (oid); errno = e; return NULL; } /* Return the resulting gfile. * No need to check cursor errors here, as we constructed the BSON * just above, and all the fields exist and have the appropriate * types. */ gfile = g_new0 (mongo_sync_gridfs_chunked_file, 1); gfile->gfs = gfs; gfile->meta.metadata = meta; gfile->meta.length = size; gfile->meta.chunk_size = gfs->chunk_size; gfile->meta.date = 0; gfile->meta.type = LMC_GRIDFS_FILE_CHUNKED; c = bson_find (meta, "_id"); bson_cursor_get_oid (c, &gfile->meta.oid); bson_cursor_find (c, "md5"); bson_cursor_get_string (c, &gfile->meta.md5); bson_cursor_free (c); g_free (oid); return gfile; }