summaryrefslogtreecommitdiff
path: root/src/sync-gridfs-chunk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sync-gridfs-chunk.c')
-rw-r--r--src/sync-gridfs-chunk.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/sync-gridfs-chunk.c b/src/sync-gridfs-chunk.c
new file mode 100644
index 0000000..9bcc62e
--- /dev/null
+++ b/src/sync-gridfs-chunk.c
@@ -0,0 +1,329 @@
+/* sync-gridfs-chunk.c - libmongo-client GridFS chunk access implementation
+ * Copyright 2011, 2012 Gergely Nagy <algernon@balabit.hu>
+ *
+ * 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 <unistd.h>
+#include <errno.h>
+
+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;
+}