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