~ubuntu-branches/ubuntu/trusty/drizzle/trusty

« back to all changes in this revision

Viewing changes to plugin/pbms/src/repository_ms.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2010-10-02 14:17:48 UTC
  • mfrom: (1.1.1 upstream)
  • mto: (2.1.17 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20101002141748-m6vbfbfjhrw1153e
Tags: 2010.09.1802-1
* New upstream release.
* Removed pid-file argument hack.
* Updated GPL-2 address to be new address.
* Directly copy in drizzledump.1 since debian doesn't have sphinx 1.0 yet.
* Link to jquery from libjs-jquery. Add it as a depend.
* Add drizzled.8 symlink to the install files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2008 PrimeBase Technologies GmbH, Germany
 
2
 *
 
3
 * PrimeBase Media Stream for MySQL
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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
 
18
 *
 
19
 * Original author: Paul McCullagh
 
20
 * Continued development: Barry Leslie
 
21
 *
 
22
 * 2007-05-25
 
23
 *
 
24
 * H&G2JCtL
 
25
 *
 
26
 * Network interface.
 
27
 *
 
28
 */
 
29
 
 
30
#ifdef DRIZZLED
 
31
#include "config.h"
 
32
#include <drizzled/common.h>
 
33
#include <drizzled/session.h>
 
34
#endif
 
35
 
 
36
#include "cslib/CSConfig.h"
 
37
#include <inttypes.h>
 
38
 
 
39
#include "defs_ms.h"
 
40
 
 
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"
 
46
 
 
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"
 
53
 
 
54
/*
 
55
 * ---------------------------------------------------------------
 
56
 * REPOSITORY FILE
 
57
 */
 
58
 
 
59
MSRepoFile::MSRepoFile():
 
60
CSFile(),
 
61
myRepo(NULL),
 
62
isFileInUse(false),
 
63
nextFile(NULL),
 
64
iNextLink(NULL),
 
65
iPrevLink(NULL)
 
66
{
 
67
}
 
68
 
 
69
MSRepoFile::~MSRepoFile()
 
70
{
 
71
        close();
 
72
}
 
73
 
 
74
void MSRepoFile::updateGarbage(uint64_t size) 
 
75
{
 
76
        MSRepoHeadRec   repo_head;
 
77
        enter_();
 
78
 
 
79
        lock_(myRepo);
 
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);
 
84
        unlock_(myRepo);
 
85
        if (!myRepo->myRepoXLock) 
 
86
                myRepo->signalCompactor();
 
87
 
 
88
        exit_();
 
89
}
 
90
 
 
91
void MSRepoFile::updateAccess(MSBlobHeadPtr blob, uint64_t rep_offset) 
 
92
{
 
93
        time_t  now = time(NULL);
 
94
        uint32_t count = CS_GET_DISK_4(blob->rb_access_count_4) +1;
 
95
 
 
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;
 
100
}
 
101
 
 
102
uint64_t MSRepoFile::readBlobChunk(PBMSBlobIDPtr blob_id, uint64_t rep_offset, uint64_t blob_offset, uint64_t buffer_size, char *buffer)
 
103
{
 
104
        MSBlobHeadRec           blob_head;
 
105
        size_t                          tfer;
 
106
        uint16_t                                head_size;
 
107
        uint64_t                                blob_size;
 
108
        uint32_t                                ac;
 
109
        uint64_t                                offset, blob_read =0;
 
110
 
 
111
        enter_();
 
112
 
 
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");
 
116
                
 
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");
 
122
 
 
123
        offset = rep_offset + blob_offset + head_size;
 
124
        
 
125
        if (blob_offset > blob_size)
 
126
                goto done;
 
127
        
 
128
        if ((blob_offset + buffer_size) > blob_size)
 
129
                buffer_size = blob_size - blob_offset;
 
130
                
 
131
        while (buffer_size > 0) {
 
132
                if (buffer_size <= (uint64_t) (SSIZE_MAX))
 
133
                        tfer = (size_t) buffer_size;
 
134
                else
 
135
                        tfer = SSIZE_MAX;
 
136
                        
 
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;
 
142
        }
 
143
 
 
144
        /* Only update the access timestamp when reading the first block: */
 
145
        if (!blob_offset) 
 
146
                updateAccess(&blob_head, rep_offset);
 
147
        
 
148
done:
 
149
        return_(blob_read);
 
150
}
 
151
 
 
152
void MSRepoFile::writeBlobChunk(PBMSBlobIDPtr blob_id, uint64_t rep_offset, uint64_t blob_offset, uint64_t data_size, char *data)
 
153
{
 
154
        size_t                          tfer;
 
155
        off64_t                         offset;
 
156
        MSBlobHeadRec           blob_head;
 
157
        uint16_t                                head_size;
 
158
        uint64_t                                blob_size;
 
159
        uint32_t                                ac;
 
160
 
 
161
        enter_();
 
162
 
 
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");
 
166
 
 
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");
 
172
 
 
173
        if ((blob_offset + data_size) > blob_size) 
 
174
                CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB write size or offset");
 
175
 
 
176
        offset = (uint64_t) head_size + rep_offset + blob_offset;
 
177
                
 
178
        while (data_size > 0) {
 
179
                if (data_size <= (uint64_t) (SSIZE_MAX))
 
180
                        tfer = (size_t) data_size;
 
181
                else
 
182
                        tfer = SSIZE_MAX;
 
183
                        
 
184
                write(data, offset, tfer);
 
185
                data += (uint64_t) tfer;
 
186
                offset += (uint64_t) tfer;
 
187
                data_size -= (uint64_t) tfer;
 
188
        }
 
189
 
 
190
        exit_();
 
191
}
 
192
 
 
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)
 
194
{
 
195
        MSConnectionHandler     *me;
 
196
        size_t                          tfer;
 
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;
 
202
        uint32_t                                ac;
 
203
        char                            num_str[CS_WIDTH_INT_64];
 
204
        bool                            redirect = false;
 
205
        
 
206
 
 
207
        enter_();
 
208
        me = (MSConnectionHandler *) self;
 
209
 
 
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");
 
219
 
 
220
        storage_type = CS_GET_DISK_1(blob_head.rb_storage_type_1);
 
221
        
 
222
        if ((!info_only) && BLOB_IN_CLOUD(storage_type)) {
 
223
                CSString *redirect_url = NULL;
 
224
                CloudKeyRec key;
 
225
                getBlobKey(&blob_head, &key);
 
226
                redirect_url =  otab->getDB()->myBlobCloud->cl_getDataURL(&key);        
 
227
                push_(redirect_url);
 
228
                stream->setStatus(301);
 
229
                stream->addHeader("Location", redirect_url->getCString());
 
230
                release_(redirect_url);
 
231
                redirect = true;
 
232
        } else
 
233
                stream->setStatus(200);
 
234
 
 
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);
 
