1 /* $OpenBSD: buf.c,v 1.72 2008/06/10 01:00:34 joris 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 cvs_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 cvs_buf_grow(BUF *, size_t); 52 53 /* 54 * cvs_buf_alloc() 55 * 56 * Create a new buffer structure and return a pointer to it. This structure 57 * uses dynamically-allocated memory and must be freed with cvs_buf_free(), 58 * once the buffer is no longer needed. 59 */ 60 BUF * 61 cvs_buf_alloc(size_t len) 62 { 63 BUF *b; 64 65 b = xmalloc(sizeof(*b)); 66 /* Postpone creation of zero-sized buffers */ 67 if (len > 0) 68 b->cb_buf = xcalloc(1, len); 69 else 70 b->cb_buf = NULL; 71 72 b->cb_size = len; 73 b->cb_len = 0; 74 75 return (b); 76 } 77 78 BUF * 79 cvs_buf_load(const char *path) 80 { 81 int fd; 82 BUF *bp; 83 84 if ((fd = open(path, O_RDONLY, 0600)) == -1) 85 fatal("cvs_buf_load: failed to load '%s' : %s", path, 86 strerror(errno)); 87 88 bp = cvs_buf_load_fd(fd); 89 (void)close(fd); 90 return (bp); 91 } 92 93 BUF * 94 cvs_buf_load_fd(int fd) 95 { 96 struct stat st; 97 BUF *buf; 98 99 if (fstat(fd, &st) == -1) 100 fatal("cvs_buf_load_fd: fstat: %s", strerror(errno)); 101 102 if (lseek(fd, 0, SEEK_SET) == -1) 103 fatal("cvs_buf_load_fd: lseek: %s", strerror(errno)); 104 105 if (st.st_size > SIZE_MAX) 106 fatal("cvs_buf_load_fd: file size too big"); 107 buf = cvs_buf_alloc(st.st_size); 108 if (atomicio(read, fd, buf->cb_buf, buf->cb_size) != buf->cb_size) 109 fatal("cvs_buf_load_fd: read: %s", strerror(errno)); 110 buf->cb_len = buf->cb_size; 111 112 return (buf); 113 } 114 115 /* 116 * cvs_buf_free() 117 * 118 * Free the buffer <b> and all associated data. 119 */ 120 void 121 cvs_buf_free(BUF *b) 122 { 123 if (b->cb_buf != NULL) 124 xfree(b->cb_buf); 125 xfree(b); 126 } 127 128 /* 129 * cvs_buf_release() 130 * 131 * Free the buffer <b>'s structural information but do not free the contents 132 * of the buffer. Instead, they are returned and should be freed later using 133 * free(). 134 */ 135 u_char * 136 cvs_buf_release(BUF *b) 137 { 138 u_char *tmp; 139 140 tmp = b->cb_buf; 141 xfree(b); 142 return (tmp); 143 } 144 145 /* 146 * cvs_buf_empty() 147 * 148 * Empty the contents of the buffer <b> and reset pointers. 149 */ 150 void 151 cvs_buf_empty(BUF *b) 152 { 153 memset(b->cb_buf, 0, b->cb_size); 154 b->cb_len = 0; 155 } 156 157 /* 158 * cvs_buf_putc() 159 * 160 * Append a single character <c> to the end of the buffer <b>. 161 */ 162 void 163 cvs_buf_putc(BUF *b, int c) 164 { 165 u_char *bp; 166 167 bp = b->cb_buf + b->cb_len; 168 if (bp == (b->cb_buf + b->cb_size)) { 169 /* extend */ 170 cvs_buf_grow(b, (size_t)BUF_INCR); 171 172 /* the buffer might have been moved */ 173 bp = b->cb_buf + b->cb_len; 174 } 175 *bp = (u_char)c; 176 b->cb_len++; 177 } 178 179 /* 180 * cvs_buf_puts() 181 * 182 * Wrapper function for constant strings to cvs_buf_append. 183 */ 184 void 185 cvs_buf_puts(BUF *b, const char *str) 186 { 187 cvs_buf_append(b, str, strlen(str)); 188 } 189 190 /* 191 * cvs_buf_getc() 192 * 193 * Return u_char at buffer position <pos>. 194 * 195 */ 196 u_char 197 cvs_buf_getc(BUF *b, size_t pos) 198 { 199 return (b->cb_buf[pos]); 200 } 201 202 /* 203 * cvs_buf_append() 204 * 205 * Append <len> bytes of data pointed to by <data> to the buffer <b>. If the 206 * buffer is too small to accept all data, it will attempt to append as much 207 * data as possible, or if the BUF_AUTOEXT flag is set for the buffer, it 208 * will get resized to an appropriate size to accept all data. 209 * Returns the number of bytes successfully appended to the buffer. 210 */ 211 void 212 cvs_buf_append(BUF *b, const void *data, size_t len) 213 { 214 size_t left; 215 u_char *bp, *bep; 216 217 bp = b->cb_buf + b->cb_len; 218 bep = b->cb_buf + b->cb_size; 219 left = bep - bp; 220 221 if (left < len) { 222 cvs_buf_grow(b, len - left); 223 bp = b->cb_buf + b->cb_len; 224 } 225 226 memcpy(bp, data, len); 227 b->cb_len += len; 228 } 229 230 /* 231 * cvs_buf_len() 232 * 233 * Returns the size of the buffer that is being used. 234 */ 235 size_t 236 cvs_buf_len(BUF *b) 237 { 238 return (b->cb_len); 239 } 240 241 /* 242 * cvs_buf_write_fd() 243 * 244 * Write the contents of the buffer <b> to the specified <fd> 245 */ 246 int 247 cvs_buf_write_fd(BUF *b, int fd) 248 { 249 if (atomicio(vwrite, fd, b->cb_buf, b->cb_len) != b->cb_len) 250 return (-1); 251 return (0); 252 } 253 254 /* 255 * cvs_buf_write() 256 * 257 * Write the contents of the buffer <b> to the file whose path is given in 258 * <path>. If the file does not exist, it is created with mode <mode>. 259 */ 260 int 261 cvs_buf_write(BUF *b, const char *path, mode_t mode) 262 { 263 int fd; 264 open: 265 if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { 266 if (errno == EACCES && unlink(path) != -1) 267 goto open; 268 else 269 fatal("open: `%s': %s", path, strerror(errno)); 270 } 271 272 if (cvs_buf_write_fd(b, fd) == -1) { 273 (void)unlink(path); 274 fatal("cvs_buf_write: cvs_buf_write_fd: `%s'", path); 275 } 276 277 if (fchmod(fd, mode) < 0) 278 cvs_log(LP_ERR, "permissions not set on file %s", path); 279 280 (void)close(fd); 281 282 return (0); 283 } 284 285 /* 286 * cvs_buf_write_stmp() 287 * 288 * Write the contents of the buffer <b> to a temporary file whose path is 289 * specified using <template> (see mkstemp.3). NB. This function will modify 290 * <template>, as per mkstemp 291 */ 292 int 293 cvs_buf_write_stmp(BUF *b, char *template, struct timeval *tv) 294 { 295 int fd; 296 297 if ((fd = mkstemp(template)) == -1) 298 fatal("mkstemp: `%s': %s", template, strerror(errno)); 299 300 if (cvs_buf_write_fd(b, fd) == -1) { 301 (void)unlink(template); 302 fatal("cvs_buf_write_stmp: cvs_buf_write_fd: `%s'", template); 303 } 304 305 if (tv != NULL) { 306 if (futimes(fd, tv) == -1) 307 fatal("cvs_buf_write_stmp: futimes failed"); 308 } 309 310 cvs_worklist_add(template, &temp_files); 311 312 if (lseek(fd, 0, SEEK_SET) < 0) 313 fatal("cvs_buf_write_stmp: lseek: %s", strerror(errno)); 314 315 return (fd); 316 } 317 318 /* 319 * cvs_buf_grow() 320 * 321 * Grow the buffer <b> by <len> bytes. The contents are unchanged by this 322 * operation regardless of the result. 323 */ 324 static void 325 cvs_buf_grow(BUF *b, size_t len) 326 { 327 b->cb_buf = xrealloc(b->cb_buf, 1, b->cb_size + len); 328 b->cb_size += len; 329 } 330 331 /* 332 * cvs_buf_copy() 333 * 334 * Copy the first <len> bytes of data in the buffer <b> starting at offset 335 * <off> in the destination buffer <dst>, which can accept up to <len> bytes. 336 * Returns the number of bytes successfully copied, or -1 on failure. 337 */ 338 ssize_t 339 cvs_buf_copy(BUF *b, size_t off, void *dst, size_t len) 340 { 341 size_t rc; 342 343 if (off > b->cb_len) 344 fatal("cvs_buf_copy failed"); 345 346 rc = MIN(len, (b->cb_len - off)); 347 memcpy(dst, b->cb_buf + off, rc); 348 349 return (ssize_t)rc; 350 } 351 352 /* 353 * cvs_buf_peek() 354 * 355 * Peek at the contents of the buffer <b> at offset <off>. 356 */ 357 const u_char * 358 cvs_buf_peek(BUF *b, size_t off) 359 { 360 if (off >= b->cb_len) 361 return (NULL); 362 363 return (b->cb_buf + off); 364 } 365 366 int 367 cvs_buf_differ(const BUF *b1, const BUF *b2) 368 { 369 if (b1->cb_len != b2->cb_len) 370 return (1); 371 372 return (memcmp(b1->cb_buf, b2->cb_buf, b1->cb_len)); 373 } 374