1176998Sphk/*-
2176998Sphk * Copyright (c) 2005-2008 Poul-Henning Kamp
3176998Sphk * All rights reserved.
4176998Sphk *
5176998Sphk * Redistribution and use in source and binary forms, with or without
6176998Sphk * modification, are permitted provided that the following conditions
7176998Sphk * are met:
8176998Sphk * 1. Redistributions of source code must retain the above copyright
9176998Sphk *    notice, this list of conditions and the following disclaimer.
10176998Sphk * 2. Redistributions in binary form must reproduce the above copyright
11176998Sphk *    notice, this list of conditions and the following disclaimer in the
12176998Sphk *    documentation and/or other materials provided with the distribution.
13176998Sphk *
14176998Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15176998Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16176998Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17176998Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18176998Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19176998Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20176998Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21176998Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22176998Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23176998Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24176998Sphk * SUCH DAMAGE.
25176998Sphk *
26176998Sphk * $FreeBSD: releng/10.3/usr.sbin/fifolog/lib/fifolog_int.c 219097 2011-02-28 14:48:00Z phk $
27176998Sphk */
28176998Sphk
29176998Sphk#include <assert.h>
30176998Sphk#include <errno.h>
31176998Sphk#include <fcntl.h>
32176998Sphk#include <stdlib.h>
33176998Sphk#include <string.h>
34176998Sphk#include <unistd.h>
35176998Sphk#include <zlib.h>
36176998Sphk
37176998Sphk#include <sys/disk.h>
38176998Sphk#include <sys/endian.h>
39176998Sphk#include <sys/stat.h>
40176998Sphk
41176998Sphk#include "miniobj.h"
42176998Sphk#include "fifolog.h"
43176998Sphk#include "libfifolog_int.h"
44176998Sphk
45176998Sphk/*
46176998Sphk * Open a fifolog file or partition for reading or writing.
47176998Sphk *
48176998Sphk * Return value is NULL for success or a error description string to
49176998Sphk * be augmented by errno if non-zero.
50176998Sphk *
51219027Sphk * The second function is just an error-handling wrapper around the
52176998Sphk * first which, does the actual work.
53176998Sphk */
54176998Sphk
55176998Sphkstatic const char *
56176998Sphkfifolog_int_open_i(struct fifolog_file *f, const char *fname, int mode)
57176998Sphk{
58176998Sphk	struct stat st;
59219097Sphk	ssize_t u;
60176998Sphk	int i;
61176998Sphk
62176998Sphk	f->fd = open(fname, mode ? O_RDWR : O_RDONLY);
63176998Sphk	if (f->fd < 0)
64176998Sphk		return ("Cannot open");
65176998Sphk
66176998Sphk	/* Determine initial record size guesstimate */
67176998Sphk	i = ioctl(f->fd, DIOCGSECTORSIZE, &f->recsize);
68176998Sphk	if (i != 0 && errno != ENOTTY)
69176998Sphk		return ("ioctl(DIOCGSECTORSIZE) failed");
70176998Sphk
71176998Sphk	if (i != 0) {
72176998Sphk		i = fstat(f->fd, &st);
73219094Sphk		assert(i == 0);
74219027Sphk		if (!S_ISREG(st.st_mode))
75176998Sphk			return ("Neither disk nor regular file");
76176998Sphk		f->recsize = 512;
77176998Sphk		f->logsize = st.st_size;
78176998Sphk	} else if (f->recsize < 64) {
79176998Sphk		return ("Disk device sectorsize smaller than 64");
80176998Sphk	} else {
81176998Sphk		i = ioctl(f->fd, DIOCGMEDIASIZE, &f->logsize);
82176998Sphk		if (i < 0 && errno != ENOTTY)
83176998Sphk			return ("ioctl(DIOCGMEDIASIZE) failed");
84176998Sphk	}
85176998Sphk
86176998Sphk	/* Allocate a record buffer */
87176998Sphk	f->recbuf = malloc(f->recsize);
88176998Sphk	if (f->recbuf == NULL)
89176998Sphk		return ("Cannot malloc");
90176998Sphk
91176998Sphk	/* Read and validate the label sector */
92176998Sphk	i = pread(f->fd, f->recbuf, f->recsize, 0);
93176998Sphk	if (i < 0 || i < (int)f->recsize)
94176998Sphk		return ("Read error, first sector");
95176998Sphk
96176998Sphk	errno = 0;
97176998Sphk	if (memcmp(f->recbuf, FIFOLOG_FMT_MAGIC, strlen(FIFOLOG_FMT_MAGIC) + 1))
98176998Sphk		return ("Wrong or missing magic string");
99176998Sphk
100176998Sphk	u = be32dec(f->recbuf + FIFOLOG_OFF_BS);
101176998Sphk	if (u < 64)
102176998Sphk		return ("Wrong record size in header (<64)");
103176998Sphk
104176998Sphk	if ((off_t)u >= f->logsize)
105176998Sphk		return ("Record size in header bigger than fifolog");
106176998Sphk
107176998Sphk	f->recsize = u;
108176998Sphk
109176998Sphk	/* Reallocate the buffer to correct size if necessary */
110176998Sphk	if (u != f->recsize) {
111176998Sphk		free(f->recbuf);
112176998Sphk		f->recbuf = NULL;
113176998Sphk		f->recsize = u;
114176998Sphk		f->recbuf = malloc(f->recsize);
115176998Sphk		if (f->recbuf == NULL)
116176998Sphk			return ("Cannot malloc");
117176998Sphk	}
118176998Sphk
119176998Sphk	/* Calculate number of records in fifolog */
120176998Sphk	f->logsize /= u;
121176998Sphk	if (f->logsize < 10)
122176998Sphk		return ("less than 10 records in fifolog");
123176998Sphk
124176998Sphk	f->logsize--;		/* the label record */
125176998Sphk
126176998Sphk	/* Initialize zlib handling */
127176998Sphk
128176998Sphk	f->zs = calloc(sizeof *f->zs, 1);
129176998Sphk	if (f->zs == NULL)
130176998Sphk		return ("cannot malloc");
131176998Sphk
132176998Sphk	return (NULL);
133176998Sphk}
134176998Sphk
135176998Sphkconst char *
136176998Sphkfifolog_int_open(struct fifolog_file **ff, const char *fname, int mode)
137176998Sphk{
138176998Sphk	struct fifolog_file fs, *f;
139176998Sphk	const char *retval;
140176998Sphk	int e;
141176998Sphk
142176998Sphk	f = &fs;
143176998Sphk	memset(f, 0, sizeof *f);
144176998Sphk	f->fd = -1;
145176998Sphk	retval = fifolog_int_open_i(f, fname, mode);
146176998Sphk	e = errno;
147176998Sphk	if (retval == NULL) {
148176998Sphk		*ff = malloc(sizeof *f);
149176998Sphk		if (*ff != NULL) {
150176998Sphk			memcpy(*ff, f, sizeof *f);
151176998Sphk			(*ff)->magic = FIFOLOG_FILE_MAGIC;
152176998Sphk			return (retval);
153176998Sphk		}
154176998Sphk	}
155176998Sphk	fifolog_int_close(&f);
156176998Sphk	errno = e;
157176998Sphk	return (retval);
158176998Sphk}
159176998Sphk
160176998Sphkvoid
161176998Sphkfifolog_int_close(struct fifolog_file **ff)
162176998Sphk{
163176998Sphk	struct fifolog_file *f;
164176998Sphk
165176998Sphk	f = *ff;
166176998Sphk	*ff = NULL;
167176998Sphk	if (f == NULL)
168176998Sphk		return;
169176998Sphk
170176998Sphk	if (f->fd >= 0)
171176998Sphk		(void)close(f->fd);
172176998Sphk	if (f->zs != NULL)
173176998Sphk		free(f->zs);
174176998Sphk	if (f->recbuf != NULL)
175176998Sphk		free(f->recbuf);
176176998Sphk}
177176998Sphk
178176998Sphkstatic void
179176998Sphkfifolog_int_file_assert(const struct fifolog_file *ff)
180176998Sphk{
181176998Sphk
182176998Sphk	CHECK_OBJ_NOTNULL(ff, FIFOLOG_FILE_MAGIC);
183176998Sphk	assert(ff->fd >= 0);
184176998Sphk	assert(ff->recbuf != NULL);
185176998Sphk}
186176998Sphk
187176998Sphk
188176998Sphk/*
189176998Sphk * Read a record.
190176998Sphk *
191176998Sphk * Return zero on success
192176998Sphk */
193176998Sphk
194176998Sphkint
195176998Sphkfifolog_int_read(const struct fifolog_file *ff, off_t recno)
196176998Sphk{
197176998Sphk	int i;
198176998Sphk
199176998Sphk	fifolog_int_file_assert(ff);
200176998Sphk	if (recno >= ff->logsize)
201176998Sphk		return (-1);
202176998Sphk	recno++;			/* label sector */
203176998Sphk	i = pread(ff->fd, ff->recbuf, ff->recsize, recno * ff->recsize);
204176998Sphk	if (i < 0)
205185790Sphk		return (-2);
206176998Sphk	if (i != (int)ff->recsize)
207185790Sphk		return (-3);
208176998Sphk	return (0);
209176998Sphk}
210176998Sphk
211176998Sphk/*
212176998Sphk * Find the last written record in the fifolog.
213176998Sphk *
214176998Sphk * Return is error string or NULL on success
215176998Sphk */
216176998Sphk
217176998Sphkconst char *
218176998Sphkfifolog_int_findend(const struct fifolog_file *ff, off_t *last)
219176998Sphk{
220176998Sphk	off_t o, s;
221176998Sphk	int e;
222176998Sphk	unsigned seq0, seq;
223176998Sphk
224176998Sphk	fifolog_int_file_assert(ff);
225176998Sphk
226176998Sphk	o = 0;
227176998Sphk	e = fifolog_int_read(ff, o);
228176998Sphk	if (e)
229176998Sphk		return("Read error, first record");
230176998Sphk
231176998Sphk	seq0 = be32dec(ff->recbuf);
232176998Sphk
233176998Sphk	/* If the first records sequence is zero, the fifolog is empty */
234176998Sphk	if (seq0 == 0) {
235176998Sphk		*last = o;
236176998Sphk		return (NULL);
237176998Sphk	}
238176998Sphk
239176998Sphk	/* Do a binary search for a discontinuity in the sequence numbers */
240176998Sphk	s = ff->logsize / 2;
241176998Sphk	do {
242176998Sphk		e = fifolog_int_read(ff, o + s);
243176998Sphk		if (e)
244176998Sphk			return ("Read error while searching");
245176998Sphk		seq = be32dec(ff->recbuf);
246176998Sphk		if (seq == seq0 + s) {
247176998Sphk			o += s;
248176998Sphk			seq0 = seq;
249176998Sphk		}
250176998Sphk		s /= 2;
251176998Sphk		assert(o < ff->logsize);
252176998Sphk	} while (s > 0);
253176998Sphk
254176998Sphk	*last = o;
255176998Sphk	return (NULL);
256176998Sphk}
257