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 * Memory handling for zlib 47 */ 48 49 static voidpf 50 fifo_zalloc(voidpf opaque __unused, uInt items, uInt size) 51 { 52 53 return calloc(items,size); 54 } 55 56 static void 57 fifo_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 73 static const char * 74 fifolog_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 154 const char * 155 fifolog_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 179 void 180 fifolog_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 } 196 197 static void 198 fifolog_int_file_assert(const struct fifolog_file *ff) 199 { 200 201 CHECK_OBJ_NOTNULL(ff, FIFOLOG_FILE_MAGIC); 202 assert(ff->fd >= 0); 203 assert(ff->recbuf != NULL); 204 } 205 206 207 /* 208 * Read a record. 209 * 210 * Return zero on success 211 */ 212 213 int 214 fifolog_int_read(const struct fifolog_file *ff, off_t recno) 215 { 216 int i; 217 218 fifolog_int_file_assert(ff); 219 if (recno >= ff->logsize) 220 return (-1); 221 recno++; /* label sector */ 222 i = pread(ff->fd, ff->recbuf, ff->recsize, recno * ff->recsize); 223 if (i < 0) 224 return (-1); 225 if (i != (int)ff->recsize) 226 return (-1); 227 return (0); 228 } 229 230 /* 231 * Find the last written record in the fifolog. 232 * 233 * Return is error string or NULL on success 234 */ 235 236 const char * 237 fifolog_int_findend(const struct fifolog_file *ff, off_t *last) 238 { 239 off_t o, s; 240 int e; 241 unsigned seq0, seq; 242 243 fifolog_int_file_assert(ff); 244 245 o = 0; 246 e = fifolog_int_read(ff, o); 247 if (e) 248 return("Read error, first record"); 249 250 seq0 = be32dec(ff->recbuf); 251 252 /* If the first records sequence is zero, the fifolog is empty */ 253 if (seq0 == 0) { 254 *last = o; 255 return (NULL); 256 } 257 258 /* Do a binary search for a discontinuity in the sequence numbers */ 259 s = ff->logsize / 2; 260 do { 261 e = fifolog_int_read(ff, o + s); 262 if (e) 263 return ("Read error while searching"); 264 seq = be32dec(ff->recbuf); 265 if (seq == seq0 + s) { 266 o += s; 267 seq0 = seq; 268 } 269 s /= 2; 270 assert(o < ff->logsize); 271 } while (s > 0); 272 273 *last = o; 274 return (NULL); 275 } 276