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