240
        }
 
241
        
 
242
        snprintf(num_str, CS_WIDTH_INT_64, "%"PRIu64"", blob_data_size);
 
243
        stream->addHeader(MS_BLOB_SIZE, num_str);
 
244
                
 
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);
 
247
 
 
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);
 
250
        
 
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);
 
253
        
 
254
        snprintf(num_str, CS_WIDTH_INT_64, "%"PRIu32"", storage_type);
 
255
        stream->addHeader(MS_BLOB_TYPE, num_str);
 
256
        
 
257
        
 
258
        // Add the meta data headers.
 
259
        if (meta_size) {
 
260
                MetaData metadata;
 
261
                char *name, *value;
 
262
                
 
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);
 
267
                }
 
268
                
 
269
        }
 
270
                
 
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;
 
275
 
 
276
        stream->setContentLength((redirect || info_only)?0:local_blob_size);
 
277
        stream->writeHead();
 
278
        me->replyPending = false;
 
279
 
 
280
        if ((!redirect) && !info_only) {
 
281
    
 
282
                while (local_blob_size > 0) {
 
283
                        if (local_blob_size <=  MS_OT_BUFFER_SIZE)
 
284
                                tfer = (size_t) local_blob_size;
 
285
                        else
 
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;
 
291
                }
 
292
        }
 
293
        stream->flush();
 
294
 
 
295
        if (!info_only) {
 
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);
 
299
        }
 
300
        
 
301
        exit_();
 
302
}
 
303
 
 
304
void MSRepoFile::update_blob_header(MSOpenTable *otab, uint64_t offset, uint64_t blob_size, uint16_t head_size, uint16_t new_head_size)
 
305
{
 
306
        uint16_t        w_offset =  offsetof(MSBlobHeadRec, rb_ref_count_2);
 
307
        MSRepoPointersRec       ptr;
 
308
        enter_();
 
309
 
 
310
        ptr.rp_chars = otab->myOTBuffer;
 
311
        CS_SET_DISK_4(ptr.rp_head->rb_mod_time_4, time(NULL));
 
312
        
 
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);
 
316
        } else {
 
317
                /* Copy to a new space, free the old: */
 
318
                off64_t                 dst_offset;
 
319
                CSStringBuffer  *buffer;
 
320
                uint16_t ref_count, ref_size;
 
321
                uint32_t tab_id;
 
322
                uint64_t blob_id;
 
323
                
 
324
                myRepo->myRepoDatabase->openWriteRepo(otab);
 
325
                dst_offset = otab->myWriteRepo->myRepoFileSize;
 
326
 
 
327
                /* Write the header. */
 
328
                otab->myWriteRepoFile->write(otab->myOTBuffer, dst_offset, new_head_size);
 
329
 
 
330
                /* We have an engine reference, copy the BLOB over: */
 
331
                new_(buffer, CSStringBuffer());
 
332
                push_(buffer);
 
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);
 
335
                release_(buffer);
 
336
 
 
337
#ifdef HAVE_ALIAS_SUPPORT
 
338
                /* Update the BLOB alias if required. */
 
339
                
 
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);
 
343
                }
 
344
#endif
 
345
 
 
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;
 
350
                
 
351
                while (ref_count) {
 
352
                        switch (CS_GET_DISK_2(ptr.rp_ref->rr_type_2)) {
 
353
                                case MS_BLOB_FREE_REF:
 
354
                                        break;
 
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);
 
358
 
 
359
                                        if (otab->getDBTable()->myTableID == tab_id)
 
360
                                                otab->getDBTable()->updateBlobHandle(otab, blob_id, otab->myWriteRepo->myRepoID, dst_offset, new_head_size);
 
361
                                        else {
 
362
                                                MSOpenTable *ref_otab;
 
363
 
 
364
                                                ref_otab = MSTableList::getOpenTableByID(myRepo->myRepoDatabase->myDatabaseID, tab_id);
 
365
                                                frompool_(ref_otab);
 
366
                                                ref_otab->getDBTable()->updateBlobHandle(ref_otab, blob_id, otab->myWriteRepo->myRepoID, dst_offset, new_head_size);
 
367
                                                backtopool_(ref_otab);
 
368
                                        }
 
369
                                        break;
 
370
                                case MS_BLOB_DELETE_REF:
 
371
                                        break;
 
372
                                default:
 
373
                                        break;
 
374
                        }
 
375
                        ptr.rp_chars += ref_size;
 
376
                        ref_count--; 
 
377
                }
 
378
 
 
379
                otab->myWriteRepo->myRepoFileSize += new_head_size + blob_size;
 
380
 
 
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());
 
389
                } else
 
390
                        CS_SET_DISK_1(ptr.rp_head->rb_status_1, MS_BLOB_DELETED);
 
391
                
 
392
                write(ptr.rp_chars + MS_BLOB_STAT_OFFS, offset + MS_BLOB_STAT_OFFS, head_size - MS_BLOB_STAT_OFFS);
 
393
                        
 
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);
 
399
                
 
400
                w_offset =  offsetof(MSBlobHeadRec, rb_alias_hash_4);
 
401
                write(otab->myOTBuffer + w_offset, offset + w_offset, head_size - w_offset);
 
402
#endif
 
403
 
 
404
                /* Increment the garbage count: */
 
405
                updateGarbage(head_size + blob_size);
 
406
                
 
407
        }
 
408
        exit_();
 
409
}
 
410
 
 
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)
 
412
{
 
413
        CSMutex                         *myLock;
 
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];
 
424
#endif
 
425
        uint64_t                                blob_size;
 
426
        
 
427
        enter_();
 
428
        /* Lock the BLOB: */
 
429
        myLock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
 
430
        lock_(myLock);
 
431
        /* Read the header: */
 
432
        if (head_size > MS_OT_BUFFER_SIZE) {
 
433
                CSException::throwAssertion(CS_CONTEXT, "BLOB header overflow");
 
434
        }
 
