1 /* $OpenBSD: buf.c,v 1.29 2021/10/24 21:24:17 deraadt 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 <stdint.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "buf.h" 40 #include "xmalloc.h" 41 #include "worklist.h" 42 43 #define BUF_INCR 128 44 45 struct buf { 46 /* buffer handle, buffer size, and data length */ 47 u_char *cb_buf; 48 size_t cb_size; 49 size_t cb_len; 50 }; 51 52 #define SIZE_LEFT(b) (b->cb_size - b->cb_len) 53 54 static void buf_grow(BUF *, size_t); 55 56 /* 57 * Create a new buffer structure and return a pointer to it. This structure 58 * uses dynamically-allocated memory and must be freed with buf_free(), once 59 * the buffer is no longer needed. 60 */ 61 BUF * 62 buf_alloc(size_t len) 63 { 64 BUF *b; 65 66 b = xmalloc(sizeof(*b)); 67 /* Postpone creation of zero-sized buffers */ 68 if (len > 0) 69 b->cb_buf = xcalloc(1, len); 70 else 71 b->cb_buf = NULL; 72 73 b->cb_size = len; 74 b->cb_len = 0; 75 76 return (b); 77 } 78 79 /* 80 * Open the file specified by <path> and load all of its contents into a 81 * buffer. 82 * Returns the loaded buffer on success or NULL on failure. 83 * Sets errno on error. 84 */ 85 BUF * 86 buf_load(const char *path) 87 { 88 int fd; 89 ssize_t ret; 90 size_t len; 91 u_char *bp; 92 struct stat st; 93 BUF *buf; 94 95 buf = NULL; 96 97 if ((fd = open(path, O_RDONLY)) == -1) 98 goto out; 99 100 if (fstat(fd, &st) == -1) 101 goto out; 102 103 if ((uintmax_t)st.st_size > SIZE_MAX) { 104 errno = EFBIG; 105 goto out; 106 } 107 buf = buf_alloc(st.st_size); 108 for (bp = buf->cb_buf; ; bp += (size_t)ret) { 109 len = SIZE_LEFT(buf); 110 ret = read(fd, bp, len); 111 if (ret == -1) { 112 int saved_errno; 113 114 saved_errno = errno; 115 buf_free(buf); 116 buf = NULL; 117 errno = saved_errno; 118 goto out; 119 } else if (ret == 0) 120 break; 121 122 buf->cb_len += (size_t)ret; 123 } 124 125 out: 126 if (fd != -1) { 127 int saved_errno; 128 129 /* We may want to preserve errno here. */ 130 saved_errno = errno; 131 (void)close(fd); 132 errno = saved_errno; 133 } 134 135 return (buf); 136 } 137 138 void 139 buf_free(BUF *b) 140 { 141 if (b == NULL) 142 return; 143 free(b->cb_buf); 144 free(b); 145 } 146 147 /* 148 * Free the buffer <b>'s structural information but do not free the contents 149 * of the buffer. Instead, they are returned and should be freed later using 150 * free(). 151 */ 152 void * 153 buf_release(BUF *b) 154 { 155 void *tmp; 156 157 tmp = b->cb_buf; 158 free(b); 159 return (tmp); 160 } 161 162 u_char * 163 buf_get(BUF *b) 164 { 165 return (b->cb_buf); 166 } 167 168 /* 169 * Empty the contents of the buffer <b> and reset pointers. 170 */ 171 void 172 buf_empty(BUF *b) 173 { 174 memset(b->cb_buf, 0, b->cb_size); 175 b->cb_len = 0; 176 } 177 178 /* 179 * Append a single character <c> to the end of the buffer <b>. 180 */ 181 void 182 buf_putc(BUF *b, int c) 183 { 184 u_char *bp; 185 186 if (SIZE_LEFT(b) == 0) 187 buf_grow(b, BUF_INCR); 188 bp = b->cb_buf + b->cb_len; 189 *bp = (u_char)c; 190 b->cb_len++; 191 } 192 193 /* 194 * Append a string <s> to the end of buffer <b>. 195 */ 196 void 197 buf_puts(BUF *b, const char *str) 198 { 199 buf_append(b, str, strlen(str)); 200 } 201 202 /* 203 * Return u_char at buffer position <pos>. 204 */ 205 u_char 206 buf_getc(BUF *b, size_t pos) 207 { 208 return (b->cb_buf[pos]); 209 } 210 211 /* 212 * Append <len> bytes of data pointed to by <data> to the buffer <b>. If the 213 * buffer is too small to accept all data, it will get resized to an 214 * appropriate size to accept all data. 215 * Returns the number of bytes successfully appended to the buffer. 216 */ 217 size_t 218 buf_append(BUF *b, const void *data, size_t len) 219 { 220 size_t left, rlen; 221 u_char *bp; 222 223 left = SIZE_LEFT(b); 224 rlen = len; 225 226 if (left < len) 227 buf_grow(b, len - left); 228 bp = b->cb_buf + b->cb_len; 229 memcpy(bp, data, rlen); 230 b->cb_len += rlen; 231 232 return (rlen); 233 } 234 235 /* 236 * Returns the size of the buffer that is being used. 237 */ 238 size_t 239 buf_len(BUF *b) 240 { 241 return (b->cb_len); 242 } 243 244 /* 245 * Write the contents of the buffer <b> to the specified <fd> 246 */ 247 int 248 buf_write_fd(BUF *b, int fd) 249 { 250 u_char *bp; 251 size_t len; 252 ssize_t ret; 253 254 len = b->cb_len; 255 bp = b->cb_buf; 256 257 do { 258 ret = write(fd, bp, len); 259 if (ret == -1) { 260 if (errno == EINTR || errno == EAGAIN) 261 continue; 262 return (-1); 263 } 264 265 len -= (size_t)ret; 266 bp += (size_t)ret; 267 } while (len > 0); 268 269 return (0); 270 } 271 272 /* 273 * Write the contents of the buffer <b> to the file whose path is given in 274 * <path>. If the file does not exist, it is created with mode <mode>. 275 */ 276 int 277 buf_write(BUF *b, const char *path, mode_t mode) 278 { 279 int fd; 280 open: 281 if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { 282 if (errno == EACCES && unlink(path) != -1) 283 goto open; 284 else 285 err(1, "%s", path); 286 } 287 288 if (buf_write_fd(b, fd) == -1) { 289 (void)unlink(path); 290 errx(1, "buf_write: buf_write_fd: `%s'", path); 291 } 292 293 if (fchmod(fd, mode) == -1) 294 warn("permissions not set on file %s", path); 295 296 (void)close(fd); 297 298 return (0); 299 } 300 301 /* 302 * Write the contents of the buffer <b> to a temporary file whose path is 303 * specified using <template> (see mkstemp.3). 304 * NB. This function will modify <template>, as per mkstemp 305 */ 306 void 307 buf_write_stmp(BUF *b, char *template) 308 { 309 int fd; 310 311 if ((fd = mkstemp(template)) == -1) 312 err(1, "%s", template); 313 314 worklist_add(template, &temp_files); 315 316 if (buf_write_fd(b, fd) == -1) { 317 (void)unlink(template); 318 errx(1, "buf_write_stmp: buf_write_fd: `%s'", template); 319 } 320 321 (void)close(fd); 322 } 323 324 /* 325 * Grow the buffer <b> by <len> bytes. The contents are unchanged by this 326 * operation regardless of the result. 327 */ 328 static void 329 buf_grow(BUF *b, size_t len) 330 { 331 b->cb_buf = xreallocarray(b->cb_buf, 1, b->cb_size + len); 332 b->cb_size += len; 333 } 334