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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
* Original author: Paul McCullagh
20
* Continued development: Barry Leslie
32
#include <drizzled/common.h>
33
#include <drizzled/session.h>
34
#include <drizzled/sql_lex.h>
37
#include "cslib/CSConfig.h"
42
#include "cslib/CSGlobal.h"
43
#include "cslib/CSLog.h"
44
#include "cslib/CSStrUtil.h"
45
#include "cslib/CSHTTPStream.h"
46
#include "cslib/CSStream.h"
48
#include "repository_ms.h"
49
#include "open_table_ms.h"
50
#include "connection_handler_ms.h"
51
#include "metadata_ms.h"
52
#include "parameters_ms.h"
53
#include "pbmsdaemon_ms.h"
56
* ---------------------------------------------------------------
60
MSRepoFile::MSRepoFile():
70
MSRepoFile::~MSRepoFile()
75
void MSRepoFile::updateGarbage(uint64_t size)
77
MSRepoHeadRec repo_head;
81
myRepo->myGarbageCount += size;
82
CS_SET_DISK_8(repo_head.rh_garbage_count_8, myRepo->myGarbageCount);
83
ASSERT(myRepo->myGarbageCount <= myRepo->myRepoFileSize);
84
write(&repo_head.rh_garbage_count_8, offsetof(MSRepoHeadRec, rh_garbage_count_8), 8);
86
if (!myRepo->myRepoXLock)
87
myRepo->signalCompactor();
92
void MSRepoFile::updateAccess(MSBlobHeadPtr blob, uint64_t rep_offset)
94
time_t now = time(NULL);
95
uint32_t count = CS_GET_DISK_4(blob->rb_access_count_4) +1;
97
CS_SET_DISK_4(blob->rb_last_access_4, now);
98
CS_SET_DISK_4(blob->rb_access_count_4, count);
99
write(&blob->rb_last_access_4, rep_offset + offsetof(MSBlobHeadRec, rb_last_access_4), 8);
100
myRepo->myLastAccessTime = now;
103
uint64_t MSRepoFile::readBlobChunk(PBMSBlobIDPtr blob_id, uint64_t rep_offset, uint64_t blob_offset, uint64_t buffer_size, char *buffer)
105
MSBlobHeadRec blob_head;
110
uint64_t offset, blob_read =0;
114
read(&blob_head, rep_offset, sizeof(MSBlobHeadRec), sizeof(MSBlobHeadRec));
115
if (CS_GET_DISK_4(blob_head.rd_magic_4) != MS_BLOB_HEADER_MAGIC)
116
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
118
blob_size = CS_GET_DISK_6(blob_head.rb_blob_repo_size_6);
119
head_size = CS_GET_DISK_2(blob_head.rb_head_size_2);
120
ac = CS_GET_DISK_4(blob_head.rb_auth_code_4);
121
if (blob_id->bi_auth_code != ac)
122
CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB identifier");
124
offset = rep_offset + blob_offset + head_size;
126
if (blob_offset > blob_size)
129
if ((blob_offset + buffer_size) > blob_size)
130
buffer_size = blob_size - blob_offset;
132
while (buffer_size > 0) {
133
if (buffer_size <= (uint64_t) (SSIZE_MAX))
134
tfer = (size_t) buffer_size;
138
read(buffer, offset, tfer, tfer);
139
offset += (uint64_t) tfer;
140
buffer += (uint64_t) tfer;
141
buffer_size -= (uint64_t) tfer;
142
blob_read += (uint64_t) tfer;
145
/* Only update the access timestamp when reading the first block: */
147
updateAccess(&blob_head, rep_offset);
153
void MSRepoFile::writeBlobChunk(PBMSBlobIDPtr blob_id, uint64_t rep_offset, uint64_t blob_offset, uint64_t data_size, char *data)
157
MSBlobHeadRec blob_head;
164
read(&blob_head, rep_offset, sizeof(MSBlobHeadRec), sizeof(MSBlobHeadRec));
165
if (CS_GET_DISK_4(blob_head.rd_magic_4) != MS_BLOB_HEADER_MAGIC)
166
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
168
blob_size = CS_GET_DISK_6(blob_head.rb_blob_repo_size_6);
169
head_size = CS_GET_DISK_2(blob_head.rb_head_size_2);
170
ac = CS_GET_DISK_4(blob_head.rb_auth_code_4);
171
if (blob_id->bi_auth_code != ac)
172
CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB identifier");
174
if ((blob_offset + data_size) > blob_size)
175
CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB write size or offset");
177
offset = (uint64_t) head_size + rep_offset + blob_offset;
179
while (data_size > 0) {
180
if (data_size <= (uint64_t) (SSIZE_MAX))
181
tfer = (size_t) data_size;
185
write(data, offset, tfer);
186
data += (uint64_t) tfer;
187
offset += (uint64_t) tfer;
188
data_size -= (uint64_t) tfer;
194
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)
196
MSConnectionHandler *me;
198
off64_t start_offset = offset;
199
MSBlobHeadRec blob_head;
200
uint8_t storage_type;
201
uint16_t head_size, meta_size;
202
uint64_t blob_data_size, local_blob_size, meta_offset;
204
char num_str[CS_WIDTH_INT_64];
205
bool redirect = false;
209
me = (MSConnectionHandler *) self;
211
read(&blob_head, start_offset, sizeof(MSBlobHeadRec), sizeof(MSBlobHeadRec));
212
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.
213
blob_data_size = CS_GET_DISK_6(blob_head.rb_blob_data_size_6);// This is the actual size of the BLOB.
214
head_size = CS_GET_DISK_2(blob_head.rb_head_size_2);
215
meta_size = CS_GET_DISK_2(blob_head.rb_mdata_size_2);
216
meta_offset = start_offset + CS_GET_DISK_2(blob_head.rb_mdata_offset_2);
217
ac = CS_GET_DISK_4(blob_head.rb_auth_code_4);
218
if ((with_auth_code && auth_code != ac) || (CS_GET_DISK_4(blob_head.rd_magic_4) != MS_BLOB_HEADER_MAGIC))
219
CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB identifier");
221
storage_type = CS_GET_DISK_1(blob_head.rb_storage_type_1);
223
if ((!info_only) && BLOB_IN_CLOUD(storage_type)) {
224
CSString *redirect_url = NULL;
226
getBlobKey(&blob_head, &key);
227
redirect_url = otab->getDB()->myBlobCloud->cl_getDataURL(&key);
229
stream->setStatus(301);
230
stream->addHeader("Location", redirect_url->getCString());
231
release_(redirect_url);
234
stream->setStatus(200);
236
if (storage_type == MS_STANDARD_STORAGE) {
237
char hex_checksum[33];
238
cs_bin_to_hex(33, hex_checksum, 16, blob_head.rb_blob_checksum_md5d.val);
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(RETAIN(otab->myWriteRepoFile), dst_offset + new_head_size, RETAIN(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->haveTable()) && (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;
589
shift = new_size - head_size;
590
mdata_size = CS_GET_DISK_2(ptr.rp_head->rb_mdata_size_2);
591
mdata_offset = CS_GET_DISK_2(ptr.rp_head->rb_mdata_offset_2);
592
alias_offset = CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2);
594
memmove(ptr.rp_chars + mdata_offset + shift, ptr.rp_chars + mdata_offset, shift);
595
memset(ptr.rp_chars + mdata_offset, 0, shift);
596
mdata_offset += shift;
597
alias_offset += shift;
599
CS_SET_DISK_2(ptr.rp_head->rb_mdata_offset_2, mdata_offset);
600
CS_SET_DISK_2(ptr.rp_head->rb_alias_offset_2, alias_offset);
603
memset(ptr.rp_chars + head_size, 0, new_size - head_size);
605
new_head_size = new_size;
607
CS_SET_DISK_2(ptr.rp_head->rb_head_size_2, new_head_size);
608
CS_SET_DISK_2(ptr.rp_head->rb_ref_count_2, ref_count + new_refs);
609
ptr.rp_chars += myRepo->myRepoBlobHeadSize + ref_count * ref_size;
612
free_ref = ptr.rp_blob_ref;
613
memset(free_ref, 0, ref_size);
614
ptr.rp_chars += ref_size;
618
free2_ref = ptr.rp_blob_ref;
619
memset(free2_ref, 0, ref_size);
622
ref_count += new_refs;
625
new_head_size = head_size;
628
tab_ref = (MSRepoTableRefPtr) free2_ref;
630
CS_SET_DISK_2(tab_ref->rr_type_2, MS_BLOB_TABLE_REF);
631
CS_SET_DISK_4(tab_ref->tr_table_id_4, tab_id);
632
CS_SET_DISK_6(tab_ref->tr_blob_id_6, blob_id);
637
tab_idx = (((char *) tab_ref - otab->myOTBuffer) - myRepo->myRepoBlobHeadSize) / ref_size;
639
CS_SET_DISK_2(free_ref->er_table_2, tab_idx+1);
640
CS_SET_DISK_2(free_ref->er_col_index_2, col_index);
641
CS_SET_DISK_8(free_ref->er_blob_ref_id_8, UNCOMMITTED(blob_ref_id));
643
update_blob_header(otab, offset, blob_size, head_size, new_head_size);
644
#ifdef HAVE_ALIAS_SUPPORT
645
if (reset_alias_index)
646
myRepo->myRepoDatabase->registerBlobAlias(myRepo->myRepoID, offset, blob_alias);
655
void MSRepoFile::setBlobMetaData(MSOpenTable *otab, uint64_t offset, const char *meta_data, uint16_t meta_data_len, bool reset_alias, const char *alias)
658
MSRepoPointersRec ptr;
660
uint16_t new_head_size;
662
uint16_t head_size, mdata_size, mdata_offset, alias_offset = 0;
667
mylock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
670
/* Read the header: */
671
if (read(&blob, offset, sizeof(MSBlobHeadRec), 0) < sizeof(MSBlobHeadRec)) {
672
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB header incomplete");
675
head_size = CS_GET_DISK_2(blob.rb_head_size_2);
677
if (head_size > MS_OT_BUFFER_SIZE) {
678
CSException::throwAssertion(CS_CONTEXT, "BLOB header overflow");
681
read_size = read(otab->myOTBuffer, offset, head_size, 0);
682
ptr.rp_chars = otab->myOTBuffer;
683
if (CS_GET_DISK_4(ptr.rp_head->rd_magic_4) != MS_BLOB_HEADER_MAGIC)
684
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
685
if (read_size < myRepo->myRepoBlobHeadSize)
686
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB header incomplete");
687
if (! IN_USE_BLOB_STATUS(CS_GET_DISK_1(ptr.rp_head->rb_status_1)))
688
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB has already been deleted");
691
blob_size = CS_GET_DISK_6(ptr.rp_head->rb_blob_repo_size_6);
692
if (read_size < head_size) {
693
/* This should not happen, because the file has been recovered,
694
* which should have already adjusted the head and blob
696
* If this happens then the file must have been truncated an the BLOB has been
697
* lost so we set the blob size to zero.
699
head_size = read_size;
702
mdata_size = CS_GET_DISK_2(ptr.rp_head->rb_mdata_size_2);
704
if ((meta_data_len < mdata_size) || MS_CAN_ADD_MDATA(ptr.rp_head, meta_data_len - mdata_size))
705
new_head_size = head_size;
706
else { // The header must be grown
708
new_head_size = head_size + meta_data_len - mdata_size;
709
if (new_head_size > MS_OT_BUFFER_SIZE)
710
CSException::throwAssertion(CS_CONTEXT, "BLOB reference header overflow");
712
memset(ptr.rp_chars + head_size, 0, new_head_size - head_size);
713
CS_SET_DISK_2(ptr.rp_head->rb_head_size_2, new_head_size);
717
// Meta data is placed at the end of the header.
719
mdata_offset = new_head_size - meta_data_len;
722
mdata_size = meta_data_len;
725
CS_SET_DISK_2(ptr.rp_head->rb_mdata_size_2, mdata_size);
726
CS_SET_DISK_2(ptr.rp_head->rb_mdata_offset_2, mdata_offset);
727
#ifdef HAVE_ALIAS_SUPPORT
728
uint32_t alias_hash = INVALID_ALIAS_HASH;
730
alias_hash = CS_GET_DISK_4(ptr.rp_head->rb_alias_hash_4);
731
alias_offset = CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2);
734
alias_hash = myRepo->myRepoDatabase->updateBlobAlias(myRepo->myRepoID, offset, alias_hash, alias);
736
alias_hash = myRepo->myRepoDatabase->registerBlobAlias(myRepo->myRepoID, offset, alias);
739
alias_offset = mdata_offset + (alias - meta_data);
741
} else if (reset_alias && CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2)) {
742
alias_offset = CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2);
743
myRepo->myRepoDatabase->deleteBlobAlias(myRepo->myRepoID, offset, CS_GET_DISK_4(ptr.rp_head->rb_alias_hash_4));
747
uint32_t alias_hash = ((uint32_t)-1);
748
if (alias || reset_alias) {
749
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_IMPLEMENTED, "No BLOB alias support.");
753
CS_SET_DISK_2(ptr.rp_head->rb_alias_offset_2, alias_offset);
754
CS_SET_DISK_4(ptr.rp_head->rb_alias_hash_4, alias_hash);
756
memcpy(ptr.rp_chars + mdata_offset, meta_data, meta_data_len);
758
update_blob_header(otab, offset, blob_size, head_size, new_head_size);
766
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)
769
MSRepoPointersRec ptr;
770
uint32_t table_ref_count = 0;
772
size_t ref_size, ref_count, read_size;
773
MSRepoTempRefPtr temp_ref = NULL;
774
uint16_t tab_index = 0;
775
MSRepoTableRefPtr tab_ref;
776
uint16_t alias_offset;
784
mylock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
786
/* Read the header: */
787
ASSERT(head_size <= MS_OT_BUFFER_SIZE);
788
read_size = read(otab->myOTBuffer, offset, head_size, 0);
789
ptr.rp_chars = otab->myOTBuffer;
790
if (CS_GET_DISK_4(ptr.rp_head->rd_magic_4) != MS_BLOB_HEADER_MAGIC)
791
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
792
if (read_size < myRepo->myRepoBlobHeadSize) {
793
removeBlob(otab, tab_id, blob_id, offset, auth_code);
796
if ((! IN_USE_BLOB_STATUS(CS_GET_DISK_1(ptr.rp_head->rb_status_1))) ||
797
CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code) {
798
removeBlob(otab, tab_id, blob_id, offset, auth_code);
802
/* Assume that what is here is correct: */
803
if (head_size != CS_GET_DISK_2(ptr.rp_head->rb_head_size_2)) {
804
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
805
read_size = read(otab->myOTBuffer, offset, head_size, myRepo->myRepoBlobHeadSize);
807
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
808
if (read_size < head_size) {
809
/* This should not happen, because the file has been recovered,
810
* which should have already adjusted the head and blob
812
* If this happens then the file must have been truncated an the BLOB has been
813
* lost so we set the blob size to zero.
815
head_size = read_size;
817
ref_size = CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1);
818
ref_count = CS_GET_DISK_2(ptr.rp_head->rb_ref_count_2);
820
alias_offset = CS_GET_DISK_2(ptr.rp_head->rb_alias_offset_2);
821
alias_hash = CS_GET_DISK_4(ptr.rp_head->rb_alias_hash_4);
823
size = head_size - myRepo->myRepoBlobHeadSize;
824
if (size > ref_size * ref_count)
825
size = ref_size * ref_count;
826
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
827
while (size >= ref_size) {
828
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
829
case MS_BLOB_FREE_REF:
830
case MS_BLOB_TABLE_REF:
832
case MS_BLOB_DELETE_REF: {
835
tabi = CS_GET_DISK_2(ptr.rp_temp_ref->tp_del_ref_2);
836
if (tabi && tabi < ref_count) {
838
tab_ref = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->myRepoBlobHeadSize + tabi * ref_size);
839
if (CS_GET_DISK_4(tab_ref->tr_table_id_4) == tab_id &&
840
CS_GET_DISK_6(tab_ref->tr_blob_id_6) == blob_id) {
841
/* This is an old free, take it out. */
842
// Barry: What happens to the record in the temp log associated with this ref
843
// that is waiting to free the BLOB?
844
// Answer: It will find that there is MS_BLOB_DELETE_REF record with the BLOB
845
// or if there is one it will be for a different free in a different temp log
846
// or with a different temp log offset.
847
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
852
default: // Must be a blob REF
853
tab_ref = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->myRepoBlobHeadSize + (CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2)-1) * ref_size);
854
if (CS_GET_DISK_4(tab_ref->tr_table_id_4) == tab_id &&
855
CS_GET_DISK_6(tab_ref->tr_blob_id_6) == blob_id) {
856
if (COMMIT_MASK(CS_GET_DISK_8(ptr.rp_blob_ref->er_blob_ref_id_8)) == blob_ref_id) {
857
/* Found the reference, remove it... */
858
tab_index = CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2)-1;
859
temp_ref = ptr.rp_temp_ref;
860
//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.
861
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
868
ptr.rp_chars += ref_size;
872
// If the refernce was found and there are no
873
// table references then the BLOB can be scheduled for deletion.
874
if ((!table_ref_count) && temp_ref) {
878
#ifdef HAVE_ALIAS_SUPPORT
879
MSDiskAliasRec aliasDiskRec;
880
MSDiskAliasPtr aliasDiskPtr = NULL;
883
CS_SET_DISK_4(aliasDiskRec.ar_repo_id_4, myRepo->myRepoID);
884
CS_SET_DISK_8(aliasDiskRec.ar_offset_8, offset);
885
CS_SET_DISK_4(aliasDiskRec.ar_hash_4, alias_hash);
886
aliasDiskPtr = &aliasDiskRec;
889
myRepo->myRepoDatabase->queueForDeletion(otab, MS_TL_BLOB_REF, tab_id, blob_id, auth_code, &log_id, &log_offset, &temp_time, aliasDiskPtr);
891
myRepo->myRepoDatabase->queueForDeletion(otab, MS_TL_BLOB_REF, tab_id, blob_id, auth_code, &log_id, &log_offset, &temp_time);
893
myRepo->myLastTempTime = temp_time;
894
CS_SET_DISK_2(temp_ref->rr_type_2, MS_BLOB_DELETE_REF);
895
CS_SET_DISK_2(temp_ref->tp_del_ref_2, tab_index+1);
896
CS_SET_DISK_4(temp_ref->tp_log_id_4, log_id);
897
CS_SET_DISK_4(temp_ref->tp_offset_4, log_offset);
899
CS_SET_DISK_1(ptr.rp_head->rb_status_1, MS_BLOB_ALLOCATED); // The BLOB is allocated but no longer referenced
902
/* The reason I do not write the header of the header, is because
903
* I want to handle the rb_last_access_4 being set at the
906
write(otab->myOTBuffer + MS_BLOB_STAT_OFFS, offset + MS_BLOB_STAT_OFFS, head_size - MS_BLOB_STAT_OFFS);
907
} else if (PBMSDaemon::isDaemonState(PBMSDaemon::DaemonStartUp) == false) {
909
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);
910
/* The reference already exists so there is nothing to do... */
911
self->myException.log(self, message);
919
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)
922
MSRepoPointersRec ptr;
924
size_t ref_size, ref_count, read_size;
925
MSRepoTableRefPtr tab_ref;
929
mylock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
931
/* Read the header: */
932
ASSERT(head_size <= MS_OT_BUFFER_SIZE);
933
read_size = read(otab->myOTBuffer, offset, head_size, 0);
934
ptr.rp_chars = otab->myOTBuffer;
935
if (CS_GET_DISK_4(ptr.rp_head->rd_magic_4) != MS_BLOB_HEADER_MAGIC)
936
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
939
if (read_size < myRepo->myRepoBlobHeadSize)
940
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB header incomplete");
941
if ( ! IN_USE_BLOB_STATUS(CS_GET_DISK_1(ptr.rp_head->rb_status_1)))
942
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB has already been deleted");
943
if (auth_code && CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code)
944
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB data does not match reference");
947
/* Assume that what is here is correct: */
948
if (head_size != CS_GET_DISK_2(ptr.rp_head->rb_head_size_2)) {
949
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
950
read_size = read(otab->myOTBuffer, offset, head_size, myRepo->myRepoBlobHeadSize);
953
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
954
if (read_size < head_size) {
955
/* This should not happen, because the file has been recovered,
956
* which should have already adjusted the head and blob
958
* If this happens then the file must have been truncated an the BLOB has been
959
* lost so we set the blob size to zero.
961
head_size = read_size;
963
ref_size = CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1);
964
ref_count = CS_GET_DISK_2(ptr.rp_head->rb_ref_count_2);
966
size = head_size - myRepo->myRepoBlobHeadSize;
967
if (size > ref_size * ref_count)
968
size = ref_size * ref_count;
969
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
970
while (size >= ref_size) {
971
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
972
case MS_BLOB_FREE_REF:
973
case MS_BLOB_TABLE_REF:
975
case MS_BLOB_DELETE_REF: {
978
default: // Must be a blob REF
979
tab_ref = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->myRepoBlobHeadSize + (CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2)-1) * ref_size);
980
if (CS_GET_DISK_4(tab_ref->tr_table_id_4) == tab_id &&
981
CS_GET_DISK_6(tab_ref->tr_blob_id_6) == blob_id) {
982
uint64_t ref_id = CS_GET_DISK_8(ptr.rp_blob_ref->er_blob_ref_id_8);
983
if (COMMIT_MASK(ref_id) == blob_ref_id) {
984
/* Found the reference, mark it as committed... */
985
CS_SET_DISK_8(ptr.rp_blob_ref->er_blob_ref_id_8, blob_ref_id);
986
offset += (ptr.rp_chars - otab->myOTBuffer) + offsetof(MSRepoBlobRefRec, er_blob_ref_id_8);
987
write(&(ptr.rp_blob_ref->er_blob_ref_id_8), offset, 8);
993
ptr.rp_chars += ref_size;
997
if (PBMSDaemon::isDaemonState(PBMSDaemon::DaemonStartUp) == false) {
999
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);
1000
self->myException.log(self, message);
1008
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)
1013
MSRepoPointersRec ptr;
1016
ptr.rp_chars = buffer;
1018
if (BLOB_IN_CLOUD(CS_GET_DISK_1(ptr.rp_head->rb_storage_type_1))) {
1020
getBlobKey(ptr.rp_head, &key);
1021
if (!myRepo->myRepoDatabase->myBlobCloud)
1022
CSException::throwException(CS_CONTEXT, CS_ERR_GENERIC_ERROR, "Deleting cloud BLOB without cloud.");
1024
myRepo->myRepoDatabase->myBlobCloud->cl_deleteData(&key);
1027
#ifdef HAVE_ALIAS_SUPPORT
1028
uint32_t alias_hash;
1029
alias_hash = CS_GET_DISK_4(ptr.rp_head->rb_alias_hash_4);
1030
if (alias_hash != INVALID_ALIAS_HASH)
1031
myRepo->myRepoDatabase->deleteBlobAlias(myRepo->myRepoID, offset, alias_hash);
1034
// Assuming the BLOB is still locked:
1035
CS_SET_DISK_1(ptr.rp_head->rb_status_1, MS_BLOB_DELETED);
1036
write(ptr.rp_chars + MS_BLOB_STAT_OFFS, offset + MS_BLOB_STAT_OFFS, head_size - MS_BLOB_STAT_OFFS);
1038
/* Update garbage count: */
1039
updateGarbage(head_size + blob_size);
1041
/* Remove all table references (should not be any)! */
1042
size = head_size - myRepo->myRepoBlobHeadSize;
1043
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
1044
while (size >= ref_size) {
1045
if (CS_GET_DISK_2(ptr.rp_ref->rr_type_2) == MS_BLOB_TABLE_REF) {
1046
tab_id = CS_GET_DISK_4(ptr.rp_tab_ref->tr_table_id_4);
1047
blob_id = CS_GET_DISK_6(ptr.rp_tab_ref->tr_blob_id_6);
1048
removeBlob(otab, tab_id, blob_id, offset, auth_code);
1050
ptr.rp_chars += ref_size;
1056
/* This function will free the BLOB reference, if the record is invalid. */
1057
void MSRepoFile::freeTableReference(MSOpenTable *otab, uint64_t offset, uint16_t head_size, uint32_t tab_id, uint64_t blob_id, uint32_t auth_code)
1060
MSRepoPointersRec ptr;
1061
uint32_t blob_ref_count = 0;
1062
uint32_t table_ref_count = 0;
1063
bool modified = false;
1065
size_t ref_size, ref_count, read_size;
1066
MSRepoTableRefPtr tab_ref = NULL;
1070
/* Lock the BLOB: */
1071
mylock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
1073
/* Read the header: */
1074
ASSERT(head_size <= MS_OT_BUFFER_SIZE);
1075
read_size = read(otab->myOTBuffer, offset, head_size, 0);
1076
ptr.rp_chars = otab->myOTBuffer;
1077
if (CS_GET_DISK_4(ptr.rp_head->rd_magic_4) != MS_BLOB_HEADER_MAGIC)
1078
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "Invalid BLOB identifier");
1079
if (read_size < myRepo->myRepoBlobHeadSize) {
1080
removeBlob(otab, tab_id, blob_id, offset, auth_code);
1083
if ((! IN_USE_BLOB_STATUS(CS_GET_DISK_1(ptr.rp_head->rb_status_1))) ||
1084
CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code) {
1085
removeBlob(otab, tab_id, blob_id, offset, auth_code);
1089
/* Assume that what is here is correct: */
1090
if (head_size != CS_GET_DISK_2(ptr.rp_head->rb_head_size_2)) {
1091
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
1092
read_size = read(otab->myOTBuffer, offset, head_size, myRepo->myRepoBlobHeadSize);
1094
head_size = CS_GET_DISK_2(ptr.rp_head->rb_head_size_2);
1095
blob_size = CS_GET_DISK_6(ptr.rp_head->rb_blob_repo_size_6);
1096
if (read_size < head_size) {
1097
/* This should not happen, because the file has been recovered,
1098
* which should have already adjusted the head and blob
1100
* If this happens then the file must have been truncated an the BLOB has been
1101
* lost so we set the blob size to zero.
1103
head_size = read_size;
1107
ref_size = CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1);
1108
ref_count = CS_GET_DISK_2(ptr.rp_head->rb_ref_count_2);
1109
size = head_size - myRepo->myRepoBlobHeadSize;
1110
if (size > ref_size * ref_count)
1111
size = ref_size * ref_count;
1112
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
1113
while (size >= ref_size) {
1114
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
1115
case MS_BLOB_FREE_REF:
1117
case MS_BLOB_TABLE_REF:
1118
if (CS_GET_DISK_4(ptr.rp_tab_ref->tr_table_id_4) == tab_id &&
1119
CS_GET_DISK_6(ptr.rp_tab_ref->tr_blob_id_6) == blob_id)
1120
tab_ref = ptr.rp_tab_ref;
1122
case MS_BLOB_DELETE_REF:
1125
MSRepoTableRefPtr tr;
1127
tr = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepo->myRepoBlobHeadSize + (CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2)-1) * ref_size);
1128
if (CS_GET_DISK_2(tr->rr_type_2) == MS_BLOB_TABLE_REF) {
1129
/* I am deleting all references of a table. So I am here to
1130
* also delete the blob references that refer to the
1131
* table reference!!!
1133
if (CS_GET_DISK_4(tr->tr_table_id_4) == tab_id && CS_GET_DISK_6(tr->tr_blob_id_6) == blob_id) {
1134
/* Free the blob reference: */
1135
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
1144
ptr.rp_chars += ref_size;
1148
if (!table_ref_count && tab_ref) {
1149
CS_SET_DISK_2(tab_ref->rr_type_2, MS_BLOB_FREE_REF);
1154
if (!blob_ref_count) {
1155
realFreeBlob(otab, otab->myOTBuffer, auth_code, offset, head_size, blob_size, ref_size);
1156
} else if (modified)
1157
/* The reason I do not write the header of the header, is because
1158
* I want to handle the rb_last_access_4 being set at the
1161
write(otab->myOTBuffer + MS_BLOB_STAT_OFFS, offset + MS_BLOB_STAT_OFFS, head_size - MS_BLOB_STAT_OFFS);
1165
if (!table_ref_count || !tab_ref)
1166
/* Free the table reference, if there are no more
1167
* blob references, reference the table reference,
1168
* or if the table reference was not found in the
1171
removeBlob(otab, tab_id, blob_id, offset, auth_code);
1180
void MSRepoFile::checkBlob(CSStringBuffer *buffer, uint64_t offset, uint32_t auth_code, uint32_t temp_log_id, uint32_t temp_log_offset)
1184
MSRepoPointersRec ptr;
1185
uint32_t blob_ref_count = 0;
1186
bool modified = false;
1188
size_t ref_size, ref_count, read_size;
1192
MSRepoTempRefPtr my_ref = NULL;
1193
uint16_t ref_type = MS_BLOB_FREE_REF;
1198
/* Lock the BLOB: */
1199
mylock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
1202
/* Read the head of the header: */
1203
if (read(&blob, offset, sizeof(MSBlobHeadRec), 0) < sizeof(MSBlobHeadRec))
1206
// Because the temp log will be replayed from the start when the server
1207
// is restarted it is likely that it will have references to BLOBs that
1208
// no longer exist. So it is not an error if the BLOB ref doesn't point to
1211
// At some point this should probably be rethought because you cannot
1212
// tell the diference between a bad ref because of old data and a bad
1213
// ref because of a BUG.
1214
if (CS_GET_DISK_4(blob.rd_magic_4) != MS_BLOB_HEADER_MAGIC)
1217
head_size = CS_GET_DISK_2(blob.rb_head_size_2);
1218
blob_size = CS_GET_DISK_6(blob.rb_blob_repo_size_6);
1219
ref_size = CS_GET_DISK_1(blob.rb_ref_size_1);
1220
ref_count = CS_GET_DISK_2(blob.rb_ref_count_2);
1221
status = CS_GET_DISK_1(blob.rb_status_1);
1222
if (! IN_USE_BLOB_STATUS(status))
1225
/* Read the entire header: */
1226
buffer->setLength(head_size);
1227
ptr.rp_chars = buffer->getBuffer(0);
1228
read_size = read(ptr.rp_chars, offset, head_size, 0);
1229
if (read_size < myRepo->myRepoBlobHeadSize)
1231
if (CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code)
1233
if (read_size < head_size) {
1234
/* This should not happen, because the file has been recovered,
1235
* which should have already adjusted the head and blob
1237
* If this happens then the file must have been truncated an the BLOB has been
1238
* lost so we set the blob size to zero.
1240
head_size = read_size;
1243
size = head_size - myRepo->myRepoBlobHeadSize;
1244
if (size > ref_size * ref_count)
1245
size = ref_size * ref_count;
1248
/* Search through all references: */
1249
ptr.rp_chars += myRepo->myRepoBlobHeadSize;
1250
while (size >= ref_size) {
1251
switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
1252
case MS_BLOB_FREE_REF:
1254
case MS_BLOB_TABLE_REF:
1256
case MS_BLOB_DELETE_REF:
1257
if (CS_GET_DISK_4(ptr.rp_temp_ref->tp_log_id_4) == temp_log_id &&
1258
CS_GET_DISK_4(ptr.rp_temp_ref->tp_offset_4) == temp_log_offset) {
1259
ref_type = CS_GET_DISK_2(ptr.rp_ref->rr_type_2);
1260
my_ref = ptr.rp_temp_ref;
1261
CS_SET_DISK_2(ptr.rp_ref->rr_type_2, MS_BLOB_FREE_REF);
1266
MSRepoTableRefPtr tr;
1269
tabi = CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2);
1270
if (tabi < ref_count) {
1271
tr = (MSRepoTableRefPtr) (buffer->getBuffer(0) + myRepo->myRepoBlobHeadSize + (tabi-1) * ref_size);
1272
if (CS_GET_DISK_2(tr->rr_type_2) == MS_BLOB_TABLE_REF)
1277
ptr.rp_chars += ref_size;
1281
if ((ref_type == (uint16_t)MS_BLOB_DELETE_REF) && !blob_ref_count) {
1282
realFreeBlob(NULL, buffer->getBuffer(0), auth_code, offset, head_size, blob_size, ref_size);
1290
void MSRepoFile::returnToPool()
1292
myRepo->myRepoDatabase->returnRepoFileToPool(this);
1295
void MSRepoFile::removeBlob(MSOpenTable *otab, uint32_t tab_id, uint64_t blob_id, uint64_t offset, uint32_t auth_code)
1298
if (otab && otab->getDBTable()->myTableID == tab_id)
1299
otab->getDBTable()->freeBlobHandle(otab, blob_id, myRepo->myRepoID, offset, auth_code);
1301
MSOpenTable *tmp_otab;
1303
if ((tmp_otab = MSTableList::getOpenTableByID(myRepo->myRepoDatabase->myDatabaseID, tab_id))) {
1304
frompool_(tmp_otab);
1305
tmp_otab->getDBTable()->freeBlobHandle(tmp_otab, blob_id, myRepo->myRepoID, offset, auth_code);
1306
backtopool_(tmp_otab);
1312
MSRepoFile *MSRepoFile::newRepoFile(MSRepository *repo, CSPath *path)
1316
if (!(f = new MSRepoFile())) {
1318
CSException::throwOSError(CS_CONTEXT, ENOMEM);
1321
f->myFilePath = path;
1326
* ---------------------------------------------------------------
1330
MSRepository::MSRepository(uint32_t id, MSDatabase *db, off64_t file_size):
1331
CSSharedRefObject(),
1333
myRepoFileSize(file_size),
1334
myRepoLockState(REPO_UNLOCKED),
1335
isRemovingFP(false),
1339
myRepoDefRefSize(0),
1340
myRepoBlobHeadSize(0),
1341
myRecoveryOffset(0),
1343
myLastAccessTime(0),
1344
myLastCreateTime(0),
1346
mustBeDeleted(false),
1352
MSRepository::~MSRepository()
1354
CSPath *path = NULL;
1357
if (mustBeDeleted) {
1358
path = getRepoFilePath();
1362
isRemovingFP = true;
1363
removeRepoFilesNotInUse();
1364
/* With this, I also delete those that are in use!: */
1374
void MSRepository::openRepoFileForWriting(MSOpenTable *otab)
1376
if (!otab->myWriteRepoFile)
1377
otab->myWriteRepoFile = openRepoFile();
1380
uint64_t MSRepository::receiveBlob(MSOpenTable *otab, uint16_t head_size, uint64_t blob_size, Md5Digest *checksum, CSInputStream *stream)
1387
offset = myRepoFileSize;
1389
offset += head_size;
1391
ASSERT(myRepoDatabase->myBlobType == MS_STANDARD_STORAGE);
1396
while (blob_size > 0) {
1397
if (blob_size <= MS_OT_BUFFER_SIZE)
1398
tfer = (size_t) blob_size;
1400
tfer = MS_OT_BUFFER_SIZE;
1401
tfer = stream->read(otab->myOTBuffer, tfer);
1403
CSException::throwOSError(CS_CONTEXT, EPIPE);
1404
if (checksum) md5.md5_append((const u_char *)(otab->myOTBuffer), tfer);
1405
otab->myWriteRepoFile->write(otab->myOTBuffer, offset, tfer);
1406
offset += (uint64_t) tfer;
1407
blob_size -= (uint64_t) tfer;
1409
if (checksum) md5.md5_get_digest(checksum);
1412
// Write 1 byte to the end to reserver the space.
1413
otab->myWriteRepoFile->write("x" , offset + blob_size -1, 1);
1416
return_( myRepoFileSize);
1419
// copyBlob() copies the BLOB and its header.
1420
uint64_t MSRepository::copyBlob(MSOpenTable *otab, uint64_t size, CSInputStream *stream)
1422
off64_t offset = myRepoFileSize;
1426
if (size <= MS_OT_BUFFER_SIZE)
1427
tfer = (size_t) size;
1429
tfer = MS_OT_BUFFER_SIZE;
1430
tfer = stream->read(otab->myOTBuffer, tfer);
1432
CSException::throwOSError(CS_CONTEXT, EPIPE);
1433
otab->myWriteRepoFile->write(otab->myOTBuffer, offset, tfer);
1434
offset += (uint64_t) tfer;
1435
size -= (uint64_t) tfer;
1438
return myRepoFileSize;
1441
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)
1443
MSBlobHeadPtr blob ;
1444
MSRepoTableRefPtr tab_ref;
1445
MSRepoTempRefPtr temp_ref;
1447
uint16_t tab_idx, max_ref_count = (head_size - myRepoBlobHeadSize - metadata_size) / ref_size;
1450
if (max_ref_count > MS_REPO_MIN_REF_COUNT)
1451
max_ref_count = MS_REPO_MIN_REF_COUNT;
1453
ASSERT(max_ref_count > 1);
1455
if (blob_type == MS_CLOUD_STORAGE)
1456
now = cloud_key->creation_time;
1460
blob = (MSBlobHeadPtr) otab->myOTBuffer;
1461
CS_SET_DISK_4(blob->rb_last_access_4, now);
1462
CS_SET_DISK_4(blob->rb_mod_time_4, now);
1463
CS_SET_DISK_4(blob->rb_access_count_4, 0);
1464
CS_SET_DISK_4(blob->rb_backup_id_4, 0);
1465
CS_SET_DISK_4(blob->rb_create_time_4, now);
1466
CS_SET_DISK_4(blob->rd_magic_4, MS_BLOB_HEADER_MAGIC);
1467
CS_SET_DISK_2(blob->rb_head_size_2, head_size);
1468
CS_SET_DISK_6(blob->rb_blob_data_size_6, blob_size);
1469
CS_SET_DISK_1(blob->rb_status_1, MS_BLOB_ALLOCATED);
1470
CS_SET_DISK_1(blob->rb_ref_size_1, ref_size);
1471
CS_SET_DISK_2(blob->rb_ref_count_2, max_ref_count);
1472
CS_SET_DISK_4(blob->rb_last_ref_4, 0);
1473
CS_SET_DISK_4(otab->myOTBuffer + myRepoBlobHeadSize - 4, auth_code);
1475
memcpy(&(blob->rb_blob_checksum_md5d), checksum, sizeof(Md5Digest));
1477
CS_SET_DISK_2(blob->rb_mdata_size_2, metadata_size);
1478
if (metadata_size) {
1479
uint16_t metadata_offset = head_size - metadata_size;
1481
CS_SET_DISK_2(blob->rb_mdata_offset_2, metadata_offset);
1482
memcpy(otab->myOTBuffer + metadata_offset, metadata, metadata_size);
1484
#ifdef HAVE_ALIAS_SUPPORT
1486
md.use_data(metadata, metadata_size);
1488
alias = md.findAlias();
1490
uint32_t alias_hash;
1491
uint16_t alias_offset = metadata_offset + (uint16_t) (alias - metadata);
1492
CS_SET_DISK_2(blob->rb_alias_offset_2, alias_offset);
1493
alias_hash = myRepoDatabase->registerBlobAlias(myRepoID, offset, alias);
1494
CS_SET_DISK_4(blob->rb_alias_hash_4, alias_hash);
1496
CS_SET_DISK_2(blob->rb_alias_offset_2, 0);
1499
CS_SET_DISK_2(blob->rb_alias_offset_2, 0);
1503
CS_SET_DISK_2(blob->rb_mdata_offset_2, 0);
1504
CS_SET_DISK_2(blob->rb_alias_offset_2, 0);
1509
tab_ref = (MSRepoTableRefPtr) (otab->myOTBuffer + myRepoBlobHeadSize);
1510
CS_SET_DISK_2(tab_ref->rr_type_2, MS_BLOB_TABLE_REF);
1511
CS_SET_DISK_4(tab_ref->tr_table_id_4, otab->getDBTable()->myTableID);
1512
CS_SET_DISK_6(tab_ref->tr_blob_id_6, blob_id);
1513
temp_ref = (MSRepoTempRefPtr) (otab->myOTBuffer + myRepoBlobHeadSize + ref_size);
1514
tab_idx = 1; // This is the index of the blob table ref in the repository record.
1515
size = myRepoBlobHeadSize + ref_size + ref_size;
1518
temp_ref = (MSRepoTempRefPtr) (otab->myOTBuffer + myRepoBlobHeadSize);
1519
tab_idx = INVALID_INDEX; // Means not used
1520
size = myRepoBlobHeadSize + ref_size;
1523
CS_SET_DISK_2(temp_ref->rr_type_2, MS_BLOB_DELETE_REF);
1524
CS_SET_DISK_2(temp_ref->tp_del_ref_2, tab_idx);
1525
CS_SET_DISK_4(temp_ref->tp_log_id_4, log_id);
1526
CS_SET_DISK_4(temp_ref->tp_offset_4, log_offset);
1528
if (blob_type == MS_CLOUD_STORAGE) { // The data is stored in the cloud and not in the repository.
1529
CS_SET_DISK_4(blob->rb_s3_key_id_4, cloud_key->ref_index);
1530
CS_SET_DISK_4(blob->rb_s3_cloud_ref_4, cloud_key->cloud_ref);
1531
blob_size = 0; // The blob is not stored in the repository so the blob storage size in the repository is zero
1534
memset(otab->myOTBuffer + size, 0, head_size - size - metadata_size);
1536
CS_SET_DISK_1(blob->rb_storage_type_1, blob_type);
1537
CS_SET_DISK_6(blob->rb_blob_repo_size_6, blob_size);
1538
otab->myWriteRepoFile->write(blob, offset, head_size);
1540
setRepoFileSize(otab, offset + head_size + blob_size);
1543
void MSRepository::setRepoFileSize(MSOpenTable *otab, off64_t offset)
1545
myRepoFileSize = offset;
1546
if (myRepoFileSize >= PBMSParameters::getRepoThreshold()
1547
/**/ || getGarbageLevel() >= PBMSParameters::getGarbageThreshold())
1548
otab->closeForWriting();
1551
void MSRepository::syncHead(MSRepoFile *fh)
1556
myRecoveryOffset = myRepoFileSize;
1557
CS_SET_DISK_8(head.rh_recovery_offset_8, myRecoveryOffset);
1558
CS_SET_DISK_4(head.rh_last_temp_time_4, myLastTempTime);
1559
CS_SET_DISK_4(head.rh_last_access_4, myLastAccessTime);
1560
CS_SET_DISK_4(head.rh_create_time_4, myLastCreateTime);
1561
CS_SET_DISK_4(head.rh_last_ref_4, myLastRefTime);
1563
fh->write(&head.rh_recovery_offset_8, offsetof(MSRepoHeadRec, rh_recovery_offset_8), 24);
1567
MSRepoFile *MSRepository::openRepoFile()
1572
fh = MSRepoFile::newRepoFile(this, getRepoFilePath());
1575
fh->open(CSFile::DEFAULT);
1577
fh->open(CSFile::CREATE);
1578
if (!myRepoHeadSize) {
1588
/* Check again after locking: */
1589
if (!myRepoHeadSize) {
1590
if (fh->read(&head, 0, offsetof(MSRepoHeadRec, rh_reserved_4), 0) < offsetof(MSRepoHeadRec, rh_reserved_4)) {
1591
CS_SET_DISK_4(head.rh_magic_4, MS_REPO_FILE_MAGIC);
1592
CS_SET_DISK_2(head.rh_version_2, MS_REPO_FILE_VERSION);
1593
CS_SET_DISK_2(head.rh_repo_head_size_2, MS_REPO_FILE_HEAD_SIZE);
1594
CS_SET_DISK_2(head.rh_blob_head_size_2, sizeof(MSBlobHeadRec));
1595
CS_SET_DISK_2(head.rh_def_ref_size_2, sizeof(MSRepoGenericRefRec));
1596
CS_SET_DISK_8(head.rh_recovery_offset_8, MS_REPO_FILE_HEAD_SIZE);
1597
CS_SET_DISK_8(head.rh_garbage_count_8, 0);
1598
CS_SET_DISK_4(head.rh_last_temp_time_4, 0);
1599
CS_SET_DISK_4(head.rh_last_access_4, 0);
1600
CS_SET_DISK_4(head.rh_create_time_4, 0);
1601
CS_SET_DISK_4(head.rh_last_ref_4, 0);
1602
CS_SET_DISK_4(head.rh_reserved_4, 0);
1603
fh->write(&head, 0, sizeof(MSRepoHeadRec));
1606
/* Check the file header: */
1607
if (CS_GET_DISK_4(head.rh_magic_4) != MS_REPO_FILE_MAGIC)
1608
CSException::throwFileError(CS_CONTEXT, fh->getPathString(), CS_ERR_BAD_HEADER_MAGIC);
1609
if (CS_GET_DISK_2(head.rh_version_2) > MS_REPO_FILE_VERSION)
1610
CSException::throwFileError(CS_CONTEXT, fh->getPathString(), CS_ERR_VERSION_TOO_NEW);
1612
/* Load the header details: */
1613
myRepoHeadSize = CS_GET_DISK_2(head.rh_repo_head_size_2);
1614
myRepoDefRefSize = CS_GET_DISK_2(head.rh_def_ref_size_2);
1615
myRepoBlobHeadSize = CS_GET_DISK_2(head.rh_blob_head_size_2);
1616
myRecoveryOffset = CS_GET_DISK_8(head.rh_recovery_offset_8);
1617
myGarbageCount = CS_GET_DISK_8(head.rh_garbage_count_8);
1618
myLastTempTime = CS_GET_DISK_4(head.rh_last_temp_time_4);
1619
myLastAccessTime = CS_GET_DISK_4(head.rh_last_access_4);
1620
myLastCreateTime = CS_GET_DISK_4(head.rh_create_time_4);
1621
myLastRefTime = CS_GET_DISK_4(head.rh_last_ref_4);
1623
/* File size, cannot be less than header size: */
1624
if (myRepoFileSize < myRepoHeadSize)
1625
myRepoFileSize = myRepoHeadSize;
1627
ASSERT(myGarbageCount <= myRepoFileSize);
1629
/* Recover the file: */
1630
while (myRecoveryOffset < myRepoFileSize) {
1631
if ((size = fh->read(&blob, myRecoveryOffset, MS_MIN_BLOB_HEAD_SIZE, 0)) < MS_MIN_BLOB_HEAD_SIZE) {
1633
myRepoFileSize = myRecoveryOffset;
1634
fh->setEOF(myRepoFileSize);
1638
uint16_t ref_count, mdata_size, mdata_offset;
1641
status = CS_GET_DISK_1(blob.rb_status_1);
1642
ref_size = CS_GET_DISK_1(blob.rb_ref_size_1);
1643
ref_count = CS_GET_DISK_2(blob.rb_ref_count_2);
1644
head_size = CS_GET_DISK_2(blob.rb_head_size_2);
1645
mdata_size = CS_GET_DISK_2(blob.rb_mdata_size_2);
1646
mdata_offset = CS_GET_DISK_2(blob.rb_mdata_offset_2);
1647
blob_size = CS_GET_DISK_6(blob.rb_blob_repo_size_6);
1648
if ((CS_GET_DISK_4(blob.rd_magic_4) != MS_BLOB_HEADER_MAGIC) ||
1649
(! IN_USE_BLOB_STATUS(status)) ||
1650
head_size < (myRepoBlobHeadSize + ref_size * MS_REPO_MIN_REF_COUNT) ||
1651
head_size < (mdata_offset + mdata_size) ||
1652
((blob_size == 0) && (BLOB_IN_REPOSITORY(CS_GET_DISK_1(blob.rb_storage_type_1)))) ||
1653
myRecoveryOffset + head_size + blob_size > myRepoFileSize) {
1654
myRepoFileSize = myRecoveryOffset;
1655
fh->setEOF(myRepoFileSize);
1658
myRecoveryOffset += head_size + blob_size;
1662
myRecoveryOffset = myRepoFileSize;
1663
CS_SET_DISK_8(head.rh_recovery_offset_8, myRecoveryOffset);
1664
fh->write(&head, offsetof(MSRepoHeadRec, rh_recovery_offset_8), 8);
1673
void MSRepository::lockRepo(RepoLockState state)
1678
myLock = &myRepoWriteLock;
1681
ASSERT(!myRepoXLock);
1683
myRepoLockState = state;
1690
void MSRepository::signalCompactor()
1692
#ifndef MS_COMPACTOR_POLLS
1693
if (!mustBeDeleted) {
1694
if (getGarbageLevel() >= PBMSParameters::getGarbageThreshold()) {
1695
if (myRepoDatabase->myCompactorThread)
1696
myRepoDatabase->myCompactorThread->wakeup();
1702
void MSRepository::unlockRepo(RepoLockState state)
1706
myLock = &myRepoWriteLock;
1709
ASSERT(myRepoLockState & state);
1711
myRepoLockState &= ~state;
1712
if (myRepoLockState == REPO_UNLOCKED) {
1713
myRepoXLock = false;
1721
// Repositories are not removed from the pool when
1722
// scheduled for backup so the REPO_BACKUP flag is
1723
// not handled here.
1724
void MSRepository::returnToPool()
1728
myLock = &myRepoWriteLock;
1730
this->myRepoLockState &= ~(REPO_COMPACTING | REPO_WRITE);
1731
if ( this->myRepoLockState == REPO_UNLOCKED) {
1732
myRepoXLock = false;
1741
void MSRepository::backupCompleted()
1745
myLock = &myRepoWriteLock;
1749
this->myRepoLockState &= ~REPO_BACKUP;
1750
if ( this->myRepoLockState == REPO_UNLOCKED) {
1751
myRepoXLock = false;
1759
bool MSRepository::lockedForBackup() { return ((myRepoLockState & REPO_BACKUP) == REPO_BACKUP);}
1761
uint32_t MSRepository::initBackup()
1767
myLock = &myRepoWriteLock;
1769
state = this->myRepoLockState;
1770
this->myRepoLockState |= REPO_BACKUP;
1771
if (this->myRepoLockState == REPO_BACKUP)
1772
this->myRepoXLock = true;
1778
MSRepoFile *MSRepository::getRepoFile()
1782
if ((file = iFilePool)) {
1783
iFilePool = file->nextFile;
1784
file->nextFile = NULL;
1785
file->isFileInUse = true;
1791
void MSRepository::addRepoFile(MSRepoFile *file)
1793
iPoolFiles.addFront(file);
1796
void MSRepository::removeRepoFile(MSRepoFile *file)
1798
iPoolFiles.remove(file);
1801
void MSRepository::returnRepoFile(MSRepoFile *file)
1803
file->isFileInUse = false;
1804
file->nextFile = iFilePool;
1808
bool MSRepository::removeRepoFilesNotInUse()
1810
MSRepoFile *file, *curr_file;
1813
/* Remove all files that are not in use: */
1814
if ((file = (MSRepoFile *) iPoolFiles.getBack())) {
1817
file = (MSRepoFile *) file->getNextLink();
1818
if (!curr_file->isFileInUse)
1819
iPoolFiles.remove(curr_file);
1822
return iPoolFiles.getSize() == 0;
1825
off64_t MSRepository::getRepoFileSize()
1827
return myRepoFileSize;
1830
size_t MSRepository::getRepoHeadSize()
1832
return myRepoHeadSize;
1835
size_t MSRepository::getRepoBlobHeadSize()
1837
return myRepoBlobHeadSize;
1840
CSMutex *MSRepository::getRepoLock(off64_t offset)
1842
return &myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
1845
uint32_t MSRepository::getRepoID()
1850
uint32_t MSRepository::getGarbageLevel()
1852
if (myRepoFileSize <= myRepoHeadSize)
1854
return myGarbageCount * 100 / (myRepoFileSize - myRepoHeadSize);
1857
CSPath *MSRepository::getRepoFilePath()
1859
char file_name[120];
1861
cs_strcpy(120, file_name, "bs-repository");
1862
cs_add_dir_char(120, file_name);
1863
cs_strcat(120, file_name, "repo-");
1864
cs_strcat(120, file_name, myRepoID);
1865
cs_strcat(120, file_name, ".bs");
1867
if (myRepoDatabase && myRepoDatabase->myDatabasePath) {
1868
return CSPath::newPath(RETAIN(myRepoDatabase->myDatabasePath), file_name);