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
57static const char *
58fifolog_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
137const char *
138fifolog_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
162void
163fifolog_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
180static void
181fifolog_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
196int
197fifolog_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
219const char *
220fifolog_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