db31f9b0e18248a2200a83c2fc9c298704ce4ff1
[kopensolaris-gnu/glibc.git] / db2 / log / log_put.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7 #include "config.h"
8
9 #ifndef lint
10 static const char sccsid[] = "@(#)log_put.c     10.12 (Sleepycat) 8/20/97";
11 #endif /* not lint */
12
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
15
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <time.h>
21 #include <unistd.h>
22 #endif
23
24 #include "db_int.h"
25 #include "shqueue.h"
26 #include "db_page.h"
27 #include "log.h"
28 #include "hash.h"
29 #include "common_ext.h"
30
31 static int __log_fill __P((DB_LOG *, void *, u_int32_t));
32 static int __log_newfd __P((DB_LOG *));
33 static int __log_write __P((DB_LOG *, void *, u_int32_t));
34 static int __log_putr __P((DB_LOG *, const DBT *, u_int32_t));
35
36 /*
37  * log_put --
38  *      Write a log record.
39  */
40 int
41 log_put(dblp, lsn, dbt, flags)
42         DB_LOG *dblp;
43         DB_LSN *lsn;
44         const DBT *dbt;
45         int flags;
46 {
47         int ret;
48
49         /* Validate arguments. */
50 #define OKFLAGS (DB_CHECKPOINT | DB_FLUSH)
51         if (flags != 0) {
52                 if ((ret =
53                     __db_fchk(dblp->dbenv, "log_put", flags, OKFLAGS)) != 0)
54                         return (ret);
55                 switch (flags) {
56                 case DB_CHECKPOINT:
57                 case DB_FLUSH:
58                 case 0:
59                         break;
60                 default:
61                         return (__db_ferr(dblp->dbenv, "log_put", 1));
62                 }
63         }
64
65         LOCK_LOGREGION(dblp);
66
67         ret = __log_put(dblp, lsn, dbt, flags);
68
69         UNLOCK_LOGREGION(dblp);
70
71         return (ret);
72 }
73
74 /*
75  * __log_put --
76  *      Write a log record; internal version.
77  *
78  * PUBLIC: int __log_put __P((DB_LOG *, DB_LSN *, const DBT *, int));
79  */
80 int
81 __log_put(dblp, lsn, dbt, flags)
82         DB_LOG *dblp;
83         DB_LSN *lsn;
84         const DBT *dbt;
85         int flags;
86 {
87         DBT t;
88         DBT fid_dbt;
89         DB_LSN r_unused;
90         FNAME *fnp;
91         LOG *lp;
92         u_int32_t lastoff;
93         int ret;
94
95         lp = dblp->lp;
96
97         /* If this information won't fit in the file, swap files. */
98         if (lp->lsn.offset + sizeof(HDR) + dbt->size > lp->persist.lg_max) {
99                 if (sizeof(HDR) +
100                     sizeof(LOGP) + dbt->size > lp->persist.lg_max) {
101                         __db_err(dblp->dbenv,
102                             "log_put: record larger than maximum file size");
103                         return (EINVAL);
104                 }
105                 if (lp->b_off != 0) {
106                         if ((ret = __log_write(dblp, lp->buf, lp->b_off)) != 0)
107                                 return (ret);
108                         if ((ret = __db_fsync(dblp->lfd)) != 0)
109                                 return (ret);
110                         lp->s_lsn.file = lp->lsn.file;
111                         lp->s_lsn.offset = lp->lsn.offset - 1;
112                 }
113
114                 /*
115                  * Save the last known offset from the previous file, we'll
116                  * need it to initialize the persistent header information.
117                  */
118                 lastoff = lp->lsn.offset;
119
120                 ++lp->lsn.file;
121                 lp->lsn.offset = 0;
122                 lp->w_off = 0;
123         } else
124                 lastoff = 0;
125
126         /*
127          * Insert persistent information as the first record in every file.
128          * Note that the previous length is wrong for the very first record
129          * of the log, but that's okay, we check for it during retrieval.
130          */
131         if (lp->lsn.offset == 0) {
132                 t.data = &lp->persist;
133                 t.size = sizeof(LOGP);
134                 if ((ret = __log_putr(dblp,
135                     &t, lastoff == 0 ? 0 : lastoff - lp->len)) != 0)
136                         return (ret);
137         }
138
139         /* Initialize the LSN information returned to the user. */
140         lsn->file = lp->lsn.file;
141         lsn->offset = lp->lsn.offset;
142
143         /* Put out the user's record. */
144         if ((ret = __log_putr(dblp, dbt, lp->lsn.offset - lp->len)) != 0)
145                 return (ret);
146
147         /*
148          * On a checkpoint, we:
149          *      Put out the checkpoint record (above).
150          *      Save the LSN of the checkpoint in the shared region.
151          *      Append the set of file name information into the log.
152          *      Flush the current buffer contents to disk.
153          *      Sync the log to disk.
154          *      Save the time the checkpoint was written.
155          *      Reset the bytes written since the last checkpoint.
156          */
157         if (flags == DB_CHECKPOINT) {
158                 lp->c_lsn = *lsn;
159
160                 for (fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname);
161                     fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) {
162                         t.data = ADDR(dblp, fnp->name_off);
163                         t.size = strlen(t.data) + 1;
164                         memset(&fid_dbt, 0, sizeof(fid_dbt));
165                         fid_dbt.data = ADDR(dblp, fnp->fileid_off);
166                         fid_dbt.size = DB_FILE_ID_LEN;
167                         if ((ret = __log_register_log(dblp, NULL, &r_unused,
168                             0, &t, &fid_dbt, fnp->id, fnp->s_type)) != 0)
169                                 return (ret);
170                 }
171                 if (lp->b_off != 0 &&
172                     (ret = __log_write(dblp, lp->buf, lp->b_off)) != 0)
173                         return (ret);
174                 (void)time(&lp->chkpt);
175                 lp->written = 0;
176
177                 if ((ret = __db_fsync(dblp->lfd)) != 0)
178                         return (ret);
179                 lp->s_lsn.file = lp->lsn.file;
180                 lp->s_lsn.offset = lp->lsn.offset - 1;
181         }
182
183         /* We always flush on a checkpoint. */
184         if (flags == DB_FLUSH || flags == DB_CHECKPOINT) {
185                 if (lp->b_off != 0 &&
186                     (ret = __log_write(dblp, lp->buf, lp->b_off)) != 0)
187                         return (ret);
188
189                 if ((ret = __db_fsync(dblp->lfd)) != 0)
190                         return (ret);
191                 lp->s_lsn.file = lp->lsn.file;
192                 lp->s_lsn.offset = lp->lsn.offset - 1;
193         }
194
195         /*
196          * If we just did I/O, i.e., this LSN could have spanned the start of
197          * the in-core buffer, we remember it so that we can flush correctly
198          * during a sync.
199          */
200         if (lsn->offset < lp->w_off && lsn->offset + lp->len > lp->w_off)
201                 lp->span_lsn = *lsn;
202         return (0);
203 }
204
205 /*
206  * __log_putr --
207  *      Actually put a record into the log.
208  */
209 static int
210 __log_putr(dblp, dbt, prev)
211         DB_LOG *dblp;
212         const DBT *dbt;
213         u_int32_t prev;
214 {
215         HDR hdr;
216         LOG *lp;
217         int ret;
218
219         lp = dblp->lp;
220
221         /*
222          * Initialize the header.  If we just switched files, lsn.offset will
223          * be 0, and what we really want is the offset of the previous record
224          * in the previous file.  Fortunately, prev holds the value we want.
225          */
226         hdr.prev = prev;
227         hdr.len = sizeof(HDR) + dbt->size;
228         hdr.cksum = __ham_func4(dbt->data, dbt->size);
229
230         if ((ret = __log_fill(dblp, &hdr, sizeof(HDR))) != 0)
231                 return (ret);
232         lp->lsn.offset += sizeof(HDR);
233
234         if ((ret = __log_fill(dblp, dbt->data, dbt->size)) != 0)
235                 return (ret);
236         lp->lsn.offset += dbt->size;
237
238         lp->len = sizeof(HDR) + dbt->size;
239         return (0);
240 }
241
242 /*
243  * log_flush --
244  *      Write all records less than or equal to the specified LSN.
245  */
246 int
247 log_flush(dblp, lsn)
248         DB_LOG *dblp;
249         const DB_LSN *lsn;
250 {
251         DB_LSN t_lsn;
252         LOG *lp;
253         int ret;
254
255         ret = 0;
256         lp = dblp->lp;
257
258         LOCK_LOGREGION(dblp);
259
260         /* If no LSN specified, flush the entire log. */
261         if (lsn == NULL) {
262                 t_lsn.file = lp->lsn.file;
263                 t_lsn.offset = lp->lsn.offset - lp->len;
264                 lsn = &t_lsn;
265         }
266
267         /* If it's a non-existent record, it's an error. */
268         if (lsn->file > lp->lsn.file ||
269             (lsn->file == lp->lsn.file && lsn->offset > lp->lsn.offset)) {
270                 __db_err(dblp->dbenv, "log_flush: LSN past current end-of-log");
271                 ret = EINVAL;
272                 goto ret1;
273         }
274
275         /*
276          * If it's from a previous file, we're done because we sync each
277          * file when we move to a new one.
278          */
279         if (lsn->file < lp->lsn.file)
280                 goto ret1;
281
282         /*
283          * If it's less than the last-sync'd offset, we've already sync'd
284          * this LSN.
285          */
286         if (lsn->offset <= lp->s_lsn.offset)
287                 goto ret1;
288
289         /*
290          * We may need to write the current buffer.  We have to write the
291          * current buffer if the sync LSN is greater than or equal to the
292          * saved spanning-LSN.
293          */
294         if (lsn->file >= lp->span_lsn.file &&
295             lsn->offset >= lp->span_lsn.offset)
296                 if ((ret = __log_write(dblp, lp->buf, lp->b_off)) != 0)
297                         goto ret1;
298
299         /* Acquire a file descriptor if we don't have one. */
300         if (dblp->lfname != dblp->lp->lsn.file &&
301             (ret = __log_newfd(dblp)) != 0)
302                 goto ret1;
303
304         if ((ret = __db_fsync(dblp->lfd)) != 0)
305                 goto ret1;
306
307         lp->s_lsn.file = lp->lsn.file;
308         lp->s_lsn.offset = lsn->offset;
309
310 ret1:   UNLOCK_LOGREGION(dblp);
311         return (ret);
312 }
313
314 /*
315  * __log_fill --
316  *      Write information into the log.
317  */
318 static int
319 __log_fill(dblp, addr, len)
320         DB_LOG *dblp;
321         void *addr;
322         u_int32_t len;
323 {
324         LOG *lp;
325         u_int32_t nrec;
326         size_t nw, remain;
327         int ret;
328
329         /* Copy out the data. */
330         for (lp = dblp->lp; len > 0;) {
331                 /*
332                  * If we're on a buffer boundary and the data is big enough,
333                  * copy as many records as we can directly from the data.
334                  */
335                 if (lp->b_off == 0 && len >= sizeof(lp->buf)) {
336                         nrec = len / sizeof(lp->buf);
337                         if ((ret = __log_write(dblp,
338                             addr, nrec * sizeof(lp->buf))) != 0)
339                                 return (ret);
340                         addr = (u_int8_t *)addr + nrec * sizeof(lp->buf);
341                         len -= nrec * sizeof(lp->buf);
342                         continue;
343                 }
344
345                 /* Figure out how many bytes we can copy this time. */
346                 remain = sizeof(lp->buf) - lp->b_off;
347                 nw = remain > len ? len : remain;
348                 memcpy(lp->buf + lp->b_off, addr, nw);
349                 addr = (u_int8_t *)addr + nw;
350                 len -= nw;
351                 lp->b_off += nw;
352
353                 /* If we fill the buffer, flush it. */
354                 if (lp->b_off == sizeof(lp->buf) &&
355                     (ret = __log_write(dblp, lp->buf, sizeof(lp->buf))) != 0)
356                         return (ret);
357         }
358         return (0);
359 }
360
361 /*
362  * __log_write --
363  *      Write the log buffer to disk.
364  */
365 static int
366 __log_write(dblp, addr, len)
367         DB_LOG *dblp;
368         void *addr;
369         u_int32_t len;
370 {
371         LOG *lp;
372         ssize_t nw;
373         int ret;
374
375         /*
376          * If we haven't opened the log file yet or the current one
377          * has changed, acquire a new log file.
378          */
379         lp = dblp->lp;
380         if (dblp->lfd == -1 || dblp->lfname != lp->lsn.file)
381                 if ((ret = __log_newfd(dblp)) != 0)
382                         return (ret);
383
384         /*
385          * Seek to the offset in the file (someone may have written it
386          * since we last did).
387          */
388         if ((ret = __db_lseek(dblp->lfd, 0, 0, lp->w_off, SEEK_SET)) != 0)
389                 return (ret);
390         if ((ret = __db_write(dblp->lfd, addr, len, &nw)) != 0)
391                 return (ret);
392         if (nw != (int32_t)len)
393                 return (EIO);
394
395         /* Update the seek offset and reset the buffer offset. */
396         lp->b_off = 0;
397         lp->w_off += len;
398         lp->written += len;
399
400         return (0);
401 }
402
403 /*
404  * log_file --
405  *      Map a DB_LSN to a file name.
406  */
407 int
408 log_file(dblp, lsn, namep, len)
409         DB_LOG *dblp;
410         const DB_LSN *lsn;
411         char *namep;
412         size_t len;
413 {
414         int ret;
415         char *p;
416
417         LOCK_LOGREGION(dblp);
418
419         ret = __log_name(dblp->dbenv, lsn->file, &p);
420
421         UNLOCK_LOGREGION(dblp);
422
423         if (ret != 0)
424                 return (ret);
425
426         /* Check to make sure there's enough room and copy the name. */
427         if (len < strlen(p)) {
428                 *namep = '\0';
429                 return (ENOMEM);
430         }
431         (void)strcpy(namep, p);
432         free(p);
433
434         return (0);
435 }
436
437 /*
438  * __log_newfd --
439  *      Acquire a file descriptor for the current log file.
440  */
441 static int
442 __log_newfd(dblp)
443         DB_LOG *dblp;
444 {
445         int ret;
446         char *p;
447
448         /* Close any previous file descriptor. */
449         if (dblp->lfd != -1) {
450                 (void)__db_close(dblp->lfd);
451                 dblp->lfd = -1;
452         }
453
454         /* Get the path of the new file and open it. */
455         dblp->lfname = dblp->lp->lsn.file;
456         if ((ret = __log_name(dblp->dbenv, dblp->lfname, &p)) != 0)
457                 return (ret);
458         if ((ret = __db_fdopen(p,
459             DB_CREATE | DB_SEQUENTIAL,
460             DB_CREATE | DB_SEQUENTIAL,
461             dblp->lp->persist.mode, &dblp->lfd)) != 0)
462                 __db_err(dblp->dbenv,
463                     "log_put: %s: %s", p, strerror(errno));
464         FREES(p);
465         return (ret);
466 }
467
468 /*
469  * __log_name --
470  *      Return the log name for a particular file.
471  *
472  * PUBLIC: int __log_name __P((DB_ENV *, int, char **));
473  */
474 int
475 __log_name(dbenv, fn, np)
476         DB_ENV *dbenv;
477         int fn;
478         char **np;
479 {
480         char name[sizeof(LFNAME) + 10];
481
482         (void)snprintf(name, sizeof(name), LFNAME, fn);
483         return (__db_appname(dbenv, DB_APP_LOG, NULL, name, NULL, np));
484 }