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