1 /* $OpenBSD: buf.c,v 1.13 2007/05/29 00:19:10 ray Exp $ */ 2 /* 3 * Copyright (c) 2003 Jean-Francois Brousseau <jfb@openbsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/queue.h> 28 #include <sys/stat.h> 29 30 #include <err.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <stdarg.h> 34 #include <stdio.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #include "buf.h" 39 #include "xmalloc.h" 40 #include "worklist.h" 41 42 #define BUF_INCR 128 43 44 struct rcs_buf { 45 u_int cb_flags; 46 47 /* buffer handle, buffer size, and data length */ 48 u_char *cb_buf; 49 size_t cb_size; 50 size_t cb_len; 51 }; 52 53 #define SIZE_LEFT(b) (b->cb_size - b->cb_len) 54 55 static void rcs_buf_grow(BUF *, size_t); 56 57 /* 58 * rcs_buf_alloc() 59 * 60 * Create a new buffer structure and return a pointer to it. This structure 61 * uses dynamically-allocated memory and must be freed with rcs_buf_free(), 62 * once the buffer is no longer needed. 63 */ 64 BUF * 65 rcs_buf_alloc(size_t len, u_int flags) 66 { 67 BUF *b; 68 69 b = xmalloc(sizeof(*b)); 70 /* Postpone creation of zero-sized buffers */ 71 if (len > 0) 72 b->cb_buf = xcalloc(1, len); 73 else 74 b->cb_buf = NULL; 75 76 b->cb_flags = flags; 77 b->cb_size = len; 78 b->cb_len = 0; 79 80 return (b); 81 } 82 83 /* 84 * rcs_buf_load() 85 * 86 * Open the file specified by <path> and load all of its contents into a 87 * buffer. 88 * Returns the loaded buffer on success or NULL on failure. 89 * Sets errno on error. 90 */ 91 BUF * 92 rcs_buf_load(const char *path, u_int flags) 93 { 94 int fd; 95 ssize_t ret; 96 size_t len; 97 u_char *bp; 98 struct stat st; 99 BUF *buf; 100 101 buf = NULL; 102 103 if ((fd = open(path, O_RDONLY, 0600)) == -1) 104 goto out; 105 106 if (fstat(fd, &st) == -1) 107 goto out; 108 109 if (st.st_size > SIZE_MAX) { 110 errno = EFBIG; 111 goto out; 112 } 113 buf = rcs_buf_alloc(st.st_size, flags); 114 for (bp = buf->cb_buf; ; bp += (size_t)ret) { 115 len = SIZE_LEFT(buf); 116 ret = read(fd, bp, len); 117 if (ret == -1) { 118 int saved_errno; 119 120 saved_errno = errno; 121 rcs_buf_free(buf); 122 buf = NULL; 123 errno = saved_errno; 124 goto out; 125 } else if (ret == 0) 126 break; 127 128 buf->cb_len += (size_t)ret; 129 } 130 131 out: 132 if (fd != -1) { 133 int saved_errno; 134 135 /* We may want to preserve errno here. */ 136 saved_errno = errno; 137 (void)close(fd); 138 errno = saved_errno; 139 } 140 141 return (buf); 142 } 143 144 /* 145 * rcs_buf_free() 146 * 147 * Free the buffer <b> and all associated data. 148 */ 149 void 150 rcs_buf_free(BUF *b) 151 { 152 if (b->cb_buf != NULL) 153 xfree(b->cb_buf); 154 xfree(b); 155 } 156 157 /* 158 * rcs_buf_release() 159 * 160 * Free the buffer <b>'s structural information but do not free the contents 161 * of the buffer. Instead, they are returned and should be freed later using 162 * free(). 163 */ 164 void * 165 rcs_buf_release(BUF *b) 166 { 167 void *tmp; 168 169 tmp = b->cb_buf; 170 xfree(b); 171 return (tmp); 172 } 173 174 /* 175 * rcs_buf_get() 176 */ 177 u_char * 178 rcs_buf_get(BUF *b) 179 { 180 return (b->cb_buf); 181 } 182 183 /* 184 * rcs_buf_empty() 185 * 186 * Empty the contents of the buffer <b> and reset pointers. 187 */ 188 void 189 rcs_buf_empty(BUF *b) 190 { 191 memset(b->cb_buf, 0, b->cb_size); 192 b->cb_len = 0; 193 } 194 195 /* 196 * rcs_buf_putc() 197 * 198 * Append a single character <c> to the end of the buffer <b>. 199 */ 200 void 201 rcs_buf_putc(BUF *b, int c) 202 { 203 u_char *bp; 204 205 bp = b->cb_buf + b->cb_len; 206 if (bp == (b->cb_buf + b->cb_size)) { 207 /* extend */ 208 if (b->cb_flags & BUF_AUTOEXT) 209 rcs_buf_grow(b, (size_t)BUF_INCR); 210 else 211 errx(1, "rcs_buf_putc failed"); 212 213 /* the buffer might have been moved */ 214 bp = b->cb_buf + b->cb_len; 215 } 216 *bp = (u_char)c; 217 b->cb_len++; 218 } 219 220 /* 221 * rcs_buf_getc() 222 * 223 * Return u_char at buffer position <pos>. 224 * 225 */ 226 u_char 227 rcs_buf_getc(BUF *b, size_t pos) 228 { 229 return (b->cb_buf[pos]); 230 } 231 232 /* 233 * rcs_buf_append() 234 * 235 * Append <len> bytes of data pointed to by <data> to the buffer <b>. If the 236 * buffer is too small to accept all data, it will attempt to append as much 237 * data as possible, or if the BUF_AUTOEXT flag is set for the buffer, it 238 * will get resized to an appropriate size to accept all data. 239 * Returns the number of bytes successfully appended to the buffer. 240 */ 241 size_t 242 rcs_buf_append(BUF *b, const void *data, size_t len) 243 { 244 size_t left, rlen; 245 u_char *bp, *bep; 246 247 bp = b->cb_buf + b->cb_len; 248 bep = b->cb_buf + b->cb_size; 249 left = bep - bp; 250 rlen = len; 251 252 if (left < len) { 253 if (b->cb_flags & BUF_AUTOEXT) { 254 rcs_buf_grow(b, len - left); 255 bp = b->cb_buf + b->cb_len; 256 } else 257 rlen = bep - bp; 258 } 259 260 memcpy(bp, data, rlen); 261 b->cb_len += rlen; 262 263 return (rlen); 264 } 265 266 /* 267 * rcs_buf_fappend() 268 * 269 */ 270 size_t 271 rcs_buf_fappend(BUF *b, const char *fmt, ...) 272 { 273 size_t ret; 274 int n; 275 char *str; 276 va_list vap; 277 278 va_start(vap, fmt); 279 n = vasprintf(&str, fmt, vap); 280 va_end(vap); 281 282 if (n == -1) 283 errx(1, "rcs_buf_fappend: failed to format data"); 284 285 ret = rcs_buf_append(b, str, n); 286 xfree(str); 287 return (ret); 288 } 289 290 /* 291 * rcs_buf_len() 292 * 293 * Returns the size of the buffer that is being used. 294 */ 295 size_t 296 rcs_buf_len(BUF *b) 297 { 298 return (b->cb_len); 299 } 300 301 /* 302 * rcs_buf_write_fd() 303 * 304 * Write the contents of the buffer <b> to the specified <fd> 305 */ 306 int 307 rcs_buf_write_fd(BUF *b, int fd) 308 { 309 u_char *bp; 310 size_t len; 311 ssize_t ret; 312 313 len = b->cb_len; 314 bp = b->cb_buf; 315 316 do { 317 ret = write(fd, bp, len); 318 if (ret == -1) { 319 if (errno == EINTR || errno == EAGAIN) 320 continue; 321 return (-1); 322 } 323 324 len -= (size_t)ret; 325 bp += (size_t)ret; 326 } while (len > 0); 327 328 return (0); 329 } 330 331 /* 332 * rcs_buf_write() 333 * 334 * Write the contents of the buffer <b> to the file whose path is given in 335 * <path>. If the file does not exist, it is created with mode <mode>. 336 */ 337 int 338 rcs_buf_write(BUF *b, const char *path, mode_t mode) 339 { 340 int fd; 341 open: 342 if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { 343 if (errno == EACCES && unlink(path) != -1) 344 goto open; 345 else 346 err(1, "%s", path); 347 } 348 349 if (rcs_buf_write_fd(b, fd) == -1) { 350 (void)unlink(path); 351 errx(1, "rcs_buf_write: rcs_buf_write_fd: `%s'", path); 352 } 353 354 if (fchmod(fd, mode) < 0) 355 warn("permissions not set on file %s", path); 356 357 (void)close(fd); 358 359 return (0); 360 } 361 362 /* 363 * rcs_buf_write_stmp() 364 * 365 * Write the contents of the buffer <b> to a temporary file whose path is 366 * specified using <template> (see mkstemp.3). NB. This function will modify 367 * <template>, as per mkstemp 368 */ 369 void 370 rcs_buf_write_stmp(BUF *b, char *template) 371 { 372 int fd; 373 374 if ((fd = mkstemp(template)) == -1) 375 err(1, "%s", template); 376 377 rcs_worklist_add(template, &rcs_temp_files); 378 379 if (rcs_buf_write_fd(b, fd) == -1) { 380 (void)unlink(template); 381 errx(1, "rcs_buf_write_stmp: rcs_buf_write_fd: `%s'", template); 382 } 383 384 (void)close(fd); 385 } 386 387 /* 388 * rcs_buf_grow() 389 * 390 * Grow the buffer <b> by <len> bytes. The contents are unchanged by this 391 * operation regardless of the result. 392 */ 393 static void 394 rcs_buf_grow(BUF *b, size_t len) 395 { 396 b->cb_buf = xrealloc(b->cb_buf, 1, b->cb_size + len); 397 b->cb_size += len; 398 } 399