1
/* Copyright (c) 2008 PrimeBase Technologies GmbH, Germany
3
* PrimeBase Media Stream for MySQL
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
* Original author: Paul McCullagh
20
* Continued development: Barry Leslie
32
#include <drizzled/common.h>
33
#include <drizzled/session.h>
36
#include "cslib/CSConfig.h"
41
#include "cslib/CSGlobal.h"
42
#include "cslib/CSLog.h"
43
#include "cslib/CSStrUtil.h"
44
#include "cslib/CSHTTPStream.h"
45
#include "cslib/CSStream.h"
47
#include "repository_ms.h"
48
#include "open_table_ms.h"
49
#include "connection_handler_ms.h"
50
#include "metadata_ms.h"
51
#include "parameters_ms.h"
52
#include "pbmsdaemon_ms.h"
55
* ---------------------------------------------------------------
59
MSRepoFile::MSRepoFile():
69
MSRepoFile::~MSRepoFile()
74
void MSRepoFile::updateGarbage(uint64_t size)
76
MSRepoHeadRec repo_head;
80
myRepo->myGarbageCount += size;
81
CS_SET_DISK_8(repo_head.rh_garbage_count_8, myRepo->myGarbageCount);
82
ASSERT(myRepo->myGarbageCount <= myRepo->myRepoFileSize);
83
write(&repo_head.rh_garbage_count_8, offsetof(MSRepoHeadRec, rh_garbage_count_8), 8);
85
if (!myRepo->myRepoXLock)
86
myRepo->signalCompactor();
91
void MSRepoFile::updateAccess(MSBlobHeadPtr blob, uint64_t rep_offset)
93
time_t now = time(NULL);
94
uint32_t count = CS_GET_DISK_4(blob->rb_access_count_4) +1;
96
CS_SET_DISK_4(blob->rb_last_access_4, now);
97
CS_SET_DISK_4(blob->rb_access_count_4, count);
98
write(&blob->rb_last_access_4, rep_offset + offsetof(MSBlobHeadRec, rb_last_access_4), 8);
99
myRepo->myLastAccessTime = now;
102
uint64_t MSRepoFile::readBlobChunk(PBMSBlobIDPtr blob_id, uint64_t rep_offset, uint64_t blob_offset, uint64_t buffer_size, char *buffer)
104
MSBlobHeadRec blob_head;
109
uint64_t offset, blob_read =0;
113
read(&blob_head, rep_offset, sizeof(MSBlobHeadRec), sizeof(MSBlobHeadRec));
114
if (CS_GET_DISK_4(blob_head.rd_magic_4) != MS_BLOB_HEADER_MAGIC)
115
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
117
blob_size = CS_GET_DISK_6(blob_head.rb_blob_repo_size_6);
118
head_size = CS_GET_DISK_2(blob_head.rb_head_size_2);
119
ac = CS_GET_DISK_4(blob_head.rb_auth_code_4);
120
if (blob_id->bi_auth_code != ac)
121
CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB identifier");
123
offset = rep_offset + blob_offset + head_size;
125
if (blob_offset > blob_size)
128
if ((blob_offset + buffer_size) > blob_size)
129
buffer_size = blob_size - blob_offset;
131
while (buffer_size > 0) {
132
if (buffer_size <= (uint64_t) (SSIZE_MAX))
133
tfer = (size_t) buffer_size;
137
read(buffer, offset, tfer, tfer);
138
offset += (uint64_t) tfer;
139
buffer += (uint64_t) tfer;
140
buffer_size -= (uint64_t) tfer;
141
blob_read += (uint64_t) tfer;
144
/* Only update the access timestamp when reading the first block: */
146
updateAccess(&blob_head, rep_offset);
152
void MSRepoFile::writeBlobChunk(PBMSBlobIDPtr blob_id, uint64_t rep_offset, uint64_t blob_offset, uint64_t data_size, char *data)
156
MSBlobHeadRec blob_head;
163
read(&blob_head, rep_offset, sizeof(MSBlobHeadRec), sizeof(MSBlobHeadRec));
164
if (CS_GET_DISK_4(blob_head.rd_magic_4) != MS_BLOB_HEADER_MAGIC)
165
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
167
blob_size = CS_GET_DISK_6(blob_head.rb_blob_repo_size_6);
168
head_size = CS_GET_DISK_2(blob_head.rb_head_size_2);
169
ac = CS_GET_DISK_4(blob_head.rb_auth_code_4);
170
if (blob_id->bi_auth_code != ac)
171
CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB identifier");
173
if ((blob_offset + data_size) > blob_size)
174
CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB write size or offset");
176
offset = (uint64_t) head_size + rep_offset + blob_offset;
178
while (data_size > 0) {
179
if (data_size <= (uint64_t) (SSIZE_MAX))
180
tfer = (size_t) data_size;
184
write(data, offset, tfer);
185
data += (uint64_t) tfer;
186
offset += (uint64_t) tfer;
187
data_size -= (uint64_t) tfer;
193
void MSRepoFile::sendBlob(MSOpenTable *otab, uint64_t offset, uint64_t req_offset, uint64_t req_size, uint32_t auth_code, bool with_auth_code, bool info_only, CSHTTPOutputStream *stream)
195
MSConnectionHandler *me;
197
off64_t start_offset = offset;
198
MSBlobHeadRec blob_head;
199
uint8_t storage_type;
200
uint16_t head_size, meta_size;
201
uint64_t blob_data_size, local_blob_size, meta_offset;
203
char num_str[CS_WIDTH_INT_64];
204
bool redirect = false;
208
me = (MSConnectionHandler *) self;
210
read(&blob_head, start_offset, sizeof(MSBlobHeadRec), sizeof(MSBlobHeadRec));
211
local_blob_size = CS_GET_DISK_6(blob_head.rb_blob_repo_size_6); // This is the size of the BLOB data in the repository. Can be 0 if the BLOB is stored some where else.
212
blob_data_size = CS_GET_DISK_6(blob_head.rb_blob_data_size_6);// This is the actual size of the BLOB.
213
head_size = CS_GET_DISK_2(blob_head.rb_head_size_2);
214
meta_size = CS_GET_DISK_2(blob_head.rb_mdata_size_2);
215
meta_offset = start_offset + CS_GET_DISK_2(blob_head.rb_mdata_offset_2);
216
ac = CS_GET_DISK_4(blob_head.rb_auth_code_4);
217
if ((with_auth_code && auth_code != ac) || (CS_GET_DISK_4(blob_head.rd_magic_4) != MS_BLOB_HEADER_MAGIC))
218
CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB identifier");
220
storage_type = CS_GET_DISK_1(blob_head.rb_storage_type_1);
222
if ((!info_only) && BLOB_IN_CLOUD(storage_type)) {
223
CSString *redirect_url = NULL;
225
getBlobKey(&blob_head, &key);
226
redirect_url = otab->getDB()->myBlobCloud->cl_getDataURL(&key);
228
stream->setStatus(301);
229
stream->addHeader("Location", redirect_url->getCString());
230
release_(redirect_url);
233
stream->setStatus(200);
235
if (storage_type == MS_STANDARD_STORAGE) {
236
char hex_checksum[33];
237
cs_bin_to_hex(33, hex_checksum, 16, blob_head.rb_blob_checksum_md5d.val);
238
hex_checksum[32] = 0;
239
stream->addHeader(MS_CHECKSUM_TAG, hex_checksum);
242
snprintf(num_str, CS_WIDTH_INT_64, "%"PRIu64"", blob_data_size);
243
stream->addHeader(MS_BLOB_SIZE, num_str);
245
snprintf(num_str, CS_WIDTH_INT_64, "%"PRIu32"", CS_GET_DISK_4(blob_head.rb_last_access_4));
246
stream->addHeader(MS_LAST_ACCESS, num_str);
248
snprintf(num_str, CS_WIDTH_INT_64, "%"PRIu32"", CS_GET_DISK_4(blob_head.rb_access_count_4));
249
stream->addHeader(MS_ACCESS_COUNT, num_str);
251
snprintf(num_str, CS_WIDTH_INT_64, "%"PRIu32"", CS_GET_DISK_4(blob_head.rb_create_time_4));
252
stream->addHeader(MS_CREATION_TIME, num_str);
254
snprintf(num_str, CS_WIDTH_INT_64, "%"PRIu32"", storage_type);
255
stream->addHeader(MS_BLOB_TYPE, num_str);
258
// Add the meta data headers.
263
read(otab->myOTBuffer, meta_offset, meta_size, meta_size);
264
metadata.use_data(otab->myOTBuffer, meta_size);
265
while ((name = metadata.findNext(&value))) {
266
stream->addHeader(name, value);
271
offset += (uint64_t) head_size + req_offset;
272
local_blob_size -= req_offset;
273
if (local_blob_size > req_size)
274
local_blob_size = req_size;
276
stream->setContentLength((redirect || info_only)?0:local_blob_size);
278
me->replyPending = false;
280
if ((!redirect) && !info_only) {
282
while (local_blob_size > 0) {
283
if (local_blob_size <= MS_OT_BUFFER_SIZE)
284
tfer = (size_t) local_blob_size;
286
tfer = MS_OT_BUFFER_SIZE;
287
read(otab->myOTBuffer, offset, tfer, tfer);
288
stream->write(otab->myOTBuffer, tfer);
289
offset += (uint64_t) tfer;
290
local_blob_size -= (uint64_t) tfer;
296
// Should the time stamp be updated if only the BLOB info was requested?
297
/* Update the access timestamp: */
298
updateAccess(&blob_head, start_offset);
304
void MSRepoFile::update_blob_header(MSOpenTable *otab, uint64_t offset, uint64_t blob_size, uint16_t head_size, uint16_t new_head_size)
306
uint16_t w_offset = offsetof(MSBlobHeadRec, rb_ref_count_2);
307
MSRepoPointersRec ptr;
310
ptr.rp_chars = otab->myOTBuffer;
311
CS_SET_DISK_4(ptr.rp_head->rb_mod_time_4, time(NULL));
313
if (head_size == new_head_size) {
314
w_offset = offsetof(MSBlobHeadRec, rb_ref_count_2);
315
write(otab->myOTBuffer + w_offset, offset + w_offset, head_size - w_offset);
317
/* Copy to a new space, free the old: */
319
CSStringBuffer *buffer;
320
uint16_t ref_count, ref_size;
324
myRepo->myRepoDatabase->openWriteRepo(otab);
325
dst_offset = otab->myWriteRepo->myRepoFileSize;
327
/* Write the header. */
328
otab->myWriteRepoFile->write(otab->myOTBuffer, dst_offset, new_head_size);
330
/* We have an engine reference, copy the BLOB over: */
331
new_(buffer, CSStringBuffer());
333
buffer->setLength(MS_COMPACTOR_BUFFER_SIZE);
334
CSFile::transfer(otab->myWriteRepoFile, dst_offset + new_head_size, this, offset + head_size, blob_size, buffer->getBuffer(0), MS_COMPACTOR_BUFFER_SIZE);
337
#ifdef HAVE_ALIAS_SUPPORT
338
/* Update the BLOB alias if required. */
340
if (CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2)) {
341
uint32_t alias_hash = CS_GET_DISK_4(ptr.rp_head->rb_alias_hash_4);
342
myRepo->myRepoDatabase->moveBlobAlias(myRepo->myRepoID, offset, alias_hash, myRepo->myRepoID, dst_offset);
346
/* Update the references: */
347
ref_size = CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1);
348
ref_count = CS_GET_DISK_2(ptr.rp_head->rb_ref_count_2);
349
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
352
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
353
case MS_BLOB_FREE_REF:
355
case MS_BLOB_TABLE_REF:
356
tab_id = CS_GET_DISK_4(ptr.rp_tab_ref->tr_table_id_4);
357
blob_id = CS_GET_DISK_6(ptr.rp_tab_ref->tr_blob_id_6);
359
if (otab->getDBTable()->myTableID == tab_id)
360
otab->getDBTable()->updateBlobHandle(otab, blob_id, otab->myWriteRepo->myRepoID, dst_offset, new_head_size);
362
MSOpenTable *ref_otab;
364
ref_otab = MSTableList::getOpenTableByID(myRepo->myRepoDatabase->myDatabaseID, tab_id);
366
ref_otab->getDBTable()->updateBlobHandle(ref_otab, blob_id, otab->myWriteRepo->myRepoID, dst_offset, new_head_size);
367
backtopool_(ref_otab);
370
case MS_BLOB_DELETE_REF:
375
ptr.rp_chars += ref_size;
379
otab->myWriteRepo->myRepoFileSize += new_head_size + blob_size;
381
/* Free the old head: */
382
ptr.rp_chars = otab->myOTBuffer;
383
if (myRepo->lockedForBackup()) {
384
// This is done to tell the backup process that this BLOB was moved
385
// after the backup had started and needs to be backed up also.
386
// (The moved BLOB doesn't though because the change took place after the backup had begone.)
387
CS_SET_DISK_1(ptr.rp_head->rb_status_1, MS_BLOB_MOVED);
388
CS_SET_DISK_4(ptr.rp_head->rb_backup_id_4, myRepo->myRepoDatabase->backupID());
390
CS_SET_DISK_1(ptr.rp_head->rb_status_1, MS_BLOB_DELETED);
392
write(ptr.rp_chars + MS_BLOB_STAT_OFFS, offset + MS_BLOB_STAT_OFFS, head_size - MS_BLOB_STAT_OFFS);
394
#ifdef DO_NOT_WIPE_BLOB
395
// Why is the BLOB header data being wiped here?
396
// The data may be needed for backup.
397
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
398
memset(ptr.rp_chars, 0, head_size - myRepo->myRepoBlobHeadSize);
400
w_offset = offsetof(MSBlobHeadRec, rb_alias_hash_4);
401
write(otab->myOTBuffer + w_offset, offset + w_offset, head_size - w_offset);
404
/* Increment the garbage count: */
405
updateGarbage(head_size + blob_size);
411
void MSRepoFile::referenceBlob(MSOpenTable *otab, uint64_t offset, uint16_t head_size, uint32_t tab_id, uint64_t blob_id, uint64_t blob_ref_id, uint32_t auth_code, uint16_t col_index)
414
MSRepoPointersRec ptr;
415
uint32_t size, ref_count;
416
size_t ref_size, read_size;
417
MSRepoBlobRefPtr free_ref = NULL;
418
MSRepoBlobRefPtr free2_ref = NULL;
419
MSRepoTableRefPtr tab_ref = NULL;
420
uint16_t new_head_size;
421
#ifdef HAVE_ALIAS_SUPPORT
422
bool reset_alias_index = false;
423
char blob_alias[BLOB_ALIAS_LENGTH];
429
myLock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
431
/* Read the header: */
432
if (head_size > MS_OT_BUFFER_SIZE) {
433
CSException::throwAssertion(CS_CONTEXT, "BLOB header overflow");
436
read_size = read(otab->myOTBuffer, offset, head_size, 0);
437
ptr.rp_chars = otab->myOTBuffer;
438
if (CS_GET_DISK_4(ptr.rp_head->rd_magic_4) != MS_BLOB_HEADER_MAGIC)
439
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
440
if (read_size < myRepo->myRepoBlobHeadSize)
441
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB header incomplete");
442
if ( ! IN_USE_BLOB_STATUS(CS_GET_DISK_1(ptr.rp_head->rb_status_1)))
443
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB has already been deleted");
444
if (CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code)
445
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB data does not match reference");
446
/* Assume that what is here is correct: */
447
if (head_size != CS_GET_DISK_2(ptr.rp_head->rb_head_size_2)) {
448
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
449
if (head_size > MS_OT_BUFFER_SIZE) { // Could happen if the header was creatd with a different version of PBMS.
450
CSException::throwAssertion(CS_CONTEXT, "BLOB header overflow");
452
read_size = read(otab->myOTBuffer, offset, head_size, myRepo->myRepoBlobHeadSize);
454
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
455
blob_size = CS_GET_DISK_6(ptr.rp_head->rb_blob_repo_size_6);
456
if (read_size < head_size) {
457
/* This should not happen, because the file has been recovered,
458
* which should have already adjusted the head and blob
460
* If this happens then the file must have been truncated an the BLOB has been
461
* lost so we set the blob size to zero.
463
head_size = read_size;
467
ref_size = CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1);
468
ref_count = CS_GET_DISK_2(ptr.rp_head->rb_ref_count_2);
470
#ifdef HAVE_ALIAS_SUPPORT
471
if (CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2)) {
472
reset_alias_index = true;
473
strcpy(blob_alias, otab->myOTBuffer + CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2));
477
size = head_size - myRepo->myRepoBlobHeadSize;
478
if (size > ref_size * ref_count)
479
size = ref_size * ref_count;
480
CS_SET_DISK_4(ptr.rp_head->rb_last_ref_4, (uint32_t) time(NULL)); // Set the reference time
481
CS_SET_DISK_1(ptr.rp_head->rb_status_1, MS_BLOB_REFERENCED);
482
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
483
while (size >= ref_size) {
484
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
485
case MS_BLOB_FREE_REF:
487
free_ref = ptr.rp_blob_ref;
489
free2_ref = ptr.rp_blob_ref;
491
case MS_BLOB_TABLE_REF:
492
#ifdef HAVE_ALIAS_SUPPORT
493
reset_alias_index = false; // No need to reset the index if the BLOB is already referenced. (We don't care what table references it.)
495
if (CS_GET_DISK_4(ptr.rp_tab_ref->tr_table_id_4) == tab_id &&
496
CS_GET_DISK_6(ptr.rp_tab_ref->tr_blob_id_6) == blob_id)
497
tab_ref = ptr.rp_tab_ref;
499
case MS_BLOB_DELETE_REF: {
502
tab_index = CS_GET_DISK_2(ptr.rp_temp_ref->tp_del_ref_2);
503
if (tab_index && tab_index < ref_count) {
504
MSRepoTableRefPtr tr;
507
tr = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->getRepoBlobHeadSize() + tab_index * ref_size);
508
if (CS_GET_DISK_4(tr->tr_table_id_4) == tab_id &&
509
CS_GET_DISK_6(tr->tr_blob_id_6) == blob_id) {
510
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
512
free2_ref = free_ref;
513
free_ref = ptr.rp_blob_ref;
516
else if (tab_index == INVALID_INDEX) {
517
/* The is a reference from the temporary log only!! */
519
free2_ref = free_ref;
520
free_ref = ptr.rp_blob_ref;
524
default: { // Must be a blob REF, check that the BLOB reference doesn't already exist.
526
tab_index = CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2);
528
if (tab_index && tab_index < ref_count) {
529
MSRepoTableRefPtr tr;
532
tr = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->getRepoBlobHeadSize() + tab_index * ref_size);
533
if (CS_GET_DISK_4(tr->tr_table_id_4) == tab_id &&
534
CS_GET_DISK_6(tr->tr_blob_id_6) == blob_id) {
535
if (COMMIT_MASK(CS_GET_DISK_8(ptr.rp_blob_ref->er_blob_ref_id_8)) == blob_ref_id) {
537
snprintf(message, 100, "Duplicate BLOB reference: db_id: %"PRIu32", tab_id:%"PRIu32", blob_ref_id: %"PRIu64"\n", myRepo->myRepoDatabase->myDatabaseID, tab_id, blob_ref_id);
538
/* The reference already exists so there is nothing to do... */
539
self->myException.log(self, message);
547
ptr.rp_chars += ref_size;
551
// A BLOB reference needs to be added and if there is not
552
// already a table reference then a table reference must be added
554
if (!free_ref || (!tab_ref && !free2_ref)) {
555
size_t new_refs = (tab_ref)?1:2;
556
ptr.rp_chars = otab->myOTBuffer;
557
size_t sp = MS_VAR_SPACE(ptr.rp_head);
559
if (sp > (new_refs * CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1))) {
560
sp = MS_MIN_BLOB_HEAD_SIZE;
563
if (MS_CAN_ADD_REFS(ptr.rp_head, new_refs)) {
564
new_head_size = head_size;
566
} else { // The header must be grown
567
size_t new_size, max_refs;
571
else if (ref_count > 32)
572
max_refs = ref_count + 32;
574
max_refs = 2 * ref_count;
576
if (max_refs > (MS_OT_BUFFER_SIZE/ref_size))
577
max_refs = (MS_OT_BUFFER_SIZE/ref_size);
579
if (max_refs < (ref_count + new_refs))
580
CSException::throwAssertion(CS_CONTEXT, "BLOB reference header overflow");
582
new_size = head_size + ref_size * max_refs;
584
//Shift the metadata in the header
585
if (CS_GET_DISK_2(ptr.rp_head->rb_mdata_size_2)) {
586
uint16_t mdata_size, mdata_offset, alias_offset, shift;
588
shift = new_size - head_size;
589
mdata_size = CS_GET_DISK_2(ptr.rp_head->rb_mdata_size_2);
590
mdata_offset = CS_GET_DISK_2(ptr.rp_head->rb_mdata_offset_2);
591
alias_offset = CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2);
593
memmove(ptr.rp_chars + mdata_offset + shift, ptr.rp_chars + mdata_offset, shift);
594
memset(ptr.rp_chars + mdata_offset, 0, shift);
595
mdata_offset += shift;
596
alias_offset += shift;
598
CS_SET_DISK_2(ptr.rp_head->rb_mdata_offset_2, mdata_offset);
599
CS_SET_DISK_2(ptr.rp_head->rb_alias_offset_2, alias_offset);
602
memset(ptr.rp_chars + head_size, 0, new_size - head_size);
604
new_head_size = new_size;
606
CS_SET_DISK_2(ptr.rp_head->rb_head_size_2, new_head_size);
607
CS_SET_DISK_2(ptr.rp_head->rb_ref_count_2, ref_count + new_refs);
608
ptr.rp_chars += myRepo->myRepoBlobHeadSize + ref_count * ref_size;
611
free_ref = ptr.rp_blob_ref;
612
memset(free_ref, 0, ref_size);
613
ptr.rp_chars += ref_size;
617
free2_ref = ptr.rp_blob_ref;
618
memset(free2_ref, 0, ref_size);
621
ref_count += new_refs;
624
new_head_size = head_size;
627
tab_ref = (MSRepoTableRefPtr) free2_ref;
629
CS_SET_DISK_2(tab_ref->rr_type_2, MS_BLOB_TABLE_REF);
630
CS_SET_DISK_4(tab_ref->tr_table_id_4, tab_id);
631
CS_SET_DISK_6(tab_ref->tr_blob_id_6, blob_id);
636
tab_idx = (((char *) tab_ref - otab->myOTBuffer) - myRepo->myRepoBlobHeadSize) / ref_size;
638
CS_SET_DISK_2(free_ref->er_table_2, tab_idx+1);
639
CS_SET_DISK_2(free_ref->er_col_index_2, col_index);
640
CS_SET_DISK_8(free_ref->er_blob_ref_id_8, UNCOMMITTED(blob_ref_id));
642
update_blob_header(otab, offset, blob_size, head_size, new_head_size);
643
#ifdef HAVE_ALIAS_SUPPORT
644
if (reset_alias_index)
645
myRepo->myRepoDatabase->registerBlobAlias(myRepo->myRepoID, offset, blob_alias);
654
void MSRepoFile::setBlobMetaData(MSOpenTable *otab, uint64_t offset, const char *meta_data, uint16_t meta_data_len, bool reset_alias, const char *alias)
657
MSRepoPointersRec ptr;
659
uint16_t new_head_size;
661
uint16_t head_size, mdata_size, mdata_offset, alias_offset = 0;
666
lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
669
/* Read the header: */
670
if (read(&blob, offset, sizeof(MSBlobHeadRec), 0) < sizeof(MSBlobHeadRec)) {
671
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB header incomplete");
674
head_size = CS_GET_DISK_2(blob.rb_head_size_2);
676
if (head_size > MS_OT_BUFFER_SIZE) {
677
CSException::throwAssertion(CS_CONTEXT, "BLOB header overflow");
680
read_size = read(otab->myOTBuffer, offset, head_size, 0);
681
ptr.rp_chars = otab->myOTBuffer;
682
if (CS_GET_DISK_4(ptr.rp_head->rd_magic_4) != MS_BLOB_HEADER_MAGIC)
683
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
684
if (read_size < myRepo->myRepoBlobHeadSize)
685
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB header incomplete");
686
if (! IN_USE_BLOB_STATUS(CS_GET_DISK_1(ptr.rp_head->rb_status_1)))
687
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB has already been deleted");
690
blob_size = CS_GET_DISK_6(ptr.rp_head->rb_blob_repo_size_6);
691
if (read_size < head_size) {
692
/* This should not happen, because the file has been recovered,
693
* which should have already adjusted the head and blob
695
* If this happens then the file must have been truncated an the BLOB has been
696
* lost so we set the blob size to zero.
698
head_size = read_size;
701
mdata_size = CS_GET_DISK_2(ptr.rp_head->rb_mdata_size_2);
703
if ((meta_data_len < mdata_size) || MS_CAN_ADD_MDATA(ptr.rp_head, meta_data_len - mdata_size))
704
new_head_size = head_size;
705
else { // The header must be grown
707
new_head_size = head_size + meta_data_len - mdata_size;
708
if (new_head_size > MS_OT_BUFFER_SIZE)
709
CSException::throwAssertion(CS_CONTEXT, "BLOB reference header overflow");
711
memset(ptr.rp_chars + head_size, 0, new_head_size - head_size);
714
// Meta data is placed at the end of the header.
716
mdata_offset = new_head_size - meta_data_len;
719
mdata_size = meta_data_len;
722
CS_SET_DISK_2(ptr.rp_head->rb_mdata_size_2, mdata_size);
723
CS_SET_DISK_2(ptr.rp_head->rb_mdata_offset_2, mdata_offset);
725
#ifdef HAVE_ALIAS_SUPPORT
726
uint32_t alias_hash = INVALID_ALIAS_HASH;
728
alias_hash = CS_GET_DISK_4(ptr.rp_head->rb_alias_hash_4);
729
alias_offset = CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2);
732
alias_hash = myRepo->myRepoDatabase->updateBlobAlias(myRepo->myRepoID, offset, alias_hash, alias);
734
alias_hash = myRepo->myRepoDatabase->registerBlobAlias(myRepo->myRepoID, offset, alias);
737
alias_offset = mdata_offset + (alias - meta_data);
739
} else if (reset_alias && CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2)) {
740
alias_offset = CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2);
741
myRepo->myRepoDatabase->deleteBlobAlias(myRepo->myRepoID, offset, CS_GET_DISK_4(ptr.rp_head->rb_alias_hash_4));
745
uint32_t alias_hash = -1;
746
if (alias || reset_alias) {
747
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_IMPLEMENTED, "No BLOB alias support.");
751
CS_SET_DISK_2(ptr.rp_head->rb_alias_offset_2, alias_offset);
752
CS_SET_DISK_4(ptr.rp_head->rb_alias_hash_4, alias_hash);
754
memcpy(ptr.rp_chars + mdata_offset, meta_data, meta_data_len);
756
update_blob_header(otab, offset, blob_size, head_size, new_head_size);
764
void MSRepoFile::releaseBlob(MSOpenTable *otab, uint64_t offset, uint16_t head_size, uint32_t tab_id, uint64_t blob_id, uint64_t blob_ref_id, uint32_t auth_code)
767
MSRepoPointersRec ptr;
768
uint32_t table_ref_count = 0;
770
size_t ref_size, ref_count, read_size;
771
MSRepoTempRefPtr temp_ref = NULL;
772
uint16_t tab_index = 0;
773
MSRepoTableRefPtr tab_ref;
774
uint16_t alias_offset;
779
lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
781
/* Read the header: */
782
ASSERT(head_size <= MS_OT_BUFFER_SIZE);
783
read_size = read(otab->myOTBuffer, offset, head_size, 0);
784
ptr.rp_chars = otab->myOTBuffer;
785
if (CS_GET_DISK_4(ptr.rp_head->rd_magic_4) != MS_BLOB_HEADER_MAGIC)
786
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
787
if (read_size < myRepo->myRepoBlobHeadSize) {
788
removeBlob(otab, tab_id, blob_id, offset, auth_code);
791
if ((! IN_USE_BLOB_STATUS(CS_GET_DISK_1(ptr.rp_head->rb_status_1))) ||
792
CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code) {
793
removeBlob(otab, tab_id, blob_id, offset, auth_code);
797
/* Assume that what is here is correct: */
798
if (head_size != CS_GET_DISK_2(ptr.rp_head->rb_head_size_2)) {
799
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
800
read_size = read(otab->myOTBuffer, offset, head_size, myRepo->myRepoBlobHeadSize);
802
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
803
if (read_size < head_size) {
804
/* This should not happen, because the file has been recovered,
805
* which should have already adjusted the head and blob
807
* If this happens then the file must have been truncated an the BLOB has been
808
* lost so we set the blob size to zero.
810
head_size = read_size;
812
ref_size = CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1);
813
ref_count = CS_GET_DISK_2(ptr.rp_head->rb_ref_count_2);
815
alias_offset = CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2);
816
alias_hash = CS_GET_DISK_4(ptr.rp_head->rb_alias_hash_4);
818
size = head_size - myRepo->myRepoBlobHeadSize;
819
if (size > ref_size * ref_count)
820
size = ref_size * ref_count;
821
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
822
while (size >= ref_size) {
823
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
824
case MS_BLOB_FREE_REF:
825
case MS_BLOB_TABLE_REF:
827
case MS_BLOB_DELETE_REF: {
830
tabi = CS_GET_DISK_2(ptr.rp_temp_ref->tp_del_ref_2);
831
if (tabi && tabi < ref_count) {
833
tab_ref = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->myRepoBlobHeadSize + tabi * ref_size);
834
if (CS_GET_DISK_4(tab_ref->tr_table_id_4) == tab_id &&
835
CS_GET_DISK_6(tab_ref->tr_blob_id_6) == blob_id) {
836
/* This is an old free, take it out. */
837
// Barry: What happens to the record in the temp log associated with this ref
838
// that is waiting to free the BLOB?
839
// Answer: It will find that there is MS_BLOB_DELETE_REF record with the BLOB
840
// or if there is one it will be for a different free in a different temp log
841
// or with a different temp log offset.
842
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
847
default: // Must be a blob REF
848
tab_ref = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->myRepoBlobHeadSize + (CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2)-1) * ref_size);
849
if (CS_GET_DISK_4(tab_ref->tr_table_id_4) == tab_id &&
850
CS_GET_DISK_6(tab_ref->tr_blob_id_6) == blob_id) {
851
if (COMMIT_MASK(CS_GET_DISK_8(ptr.rp_blob_ref->er_blob_ref_id_8)) == blob_ref_id) {
852
/* Found the reference, remove it... */
853
tab_index = CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2)-1;
854
temp_ref = ptr.rp_temp_ref;
855
//temp_ref = (MSRepoTempRefPtr) tab_ref; // Set temp ref to the table ref so that it will be removed if there are no more references to it.
856
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
863
ptr.rp_chars += ref_size;
867
// If the refernce was found and there are no
868
// table references then the BLOB can be scheduled for deletion.
869
if ((!table_ref_count) && temp_ref) {
873
#ifdef HAVE_ALIAS_SUPPORT
874
MSDiskAliasRec aliasDiskRec;
875
MSDiskAliasPtr aliasDiskPtr = NULL;
878
CS_SET_DISK_4(aliasDiskRec.ar_repo_id_4, myRepo->myRepoID);
879
CS_SET_DISK_8(aliasDiskRec.ar_offset_8, offset);
880
CS_SET_DISK_4(aliasDiskRec.ar_hash_4, alias_hash);
881
aliasDiskPtr = &aliasDiskRec;
884
myRepo->myRepoDatabase->queueForDeletion(otab, MS_TL_BLOB_REF, tab_id, blob_id, auth_code, &log_id, &log_offset, &temp_time, aliasDiskPtr);
886
myRepo->myRepoDatabase->queueForDeletion(otab, MS_TL_BLOB_REF, tab_id, blob_id, auth_code, &log_id, &log_offset, &temp_time);
888
myRepo->myLastTempTime = temp_time;
889
CS_SET_DISK_2(temp_ref->rr_type_2, MS_BLOB_DELETE_REF);
890
CS_SET_DISK_2(temp_ref->tp_del_ref_2, tab_index+1);
891
CS_SET_DISK_4(temp_ref->tp_log_id_4, log_id);
892
CS_SET_DISK_4(temp_ref->tp_offset_4, log_offset);
894
CS_SET_DISK_1(ptr.rp_head->rb_status_1, MS_BLOB_ALLOCATED); // The BLOB is allocated but no longer referenced
897
/* The reason I do not write the header of the header, is because
898
* I want to handle the rb_last_access_4 being set at the
901
write(otab->myOTBuffer + MS_BLOB_STAT_OFFS, offset + MS_BLOB_STAT_OFFS, head_size - MS_BLOB_STAT_OFFS);
902
} else if (PBMSDaemon::isDaemonState(PBMSDaemon::DaemonStartUp) == false) {
904
snprintf(message, 100, "BLOB reference not found: db_id: %"PRIu32", tab_id:%"PRIu32", blob_ref_id: %"PRIu64"\n", myRepo->myRepoDatabase->myDatabaseID, tab_id, blob_ref_id);
905
/* The reference already exists so there is nothing to do... */
906
self->myException.log(self, message);
914
void MSRepoFile::commitBlob(MSOpenTable *otab, uint64_t offset, uint16_t head_size, uint32_t tab_id, uint64_t blob_id, uint64_t blob_ref_id, uint32_t auth_code)
917
MSRepoPointersRec ptr;
919
size_t ref_size, ref_count, read_size;
920
MSRepoTableRefPtr tab_ref;
924
lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
926
/* Read the header: */
927
ASSERT(head_size <= MS_OT_BUFFER_SIZE);
928
read_size = read(otab->myOTBuffer, offset, head_size, 0);
929
ptr.rp_chars = otab->myOTBuffer;
930
if (CS_GET_DISK_4(ptr.rp_head->rd_magic_4) != MS_BLOB_HEADER_MAGIC)
931
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
934
if (read_size < myRepo->myRepoBlobHeadSize)
935
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB header incomplete");
936
if ( ! IN_USE_BLOB_STATUS(CS_GET_DISK_1(ptr.rp_head->rb_status_1)))
937
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB has already been deleted");
938
if (auth_code && CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code)
939
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB data does not match reference");
942
/* Assume that what is here is correct: */
943
if (head_size != CS_GET_DISK_2(ptr.rp_head->rb_head_size_2)) {
944
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
945
read_size = read(otab->myOTBuffer, offset, head_size, myRepo->myRepoBlobHeadSize);
948
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
949
if (read_size < head_size) {
950
/* This should not happen, because the file has been recovered,
951
* which should have already adjusted the head and blob
953
* If this happens then the file must have been truncated an the BLOB has been
954
* lost so we set the blob size to zero.
956
head_size = read_size;
958
ref_size = CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1);
959
ref_count = CS_GET_DISK_2(ptr.rp_head->rb_ref_count_2);
961
size = head_size - myRepo->myRepoBlobHeadSize;
962
if (size > ref_size * ref_count)
963
size = ref_size * ref_count;
964
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
965
while (size >= ref_size) {
966
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
967
case MS_BLOB_FREE_REF:
968
case MS_BLOB_TABLE_REF:
970
case MS_BLOB_DELETE_REF: {
973
default: // Must be a blob REF
974
tab_ref = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->myRepoBlobHeadSize + (CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2)-1) * ref_size);
975
if (CS_GET_DISK_4(tab_ref->tr_table_id_4) == tab_id &&
976
CS_GET_DISK_6(tab_ref->tr_blob_id_6) == blob_id) {
977
uint64_t ref_id = CS_GET_DISK_8(ptr.rp_blob_ref->er_blob_ref_id_8);
978
if (COMMIT_MASK(ref_id) == blob_ref_id) {
979
/* Found the reference, mark it as committed... */
980
CS_SET_DISK_8(ptr.rp_blob_ref->er_blob_ref_id_8, blob_ref_id);
981
offset += (ptr.rp_chars - otab->myOTBuffer) + offsetof(MSRepoBlobRefRec, er_blob_ref_id_8);
982
write(&(ptr.rp_blob_ref->er_blob_ref_id_8), offset, 8);
988
ptr.rp_chars += ref_size;
992
if (PBMSDaemon::isDaemonState(PBMSDaemon::DaemonStartUp) == false) {
994
snprintf(message, 100, "BLOB reference not found: db_id: %"PRIu32", tab_id:%"PRIu32", blob_ref_id: %"PRIu64"\n", myRepo->myRepoDatabase->myDatabaseID, tab_id, blob_ref_id);
995
self->myException.log(self, message);
1003
void MSRepoFile::realFreeBlob(MSOpenTable *otab, char *buffer, uint32_t auth_code, uint64_t offset, uint16_t head_size, uint64_t blob_size, size_t ref_size)
1008
MSRepoPointersRec ptr;
1011
ptr.rp_chars = buffer;
1013
if (BLOB_IN_CLOUD(CS_GET_DISK_1(ptr.rp_head->rb_storage_type_1))) {
1015
getBlobKey(ptr.rp_head, &key);
1016
if (!myRepo->myRepoDatabase->myBlobCloud)
1017
CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "Deleting cloud BLOB without cloud.");
1019
myRepo->myRepoDatabase->myBlobCloud->cl_deleteData(&key);
1022
#ifdef HAVE_ALIAS_SUPPORT
1023
uint32_t alias_hash;
1024
alias_hash = CS_GET_DISK_4(ptr.rp_head->rb_alias_hash_4);
1025
if (alias_hash != INVALID_ALIAS_HASH)
1026
myRepo->myRepoDatabase->deleteBlobAlias(myRepo->myRepoID, offset, alias_hash);
1029
// Assuming the BLOB is still locked:
1030
CS_SET_DISK_1(ptr.rp_head->rb_status_1, MS_BLOB_DELETED);
1031
write(ptr.rp_chars + MS_BLOB_STAT_OFFS, offset + MS_BLOB_STAT_OFFS, head_size - MS_BLOB_STAT_OFFS);
1033
/* Update garbage count: */
1034
updateGarbage(head_size + blob_size);
1036
/* Remove all table references (should not be any)! */
1037
size = head_size - myRepo->myRepoBlobHeadSize;
1038
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
1039
while (size >= ref_size) {
1040
if (CS_GET_DISK_2(ptr.rp_ref->rr_type_2) == MS_BLOB_TABLE_REF) {
1041
tab_id = CS_GET_DISK_4(ptr.rp_tab_ref->tr_table_id_4);
1042
blob_id = CS_GET_DISK_6(ptr.rp_tab_ref->tr_blob_id_6);
1043
removeBlob(otab, tab_id, blob_id, offset, auth_code);
1045
ptr.rp_chars += ref_size;
1051
/* This function will free the BLOB reference, if the record is invalid. */
1052
void MSRepoFile::freeTableReference(MSOpenTable *otab, uint64_t offset, uint16_t head_size, uint32_t tab_id, uint64_t blob_id, uint32_t auth_code)
1055
MSRepoPointersRec ptr;
1056
uint32_t blob_ref_count = 0;
1057
uint32_t table_ref_count = 0;
1058
bool modified = false;
1060
size_t ref_size, ref_count, read_size;
1061
MSRepoTableRefPtr tab_ref = NULL;
1065
/* Lock the BLOB: */
1066
lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
1068
/* Read the header: */
1069
ASSERT(head_size <= MS_OT_BUFFER_SIZE);
1070
read_size = read(otab->myOTBuffer, offset, head_size, 0);
1071
ptr.rp_chars = otab->myOTBuffer;
1072
if (CS_GET_DISK_4(ptr.rp_head->rd_magic_4) != MS_BLOB_HEADER_MAGIC)
1073
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
1074
if (read_size < myRepo->myRepoBlobHeadSize) {
1075
removeBlob(otab, tab_id, blob_id, offset, auth_code);
1078
if ((! IN_USE_BLOB_STATUS(CS_GET_DISK_1(ptr.rp_head->rb_status_1))) ||
1079
CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code) {
1080
removeBlob(otab, tab_id, blob_id, offset, auth_code);
1084
/* Assume that what is here is correct: */
1085
if (head_size != CS_GET_DISK_2(ptr.rp_head->rb_head_size_2)) {
1086
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
1087
read_size = read(otab->myOTBuffer, offset, head_size, myRepo->myRepoBlobHeadSize);
1089
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
1090
blob_size = CS_GET_DISK_6(ptr.rp_head->rb_blob_repo_size_6);
1091
if (read_size < head_size) {
1092
/* This should not happen, because the file has been recovered,
1093
* which should have already adjusted the head and blob
1095
* If this happens then the file must have been truncated an the BLOB has been
1096
* lost so we set the blob size to zero.
1098
head_size = read_size;
1102
ref_size = CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1);
1103
ref_count = CS_GET_DISK_2(ptr.rp_head->rb_ref_count_2);
1104
size = head_size - myRepo->myRepoBlobHeadSize;
1105
if (size > ref_size * ref_count)
1106
size = ref_size * ref_count;
1107
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
1108
while (size >= ref_size) {
1109
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
1110
case MS_BLOB_FREE_REF:
1112
case MS_BLOB_TABLE_REF:
1113
if (CS_GET_DISK_4(ptr.rp_tab_ref->tr_table_id_4) == tab_id &&
1114
CS_GET_DISK_6(ptr.rp_tab_ref->tr_blob_id_6) == blob_id)
1115
tab_ref = ptr.rp_tab_ref;
1117
case MS_BLOB_DELETE_REF:
1120
MSRepoTableRefPtr tr;
1122
tr = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->myRepoBlobHeadSize + (CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2)-1) * ref_size);
1123
if (CS_GET_DISK_2(tr->rr_type_2) == MS_BLOB_TABLE_REF) {
1124
/* I am deleting all references of a table. So I am here to
1125
* also delete the blob references that refer to the
1126
* table reference!!!
1128
if (CS_GET_DISK_4(tr->tr_table_id_4) == tab_id && CS_GET_DISK_6(tr->tr_blob_id_6) == blob_id) {
1129
/* Free the blob reference: */
1130
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
1139
ptr.rp_chars += ref_size;
1143
if (!table_ref_count && tab_ref) {
1144
CS_SET_DISK_2(tab_ref->rr_type_2, MS_BLOB_FREE_REF);
1149
if (!blob_ref_count) {
1150
realFreeBlob(otab, otab->myOTBuffer, auth_code, offset, head_size, blob_size, ref_size);
1151
} else if (modified)
1152
/* The reason I do not write the header of the header, is because
1153
* I want to handle the rb_last_access_4 being set at the
1156
write(otab->myOTBuffer + MS_BLOB_STAT_OFFS, offset + MS_BLOB_STAT_OFFS, head_size - MS_BLOB_STAT_OFFS);
1160
if (!table_ref_count || !tab_ref)
1161
/* Free the table reference, if there are no more
1162
* blob references, reference the table reference,
1163
* or if the table reference was not found in the
1166
removeBlob(otab, tab_id, blob_id, offset, auth_code);
1175
void MSRepoFile::checkBlob(CSStringBuffer *buffer, uint64_t offset, uint32_t auth_code, uint32_t temp_log_id, uint32_t temp_log_offset)
1179
MSRepoPointersRec ptr;
1180
uint32_t blob_ref_count = 0;
1181
bool modified = false;
1183
size_t ref_size, ref_count, read_size;
1187
MSRepoTempRefPtr my_ref = NULL;
1188
uint16_t ref_type = MS_BLOB_FREE_REF;
1191
/* Lock the BLOB: */
1192
lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
1195
/* Read the head of the header: */
1196
if (read(&blob, offset, sizeof(MSBlobHeadRec), 0) < sizeof(MSBlobHeadRec))
1199
// Because the temp log will be replayed from the start when the server
1200
// is restarted it is likely that it will have references to BLOBs that
1201
// no longer exist. So it is not an error if the BLOB ref doesn't point to
1204
// At some point this should probably be rethought because you cannot
1205
// tell the diference between a bad ref because of old data and a bad
1206
// ref because of a BUG.
1207
if (CS_GET_DISK_4(blob.rd_magic_4) != MS_BLOB_HEADER_MAGIC)
1210
head_size = CS_GET_DISK_2(blob.rb_head_size_2);
1211
blob_size = CS_GET_DISK_6(blob.rb_blob_repo_size_6);
1212
ref_size = CS_GET_DISK_1(blob.rb_ref_size_1);
1213
ref_count = CS_GET_DISK_2(blob.rb_ref_count_2);
1214
status = CS_GET_DISK_1(blob.rb_status_1);
1215
if (! IN_USE_BLOB_STATUS(status))
1218
/* Read the entire header: */
1219
buffer->setLength(head_size);
1220
ptr.rp_chars = buffer->getBuffer(0);
1221
read_size = read(ptr.rp_chars, offset, head_size, 0);
1222
if (read_size < myRepo->myRepoBlobHeadSize)
1224
if (CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code)
1226
if (read_size < head_size) {
1227
/* This should not happen, because the file has been recovered,
1228
* which should have already adjusted the head and blob
1230
* If this happens then the file must have been truncated an the BLOB has been
1231
* lost so we set the blob size to zero.
1233
head_size = read_size;
1236
size = head_size - myRepo->myRepoBlobHeadSize;
1237
if (size > ref_size * ref_count)
1238
size = ref_size * ref_count;
1241
/* Search through all references: */
1242
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
1243
while (size >= ref_size) {
1244
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
1245
case MS_BLOB_FREE_REF:
1247
case MS_BLOB_TABLE_REF:
1249
case MS_BLOB_DELETE_REF:
1250
if (CS_GET_DISK_4(ptr.rp_temp_ref->tp_log_id_4) == temp_log_id &&
1251
CS_GET_DISK_4(ptr.rp_temp_ref->tp_offset_4) == temp_log_offset) {
1252
ref_type = CS_GET_DISK_2(ptr.rp_ref->rr_type_2);
1253
my_ref = ptr.rp_temp_ref;
1254
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
1259
MSRepoTableRefPtr tr;
1262
tabi = CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2);
1263
if (tabi < ref_count) {
1264
tr = (MSRepoTableRefPtr) (buffer->getBuffer(0) + myRepo->myRepoBlobHeadSize + (tabi-1) * ref_size);
1265
if (CS_GET_DISK_2(tr->rr_type_2) == MS_BLOB_TABLE_REF)
1270
ptr.rp_chars += ref_size;
1274
if ((ref_type == (uint16_t)MS_BLOB_DELETE_REF) && !blob_ref_count) {
1275
realFreeBlob(NULL, buffer->getBuffer(0), auth_code, offset, head_size, blob_size, ref_size);
1283
void MSRepoFile::returnToPool()
1285
myRepo->myRepoDatabase->returnRepoFileToPool(this);
1288
void MSRepoFile::removeBlob(MSOpenTable *otab, uint32_t tab_id, uint64_t blob_id, uint64_t offset, uint32_t auth_code)
1291
if (otab && otab->getDBTable()->myTableID == tab_id)
1292
otab->getDBTable()->freeBlobHandle(otab, blob_id, myRepo->myRepoID, offset, auth_code);
1294
MSOpenTable *tmp_otab;
1296
if ((tmp_otab = MSTableList::getOpenTableByID(myRepo->myRepoDatabase->myDatabaseID, tab_id))) {
1297
frompool_(tmp_otab);
1298
tmp_otab->getDBTable()->freeBlobHandle(tmp_otab, blob_id, myRepo->myRepoID, offset, auth_code);
1299
backtopool_(tmp_otab);
1305
MSRepoFile *MSRepoFile::newRepoFile(MSRepository *repo, CSPath *path)
1309
if (!(f = new MSRepoFile())) {
1311
CSException::throwOSError(CS_CONTEXT, ENOMEM);
1314
f->myFilePath = path;
1319
* ---------------------------------------------------------------
1323
MSRepository::MSRepository(uint32_t id, MSDatabase *db, off64_t file_size):
1324
CSSharedRefObject(),
1326
myRepoFileSize(file_size),
1327
myRepoLockState(REPO_UNLOCKED),
1328
isRemovingFP(false),
1332
myRepoDefRefSize(0),
1333
myRepoBlobHeadSize(0),
1334
myRecoveryOffset(0),
1336
myLastAccessTime(0),
1337
myLastCreateTime(0),
1339
mustBeDeleted(false),
1345
MSRepository::~MSRepository()
1347
CSPath *path = NULL;
1350
if (mustBeDeleted) {
1351
path = getRepoFilePath();
1355
isRemovingFP = true;
1356
removeRepoFilesNotInUse();
1357
/* With this, I also delete those that are in use!: */
1367
void MSRepository::openRepoFileForWriting(MSOpenTable *otab)
1369
if (!otab->myWriteRepoFile)
1370
otab->myWriteRepoFile = openRepoFile();
1373
uint64_t MSRepository::receiveBlob(MSOpenTable *otab, uint16_t head_size, uint64_t blob_size, Md5Digest *checksum, CSInputStream *stream)
1380
offset = myRepoFileSize;
1382
offset += head_size;
1384
ASSERT(myRepoDatabase->myBlobType == MS_STANDARD_STORAGE);
1389
while (blob_size > 0) {
1390
if (blob_size <= MS_OT_BUFFER_SIZE)
1391
tfer = (size_t) blob_size;
1393
tfer = MS_OT_BUFFER_SIZE;
1394
tfer = stream->read(otab->myOTBuffer, tfer);
1396
CSException::throwOSError(CS_CONTEXT, EPIPE);
1397
if (checksum) md5.md5_append((const u_char *)(otab->myOTBuffer), tfer);
1398
otab->myWriteRepoFile->write(otab->myOTBuffer, offset, tfer);
1399
offset += (uint64_t) tfer;
1400
blob_size -= (uint64_t) tfer;
1402
if (checksum) md5.md5_digest(checksum);
1405
// Write 1 byte to the end to reserver the space.
1406
otab->myWriteRepoFile->write("x" , offset + blob_size -1, 1);
1409
return_( myRepoFileSize);
1412
// copyBlob() copies the BLOB and its header.
1413
uint64_t MSRepository::copyBlob(MSOpenTable *otab, uint64_t size, CSInputStream *stream)
1415
off64_t offset = myRepoFileSize;
1419
if (size <= MS_OT_BUFFER_SIZE)
1420
tfer = (size_t) size;
1422
tfer = MS_OT_BUFFER_SIZE;
1423
tfer = stream->read(otab->myOTBuffer, tfer);
1425
CSException::throwOSError(CS_CONTEXT, EPIPE);
1426
otab->myWriteRepoFile->write(otab->myOTBuffer, offset, tfer);
1427
offset += (uint64_t) tfer;
1428
size -= (uint64_t) tfer;
1431
return myRepoFileSize;
1434
void MSRepository::writeBlobHead(MSOpenTable *otab, uint64_t offset, uint8_t ref_size, uint16_t head_size, uint64_t blob_size, Md5Digest *checksum, char *metadata, uint16_t metadata_size, uint64_t blob_id, uint32_t auth_code, uint32_t log_id, uint32_t log_offset, uint8_t blob_type, CloudKeyPtr cloud_key)
1436
MSBlobHeadPtr blob ;
1437
MSRepoTableRefPtr tab_ref;
1438
MSRepoTempRefPtr temp_ref;
1440
uint16_t tab_idx, max_ref_count = (head_size - myRepoBlobHeadSize - metadata_size) / ref_size;
1443
if (max_ref_count > MS_REPO_MIN_REF_COUNT)
1444
max_ref_count = MS_REPO_MIN_REF_COUNT;
1446
ASSERT(max_ref_count > 1);
1448
if (blob_type == MS_CLOUD_STORAGE)
1449
now = cloud_key->creation_time;
1453
blob = (MSBlobHeadPtr) otab->myOTBuffer;
1454
CS_SET_DISK_4(blob->rb_last_access_4, now);
1455
CS_SET_DISK_4(blob->rb_mod_time_4, now);
1456
CS_SET_DISK_4(blob->rb_access_count_4, 0);
1457
CS_SET_DISK_4(blob->rb_backup_id_4, 0);
1458
CS_SET_DISK_4(blob->rb_create_time_4, now);
1459
CS_SET_DISK_4(blob->rd_magic_4, MS_BLOB_HEADER_MAGIC);
1460
CS_SET_DISK_2(blob->rb_head_size_2, head_size);
1461
CS_SET_DISK_6(blob->rb_blob_data_size_6, blob_size);
1462
CS_SET_DISK_1(blob->rb_status_1, MS_BLOB_ALLOCATED);
1463
CS_SET_DISK_1(blob->rb_ref_size_1, ref_size);
1464
CS_SET_DISK_2(blob->rb_ref_count_2, max_ref_count);
1465
CS_SET_DISK_4(blob->rb_last_ref_4, 0);
1466
CS_SET_DISK_4(otab->myOTBuffer + myRepoBlobHeadSize - 4, auth_code);
1468
memcpy(&(blob->rb_blob_checksum_md5d), checksum, sizeof(Md5Digest));
1470
CS_SET_DISK_2(blob->rb_mdata_size_2, metadata_size);
1471
if (metadata_size) {
1472
uint16_t metadata_offset = head_size - metadata_size;
1474
CS_SET_DISK_2(blob->rb_mdata_offset_2, metadata_offset);
1475
memcpy(otab->myOTBuffer + metadata_offset, metadata, metadata_size);
1477
#ifdef HAVE_ALIAS_SUPPORT
1479
md.use_data(metadata, metadata_size);
1481
alias = md.findAlias();
1483
uint32_t alias_hash;
1484
uint16_t alias_offset = metadata_offset + (uint16_t) (alias - metadata);
1485
CS_SET_DISK_2(blob->rb_alias_offset_2, alias_offset);
1486
alias_hash = myRepoDatabase->registerBlobAlias(myRepoID, offset, alias);
1487
CS_SET_DISK_4(blob->rb_alias_hash_4, alias_hash);
1489
CS_SET_DISK_2(blob->rb_alias_offset_2, 0);
1492
CS_SET_DISK_2(blob->rb_alias_offset_2, 0);
1496
CS_SET_DISK_2(blob->rb_mdata_offset_2, 0);
1497
CS_SET_DISK_2(blob->rb_alias_offset_2, 0);
1502
tab_ref = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepoBlobHeadSize);
1503
CS_SET_DISK_2(tab_ref->rr_type_2, MS_BLOB_TABLE_REF);
1504
CS_SET_DISK_4(tab_ref->tr_table_id_4, otab->getDBTable()->myTableID);
1505
CS_SET_DISK_6(tab_ref->tr_blob_id_6, blob_id);
1506
temp_ref = (MSRepoTempRefPtr) (otab->myOTBuffer + myRepoBlobHeadSize + ref_size);
1507
tab_idx = 1; // This is the index of the blob table ref in the repository record.
1508
size = myRepoBlobHeadSize + ref_size + ref_size;
1511
temp_ref = (MSRepoTempRefPtr) (otab->myOTBuffer + myRepoBlobHeadSize);
1512
tab_idx = INVALID_INDEX; // Means not used
1513
size = myRepoBlobHeadSize + ref_size;
1516
CS_SET_DISK_2(temp_ref->rr_type_2, MS_BLOB_DELETE_REF);
1517
CS_SET_DISK_2(temp_ref->tp_del_ref_2, tab_idx);
1518
CS_SET_DISK_4(temp_ref->tp_log_id_4, log_id);
1519
CS_SET_DISK_4(temp_ref->tp_offset_4, log_offset);
1521
if (blob_type == MS_CLOUD_STORAGE) { // The data is stored in the cloud and not in the repository.
1522
CS_SET_DISK_4(blob->rb_s3_key_id_4, cloud_key->ref_index);
1523
CS_SET_DISK_4(blob->rb_s3_cloud_ref_4, cloud_key->cloud_ref);
1524
blob_size = 0; // The blob is not stored in the repository so the blob storage size in the repository is zero
1527
memset(otab->myOTBuffer + size, 0, head_size - size - metadata_size);
1529
CS_SET_DISK_1(blob->rb_storage_type_1, blob_type);
1530
CS_SET_DISK_6(blob->rb_blob_repo_size_6, blob_size);
1531
otab->myWriteRepoFile->write(blob, offset, head_size);
1533
setRepoFileSize(otab, offset + head_size + blob_size);
1536
void MSRepository::setRepoFileSize(MSOpenTable *otab, off64_t offset)
1538
myRepoFileSize = offset;
1539
if (myRepoFileSize >= PBMSParameters::getRepoThreshold()
1540
/**/ || getGarbageLevel() >= PBMSParameters::getGarbageThreshold())
1541
otab->closeForWriting();
1544
void MSRepository::syncHead(MSRepoFile *fh)
1549
myRecoveryOffset = myRepoFileSize;
1550
CS_SET_DISK_8(head.rh_recovery_offset_8, myRecoveryOffset);
1551
CS_SET_DISK_4(head.rh_last_temp_time_4, myLastTempTime);
1552
CS_SET_DISK_4(head.rh_last_access_4, myLastAccessTime);
1553
CS_SET_DISK_4(head.rh_create_time_4, myLastCreateTime);
1554
CS_SET_DISK_4(head.rh_last_ref_4, myLastRefTime);
1556
fh->write(&head.rh_recovery_offset_8, offsetof(MSRepoHeadRec, rh_recovery_offset_8), 24);
1560
MSRepoFile *MSRepository::openRepoFile()
1565
fh = MSRepoFile::newRepoFile(this, getRepoFilePath());
1568
fh->open(CSFile::DEFAULT);
1570
fh->open(CSFile::CREATE);
1571
if (!myRepoHeadSize) {
1581
/* Check again after locking: */
1582
if (!myRepoHeadSize) {
1583
if (fh->read(&head, 0, offsetof(MSRepoHeadRec, rh_reserved_4), 0) < offsetof(MSRepoHeadRec, rh_reserved_4)) {
1584
CS_SET_DISK_4(head.rh_magic_4, MS_REPO_FILE_MAGIC);
1585
CS_SET_DISK_2(head.rh_version_2, MS_REPO_FILE_VERSION);
1586
CS_SET_DISK_2(head.rh_repo_head_size_2, MS_REPO_FILE_HEAD_SIZE);
1587
CS_SET_DISK_2(head.rh_blob_head_size_2, sizeof(MSBlobHeadRec));
1588
CS_SET_DISK_2(head.rh_def_ref_size_2, sizeof(MSRepoGenericRefRec));
1589
CS_SET_DISK_8(head.rh_recovery_offset_8, MS_REPO_FILE_HEAD_SIZE);
1590
CS_SET_DISK_8(head.rh_garbage_count_8, 0);
1591
CS_SET_DISK_4(head.rh_last_temp_time_4, 0);
1592
CS_SET_DISK_4(head.rh_last_access_4, 0);
1593
CS_SET_DISK_4(head.rh_create_time_4, 0);
1594
CS_SET_DISK_4(head.rh_last_ref_4, 0);
1595
CS_SET_DISK_4(head.rh_reserved_4, 0);
1596
fh->write(&head, 0, sizeof(MSRepoHeadRec));
1599
/* Check the file header: */
1600
if (CS_GET_DISK_4(head.rh_magic_4) != MS_REPO_FILE_MAGIC)
1601
CSException::throwFileError(CS_CONTEXT, fh->getPathString(), CS_ERR_BAD_HEADER_MAGIC);
1602
if (CS_GET_DISK_2(head.rh_version_2) > MS_REPO_FILE_VERSION)
1603
CSException::throwFileError(CS_CONTEXT, fh->getPathString(), CS_ERR_VERSION_TOO_NEW);
1605
/* Load the header details: */
1606
myRepoHeadSize = CS_GET_DISK_2(head.rh_repo_head_size_2);
1607
myRepoDefRefSize = CS_GET_DISK_2(head.rh_def_ref_size_2);
1608
myRepoBlobHeadSize = CS_GET_DISK_2(head.rh_blob_head_size_2);
1609
myRecoveryOffset = CS_GET_DISK_8(head.rh_recovery_offset_8);
1610
myGarbageCount = CS_GET_DISK_8(head.rh_garbage_count_8);
1611
myLastTempTime = CS_GET_DISK_4(head.rh_last_temp_time_4);
1612
myLastAccessTime = CS_GET_DISK_4(head.rh_last_access_4);
1613
myLastCreateTime = CS_GET_DISK_4(head.rh_create_time_4);
1614
myLastRefTime = CS_GET_DISK_4(head.rh_last_ref_4);
1616
/* File size, cannot be less than header size: */
1617
if (myRepoFileSize < myRepoHeadSize)
1618
myRepoFileSize = myRepoHeadSize;
1620
ASSERT(myGarbageCount <= myRepoFileSize);
1622
/* Recover the file: */
1623
while (myRecoveryOffset < myRepoFileSize) {
1624
if ((size = fh->read(&blob, myRecoveryOffset, MS_MIN_BLOB_HEAD_SIZE, 0)) < MS_MIN_BLOB_HEAD_SIZE) {
1626
myRepoFileSize = myRecoveryOffset;
1627
fh->setEOF(myRepoFileSize);
1631
uint16_t ref_count, mdata_size, mdata_offset;
1633
status = CS_GET_DISK_1(blob.rb_status_1);
1634
ref_size = CS_GET_DISK_1(blob.rb_ref_size_1);
1635
ref_count = CS_GET_DISK_2(blob.rb_ref_count_2);
1636
head_size = CS_GET_DISK_2(blob.rb_head_size_2);
1637
mdata_size = CS_GET_DISK_2(blob.rb_mdata_size_2);
1638
mdata_offset = CS_GET_DISK_2(blob.rb_mdata_offset_2);
1639
blob_size = CS_GET_DISK_6(blob.rb_blob_repo_size_6);
1640
if ((CS_GET_DISK_4(blob.rd_magic_4) != MS_BLOB_HEADER_MAGIC) ||
1641
(! IN_USE_BLOB_STATUS(status)) ||
1642
head_size < (myRepoBlobHeadSize + ref_size * MS_REPO_MIN_REF_COUNT) ||
1643
head_size < (mdata_offset + mdata_size) ||
1644
((blob_size == 0) && (BLOB_IN_REPOSITORY(CS_GET_DISK_1(blob.rb_storage_type_1)))) ||
1645
myRecoveryOffset + head_size + blob_size > myRepoFileSize) {
1646
myRepoFileSize = myRecoveryOffset;
1647
fh->setEOF(myRepoFileSize);
1650
myRecoveryOffset += head_size + blob_size;
1654
myRecoveryOffset = myRepoFileSize;
1655
CS_SET_DISK_8(head.rh_recovery_offset_8, myRecoveryOffset);
1656
fh->write(&head, offsetof(MSRepoHeadRec, rh_recovery_offset_8), 8);
1665
void MSRepository::lockRepo(RepoLockState state)
1670
myLock = &myRepoLock[0];
1673
ASSERT(!myRepoXLock);
1675
myRepoLockState = state;
1682
void MSRepository::signalCompactor()
1684
#ifndef MS_COMPACTOR_POLLS
1685
if (!mustBeDeleted) {
1686
if (getGarbageLevel() >= PBMSParameters::getGarbageThreshold()) {
1687
if (myRepoDatabase->myCompactorThread)
1688
myRepoDatabase->myCompactorThread->wakeup();
1694
void MSRepository::unlockRepo(RepoLockState state)
1698
myLock = &myRepoLock[0];
1701
ASSERT(myRepoLockState & state);
1703
myRepoLockState &= ~state;
1704
if (myRepoLockState == REPO_UNLOCKED) {
1705
myRepoXLock = false;
1713
// Repositories are not removed from the pool when
1714
// scheduled for backup so the REPO_BACKUP flag is
1715
// not handled here.
1716
void MSRepository::returnToPool()
1720
myLock = &myRepoLock[0];
1722
this->myRepoLockState &= ~(REPO_COMPACTING | REPO_WRITE);
1723
if ( this->myRepoLockState == REPO_UNLOCKED) {
1724
myRepoXLock = false;
1733
void MSRepository::backupCompleted()
1737
myLock = &myRepoLock[0];
1741
this->myRepoLockState &= ~REPO_BACKUP;
1742
if ( this->myRepoLockState == REPO_UNLOCKED) {
1743
myRepoXLock = false;
1751
bool MSRepository::lockedForBackup() { return ((myRepoLockState & REPO_BACKUP) == REPO_BACKUP);}
1753
uint32_t MSRepository::initBackup()
1759
myLock = &myRepoLock[0];
1761
state = this->myRepoLockState;
1762
this->myRepoLockState |= REPO_BACKUP;
1763
if (this->myRepoLockState == REPO_BACKUP)
1764
this->myRepoXLock = true;
1770
MSRepoFile *MSRepository::getRepoFile()
1774
if ((file = iFilePool)) {
1775
iFilePool = file->nextFile;
1776
file->nextFile = NULL;
1777
file->isFileInUse = true;
1783
void MSRepository::addRepoFile(MSRepoFile *file)
1785
iPoolFiles.addFront(file);
1788
void MSRepository::removeRepoFile(MSRepoFile *file)
1790
iPoolFiles.remove(file);
1793
void MSRepository::returnRepoFile(MSRepoFile *file)
1795
file->isFileInUse = false;
1796
file->nextFile = iFilePool;
1800
bool MSRepository::removeRepoFilesNotInUse()
1802
MSRepoFile *file, *curr_file;
1805
/* Remove all files that are not in use: */
1806
if ((file = (MSRepoFile *) iPoolFiles.getBack())) {
1809
file = (MSRepoFile *) file->getNextLink();
1810
if (!curr_file->isFileInUse)
1811
iPoolFiles.remove(curr_file);
1814
return iPoolFiles.getSize() == 0;
1817
off64_t MSRepository::getRepoFileSize()
1819
return myRepoFileSize;
1822
size_t MSRepository::getRepoHeadSize()
1824
return myRepoHeadSize;
1827
size_t MSRepository::getRepoBlobHeadSize()
1829
return myRepoBlobHeadSize;
1832
CSMutex *MSRepository::getRepoLock(off64_t offset)
1834
return &myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
1837
uint32_t MSRepository::getRepoID()
1842
uint32_t MSRepository::getGarbageLevel()
1844
if (myRepoFileSize <= myRepoHeadSize)
1846
return myGarbageCount * 100 / (myRepoFileSize - myRepoHeadSize);
1849
CSPath *MSRepository::getRepoFilePath()
1851
char file_name[120];
1853
cs_strcpy(120, file_name, "bs-repository");
1854
cs_add_dir_char(120, file_name);
1855
cs_strcat(120, file_name, "repo-");
1856
cs_strcat(120, file_name, myRepoID);
1857
cs_strcat(120, file_name, ".bs");
1859
if (myRepoDatabase && myRepoDatabase->myDatabasePath) {
1860
return CSPath::newPath(RETAIN(myRepoDatabase->myDatabasePath), file_name);