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
55static const char *
56fifolog_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
135const char *
136fifolog_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
160void
161fifolog_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
178static void
179fifolog_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
194int
195fifolog_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
217const char *
218fifolog_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