435
        
 
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");
 
451
                }
 
452
                read_size = read(otab->myOTBuffer, offset, head_size, myRepo->myRepoBlobHeadSize);
 
453
        }
 
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
 
459
                 * size.
 
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.
 
462
                 */
 
463
                head_size = read_size;
 
464
                blob_size = 0;
 
465
                
 
466
        }
 
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);
 
469
        
 
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));
 
474
        }
 
475
#endif
 
476
        
 
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:
 
486
                                if (!free_ref)
 
487
                                        free_ref = ptr.rp_blob_ref;
 
488
                                else if (!free2_ref)
 
489
                                        free2_ref = ptr.rp_blob_ref;
 
490
                                break;
 
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.)
 
494
#endif
 
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;
 
498
                                break;
 
499
                        case MS_BLOB_DELETE_REF: {
 
500
                                uint32_t tab_index;
 
501
 
 
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;
 
505
 
 
506
                                        tab_index--;
 
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);
 
511
                                                if (free_ref)
 
512
                                                        free2_ref = free_ref;
 
513
                                                free_ref = ptr.rp_blob_ref;
 
514
                                        }
 
515
                                }
 
516
                                else if (tab_index == INVALID_INDEX) {
 
517
                                        /* The is a reference from the temporary log only!! */
 
518
                                        if (free_ref)
 
519
                                                free2_ref = free_ref;
 
520
                                        free_ref = ptr.rp_blob_ref;
 
521
                                }
 
522
                                break;
 
523
                        }
 
524
                        default: { // Must be a blob REF, check that the BLOB reference doesn't already exist.
 
525
                                uint32_t tab_index;
 
526
                                tab_index = CS_GET_DISK_2(ptr.rp_blob_ref->er_table_2);
 
527
                                
 
528
                                if (tab_index && tab_index < ref_count) {
 
529
                                        MSRepoTableRefPtr tr;
 
530
 
 
531
                                        tab_index--;
 
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) {
 
536
                                                        char message[100];
 
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);
 
540
                                                        goto done;
 
541
                                                }
 
542
                                        }
 
543
                                }
 
544
                                break;
 
545
                        }
 
546
                }
 
547
                ptr.rp_chars += ref_size;
 
548
                size -= ref_size;
 
549
        }
 
550
 
 
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
 
553
        // also.
 
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);
 
558
                
 
559
                if (sp > (new_refs * CS_GET_DISK_1(ptr.rp_head->rb_ref_size_1))) {
 
560
                        sp = MS_MIN_BLOB_HEAD_SIZE;
 
561
                }
 
562
                
 
563
                if (MS_CAN_ADD_REFS(ptr.rp_head, new_refs)) {
 
564
                        new_head_size = head_size;
 
565
 
 
566
                } else { // The header must be grown
 
567
                        size_t new_size, max_refs;
 
568
 
 
569
                        if (ref_count < 2)
 
570
                                max_refs = 4;
 
571
                        else if (ref_count > 32)
 
572
                                max_refs = ref_count + 32;
 
573
                        else
 
574
                                max_refs = 2 * ref_count;
 
575
                                
 
576
                        if (max_refs > (MS_OT_BUFFER_SIZE/ref_size))
 
577
                                max_refs = (MS_OT_BUFFER_SIZE/ref_size);
 
578
                                
 
579
                        if (max_refs < (ref_count + new_refs))
 
580
                                CSException::throwAssertion(CS_CONTEXT, "BLOB reference header overflow");
 
581
 
 
582
                        new_size = head_size + ref_size * max_refs;
 
583
 
 
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;
 
587
                                
 
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);
 
592
                                
 
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;
 
597
                                
 
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);
 
600
                                
 
601
                        } else
 
602
                                memset(ptr.rp_chars + head_size, 0, new_size - head_size);
 
603
                        
 
604
                        new_head_size = new_size;
 
605
                }
 
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;
 
609
                
 
610
                if (!free_ref) {
 
611
                        free_ref = ptr.rp_blob_ref;
 
612
                        memset(free_ref, 0, ref_size);
 
613
                        ptr.rp_chars += ref_size;
 
614
                }
 
615
 
 
616
                if (!tab_ref) {
 
617
                        free2_ref = ptr.rp_blob_ref;
 
618
                        memset(free2_ref, 0, ref_size); 
 
619
                }
 
620
                
 
621
                ref_count += new_refs;
 
622
        }
 
623
        else
 
624
                new_head_size = head_size;
 
625
 
 
626
        if (!tab_ref) {
 
627
                tab_ref = (MSRepoTableRefPtr) free2_ref;
 
628
 
 
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);
 
632
        }
 
633
 
 
634
        size_t tab_idx;
 
635
 
 
636
        tab_idx = (((char *) tab_ref - otab->myOTBuffer) - myRepo->myRepoBlobHeadSize) / ref_size;
 
637
 
 
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));
 
641
 
 
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);
 
646
#endif
 
647
 
 
648
done:
 
649
        
 
650
        unlock_(myLock);
 
651
        exit_();
 
652
}
 
653
 
 
654
void MSRepoFile::setBlobMetaData(MSOpenTable *otab, uint64_t offset, const char *meta_data, uint16_t meta_data_len, bool reset_alias, const char  *alias)
 
655
{
 
656
        CSMutex                         *lock;
 
657
        MSRepoPointersRec       ptr;
 
658
        size_t                          read_size;
 
659
        uint16_t                                new_head_size;
 
660
        uint64_t                                blob_size;
 
661
        uint16_t                                head_size, mdata_size, mdata_offset, alias_offset = 0;
 
662
        MSBlobHeadRec           blob;
 
663
        
 
664
        enter_();
 
665
        /* Lock the BLOB: */
 
666
        lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
 
667
        lock_(lock);
 
668
 
 
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");
 
672
        }
 
673
 
 
674
        head_size = CS_GET_DISK_2(blob.rb_head_size_2);
 
675
 
 
676
        if (head_size > MS_OT_BUFFER_SIZE) {
 
677
                CSException::throwAssertion(CS_CONTEXT, "BLOB header overflow");
 
678
        }
 
679
        
 
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");
 
688
 
 
689
 
 
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
 
694
                 * size.
 
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.
 
697
                 */
 
698
                head_size = read_size;
 
699
                blob_size = 0;          
 
