Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / budb / db_text.c
blob5e1bbbee17a8eee286c4d076c491eeef74fd2fa7
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
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 */
10 #include <afsconfig.h>
11 #include <afs/param.h>
14 #ifdef AFS_NT40_ENV
15 #include <winsock2.h>
16 #include <fcntl.h>
17 #include <io.h>
18 #else
19 #include <netinet/in.h>
20 #include <sys/file.h>
21 #include <sys/param.h>
22 #endif
23 #include <string.h>
24 #include <sys/types.h>
25 #include <ubik.h>
26 #include <afs/bubasics.h>
27 #include "budb_errs.h"
28 #include "database.h"
29 #include "error_macros.h"
30 #include "budb_internal.h"
31 #include "afs/audit.h"
32 #include <afs/afsutil.h>
34 /* --------------------------------
35 * interface & support code to manage text blocks within the
36 * database
37 * --------------------------------
40 /* BUDB_GetText
41 * notes:
42 * routine mallocs storage for charListPtr, freed by stub
45 afs_int32 GetText(struct rx_call *, afs_uint32, afs_int32, afs_int32,
46 afs_int32, afs_int32 *, charListT *);
47 afs_int32 GetTextVersion(struct rx_call *, afs_int32, afs_uint32 *);
48 afs_int32 SaveText(struct rx_call *, afs_uint32, afs_int32, afs_int32,
49 afs_int32, charListT *);
51 afs_int32
52 SBUDB_GetText(struct rx_call *call, afs_uint32 lockHandle, afs_int32 textType,
53 afs_int32 maxLength, afs_int32 offset, afs_int32 *nextOffset,
54 charListT *charListPtr)
56 afs_int32 code;
58 code =
59 GetText(call, lockHandle, textType, maxLength, offset, nextOffset,
60 charListPtr);
61 osi_auditU(call, BUDB_GetTxtEvent, code, AUD_LONG, textType, AUD_END);
62 return code;
65 afs_int32
66 GetText(struct rx_call *call, afs_uint32 lockHandle, afs_int32 textType,
67 afs_int32 maxLength, afs_int32 offset, afs_int32 *nextOffset,
68 charListT *charListPtr)
70 struct ubik_trans *ut = 0;
71 struct block block;
72 afs_int32 transferSize, chunkSize;
73 afs_int32 blockOffset;
74 dbadr lastBlockAddr;
75 afs_int32 nblocks;
76 struct textBlock *tbPtr;
77 afs_int32 textRemaining;
78 char *textPtr;
79 afs_int32 code;
81 LogDebug(5, "GetText: type %d, offset %d, nextOffset %"AFS_PTR_FMT
82 ", maxLength %d\n", textType, offset, nextOffset, maxLength);
84 if (callPermitted(call) == 0) {
85 code = BUDB_NOTPERMITTED;
86 goto no_xfer_abort;
89 /* check parameters */
90 if ((offset < 0)
91 || (textType < 0)
92 || (textType >= TB_NUM)
93 ) {
94 code = BUDB_BADARGUMENT;
95 goto no_xfer_abort;
98 /* start the transaction */
99 code = InitRPC(&ut, LOCKWRITE, 1);
100 if (code)
101 goto no_xfer_abort;
103 /* fetch the lock state */
104 if (checkLockHandle(ut, lockHandle) == 0) {
105 code = BUDB_NOTLOCKED;
106 goto no_xfer_abort;
109 tbPtr = &db.h.textBlock[textType];
111 if ((ntohl(tbPtr->size) > 0)
112 && (offset >= ntohl(tbPtr->size))
114 code = BUDB_BADARGUMENT;
115 goto no_xfer_abort;
118 LogDebug(5, "GetText: store size is %d\n", ntohl(tbPtr->size));
120 /* compute minimum of remaining text or user buffer */
121 textRemaining = ntohl(tbPtr->size) - offset;
122 transferSize = MIN(textRemaining, maxLength);
124 /* allocate the transfer storage */
125 if (transferSize <= 0) {
126 charListPtr->charListT_len = 0L;
127 charListPtr->charListT_val = NULL;
128 } else {
129 charListPtr->charListT_len = transferSize;
130 charListPtr->charListT_val = (char *)malloc(transferSize);
131 if (charListPtr->charListT_val == 0)
132 ABORT(BUDB_NOMEM);
135 textPtr = charListPtr->charListT_val;
136 *nextOffset = offset + transferSize;
138 /* setup the datablock. read and discard all blocks up to the one the
139 * offset specifies
141 nblocks = offset / BLOCK_DATA_SIZE;
142 lastBlockAddr = ntohl(tbPtr->textAddr);
144 while (nblocks--) {
145 code = dbread(ut, lastBlockAddr, (char *)&block, sizeof(block));
146 if (code)
147 ABORT(BUDB_IO);
148 lastBlockAddr = ntohl(block.h.next);
151 while (transferSize > 0) {
152 code = dbread(ut, lastBlockAddr, (char *)&block, sizeof(block));
153 if (code)
154 ABORT(BUDB_IO);
156 LogDebug(5, "fetched block %d\n", lastBlockAddr);
158 /* compute the data size to extract */
159 blockOffset = offset % BLOCK_DATA_SIZE;
160 textRemaining = BLOCK_DATA_SIZE - blockOffset;
161 chunkSize = min(textRemaining, transferSize);
163 memcpy(textPtr, &block.a[blockOffset], chunkSize);
165 /* LogDebug(5, "transfering %d bytes: %s\n", chunkSize, textPtr); */
167 transferSize -= chunkSize;
168 offset += chunkSize;
169 textPtr += chunkSize;
171 if (transferSize) {
172 /* setup lastBlockAddr */
173 lastBlockAddr = ntohl(block.h.next);
177 if (*nextOffset == ntohl(tbPtr->size)) {
178 /* all done */
179 *nextOffset = -1;
182 /* error_exit: */
183 code = ubik_EndTrans(ut);
184 /* printf("in error exit, code=%ld\n", code); */
185 return (code);
187 no_xfer_abort:
188 charListPtr->charListT_len = 0;
189 charListPtr->charListT_val = (char *)malloc(0);
191 abort_exit:
192 if (ut)
193 ubik_AbortTrans(ut);
194 /* printf("in abort exit, code=%ld\n", code); */
195 return (code);
199 freeOldBlockChain(struct ubik_trans *ut, dbadr diskAddr)
201 struct blockHeader blockHeader;
202 dbadr nextDiskAddr;
203 afs_int32 code = 0;
205 while (diskAddr != 0) {
206 /* read in the header */
207 code =
208 dbread(ut, diskAddr, (char *)&blockHeader, sizeof(blockHeader));
209 if (code)
210 ABORT(code);
211 nextDiskAddr = ntohl(blockHeader.next);
212 code = FreeBlock(ut, &blockHeader, diskAddr);
213 if (code)
214 ABORT(code);
215 diskAddr = nextDiskAddr;
217 abort_exit:
218 return (code);
221 /* BUDB_GetTextVersion
222 * get the version number for the specified text block
225 afs_int32
226 SBUDB_GetTextVersion(struct rx_call *call, afs_int32 textType,
227 afs_uint32 *tversion)
229 afs_int32 code;
231 code = GetTextVersion(call, textType, tversion);
232 osi_auditU(call, BUDB_GetTxVEvent, code, AUD_LONG, textType, AUD_END);
233 return code;
236 afs_int32
237 GetTextVersion(struct rx_call *call, afs_int32 textType,
238 afs_uint32 *tversion)
240 afs_int32 code;
241 struct ubik_trans *ut;
243 if (callPermitted(call) == 0)
244 return (BUDB_NOTPERMITTED);
246 if ((textType < 0) || (textType >= TB_NUM))
247 return (BUDB_BADARGUMENT);
249 code = InitRPC(&ut, LOCKREAD, 1);
250 if (code)
251 return (code);
253 *tversion = ntohl(db.h.textBlock[textType].version);
254 code = ubik_EndTrans(ut);
255 return (code);
258 /* text blocks
259 * next - next disk addr
260 * host/network ordering????
263 /* BUDB_SaveText
264 * notes:
265 * charListPtr storage automatically malloced and freed
268 afs_int32
269 SBUDB_SaveText(struct rx_call *call, afs_uint32 lockHandle,
270 afs_int32 textType, afs_int32 offset, afs_int32 flags,
271 charListT *charListPtr)
273 afs_int32 code;
275 code = SaveText(call, lockHandle, textType, offset, flags, charListPtr);
276 osi_auditU(call, BUDB_SavTxtEvent, code, AUD_LONG, textType, AUD_END);
277 return code;
280 afs_int32
281 SaveText(struct rx_call *call, afs_uint32 lockHandle, afs_int32 textType,
282 afs_int32 offset, afs_int32 flags, charListT *charListPtr)
284 struct ubik_trans *ut;
285 struct block diskBlock;
286 dbadr diskBlockAddr;
287 afs_int32 remainingInBlock, chunkSize;
288 struct textBlock *tbPtr;
289 afs_int32 textLength = charListPtr->charListT_len;
290 char *textptr = charListPtr->charListT_val;
291 afs_int32 code;
293 LogDebug(5, "SaveText: type %d, offset %d, length %d\n", textType, offset,
294 textLength);
296 if (callPermitted(call) == 0)
297 return (BUDB_NOTPERMITTED);
299 if ((textLength > BLOCK_DATA_SIZE) || (offset < 0))
300 return (BUDB_BADARGUMENT);
302 code = InitRPC(&ut, LOCKWRITE, 1);
303 if (code)
304 return (code);
306 /* fetch the lock state */
307 if (checkLockHandle(ut, lockHandle) == 0)
308 ABORT(BUDB_NOTLOCKED);
310 if ((textType < 0) || (textType >= TB_NUM))
311 ABORT(BUDB_BADARGUMENT);
313 tbPtr = &db.h.textBlock[textType];
315 LogDebug(5,
316 "SaveText: lockHandle %d textType %d offset %d flags %d txtlength %d\n",
317 lockHandle, textType, offset, flags, textLength);
319 if (offset == 0) {
320 /* release any blocks from previous transactions */
321 diskBlockAddr = ntohl(tbPtr->newTextAddr);
322 freeOldBlockChain(ut, diskBlockAddr);
324 if (textLength) {
325 code = AllocBlock(ut, &diskBlock, &diskBlockAddr);
326 if (code)
327 ABORT(code);
329 LogDebug(5, "allocated block %d\n", diskBlockAddr);
331 /* set block type */
332 diskBlock.h.type = text_BLOCK;
334 /* save it in the database header */
335 tbPtr->newsize = 0;
336 tbPtr->newTextAddr = htonl(diskBlockAddr);
337 dbwrite(ut, (char *)tbPtr - (char *)&db.h, (char *)tbPtr,
338 sizeof(struct textBlock));
339 } else {
340 tbPtr->newsize = 0;
341 tbPtr->newTextAddr = 0;
343 } else {
344 /* non-zero offset */
345 int nblocks;
347 if (offset != ntohl(tbPtr->newsize))
348 ABORT(BUDB_BADARGUMENT);
350 /* locate the block to which offset refers */
351 nblocks = offset / BLOCK_DATA_SIZE;
353 diskBlockAddr = ntohl(tbPtr->newTextAddr);
354 if (diskBlockAddr == 0)
355 ABORT(BUDB_BADARGUMENT);
357 code =
358 dbread(ut, diskBlockAddr, (char *)&diskBlock, sizeof(diskBlock));
359 if (code)
360 ABORT(code);
362 while (nblocks--) {
363 diskBlockAddr = ntohl(diskBlock.h.next);
364 code =
365 dbread(ut, diskBlockAddr, (char *)&diskBlock,
366 sizeof(diskBlock));
367 if (code)
368 ABORT(code);
372 /* diskBlock and diskBlockAddr now point to the last block in the chain */
374 while (textLength) {
375 /* compute the transfer size */
376 remainingInBlock = (BLOCK_DATA_SIZE - (offset % BLOCK_DATA_SIZE));
377 chunkSize = MIN(remainingInBlock, textLength);
379 /* copy in the data */
380 memcpy(&diskBlock.a[offset % BLOCK_DATA_SIZE], textptr, chunkSize);
382 /* LogDebug(5, "text is %s\n", textptr); */
384 textLength -= chunkSize;
385 textptr += chunkSize;
386 offset += chunkSize;
387 tbPtr->newsize = htonl(ntohl(tbPtr->newsize) + chunkSize);
389 if (textLength > 0) {
390 afs_int32 prevBlockAddr;
391 afs_int32 linkOffset;
392 afs_int32 linkValue;
394 /* have to add another block to the chain */
396 code =
397 dbwrite(ut, diskBlockAddr, (char *)&diskBlock,
398 sizeof(diskBlock));
399 if (code)
400 ABORT(code);
402 prevBlockAddr = (afs_int32) diskBlockAddr;
403 code = AllocBlock(ut, &diskBlock, &diskBlockAddr);
404 if (code)
405 ABORT(code);
407 LogDebug(5, "allocated block %d\n", diskBlockAddr);
409 /* set block type */
410 diskBlock.h.type = text_BLOCK;
412 /* now have to update the previous block's link */
413 linkOffset =
414 (afs_int32) ((char*)& diskBlock.h.next - (char*)& diskBlock);
415 linkValue = htonl(diskBlockAddr);
417 code =
418 dbwrite(ut, (afs_int32) prevBlockAddr + linkOffset,
419 (char *)&linkValue, sizeof(afs_int32));
420 if (code)
421 ABORT(code);
422 } else {
423 /* just write the old block */
424 code =
425 dbwrite(ut, diskBlockAddr, (char *)&diskBlock,
426 sizeof(diskBlock));
427 if (code)
428 ABORT(code);
432 if (flags & BUDB_TEXT_COMPLETE) { /* done */
433 /* this was the last chunk of text */
434 diskBlockAddr = ntohl(tbPtr->textAddr);
435 freeOldBlockChain(ut, diskBlockAddr);
437 tbPtr->textAddr = tbPtr->newTextAddr;
438 tbPtr->newTextAddr = 0;
439 tbPtr->size = tbPtr->newsize;
440 tbPtr->newsize = 0;
441 tbPtr->version = htonl(ntohl(tbPtr->version) + 1);
443 /* saveTextToFile(ut, tbPtr); */
446 /* update size and other text header info */
447 code =
448 dbwrite(ut, (char *)tbPtr - (char *)&db.h, (char *)tbPtr,
449 sizeof(struct textBlock));
450 if (code)
451 ABORT(code);
453 /*error_exit: */
454 code = ubik_EndTrans(ut);
455 return (code);
457 abort_exit:
458 ubik_AbortTrans(ut);
459 return (code);
462 /* debug support */
463 void
464 saveTextToFile(struct ubik_trans *ut, struct textBlock *tbPtr)
466 afs_int32 blockAddr;
467 struct block block;
468 char filename[128];
469 afs_int32 size, totalSize, chunkSize;
470 int fid;
472 sprintf(filename, "%s/%s", gettmpdir(), "dbg_XXXXXX");
474 fid = mkstemp(filename);
475 totalSize = size = ntohl(tbPtr->size);
476 blockAddr = ntohl(tbPtr->textAddr);
477 while (size) {
478 chunkSize = MIN(BLOCK_DATA_SIZE, size);
479 dbread(ut, blockAddr, (char *)&block, sizeof(block));
480 if (write(fid, &block.a[0], chunkSize) < 0)
481 break;
482 blockAddr = ntohl(block.h.next);
483 size -= chunkSize;
485 close(fid);
486 if (size) {
487 printf("Wrote partial debug file (%ld bytes out of %ld)\n",
488 (long)(totalSize - size), (long)totalSize);
489 } else {
490 printf("wrote debug file %s\n", filename);
495 #if (defined(AFS_HPUX_ENV)) || defined(AFS_NT40_ENV)
497 /* mkstemp
498 * entry:
499 * st - string containing template for a tmp file name
500 * exit:
501 * -1 - failed
502 * 0-n - open file descriptor
503 * notes:
504 * 1) missing in Ultrix, HP/UX and AIX 221 environment
505 * 2) iterate some number of times to alleviate the race?
509 mkstemp(char *st)
511 int retval = -1;
513 #ifdef AFS_LINUX20_ENV
514 retval = open(mkstemp(st), O_RDWR | O_CREAT | O_EXCL, 0600);
515 #else
516 retval = open(mktemp(st), O_RDWR | O_CREAT | O_EXCL, 0600);
517 #endif
519 return (retval);
521 #endif