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