libmongo-client 0.1.4
src/sync-gridfs-chunk.c
Go to the documentation of this file.
00001 /* sync-gridfs-chunk.c - libmongo-client GridFS chunk access implementation
00002  * Copyright 2011 Gergely Nagy <algernon@balabit.hu>
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00021 #include "sync-gridfs-chunk.h"
00022 #include "libmongo-private.h"
00023 
00024 #include <unistd.h>
00025 #include <errno.h>
00026 
00027 void
00028 mongo_sync_gridfs_chunked_file_free (mongo_sync_gridfs_chunked_file *gfile)
00029 {
00030   if (!gfile)
00031     {
00032       errno = ENOTCONN;
00033       return;
00034     }
00035   bson_free (gfile->meta.metadata);
00036   g_free (gfile);
00037 
00038   errno = 0;
00039 }
00040 
00041 mongo_sync_gridfs_chunked_file *
00042 mongo_sync_gridfs_chunked_find (mongo_sync_gridfs *gfs, const bson *query)
00043 {
00044   mongo_sync_gridfs_chunked_file *f;
00045   mongo_packet *p;
00046   bson_cursor *c;
00047 
00048   if (!gfs)
00049     {
00050       errno = ENOTCONN;
00051       return NULL;
00052     }
00053   if (!query)
00054     {
00055       errno = EINVAL;
00056       return NULL;
00057     }
00058 
00059   p = mongo_sync_cmd_query (gfs->conn, gfs->ns.files, 0, 0, 1, query, NULL);
00060   if (!p)
00061     return NULL;
00062 
00063   f = g_new0 (mongo_sync_gridfs_chunked_file, 1);
00064   f->gfs = gfs;
00065   f->meta.type = LMC_GRIDFS_FILE_CHUNKED;
00066 
00067   mongo_wire_reply_packet_get_nth_document (p, 1, &f->meta.metadata);
00068   bson_finish (f->meta.metadata);
00069   mongo_wire_packet_free (p);
00070 
00071   c = bson_find (f->meta.metadata, "_id");
00072   if (!bson_cursor_get_oid (c, &f->meta.oid))
00073     {
00074       mongo_sync_gridfs_chunked_file_free (f);
00075       bson_cursor_free (c);
00076       errno = EPROTO;
00077       return NULL;
00078     }
00079 
00080   bson_cursor_find (c, "length");
00081   bson_cursor_get_int64 (c, &f->meta.length);
00082 
00083   if (f->meta.length == 0)
00084     {
00085       gint32 i = 0;
00086 
00087       bson_cursor_get_int32 (c, &i);
00088       f->meta.length = i;
00089     }
00090 
00091   bson_cursor_find (c, "chunkSize");
00092   bson_cursor_get_int32 (c, &f->meta.chunk_size);
00093 
00094   if (f->meta.length == 0 || f->meta.chunk_size == 0)
00095     {
00096       bson_cursor_free (c);
00097       mongo_sync_gridfs_chunked_file_free (f);
00098       errno = EPROTO;
00099       return NULL;
00100     }
00101 
00102   bson_cursor_find (c, "uploadDate");
00103   if (!bson_cursor_get_utc_datetime (c, &f->meta.date))
00104     {
00105       mongo_sync_gridfs_chunked_file_free (f);
00106       bson_cursor_free (c);
00107       errno = EPROTO;
00108       return NULL;
00109     }
00110 
00111   bson_cursor_find (c, "md5");
00112   if (!bson_cursor_get_string (c, &f->meta.md5))
00113     {
00114       mongo_sync_gridfs_chunked_file_free (f);
00115       bson_cursor_free (c);
00116       errno = EPROTO;
00117       return NULL;
00118     }
00119   bson_cursor_free (c);
00120 
00121   return f;
00122 }
00123 
00124 mongo_sync_cursor *
00125 mongo_sync_gridfs_chunked_file_cursor_new (mongo_sync_gridfs_chunked_file *gfile,
00126                                            gint start, gint num)
00127 {
00128   bson *q;
00129   mongo_sync_cursor *cursor;
00130   mongo_packet *p;
00131 
00132   if (!gfile)
00133     {
00134       errno = ENOTCONN;
00135       return NULL;
00136     }
00137   if (start < 0 || num < 0)
00138     {
00139       errno = EINVAL;
00140       return NULL;
00141     }
00142 
00143   q = bson_new_sized (32);
00144   bson_append_oid (q, "files_id", gfile->meta.oid);
00145   bson_finish (q);
00146 
00147   p = mongo_sync_cmd_query (gfile->gfs->conn, gfile->gfs->ns.chunks, 0,
00148                             start, num, q, NULL);
00149   cursor = mongo_sync_cursor_new (gfile->gfs->conn,
00150                                   gfile->gfs->ns.chunks, p);
00151   bson_free (q);
00152 
00153   return cursor;
00154 }
00155 
00156 guint8 *
00157 mongo_sync_gridfs_chunked_file_cursor_get_chunk (mongo_sync_cursor *cursor,
00158                                                  gint32 *size)
00159 {
00160   bson *b;
00161   bson_cursor *c;
00162   const guint8 *d;
00163   guint8 *data;
00164   gint32 s;
00165   bson_binary_subtype sub = BSON_BINARY_SUBTYPE_USER_DEFINED;
00166   gboolean r;
00167 
00168   if (!cursor)
00169     {
00170       errno = ENOTCONN;
00171       return NULL;
00172     }
00173 
00174   b = mongo_sync_cursor_get_data (cursor);
00175   c = bson_find (b, "data");
00176   r = bson_cursor_get_binary (c, &sub, &d, &s);
00177   if (!r || sub != BSON_BINARY_SUBTYPE_GENERIC)
00178     {
00179       bson_cursor_free (c);
00180       errno = EPROTO;
00181       return NULL;
00182     }
00183   bson_cursor_free (c);
00184 
00185   data = g_malloc (s);
00186   memcpy (data, d, s);
00187 
00188   if (size)
00189     *size = s;
00190 
00191   bson_free (b);
00192   return data;
00193 }
00194 
00195 mongo_sync_gridfs_chunked_file *
00196 mongo_sync_gridfs_chunked_file_new_from_buffer (mongo_sync_gridfs *gfs,
00197                                                 const bson *metadata,
00198                                                 const guint8 *data,
00199                                                 gint64 size)
00200 {
00201   mongo_sync_gridfs_chunked_file *gfile;
00202   bson *meta;
00203   bson_cursor *c;
00204   guint8 *oid;
00205   gint64 pos = 0, chunk_n = 0, upload_date;
00206   GTimeVal tv;
00207   GChecksum *chk;
00208 
00209   if (!gfs)
00210     {
00211       errno = ENOTCONN;
00212       return NULL;
00213     }
00214   if (!data || size <= 0)
00215     {
00216       errno = EINVAL;
00217       return NULL;
00218     }
00219 
00220   oid = mongo_util_oid_new
00221     (mongo_connection_get_requestid ((mongo_connection *)gfs->conn));
00222   if (!oid)
00223     {
00224       errno = EFAULT;
00225       return NULL;
00226     }
00227 
00228   chk = g_checksum_new (G_CHECKSUM_MD5);
00229 
00230   /* Insert chunks first */
00231   while (pos < size)
00232     {
00233       bson *chunk;
00234       gint32 csize = gfs->chunk_size;
00235 
00236       if (size - pos < csize)
00237         csize = size - pos;
00238 
00239       chunk = bson_new_sized (gfs->chunk_size + 128);
00240       bson_append_oid (chunk, "files_id", oid);
00241       bson_append_int64 (chunk, "n", (gint64)chunk_n);
00242       bson_append_binary (chunk, "data", BSON_BINARY_SUBTYPE_GENERIC,
00243                           data + pos, csize);
00244       bson_finish (chunk);
00245 
00246       g_checksum_update (chk, data + pos, csize);
00247 
00248       if (!mongo_sync_cmd_insert (gfs->conn, gfs->ns.chunks, chunk, NULL))
00249         {
00250           int e = errno;
00251 
00252           bson_free (chunk);
00253           g_free (oid);
00254           errno = e;
00255           return NULL;
00256         }
00257       bson_free (chunk);
00258 
00259       pos += csize;
00260       chunk_n++;
00261     }
00262 
00263   /* Insert metadata */
00264   if (metadata)
00265     meta = bson_new_from_data (bson_data (metadata),
00266                                bson_size (metadata) - 1);
00267   else
00268     meta = bson_new_sized (128);
00269 
00270   g_get_current_time (&tv);
00271   upload_date =  (((gint64) tv.tv_sec) * 1000) + (gint64)(tv.tv_usec / 1000);
00272 
00273   bson_append_int64 (meta, "length", size);
00274   bson_append_int32 (meta, "chunkSize", gfs->chunk_size);
00275   bson_append_utc_datetime (meta, "uploadDate", upload_date);
00276   bson_append_string (meta, "md5", g_checksum_get_string (chk), -1);
00277   bson_append_oid (meta, "_id", oid);
00278   bson_finish (meta);
00279 
00280   g_checksum_free (chk);
00281 
00282   if (!mongo_sync_cmd_insert (gfs->conn, gfs->ns.files, meta, NULL))
00283     {
00284       int e = errno;
00285 
00286       bson_free (meta);
00287       g_free (oid);
00288       errno = e;
00289       return NULL;
00290     }
00291 
00292   /* Return the resulting gfile.
00293    * No need to check cursor errors here, as we constructed the BSON
00294    * just above, and all the fields exist and have the appropriate
00295    * types.
00296    */
00297   gfile = g_new0 (mongo_sync_gridfs_chunked_file, 1);
00298   gfile->gfs = gfs;
00299 
00300   gfile->meta.metadata = meta;
00301   gfile->meta.length = size;
00302   gfile->meta.chunk_size = gfs->chunk_size;
00303   gfile->meta.date = 0;
00304   gfile->meta.type = LMC_GRIDFS_FILE_CHUNKED;
00305 
00306   c = bson_find (meta, "_id");
00307   bson_cursor_get_oid (c, &gfile->meta.oid);
00308 
00309   bson_cursor_find (c, "md5");
00310   bson_cursor_get_string (c, &gfile->meta.md5);
00311   bson_cursor_free (c);
00312 
00313   return gfile;
00314 }
 All Data Structures Files Functions Variables Enumerations Enumerator Defines