700
        }
 
701
        mdata_size = CS_GET_DISK_2(ptr.rp_head->rb_mdata_size_2);
 
702
 
 
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
 
706
 
 
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");
 
710
 
 
711
                memset(ptr.rp_chars + head_size, 0, new_head_size - head_size);         
 
712
        }       
 
713
                                
 
714
        // Meta data is placed at the end of the header.
 
715
        if (meta_data_len)
 
716
                mdata_offset = new_head_size - meta_data_len;
 
717
        else
 
718
                mdata_offset = 0;
 
719
        mdata_size      = meta_data_len;
 
720
                
 
721
        
 
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);
 
724
        
 
725
#ifdef HAVE_ALIAS_SUPPORT
 
726
        uint32_t alias_hash = INVALID_ALIAS_HASH;
 
727
        if (alias) {
 
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);
 
730
                if (reset_alias) {
 
731
                        if (alias_offset)
 
732
                                alias_hash = myRepo->myRepoDatabase->updateBlobAlias(myRepo->myRepoID, offset, alias_hash, alias);
 
733
                        else {
 
734
                                alias_hash = myRepo->myRepoDatabase->registerBlobAlias(myRepo->myRepoID, offset, alias);
 
735
                        }
 
736
                }
 
737
                alias_offset = mdata_offset + (alias - meta_data);
 
738
                
 
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));
 
742
                alias_offset = 0;
 
743
        }
 
744
#else
 
745
        uint32_t alias_hash = -1;
 
746
        if (alias || reset_alias) {
 
747
                CSException::throwException(CS_CONTEXT, MS_ERR_NOT_IMPLEMENTED, "No BLOB alias support.");
 
748
        }
 
749
#endif
 
750
 
 
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);
 
753
        
 
754
        memcpy(ptr.rp_chars + mdata_offset, meta_data, meta_data_len);
 
755
                
 
756
        update_blob_header(otab, offset, blob_size, head_size, new_head_size);
 
757
                
 
758
        unlock_(lock);
 
759
        exit_();
 
760
 
 
761
}
 
762
 
 
763
 
 
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)
 
765
{
 
766
        CSMutex                         *lock;
 
767
        MSRepoPointersRec       ptr;
 
768
        uint32_t                        table_ref_count = 0;
 
769
        uint32_t                        size;
 
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;
 
775
        uint32_t                        alias_hash;
 
776
 
 
777
        enter_();
 
778
        /* Lock the BLOB: */
 
779
        lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
 
780
        lock_(lock);
 
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);
 
789
                goto exit;
 
790
        }
 
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);
 
794
                goto exit;
 
795
        }
 
796
        
 
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);
 
801
        }
 
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
 
806
                 * size.
 
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.
 
809
                 */
 
810
                head_size = read_size;
 
811
        }
 
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);
 
814
 
 
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);
 
817
 
 
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:
 
826
                                break;
 
827
                        case MS_BLOB_DELETE_REF: {
 
828
                                uint32_t tabi;
 
829
 
 
830
                                tabi = CS_GET_DISK_2(ptr.rp_temp_ref->tp_del_ref_2);
 
831
                                if (tabi && tabi < ref_count) {
 
832
                                        tabi--;
 
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);
 
843
                                        }
 
844
                                }
 
845
                                break;
 
846
                        }
 
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);                                         
 
857
                                        }
 
858
                                        else
 
859
                                                table_ref_count++;
 
860
                                }
 
861
                                break;
 
862
                }
 
863
                ptr.rp_chars += ref_size;
 
864
                size -= ref_size;
 
865
        }
 
866
 
 
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) {
 
870
                uint32_t        log_id;
 
871
                uint32_t log_offset;
 
872
                uint32_t temp_time;
 
873
#ifdef HAVE_ALIAS_SUPPORT
 
874
                MSDiskAliasRec aliasDiskRec;
 
875
                MSDiskAliasPtr aliasDiskPtr = NULL;
 
876
                
 
877
                if (alias_offset) {
 
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;
 
882
                }
 
883
                
 
884
                myRepo->myRepoDatabase->queueForDeletion(otab, MS_TL_BLOB_REF, tab_id, blob_id, auth_code, &log_id, &log_offset, &temp_time, aliasDiskPtr);
 
885
#else
 
886
                myRepo->myRepoDatabase->queueForDeletion(otab, MS_TL_BLOB_REF, tab_id, blob_id, auth_code, &log_id, &log_offset, &temp_time);
 
887
#endif
 
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);
 
893
                
 
894
                CS_SET_DISK_1(ptr.rp_head->rb_status_1, MS_BLOB_ALLOCATED); // The BLOB is allocated but no longer referenced
 
895
        }
 
896
        if (temp_ref) {
 
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
 
899
                 * same time!
 
900
                 */
 
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) {
 
903
                char message[100];
 
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);
 
907
        }
 
908
 
 
909
        exit:
 
910
        unlock_(lock);
 
911
        exit_();
 
912
}
 
913
 
 
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)
 
915
{
 
916
        CSMutex                         *lock;
 
917
        MSRepoPointersRec       ptr;
 
918
        uint32_t                                size;
 
919
        size_t                          ref_size, ref_count, read_size;
 
920
        MSRepoTableRefPtr       tab_ref;
 
921
 
 
922
        enter_();
 
923
        /* Lock the BLOB: */
 
924
        lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
 
925
        lock_(lock);
 
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");
 
932
 
 
933
 
 
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");
 
940
 
 
941
        
 
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);
 
946
        }
 
947
        
 
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
 
952
                 * size.
 
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.
 
955
                 */
 
956
                head_size = read_size;
 
957
        }
 
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);
 
960
 
 
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:
 
969
                                break;
 
970
                        case MS_BLOB_DELETE_REF: {
 
971
                                break;
 
972
                        }
 
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);                                 
 
983
                                                goto exit;
 
984
                                        }
 
985
                                }
 
986
                                break;
 
987
                }
 
988
                ptr.rp_chars += ref_size;
 
989
                size -= ref_size;
 
990
        }
 
991
 
 
992
        if (PBMSDaemon::isDaemonState(PBMSDaemon::DaemonStartUp) == false) {
 
993
                char message[100];
 
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);
 
996
        }
 
997
        
 
998
        exit:
 
999
        unlock_(lock);
 
1000
        exit_();
 
1001
}
 
1002
 
 
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)
 
