libmongo-client 0.1.4
|
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 }