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