Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / budb / db_text.c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <roken.h>
14
15 #include <ubik.h>
16 #include <afs/bubasics.h>
17 #include <afs/audit.h>
18 #include <afs/afsutil.h>
19
20 #include "budb_errs.h"
21 #include "database.h"
22 #include "error_macros.h"
23 #include "budb_internal.h"
24
25 /* --------------------------------
26 * interface & support code to manage text blocks within the
27 * database
28 * --------------------------------
29 */
30
31 /* BUDB_GetText
32 * notes:
33 * routine mallocs storage for charListPtr, freed by stub
34 */
35
36 afs_int32 GetText(struct rx_call *, afs_uint32, afs_int32, afs_int32,
37 afs_int32, afs_int32 *, charListT *);
38 afs_int32 GetTextVersion(struct rx_call *, afs_int32, afs_uint32 *);
39 afs_int32 SaveText(struct rx_call *, afs_uint32, afs_int32, afs_int32,
40 afs_int32, charListT *);
41
42 afs_int32
43 SBUDB_GetText(struct rx_call *call, afs_uint32 lockHandle, afs_int32 textType,
44 afs_int32 maxLength, afs_int32 offset, afs_int32 *nextOffset,
45 charListT *charListPtr)
46 {
47 afs_int32 code;
48
49 code =
50 GetText(call, lockHandle, textType, maxLength, offset, nextOffset,
51 charListPtr);
52 osi_auditU(call, BUDB_GetTxtEvent, code, AUD_LONG, textType, AUD_END);
53 return code;
54 }
55
56 afs_int32
57 GetText(struct rx_call *call, afs_uint32 lockHandle, afs_int32 textType,
58 afs_int32 maxLength, afs_int32 offset, afs_int32 *nextOffset,
59 charListT *charListPtr)
60 {
61 struct ubik_trans *ut = 0;
62 struct block block;
63 afs_int32 transferSize, chunkSize;
64 afs_int32 blockOffset;
65 dbadr lastBlockAddr;
66 afs_int32 nblocks;
67 struct textBlock *tbPtr;
68 afs_int32 textRemaining;
69 char *textPtr;
70 afs_int32 code;
71
72 LogDebug(5, "GetText: type %d, offset %d, nextOffset %"AFS_PTR_FMT
73 ", maxLength %d\n", textType, offset, nextOffset, maxLength);
74
75 if (callPermitted(call) == 0) {
76 code = BUDB_NOTPERMITTED;
77 goto no_xfer_abort;
78 }
79
80 /* check parameters */
81 if ((offset < 0)
82 || (textType < 0)
83 || (textType >= TB_NUM)
84 ) {
85 code = BUDB_BADARGUMENT;
86 goto no_xfer_abort;
87 }
88
89 /* start the transaction */
90 code = InitRPC(&ut, LOCKWRITE, 1);
91 if (code)
92 goto no_xfer_abort;
93
94 /* fetch the lock state */
95 if (checkLockHandle(ut, lockHandle) == 0) {
96 code = BUDB_NOTLOCKED;
97 goto no_xfer_abort;
98 }
99
100 tbPtr = &db.h.textBlock[textType];
101
102 if ((ntohl(tbPtr->size) > 0)
103 && (offset >= ntohl(tbPtr->size))
104 ) {
105 code = BUDB_BADARGUMENT;
106 goto no_xfer_abort;
107 }
108
109 LogDebug(5, "GetText: store size is %d\n", ntohl(tbPtr->size));
110
111 /* compute minimum of remaining text or user buffer */
112 textRemaining = ntohl(tbPtr->size) - offset;
113 transferSize = min(textRemaining, maxLength);
114
115 /* allocate the transfer storage */
116 if (transferSize <= 0) {
117 charListPtr->charListT_len = 0L;
118 charListPtr->charListT_val = NULL;
119 } else {
120 charListPtr->charListT_len = transferSize;
121 charListPtr->charListT_val = malloc(transferSize);
122 if (charListPtr->charListT_val == 0)
123 ABORT(BUDB_NOMEM);
124 }
125
126 textPtr = charListPtr->charListT_val;
127 *nextOffset = offset + transferSize;
128
129 /* setup the datablock. read and discard all blocks up to the one the
130 * offset specifies
131 */
132 nblocks = offset / BLOCK_DATA_SIZE;
133 lastBlockAddr = ntohl(tbPtr->textAddr);
134
135 while (nblocks--) {
136 code = dbread(ut, lastBlockAddr, (char *)&block, sizeof(block));
137 if (code)
138 ABORT(BUDB_IO);
139 lastBlockAddr = ntohl(block.h.next);
140 }
141
142 while (transferSize > 0) {
143 code = dbread(ut, lastBlockAddr, (char *)&block, sizeof(block));
144 if (code)
145 ABORT(BUDB_IO);
146
147 LogDebug(5, "fetched block %d\n", lastBlockAddr);
148
149 /* compute the data size to extract */
150 blockOffset = offset % BLOCK_DATA_SIZE;
151 textRemaining = BLOCK_DATA_SIZE - blockOffset;
152 chunkSize = min(textRemaining, transferSize);
153
154 memcpy(textPtr, &block.a[blockOffset], chunkSize);
155
156 /* LogDebug(5, "transfering %d bytes: %s\n", chunkSize, textPtr); */
157
158 transferSize -= chunkSize;
159 offset += chunkSize;
160 textPtr += chunkSize;
161
162 if (transferSize) {
163 /* setup lastBlockAddr */
164 lastBlockAddr = ntohl(block.h.next);
165 }
166 }
167
168 if (*nextOffset == ntohl(tbPtr->size)) {
169 /* all done */
170 *nextOffset = -1;
171 }
172
173 /* error_exit: */
174 code = ubik_EndTrans(ut);
175 /* printf("in error exit, code=%ld\n", code); */
176 return (code);
177
178 no_xfer_abort:
179 charListPtr->charListT_len = 0;
180 charListPtr->charListT_val = NULL;
181
182 abort_exit:
183 if (ut)
184 ubik_AbortTrans(ut);
185 /* printf("in abort exit, code=%ld\n", code); */
186 return (code);
187 }
188
189 int
190 freeOldBlockChain(struct ubik_trans *ut, dbadr diskAddr)
191 {
192 struct blockHeader blockHeader;
193 dbadr nextDiskAddr;
194 afs_int32 code = 0;
195
196 while (diskAddr != 0) {
197 /* read in the header */
198 code =
199 dbread(ut, diskAddr, (char *)&blockHeader, sizeof(blockHeader));
200 if (code)
201 ABORT(code);
202 nextDiskAddr = ntohl(blockHeader.next);
203 code = FreeBlock(ut, &blockHeader, diskAddr);
204 if (code)
205 ABORT(code);
206 diskAddr = nextDiskAddr;
207 }
208 abort_exit:
209 return (code);
210 }
211
212 /* BUDB_GetTextVersion
213 * get the version number for the specified text block
214 */
215
216 afs_int32
217 SBUDB_GetTextVersion(struct rx_call *call, afs_int32 textType,
218 afs_uint32 *tversion)
219 {
220 afs_int32 code;
221
222 code = GetTextVersion(call, textType, tversion);
223 osi_auditU(call, BUDB_GetTxVEvent, code, AUD_LONG, textType, AUD_END);
224 return code;
225 }
226
227 afs_int32
228 GetTextVersion(struct rx_call *call, afs_int32 textType,
229 afs_uint32 *tversion)
230 {
231 afs_int32 code;
232 struct ubik_trans *ut;
233
234 if (callPermitted(call) == 0)
235 return (BUDB_NOTPERMITTED);
236
237 if ((textType < 0) || (textType >= TB_NUM))
238 return (BUDB_BADARGUMENT);
239
240 code = InitRPC(&ut, LOCKREAD, 1);
241 if (code)
242 return (code);
243
244 *tversion = ntohl(db.h.textBlock[textType].version);
245 code = ubik_EndTrans(ut);
246 return (code);
247 }
248
249 /* text blocks
250 * next - next disk addr
251 * host/network ordering????
252 */
253
254 /* BUDB_SaveText
255 * notes:
256 * charListPtr storage automatically malloced and freed
257 */
258
259 afs_int32
260 SBUDB_SaveText(struct rx_call *call, afs_uint32 lockHandle,
261 afs_int32 textType, afs_int32 offset, afs_int32 flags,
262 charListT *charListPtr)
263 {
264 afs_int32 code;
265
266 code = SaveText(call, lockHandle, textType, offset, flags, charListPtr);
267 osi_auditU(call, BUDB_SavTxtEvent, code, AUD_LONG, textType, AUD_END);
268 return code;
269 }
270
271 afs_int32
272 SaveText(struct rx_call *call, afs_uint32 lockHandle, afs_int32 textType,
273 afs_int32 offset, afs_int32 flags, charListT *charListPtr)
274 {
275 struct ubik_trans *ut;
276 struct block diskBlock;
277 dbadr diskBlockAddr;
278 afs_int32 remainingInBlock, chunkSize;
279 struct textBlock *tbPtr;
280 afs_int32 textLength = charListPtr->charListT_len;
281 char *textptr = charListPtr->charListT_val;
282 afs_int32 code;
283
284 LogDebug(5, "SaveText: type %d, offset %d, length %d\n", textType, offset,
285 textLength);
286
287 if (callPermitted(call) == 0)
288 return (BUDB_NOTPERMITTED);
289
290 if ((textLength > BLOCK_DATA_SIZE) || (offset < 0))
291 return (BUDB_BADARGUMENT);
292
293 code = InitRPC(&ut, LOCKWRITE, 1);
294 if (code)
295 return (code);
296
297 /* fetch the lock state */
298 if (checkLockHandle(ut, lockHandle) == 0)
299 ABORT(BUDB_NOTLOCKED);
300
301 if ((textType < 0) || (textType >= TB_NUM))
302 ABORT(BUDB_BADARGUMENT);
303
304 tbPtr = &db.h.textBlock[textType];
305
306 LogDebug(5,
307 "SaveText: lockHandle %d textType %d offset %d flags %d txtlength %d\n",
308 lockHandle, textType, offset, flags, textLength);
309
310 if (offset == 0) {
311 /* release any blocks from previous transactions */
312 diskBlockAddr = ntohl(tbPtr->newTextAddr);
313 freeOldBlockChain(ut, diskBlockAddr);
314
315 if (textLength) {
316 code = AllocBlock(ut, &diskBlock, &diskBlockAddr);
317 if (code)
318 ABORT(code);
319
320 LogDebug(5, "allocated block %d\n", diskBlockAddr);
321
322 /* set block type */
323 diskBlock.h.type = text_BLOCK;
324
325 /* save it in the database header */
326 tbPtr->newsize = 0;
327 tbPtr->newTextAddr = htonl(diskBlockAddr);
328 dbwrite(ut, (char *)tbPtr - (char *)&db.h, (char *)tbPtr,
329 sizeof(struct textBlock));
330 } else {
331 tbPtr->newsize = 0;
332 tbPtr->newTextAddr = 0;
333 }
334 } else {
335 /* non-zero offset */
336 int nblocks;
337
338 if (offset != ntohl(tbPtr->newsize))
339 ABORT(BUDB_BADARGUMENT);
340
341 /* locate the block to which offset refers */
342 nblocks = offset / BLOCK_DATA_SIZE;
343
344 diskBlockAddr = ntohl(tbPtr->newTextAddr);
345 if (diskBlockAddr == 0)
346 ABORT(BUDB_BADARGUMENT);
347
348 code =
349 dbread(ut, diskBlockAddr, (char *)&diskBlock, sizeof(diskBlock));
350 if (code)
351 ABORT(code);
352
353 while (nblocks--) {
354 diskBlockAddr = ntohl(diskBlock.h.next);
355 code =
356 dbread(ut, diskBlockAddr, (char *)&diskBlock,
357 sizeof(diskBlock));
358 if (code)
359 ABORT(code);
360 }
361 }
362
363 /* diskBlock and diskBlockAddr now point to the last block in the chain */
364
365 while (textLength) {
366 /* compute the transfer size */
367 remainingInBlock = (BLOCK_DATA_SIZE - (offset % BLOCK_DATA_SIZE));
368 chunkSize = min(remainingInBlock, textLength);
369
370 /* copy in the data */
371 memcpy(&diskBlock.a[offset % BLOCK_DATA_SIZE], textptr, chunkSize);
372
373 /* LogDebug(5, "text is %s\n", textptr); */
374
375 textLength -= chunkSize;
376 textptr += chunkSize;
377 offset += chunkSize;
378 tbPtr->newsize = htonl(ntohl(tbPtr->newsize) + chunkSize);
379
380 if (textLength > 0) {
381 afs_int32 prevBlockAddr;
382 afs_int32 linkOffset;
383 afs_int32 linkValue;
384
385 /* have to add another block to the chain */
386
387 code =
388 dbwrite(ut, diskBlockAddr, (char *)&diskBlock,
389 sizeof(diskBlock));
390 if (code)
391 ABORT(code);
392
393 prevBlockAddr = (afs_int32) diskBlockAddr;
394 code = AllocBlock(ut, &diskBlock, &diskBlockAddr);
395 if (code)
396 ABORT(code);
397
398 LogDebug(5, "allocated block %d\n", diskBlockAddr);
399
400 /* set block type */
401 diskBlock.h.type = text_BLOCK;
402
403 /* now have to update the previous block's link */
404 linkOffset =
405 (afs_int32) ((char*)& diskBlock.h.next - (char*)& diskBlock);
406 linkValue = htonl(diskBlockAddr);
407
408 code =
409 dbwrite(ut, (afs_int32) prevBlockAddr + linkOffset,
410 (char *)&linkValue, sizeof(afs_int32));
411 if (code)
412 ABORT(code);
413 } else {
414 /* just write the old block */
415 code =
416 dbwrite(ut, diskBlockAddr, (char *)&diskBlock,
417 sizeof(diskBlock));
418 if (code)
419 ABORT(code);
420 }
421 }
422
423 if (flags & BUDB_TEXT_COMPLETE) { /* done */
424 /* this was the last chunk of text */
425 diskBlockAddr = ntohl(tbPtr->textAddr);
426 freeOldBlockChain(ut, diskBlockAddr);
427
428 tbPtr->textAddr = tbPtr->newTextAddr;
429 tbPtr->newTextAddr = 0;
430 tbPtr->size = tbPtr->newsize;
431 tbPtr->newsize = 0;
432 tbPtr->version = htonl(ntohl(tbPtr->version) + 1);
433
434 /* saveTextToFile(ut, tbPtr); */
435 }
436
437 /* update size and other text header info */
438 code =
439 dbwrite(ut, (char *)tbPtr - (char *)&db.h, (char *)tbPtr,
440 sizeof(struct textBlock));
441 if (code)
442 ABORT(code);
443
444 /*error_exit: */
445 code = ubik_EndTrans(ut);
446 return (code);
447
448 abort_exit:
449 ubik_AbortTrans(ut);
450 return (code);
451 }
452
453 /* debug support */
454 void
455 saveTextToFile(struct ubik_trans *ut, struct textBlock *tbPtr)
456 {
457 afs_int32 blockAddr;
458 struct block block;
459 char filename[128];
460 afs_int32 size, totalSize, chunkSize;
461 int fid;
462
463 sprintf(filename, "%s/%s", gettmpdir(), "dbg_XXXXXX");
464
465 fid = mkstemp(filename);
466 totalSize = size = ntohl(tbPtr->size);
467 blockAddr = ntohl(tbPtr->textAddr);
468 while (size) {
469 chunkSize = min(BLOCK_DATA_SIZE, size);
470 dbread(ut, blockAddr, (char *)&block, sizeof(block));
471 if (write(fid, &block.a[0], chunkSize) < 0)
472 break;
473 blockAddr = ntohl(block.h.next);
474 size -= chunkSize;
475 }
476 close(fid);
477 if (size) {
478 printf("Wrote partial debug file (%ld bytes out of %ld)\n",
479 (long)(totalSize - size), (long)totalSize);
480 } else {
481 printf("wrote debug file %s\n", filename);
482 }
483 }
484