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