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