1004
{
 
1005
        uint32_t                                tab_id;
 
1006
        uint64_t                                blob_id;
 
1007
        size_t                          size;
 
1008
        MSRepoPointersRec       ptr;
 
1009
        enter_();
 
1010
        
 
1011
        ptr.rp_chars = buffer;
 
1012
        
 
1013
        if (BLOB_IN_CLOUD(CS_GET_DISK_1(ptr.rp_head->rb_storage_type_1))) {
 
1014
                CloudKeyRec key;
 
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.");
 
1018
                        
 
1019
                myRepo->myRepoDatabase->myBlobCloud->cl_deleteData(&key); 
 
1020
        }
 
1021
                
 
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);
 
1027
#endif
 
1028
 
 
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);
 
1032
 
 
1033
        /* Update garbage count: */
 
1034
        updateGarbage(head_size + blob_size);
 
1035
 
 
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);
 
1044
                }
 
1045
                ptr.rp_chars += ref_size;
 
1046
                size -= ref_size;
 
1047
        }
 
1048
        exit_();
 
1049
}
 
1050
 
 
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)
 
1053
{
 
1054
        CSMutex                         *lock;
 
1055
        MSRepoPointersRec       ptr;
 
1056
        uint32_t                                blob_ref_count = 0;
 
1057
        uint32_t                                table_ref_count = 0;
 
1058
        bool                            modified = false;
 
1059
        uint32_t                                size;
 
1060
        size_t                          ref_size, ref_count, read_size;
 
1061
        MSRepoTableRefPtr       tab_ref = NULL;
 
1062
        uint64_t                                blob_size;
 
1063
 
 
1064
        enter_();
 
1065
        /* Lock the BLOB: */
 
1066
        lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
 
1067
        lock_(lock);
 
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);
 
1076
                goto exit;
 
1077
        }
 
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);
 
1081
                goto exit;
 
1082
        }
 
1083
        
 
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);
 
1088
        }
 
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
 
1094
                 * size.
 
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.
 
1097
                 */
 
1098
                head_size = read_size;
 
1099
                blob_size = 0; 
 
1100
                
 
1101
        }
 
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:
 
1111
                                break;
 
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;
 
1116
                                break;
 
1117
                        case MS_BLOB_DELETE_REF:
 
1118
                        break;
 
1119
                        default:
 
1120
                                MSRepoTableRefPtr tr;
 
1121
 
 
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!!!
 
1127
                                         */
 
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);
 
1131
                                                modified = true;
 
1132
                                        }
 
1133
                                        else
 
1134
                                                blob_ref_count++;
 
1135
                                
 
1136
                                }
 
1137
                                break;
 
1138
                }
 
1139
                ptr.rp_chars += ref_size;
 
1140
                size -= ref_size;
 
1141
        }
 
1142
 
 
1143
        if (!table_ref_count && tab_ref) {
 
1144
                CS_SET_DISK_2(tab_ref->rr_type_2, MS_BLOB_FREE_REF);
 
1145
                modified = true;
 
1146
        }
 
1147
        
 
1148
        
 
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
 
1154
                 * same time!
 
1155
                 */
 
1156
                write(otab->myOTBuffer + MS_BLOB_STAT_OFFS, offset + MS_BLOB_STAT_OFFS, head_size - MS_BLOB_STAT_OFFS);
 
1157
 
 
1158
        unlock_(lock);
 
1159
 
 
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
 
1164
                 * BLOB at all!
 
1165
                 */
 
1166
                removeBlob(otab, tab_id, blob_id, offset, auth_code);
 
1167
 
 
1168
        exit_();
 
1169
 
 
1170
        exit:
 
1171
        unlock_(lock);
 
1172
        exit_();
 
1173
}
 
1174
 
 
1175
void MSRepoFile::checkBlob(CSStringBuffer *buffer, uint64_t offset, uint32_t auth_code, uint32_t temp_log_id, uint32_t temp_log_offset)
 
1176
{
 
1177
        CSMutex                         *lock;
 
1178
        MSBlobHeadRec           blob;
 
1179
        MSRepoPointersRec       ptr;
 
1180
        uint32_t                                blob_ref_count = 0;
 
1181
        bool                            modified = false;
 
1182
        uint32_t                                size;
 
1183
        size_t                          ref_size, ref_count, read_size;
 
1184
        uint8_t                         status;
 
1185
        uint16_t                                head_size;
 
1186
        uint64_t                                blob_size;
 
1187
        MSRepoTempRefPtr        my_ref = NULL;
 
1188
        uint16_t                                ref_type = MS_BLOB_FREE_REF;
 
1189
        enter_();
 
1190
        
 
1191
        /* Lock the BLOB: */
 
1192
        lock = &myRepo->myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
 
1193
        lock_(lock);
 
1194
 
 
1195
        /* Read the head of the header: */
 
1196
        if (read(&blob, offset, sizeof(MSBlobHeadRec), 0) < sizeof(MSBlobHeadRec)) 
 
1197
                goto exit;
 
1198
 
 
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
 
1202
        // a valid BLOB. 
 
1203
        //
 
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) 
 
1208
                goto exit;
 
1209
        
 
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))
 
1216
                goto exit;
 
1217
 
 
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)
 
1223
                goto exit;
 
1224
        if (CS_GET_DISK_4(ptr.rp_bytes + myRepo->myRepoBlobHeadSize - 4) != auth_code)
 
1225
                goto exit;
 
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
 
1229
                 * size.
 
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.
 
1232
                 */
 
1233
                head_size = read_size;
 
1234
                blob_size = 0; 
 
1235
        }
 
1236
        size = head_size - myRepo->myRepoBlobHeadSize;
 
1237
        if (size > ref_size * ref_count)
 
1238
                size = ref_size * ref_count;
 
1239
 
 
1240
        
 
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:
 
1246
                                break;
 
1247
                        case MS_BLOB_TABLE_REF:
 
1248
                                break;
 
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);
 
1255
                                        modified = true;
 
1256
                                }
 
1257
                                break;
 
1258
                        default:
 
1259
                                MSRepoTableRefPtr       tr;
 
1260
                                uint32_t                                tabi;
 
1261
 
 
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)
 
1266
                                                blob_ref_count++;
 
1267
                                }
 
1268
                                break;
 
1269
                }
 
1270
                ptr.rp_chars += ref_size;
 
1271
                size -= ref_size;
 
