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