xref: /freebsd/usr.sbin/fifolog/lib/fifolog_int.c (revision 09a53ad8f1318c5daae6cfb19d97f4f6459f0013)
1 /*-
2  * Copyright (c) 2005-2008 Poul-Henning Kamp
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
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 *
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 *
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
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
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
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 *
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