1272
        }
 
1273
 
 
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);
 
1276
        }
 
1277
        
 
1278
        exit:
 
1279
        unlock_(lock);
 
1280
        exit_();
 
1281
}
 
1282
 
 
1283
void MSRepoFile::returnToPool()
 
1284
{
 
1285
        myRepo->myRepoDatabase->returnRepoFileToPool(this);
 
1286
}
 
1287
 
 
1288
void MSRepoFile::removeBlob(MSOpenTable *otab, uint32_t tab_id, uint64_t blob_id, uint64_t offset, uint32_t auth_code)
 
1289
{
 
1290
        enter_();
 
1291
        if (otab && otab->getDBTable()->myTableID == tab_id)
 
1292
                otab->getDBTable()->freeBlobHandle(otab, blob_id, myRepo->myRepoID, offset, auth_code);
 
1293
        else {
 
1294
                MSOpenTable *tmp_otab;
 
1295
 
 
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);
 
1300
                }
 
1301
        }
 
1302
        exit_();
 
1303
}
 
1304
 
 
1305
MSRepoFile *MSRepoFile::newRepoFile(MSRepository *repo, CSPath *path)
 
1306
{
 
1307
        MSRepoFile *f;
 
1308
        
 
1309
        if (!(f = new MSRepoFile())) {
 
1310
                path->release();
 
1311
                CSException::throwOSError(CS_CONTEXT, ENOMEM);
 
1312
        }
 
1313
        f->myRepo = repo;
 
1314
        f->myFilePath = path;
 
1315
        return f;
 
1316
}
 
1317
 
 
1318
/*
 
1319
 * ---------------------------------------------------------------
 
1320
 * REPOSITORY
 
1321
 */
 
1322
 
 
1323
MSRepository::MSRepository(uint32_t id, MSDatabase *db, off64_t file_size):
 
1324
CSSharedRefObject(),
 
1325
myRepoID(id),
 
1326
myRepoFileSize(file_size),
 
1327
myRepoLockState(REPO_UNLOCKED),
 
1328
isRemovingFP(false),
 
1329
myRepoDatabase(db),
 
1330
myGarbageCount(0),
 
1331
myRepoHeadSize(0),
 
1332
myRepoDefRefSize(0),
 
1333
myRepoBlobHeadSize(0),
 
1334
myRecoveryOffset(0),
 
1335
myLastTempTime(0),
 
1336
myLastAccessTime(0),
 
1337
myLastCreateTime(0),
 
1338
myLastRefTime(0),
 
1339
mustBeDeleted(false),
 
1340
myRepoXLock(false),
 
1341
iFilePool(NULL)
 
1342
{
 
1343
}
 
1344
 
 
1345
MSRepository::~MSRepository()
 
1346
{
 
1347
        CSPath *path = NULL;
 
1348
 
 
1349
        enter_();
 
1350
        if (mustBeDeleted) {
 
1351
                path = getRepoFilePath();
 
1352
                push_(path);
 
1353
        }
 
1354
 
 
1355
        isRemovingFP = true;
 
1356
        removeRepoFilesNotInUse();
 
1357
        /* With this, I also delete those that are in use!: */
 
1358
        iPoolFiles.clear();
 
1359
 
 
1360
        if (path) {
 
1361
                path->removeFile();
 
1362
                release_(path);
 
1363
        }
 
1364
        exit_();
 
1365
}
 
1366
 
 
1367
void MSRepository::openRepoFileForWriting(MSOpenTable *otab)
 
1368
{
 
1369
        if (!otab->myWriteRepoFile)
 
1370
                otab->myWriteRepoFile = openRepoFile();
 
1371
}
 
1372
 
 
1373
uint64_t MSRepository::receiveBlob(MSOpenTable *otab, uint16_t head_size, uint64_t blob_size, Md5Digest *checksum, CSInputStream *stream)
 
1374
{
 
1375
        off64_t offset;
 
1376
        size_t  tfer;
 
1377
 
 
1378
        enter_();
 
1379
                
 
1380
        offset = myRepoFileSize;
 
1381
 
 
1382
        offset += head_size;
 
1383
        
 
1384
        ASSERT(myRepoDatabase->myBlobType == MS_STANDARD_STORAGE);
 
1385
        if (stream) {
 
1386
                CSMd5 md5;
 
1387
                push_(stream);
 
1388
                md5.md5_init();
 
1389
                while (blob_size > 0) {
 
1390
                        if (blob_size <=  MS_OT_BUFFER_SIZE)
 
1391
                                tfer = (size_t) blob_size;
 
1392
                        else
 
1393
                                tfer = MS_OT_BUFFER_SIZE;
 
1394
                        tfer = stream->read(otab->myOTBuffer, tfer);
 
1395
                        if (!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;
 
1401
                }
 
1402
                if (checksum) md5.md5_digest(checksum);
 
1403
                release_(stream);
 
1404
        } else {
 
1405
                // Write 1 byte to the end to reserver the space.
 
1406
                otab->myWriteRepoFile->write("x" , offset + blob_size -1, 1);
 
1407
        }
 
1408
 
 
1409
        return_( myRepoFileSize);
 
1410
}
 
1411
 
 
1412
// copyBlob() copies the BLOB and its header.
 
1413
uint64_t MSRepository::copyBlob(MSOpenTable *otab, uint64_t size, CSInputStream *stream)
 
1414
{
 
1415
        off64_t offset = myRepoFileSize;
 
1416
        size_t  tfer;
 
1417
 
 
1418
        while (size > 0) {
 
1419
                if (size <= MS_OT_BUFFER_SIZE)
 
1420
                        tfer = (size_t) size;
 
1421
                else
 
1422
                        tfer = MS_OT_BUFFER_SIZE;
 
1423
                tfer = stream->read(otab->myOTBuffer, tfer);
 
1424
                if (!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;
 
1429
        }
 
1430
        
 
1431
        return myRepoFileSize;
 
1432
}
 
1433
 
 
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)
 
1435
{
 
1436
        MSBlobHeadPtr           blob ;
 
1437
        MSRepoTableRefPtr       tab_ref;
 
1438
        MSRepoTempRefPtr        temp_ref;
 
1439
        time_t                          now;
 
1440
        uint16_t                                tab_idx, max_ref_count = (head_size - myRepoBlobHeadSize - metadata_size) / ref_size;
 
1441
        size_t                          size;
 
1442
        
 
1443
        if (max_ref_count > MS_REPO_MIN_REF_COUNT)
 
1444
                max_ref_count = MS_REPO_MIN_REF_COUNT;
 
1445
                
 
1446
        ASSERT(max_ref_count > 1);
 
1447
        
 
1448
        if (blob_type == MS_CLOUD_STORAGE) 
 
1449
                now = cloud_key->creation_time;
 
1450
        else
 
1451
                now = time(NULL);
 
1452
        
 
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);
 
1467
        if (checksum)
 
1468
                memcpy(&(blob->rb_blob_checksum_md5d), checksum, sizeof(Md5Digest));
 
1469
 
 
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;
 
1473
                
 
1474
                CS_SET_DISK_2(blob->rb_mdata_offset_2, metadata_offset);
 
1475
                memcpy(otab->myOTBuffer + metadata_offset, metadata, metadata_size);
 
1476
                
 
1477
#ifdef HAVE_ALIAS_SUPPORT
 
1478
                MetaData md;    
 
1479
                md.use_data(metadata, metadata_size);
 
1480
                const char *alias;      
 
1481
                alias = md.findAlias();
 
1482
                if (alias) {
 
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);
 
1488
                } else {
 
1489
                        CS_SET_DISK_2(blob->rb_alias_offset_2, 0);
 
1490
                }
 
