]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/fifolog/lib/fifolog_int.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / fifolog / lib / fifolog_int.c
1 /*-
2  * Copyright (c) 2005-2008 Poul-Henning Kamp
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <assert.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <zlib.h>
36
37 #include <sys/disk.h>
38 #include <sys/endian.h>
39 #include <sys/stat.h>
40
41 #include "miniobj.h"
42 #include "fifolog.h"
43 #include "libfifolog_int.h"
44
45 /*
46  * Memory handling for zlib
47  */
48
49 static voidpf
50 fifo_zalloc(voidpf opaque __unused, uInt items, uInt size)
51 {
52
53         return calloc(items,size);
54 }
55
56 static void
57 fifo_zfree(voidpf opaque __unused, voidpf address)
58 {
59
60         free(address);
61 }
62
63 /*
64  * Open a fifolog file or partition for reading or writing.
65  *
66  * Return value is NULL for success or a error description string to
67  * be augmented by errno if non-zero.
68  *
69  * The second function is just an error-handling wrapper around the 
70  * first which, does the actual work.
71  */
72
73 static const char *
74 fifolog_int_open_i(struct fifolog_file *f, const char *fname, int mode)
75 {
76         struct stat st;
77         unsigned u;
78         int i;
79
80         f->fd = open(fname, mode ? O_RDWR : O_RDONLY);
81         if (f->fd < 0)
82                 return ("Cannot open");
83
84         /* Determine initial record size guesstimate */
85         i = ioctl(f->fd, DIOCGSECTORSIZE, &f->recsize);
86         if (i != 0 && errno != ENOTTY)
87                 return ("ioctl(DIOCGSECTORSIZE) failed");
88
89         if (i != 0) {
90                 i = fstat(f->fd, &st);
91                 if (!S_ISREG(st.st_mode)) 
92                         return ("Neither disk nor regular file");
93                 f->recsize = 512;
94                 f->logsize = st.st_size;
95         } else if (f->recsize < 64) {
96                 return ("Disk device sectorsize smaller than 64");
97         } else {
98                 i = ioctl(f->fd, DIOCGMEDIASIZE, &f->logsize);
99                 if (i < 0 && errno != ENOTTY)
100                         return ("ioctl(DIOCGMEDIASIZE) failed");
101         }
102
103         /* Allocate a record buffer */
104         f->recbuf = malloc(f->recsize);
105         if (f->recbuf == NULL)
106                 return ("Cannot malloc");
107
108         /* Read and validate the label sector */
109         i = pread(f->fd, f->recbuf, f->recsize, 0);
110         if (i < 0 || i < (int)f->recsize)
111                 return ("Read error, first sector");
112
113         errno = 0;
114         if (memcmp(f->recbuf, FIFOLOG_FMT_MAGIC, strlen(FIFOLOG_FMT_MAGIC) + 1))
115                 return ("Wrong or missing magic string");
116
117         u = be32dec(f->recbuf + FIFOLOG_OFF_BS);
118         if (u < 64)
119                 return ("Wrong record size in header (<64)");
120
121         if ((off_t)u >= f->logsize)
122                 return ("Record size in header bigger than fifolog");
123
124         f->recsize = u;
125
126         /* Reallocate the buffer to correct size if necessary */
127         if (u != f->recsize) {
128                 free(f->recbuf);
129                 f->recbuf = NULL;
130                 f->recsize = u;
131                 f->recbuf = malloc(f->recsize);
132                 if (f->recbuf == NULL)
133                         return ("Cannot malloc");
134         }
135
136         /* Calculate number of records in fifolog */
137         f->logsize /= u;
138         if (f->logsize < 10)
139                 return ("less than 10 records in fifolog");
140
141         f->logsize--;           /* the label record */
142
143         /* Initialize zlib handling */
144
145         f->zs = calloc(sizeof *f->zs, 1);
146         if (f->zs == NULL)
147                 return ("cannot malloc");
148         f->zs->zalloc = fifo_zalloc;
149         f->zs->zfree = fifo_zfree;
150
151         return (NULL);
152 }
153
154 const char *
155 fifolog_int_open(struct fifolog_file **ff, const char *fname, int mode)
156 {
157         struct fifolog_file fs, *f;
158         const char *retval;
159         int e;
160
161         f = &fs;
162         memset(f, 0, sizeof *f);
163         f->fd = -1;
164         retval = fifolog_int_open_i(f, fname, mode);
165         e = errno;
166         if (retval == NULL) {
167                 *ff = malloc(sizeof *f);
168                 if (*ff != NULL) {
169                         memcpy(*ff, f, sizeof *f);
170                         (*ff)->magic = FIFOLOG_FILE_MAGIC;
171                         return (retval);
172                 }
173         }
174         fifolog_int_close(&f);
175         errno = e;
176         return (retval);
177 }
178
179 void
180 fifolog_int_close(struct fifolog_file **ff)
181 {
182         struct fifolog_file *f;
183
184         f = *ff;
185         *ff = NULL;
186         if (f == NULL)
187                 return;
188
189         if (f->fd >= 0)
190                 (void)close(f->fd);
191         if (f->zs != NULL)
192                 free(f->zs);
193         if (f->recbuf != NULL)
194                 free(f->recbuf);
195 }
196
197 static void
198 fifolog_int_file_assert(const struct fifolog_file *ff)
199 {
200
201         CHECK_OBJ_NOTNULL(ff, FIFOLOG_FILE_MAGIC);
202         assert(ff->fd >= 0);
203         assert(ff->recbuf != NULL);
204 }
205
206
207 /*
208  * Read a record.
209  *
210  * Return zero on success
211  */
212
213 int
214 fifolog_int_read(const struct fifolog_file *ff, off_t recno)
215 {
216         int i;
217
218         fifolog_int_file_assert(ff);
219         if (recno >= ff->logsize)
220                 return (-1);
221         recno++;                        /* label sector */
222         i = pread(ff->fd, ff->recbuf, ff->recsize, recno * ff->recsize);
223         if (i < 0)
224                 return (-2);
225         if (i != (int)ff->recsize)
226                 return (-3);
227         return (0);
228 }
229
230 /*
231  * Find the last written record in the fifolog.
232  *
233  * Return is error string or NULL on success
234  */
235
236 const char *
237 fifolog_int_findend(const struct fifolog_file *ff, off_t *last)
238 {
239         off_t o, s;
240         int e;
241         unsigned seq0, seq;
242
243         fifolog_int_file_assert(ff);
244
245         o = 0;
246         e = fifolog_int_read(ff, o);
247         if (e)
248                 return("Read error, first record");
249
250         seq0 = be32dec(ff->recbuf);
251
252         /* If the first records sequence is zero, the fifolog is empty */
253         if (seq0 == 0) {
254                 *last = o;
255                 return (NULL);
256         }
257
258         /* Do a binary search for a discontinuity in the sequence numbers */
259         s = ff->logsize / 2;
260         do {
261                 e = fifolog_int_read(ff, o + s);
262                 if (e)
263                         return ("Read error while searching");
264                 seq = be32dec(ff->recbuf);
265                 if (seq == seq0 + s) {
266                         o += s;
267                         seq0 = seq;
268                 }
269                 s /= 2;
270                 assert(o < ff->logsize);
271         } while (s > 0);
272
273         *last = o;
274         return (NULL);
275 }