fifolog_int.c revision 219097
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 219097 2011-02-28 14:48:00Z 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 * 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(sizeof *f->zs, 1); 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