1491
#else
 
1492
                CS_SET_DISK_2(blob->rb_alias_offset_2, 0);
 
1493
#endif
 
1494
                
 
1495
        } else {
 
1496
                CS_SET_DISK_2(blob->rb_mdata_offset_2, 0);
 
1497
                CS_SET_DISK_2(blob->rb_alias_offset_2, 0);
 
1498
        }
 
1499
        
 
1500
 
 
1501
        if (blob_id) {
 
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;
 
1509
        }
 
1510
        else {
 
1511
                temp_ref = (MSRepoTempRefPtr) (otab->myOTBuffer + myRepoBlobHeadSize);
 
1512
                tab_idx = INVALID_INDEX;  // Means not used
 
1513
                size = myRepoBlobHeadSize + ref_size;
 
1514
        }
 
1515
 
 
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);
 
1520
        
 
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
 
1525
        }
 
1526
 
 
1527
        memset(otab->myOTBuffer + size, 0, head_size - size - metadata_size);
 
1528
        
 
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);
 
1532
        
 
1533
        setRepoFileSize(otab, offset + head_size + blob_size);
 
1534
}
 
1535
 
 
1536
void MSRepository::setRepoFileSize(MSOpenTable *otab, off64_t offset)
 
1537
{
 
1538
        myRepoFileSize = offset;
 
1539
        if (myRepoFileSize >= PBMSParameters::getRepoThreshold()
 
1540
                /**/ || getGarbageLevel() >= PBMSParameters::getGarbageThreshold())
 
1541
                otab->closeForWriting();
 
1542
}
 
1543
 
 
1544
void MSRepository::syncHead(MSRepoFile *fh)
 
1545
{
 
1546
        MSRepoHeadRec head;
 
1547
 
 
1548
        fh->sync();
 
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);
 
1555
 
 
1556
        fh->write(&head.rh_recovery_offset_8, offsetof(MSRepoHeadRec, rh_recovery_offset_8), 24);
 
1557
        fh->sync();
 
1558
}
 
1559
 
 
1560
MSRepoFile *MSRepository::openRepoFile()
 
1561
{
 
1562
        MSRepoFile      *fh;
 
1563
 
 
1564
        enter_();
 
1565
        fh = MSRepoFile::newRepoFile(this, getRepoFilePath());
 
1566
        push_(fh);
 
1567
        if (myRepoFileSize)
 
1568
                fh->open(CSFile::DEFAULT);
 
1569
        else
 
1570
                fh->open(CSFile::CREATE);
 
1571
        if (!myRepoHeadSize) {
 
1572
                MSRepoHeadRec   head;
 
1573
                MSBlobHeadRec   blob;
 
1574
                size_t                  size;
 
1575
                int                             status;
 
1576
                int                             ref_size;
 
1577
                uint16_t                head_size;
 
1578
                uint64_t                blob_size;
 
1579
 
 
1580
                lock_(this);
 
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));
 
1597
                        }
 
1598
                        
 
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);
 
1604
 
 
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);
 
1615
 
 
1616
                        /* File size, cannot be less than header size: */
 
1617
                        if (myRepoFileSize < myRepoHeadSize)
 
1618
                                myRepoFileSize = myRepoHeadSize;
 
1619
 
 
1620
                        ASSERT(myGarbageCount <= myRepoFileSize);
 
1621
 
 
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) {
 
1625
                                        if (size != 0) {
 
1626
                                                myRepoFileSize = myRecoveryOffset;
 
1627
                                                fh->setEOF(myRepoFileSize);
 
1628
                                        }
 
1629
                                        break;
 
1630
                                }
 
1631
                                uint16_t ref_count, mdata_size, mdata_offset;
 
1632
                                
 
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);
 
1648
                                        break;
 
1649
                                }
 
1650
                                myRecoveryOffset += head_size + blob_size;
 
1651
                        }
 
1652
 
 
1653
                        fh->sync();
 
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);
 
1657
                        fh->sync();
 
1658
                }
 
1659
                unlock_(this);
 
1660
        }
 
1661
        pop_(fh);
 
1662
        return_(fh);
 
1663
}
 
1664
 
 
1665
void MSRepository::lockRepo(RepoLockState state)
 
1666
{
 
1667
        CSMutex *myLock;
 
1668
        enter_();
 
1669
        
 
1670
        myLock = &myRepoLock[0];
 
1671
        lock_(myLock);
 
1672
        
 
1673
        ASSERT(!myRepoXLock);
 
1674
        
 
1675
        myRepoLockState = state;
 
1676
        myRepoXLock = true;
 
1677
                
 
1678
        unlock_(myLock);
 
1679
        exit_();
 
1680
}
 
1681
 
 
1682
void MSRepository::signalCompactor()
 
1683
{
 
1684
#ifndef MS_COMPACTOR_POLLS
 
1685
        if (!mustBeDeleted) {
 
1686
                if (getGarbageLevel() >= PBMSParameters::getGarbageThreshold()) {
 
1687
                        if (myRepoDatabase->myCompactorThread)
 
1688
                                myRepoDatabase->myCompactorThread->wakeup();
 
1689
                }
 
1690
        }
 
1691
#endif
 
1692
}
 
