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