1 /* $OpenBSD: buf.c,v 1.86 2021/10/24 21:24:16 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/stat.h> 28 #include <sys/time.h> 29 30 #include <errno.h> 31 #include <fcntl.h> 32 #include <stdint.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 37 #include "atomicio.h" 38 #include "cvs.h" 39 #include "buf.h" 40 41 #define BUF_INCR 128 42 43 struct buf { 44 /* buffer handle, buffer size, and data length */ 45 u_char *cb_buf; 46 size_t cb_size; 47 size_t cb_len; 48 }; 49 50 #define SIZE_LEFT(b) (b->cb_size - b->cb_len) 51 52 static void buf_grow(BUF *, size_t); 53 54 /* 55 * Create a new buffer structure and return a pointer to it. This structure 56 * uses dynamically-allocated memory and must be freed with buf_free(), once 57 * the buffer is no longer needed. 58 */ 59 BUF * 60 buf_alloc(size_t len) 61 { 62 BUF *b; 63 64 b = xmalloc(sizeof(*b)); 65 /* Postpone creation of zero-sized buffers */ 66 if (len > 0) 67 b->cb_buf = xcalloc(1, len); 68 else 69 b->cb_buf = NULL; 70 71 b->cb_size = len; 72 b->cb_len = 0; 73 74 return (b); 75 } 76 77 /* 78 * Open the file specified by <path> and load all of its contents into a 79 * buffer. 80 * Returns the loaded buffer. 81 */ 82 BUF * 83 buf_load(const char *path) 84 { 85 int fd; 86 BUF *bp; 87 88 if ((fd = open(path, O_RDONLY)) == -1) 89 fatal("buf_load: failed to load '%s' : %s", path, 90 strerror(errno)); 91 92 bp = buf_load_fd(fd); 93 (void)close(fd); 94 return (bp); 95 } 96 97 BUF * 98 buf_load_fd(int fd) 99 { 100 struct stat st; 101 BUF *buf; 102 103 if (fstat(fd, &st) == -1) 104 fatal("buf_load_fd: fstat: %s", strerror(errno)); 105 106 if (lseek(fd, 0, SEEK_SET) == -1) 107 fatal("buf_load_fd: lseek: %s", strerror(errno)); 108 109 if ((uintmax_t)st.st_size > SIZE_MAX) 110 fatal("buf_load_fd: file size too big"); 111 buf = buf_alloc(st.st_size); 112 if (atomicio(read, fd, buf->cb_buf, buf->cb_size) != buf->cb_size) 113 fatal("buf_load_fd: read: %s", strerror(errno)); 114 buf->cb_len = buf->cb_size; 115 116 return (buf); 117 } 118 119 void 120 buf_free(BUF *b) 121 { 122 if (b == NULL) 123 return; 124 free(b->cb_buf); 125 free(b); 126 } 127 128 /* 129 * Free the buffer <b>'s structural information but do not free the contents 130 * of the buffer. Instead, they are returned and should be freed later. 131 */ 132 void * 133 buf_release(BUF *b) 134 { 135 void *tmp; 136 137 tmp = b->cb_buf; 138 free(b); 139 return (tmp); 140 } 141 142 /* 143 * Append a single character <c> to the end of the buffer <b>. 144 */ 145 void 146 buf_putc(BUF *b, int c) 147 { 148 u_char *bp; 149 150 if (SIZE_LEFT(b) == 0) 151 buf_grow(b, BUF_INCR); 152 bp = b->cb_buf + b->cb_len; 153 *bp = (u_char)c; 154 b->cb_len++; 155 } 156 157 /* 158 * Append a C-string <str> to the end of the buffer <b>. 159 */ 160 void 161 buf_puts(BUF *b, const char *str) 162 { 163 buf_append(b, str, strlen(str)); 164 } 165 166 /* 167 * Append <len> bytes of data pointed to by <data> to the buffer <b>. If the 168 * buffer is too small to accept all data, it will get resized to an 169 * appropriate size to accept all data. 170 */ 171 void 172 buf_append(BUF *b, const void *data, size_t len) 173 { 174 size_t left; 175 u_char *bp; 176 177 left = SIZE_LEFT(b); 178 179 if (left < len) 180 buf_grow(b, len - left); 181 182 bp = b->cb_buf + b->cb_len; 183 memcpy(bp, data, len); 184 b->cb_len += len; 185 } 186 187 /* 188 * Returns the size of the buffer that is being used. 189 */ 190 size_t 191 buf_len(BUF *b) 192 { 193 return (b->cb_len); 194 } 195 196 /* 197 * Write the contents of the buffer <b> to the specified <fd> 198 */ 199 int 200 buf_write_fd(BUF *b, int fd) 201 { 202 if (atomicio(vwrite, fd, b->cb_buf, b->cb_len) != b->cb_len) 203 return (-1); 204 return (0); 205 } 206 207 /* 208 * Write the contents of the buffer <b> to the file whose path is given in 209 * <path>. If the file does not exist, it is created with mode <mode>. 210 */ 211 int 212 buf_write(BUF *b, const char *path, mode_t mode) 213 { 214 int fd; 215 open: 216 if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { 217 if (errno == EACCES && unlink(path) != -1) 218 goto open; 219 else 220 fatal("open: `%s': %s", path, strerror(errno)); 221 } 222 223 if (buf_write_fd(b, fd) == -1) { 224 (void)unlink(path); 225 fatal("buf_write: buf_write_fd: `%s'", path); 226 } 227 228 if (fchmod(fd, mode) == -1) 229 cvs_log(LP_ERR, "permissions not set on file %s", path); 230 231 (void)close(fd); 232 233 return (0); 234 } 235 236 /* 237 * Write the contents of the buffer <b> to a temporary file whose path is 238 * specified using <template> (see mkstemp.3). If <tv> is specified file 239 * access and modification time is set to <tv>. 240 * NB. This function will modify <template>, as per mkstemp 241 */ 242 int 243 buf_write_stmp(BUF *b, char *template, struct timeval *tv) 244 { 245 int fd; 246 247 if ((fd = mkstemp(template)) == -1) 248 fatal("mkstemp: `%s': %s", template, strerror(errno)); 249 250 if (buf_write_fd(b, fd) == -1) { 251 (void)unlink(template); 252 fatal("buf_write_stmp: buf_write_fd: `%s'", template); 253 } 254 255 if (tv != NULL) { 256 if (futimes(fd, tv) == -1) 257 fatal("buf_write_stmp: futimes failed"); 258 } 259 260 worklist_add(template, &temp_files); 261 262 if (lseek(fd, 0, SEEK_SET) == -1) 263 fatal("buf_write_stmp: lseek: %s", strerror(errno)); 264 265 return (fd); 266 } 267 268 u_char * 269 buf_get(BUF *bp) 270 { 271 return (bp->cb_buf); 272 } 273 274 int 275 buf_differ(const BUF *b1, const BUF *b2) 276 { 277 if (b1->cb_len != b2->cb_len) 278 return (1); 279 280 return (memcmp(b1->cb_buf, b2->cb_buf, b1->cb_len)); 281 } 282 283 /* 284 * Grow the buffer <b> by <len> bytes. The contents are unchanged by this 285 * operation regardless of the result. 286 */ 287 static void 288 buf_grow(BUF *b, size_t len) 289 { 290 b->cb_buf = xreallocarray(b->cb_buf, 1, b->cb_size + len); 291 b->cb_size += len; 292 } 293