1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 * $FreeBSD$
29 */
30
31 #include <assert.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <zlib.h>
38
39 #include <sys/disk.h>
40 #include <sys/endian.h>
41 #include <sys/stat.h>
42
43 #include "miniobj.h"
44 #include "fifolog.h"
45 #include "libfifolog_int.h"
46
47 /*
48 * Open a fifolog file or partition for reading or writing.
49 *
50 * Return value is NULL for success or a error description string to
51 * be augmented by errno if non-zero.
52 *
53 * The second function is just an error-handling wrapper around the
54 * first which, does the actual work.
55 */
56
57 static const char *
fifolog_int_open_i(struct fifolog_file * f,const char * fname,int mode)58 fifolog_int_open_i(struct fifolog_file *f, const char *fname, int mode)
59 {
60 struct stat st;
61 ssize_t u;
62 int i;
63
64 f->fd = open(fname, mode ? O_RDWR : O_RDONLY);
65 if (f->fd < 0)
66 return ("Cannot open");
67
68 /* Determine initial record size guesstimate */
69 i = ioctl(f->fd, DIOCGSECTORSIZE, &f->recsize);
70 if (i != 0 && errno != ENOTTY)
71 return ("ioctl(DIOCGSECTORSIZE) failed");
72
73 if (i != 0) {
74 i = fstat(f->fd, &st);
75 assert(i == 0);
76 if (!S_ISREG(st.st_mode))
77 return ("Neither disk nor regular file");
78 f->recsize = 512;
79 f->logsize = st.st_size;
80 } else if (f->recsize < 64) {
81 return ("Disk device sectorsize smaller than 64");
82 } else {
83 i = ioctl(f->fd, DIOCGMEDIASIZE, &f->logsize);
84 if (i < 0 && errno != ENOTTY)
85 return ("ioctl(DIOCGMEDIASIZE) failed");
86 }
87
88 /* Allocate a record buffer */
89 f->recbuf = malloc(f->recsize);
90 if (f->recbuf == NULL)
91 return ("Cannot malloc");
92
93 /* Read and validate the label sector */
94 i = pread(f->fd, f->recbuf, f->recsize, 0);
95 if (i < 0 || i < (int)f->recsize)
96 return ("Read error, first sector");
97
98 errno = 0;
99 if (memcmp(f->recbuf, FIFOLOG_FMT_MAGIC, strlen(FIFOLOG_FMT_MAGIC) + 1))
100 return ("Wrong or missing magic string");
101
102 u = be32dec(f->recbuf + FIFOLOG_OFF_BS);
103 if (u < 64)
104 return ("Wrong record size in header (<64)");
105
106 if ((off_t)u >= f->logsize)
107 return ("Record size in header bigger than fifolog");
108
109 f->recsize = u;
110
111 /* Reallocate the buffer to correct size if necessary */
112 if (u != f->recsize) {
113 free(f->recbuf);
114 f->recbuf = NULL;
115 f->recsize = u;
116 f->recbuf = malloc(f->recsize);
117 if (f->recbuf == NULL)
118 return ("Cannot malloc");
119 }
120
121 /* Calculate number of records in fifolog */
122 f->logsize /= u;
123 if (f->logsize < 10)
124 return ("less than 10 records in fifolog");
125
126 f->logsize--; /* the label record */
127
128 /* Initialize zlib handling */
129
130 f->zs = calloc(1, sizeof(*f->zs));
131 if (f->zs == NULL)
132 return ("cannot malloc");
133
134 return (NULL);
135 }
136
137 const char *
fifolog_int_open(struct fifolog_file ** ff,const char * fname,int mode)138 fifolog_int_open(struct fifolog_file **ff, const char *fname, int mode)
139 {
140 struct fifolog_file fs, *f;
141 const char *retval;
142 int e;
143
144 f = &fs;
145 memset(f, 0, sizeof *f);
146 f->fd = -1;
147 retval = fifolog_int_open_i(f, fname, mode);
148 e = errno;
149 if (retval == NULL) {
150 *ff = malloc(sizeof *f);
151 if (*ff != NULL) {
152 memcpy(*ff, f, sizeof *f);
153 (*ff)->magic = FIFOLOG_FILE_MAGIC;
154 return (retval);
155 }
156 }
157 fifolog_int_close(&f);
158 errno = e;
159 return (retval);
160 }
161
162 void
fifolog_int_close(struct fifolog_file ** ff)163 fifolog_int_close(struct fifolog_file **ff)
164 {
165 struct fifolog_file *f;
166
167 f = *ff;
168 *ff = NULL;
169 if (f == NULL)
170 return;
171
172 if (f->fd >= 0)
173 (void)close(f->fd);
174 if (f->zs != NULL)
175 free(f->zs);
176 if (f->recbuf != NULL)
177 free(f->recbuf);
178 }
179
180 static void
fifolog_int_file_assert(const struct fifolog_file * ff)181 fifolog_int_file_assert(const struct fifolog_file *ff)
182 {
183
184 CHECK_OBJ_NOTNULL(ff, FIFOLOG_FILE_MAGIC);
185 assert(ff->fd >= 0);
186 assert(ff->recbuf != NULL);
187 }
188
189
190 /*
191 * Read a record.
192 *
193 * Return zero on success
194 */
195
196 int
fifolog_int_read(const struct fifolog_file * ff,off_t recno)197 fifolog_int_read(const struct fifolog_file *ff, off_t recno)
198 {
199 int i;
200
201 fifolog_int_file_assert(ff);
202 if (recno >= ff->logsize)
203 return (-1);
204 recno++; /* label sector */
205 i = pread(ff->fd, ff->recbuf, ff->recsize, recno * ff->recsize);
206 if (i < 0)
207 return (-2);
208 if (i != (int)ff->recsize)
209 return (-3);
210 return (0);
211 }
212
213 /*
214 * Find the last written record in the fifolog.
215 *
216 * Return is error string or NULL on success
217 */
218
219 const char *
fifolog_int_findend(const struct fifolog_file * ff,off_t * last)220 fifolog_int_findend(const struct fifolog_file *ff, off_t *last)
221 {
222 off_t o, s;
223 int e;
224 unsigned seq0, seq;
225
226 fifolog_int_file_assert(ff);
227
228 o = 0;
229 e = fifolog_int_read(ff, o);
230 if (e)
231 return("Read error, first record");
232
233 seq0 = be32dec(ff->recbuf);
234
235 /* If the first records sequence is zero, the fifolog is empty */
236 if (seq0 == 0) {
237 *last = o;
238 return (NULL);
239 }
240
241 /* Do a binary search for a discontinuity in the sequence numbers */
242 s = ff->logsize / 2;
243 do {
244 e = fifolog_int_read(ff, o + s);
245 if (e)
246 return ("Read error while searching");
247 seq = be32dec(ff->recbuf);
248 if (seq == seq0 + s) {
249 o += s;
250 seq0 = seq;
251 }
252 s /= 2;
253 assert(o < ff->logsize);
254 } while (s > 0);
255
256 *last = o;
257 return (NULL);
258 }
259