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