1693
 
 
1694
void MSRepository::unlockRepo(RepoLockState state)
 
1695
{
 
1696
        CSMutex *myLock;
 
1697
        enter_();
 
1698
        myLock = &myRepoLock[0];
 
1699
        lock_(myLock);
 
1700
        
 
1701
        ASSERT(myRepoLockState & state);
 
1702
        
 
1703
        myRepoLockState &= ~state;
 
1704
        if (myRepoLockState == REPO_UNLOCKED) {
 
1705
                myRepoXLock = false;
 
1706
                signalCompactor();
 
1707
        }
 
1708
        unlock_(myLock);
 
1709
        
 
1710
        exit_();
 
1711
}
 
1712
 
 
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()
 
1717
{
 
1718
        CSMutex *myLock;
 
1719
        enter_();
 
1720
        myLock = &myRepoLock[0];
 
1721
        lock_(myLock);
 
1722
        this->myRepoLockState &= ~(REPO_COMPACTING | REPO_WRITE);
 
1723
        if ( this->myRepoLockState == REPO_UNLOCKED) {
 
1724
                myRepoXLock = false;
 
1725
                signalCompactor();
 
1726
        }
 
1727
        unlock_(myLock);
 
1728
        
 
1729
        this->release();
 
1730
        exit_();
 
1731
}
 
1732
 
 
1733
void MSRepository::backupCompleted()
 
1734
{
 
1735
        CSMutex *myLock;
 
1736
        enter_();
 
1737
        myLock = &myRepoLock[0];
 
1738
        lock_(myLock);
 
1739
        
 
1740
        
 
1741
        this->myRepoLockState &= ~REPO_BACKUP;
 
1742
        if ( this->myRepoLockState == REPO_UNLOCKED) {
 
1743
                myRepoXLock = false;
 
1744
                signalCompactor();
 
1745
        }
 
1746
                
 
1747
        unlock_(myLock);
 
1748
        exit_();
 
1749
}
 
1750
 
 
1751
bool MSRepository::lockedForBackup() { return ((myRepoLockState & REPO_BACKUP) == REPO_BACKUP);}
 
1752
 
 
1753
uint32_t MSRepository::initBackup()
 
1754
{
 
1755
        CSMutex *myLock;
 
1756
        uint32_t state;
 
1757
        enter_();
 
1758
        
 
1759
        myLock = &myRepoLock[0];
 
1760
        lock_(myLock);
 
1761
        state = this->myRepoLockState;
 
1762
        this->myRepoLockState |= REPO_BACKUP;
 
1763
        if (this->myRepoLockState == REPO_BACKUP) 
 
1764
                this->myRepoXLock = true;
 
1765
                
 
1766
        unlock_(myLock);
 
1767
        return_(state);
 
1768
}
 
1769
 
 
1770
MSRepoFile *MSRepository::getRepoFile()
 
1771
{
 
1772
        MSRepoFile *file;
 
1773
 
 
1774
        if ((file = iFilePool)) {
 
1775
                iFilePool = file->nextFile;
 
1776
                file->nextFile = NULL;
 
1777
                file->isFileInUse = true;
 
1778
                file->retain();
 
1779
        }
 
1780
        return file;
 
1781
}
 
1782
 
 
1783
void MSRepository::addRepoFile(MSRepoFile *file)
 
1784
{
 
1785
        iPoolFiles.addFront(file);
 
1786
}
 
1787
 
 
1788
void MSRepository::removeRepoFile(MSRepoFile *file)
 
1789
{
 
1790
        iPoolFiles.remove(file);
 
1791
}
 
1792
 
 
1793
void MSRepository::returnRepoFile(MSRepoFile *file)
 
1794
{
 
1795
        file->isFileInUse = false;
 
1796
        file->nextFile = iFilePool;
 
1797
        iFilePool = file;
 
1798
}
 
1799
 
 
1800
bool MSRepository::removeRepoFilesNotInUse()
 
1801
{
 
1802
        MSRepoFile *file, *curr_file;
 
1803
 
 
1804
        iFilePool = NULL;
 
1805
        /* Remove all files that are not in use: */
 
1806
        if ((file = (MSRepoFile *) iPoolFiles.getBack())) {
 
1807
                do {
 
1808
                        curr_file = file;
 
1809
                        file = (MSRepoFile *) file->getNextLink();
 
1810
                        if (!curr_file->isFileInUse)
 
1811
                                iPoolFiles.remove(curr_file);
 
1812
                } while (file);
 
1813
        }
 
1814
        return iPoolFiles.getSize() == 0;
 
1815
}
 
1816
 
 
1817
off64_t MSRepository::getRepoFileSize()
 
1818
{
 
1819
        return myRepoFileSize;
 
1820
}
 
1821
 
 
1822
size_t MSRepository::getRepoHeadSize()
 
1823
{
 
1824
        return myRepoHeadSize;
 
1825
}
 
1826
 
 
1827
size_t MSRepository::getRepoBlobHeadSize()
 
1828
{
 
1829
        return myRepoBlobHeadSize;
 
1830
}
 
1831
 
 
1832
CSMutex *MSRepository::getRepoLock(off64_t offset)
 
1833
{
 
1834
        return &myRepoLock[offset % CS_REPO_REC_LOCK_COUNT];
 
1835
}
 
1836
 
 
1837
uint32_t MSRepository::getRepoID()
 
1838
{
 
1839
        return myRepoID;
 
1840
}
 
1841
 
 
1842
uint32_t MSRepository::getGarbageLevel()
 
1843
{
 
1844
        if (myRepoFileSize <= myRepoHeadSize)
 
1845
                return 0;
 
1846
        return myGarbageCount * 100 / (myRepoFileSize - myRepoHeadSize);
 
1847
}
 
1848
 
 
1849
CSPath *MSRepository::getRepoFilePath()
 
1850
{
 
1851
        char file_name[120];
 
1852
 
 
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");
 
1858
 
 
1859
        if (myRepoDatabase && myRepoDatabase->myDatabasePath) {
 
1860
                return CSPath::newPath(RETAIN(myRepoDatabase->myDatabasePath), file_name);
 
1861
        }
 
1862
        return NULL;
 
1863
}
 
1864