1*b7041c07Sderaadt /* $OpenBSD: buf.c,v 1.86 2021/10/24 21:24:16 deraadt Exp $ */
26c121f58Sjfb /*
3f3d6a35aSjfb * Copyright (c) 2003 Jean-Francois Brousseau <jfb@openbsd.org>
46c121f58Sjfb * All rights reserved.
56c121f58Sjfb *
66c121f58Sjfb * Redistribution and use in source and binary forms, with or without
76c121f58Sjfb * modification, are permitted provided that the following conditions
86c121f58Sjfb * are met:
96c121f58Sjfb *
106c121f58Sjfb * 1. Redistributions of source code must retain the above copyright
116c121f58Sjfb * notice, this list of conditions and the following disclaimer.
126c121f58Sjfb * 2. The name of the author may not be used to endorse or promote products
136c121f58Sjfb * derived from this software without specific prior written permission.
146c121f58Sjfb *
156c121f58Sjfb * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
166c121f58Sjfb * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
176c121f58Sjfb * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
186c121f58Sjfb * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
196c121f58Sjfb * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
206c121f58Sjfb * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
216c121f58Sjfb * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
226c121f58Sjfb * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
236c121f58Sjfb * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
246c121f58Sjfb * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
256c121f58Sjfb */
266c121f58Sjfb
271f8531bdSotto #include <sys/stat.h>
28bed6eeadStobias #include <sys/time.h>
291f8531bdSotto
301f8531bdSotto #include <errno.h>
311f8531bdSotto #include <fcntl.h>
321357284aSmillert #include <stdint.h>
33d06884b8Schl #include <stdlib.h>
341f8531bdSotto #include <string.h>
351f8531bdSotto #include <unistd.h>
366c121f58Sjfb
37bb59aeccStobias #include "atomicio.h"
383ad3fb45Sjoris #include "cvs.h"
396c121f58Sjfb #include "buf.h"
406c121f58Sjfb
416c121f58Sjfb #define BUF_INCR 128
426c121f58Sjfb
437bb3ddb0Sray struct buf {
44959ebdeaSnicm /* buffer handle, buffer size, and data length */
454f480b08Sjfb u_char *cb_buf;
466c121f58Sjfb size_t cb_size;
476c121f58Sjfb size_t cb_len;
486c121f58Sjfb };
496c121f58Sjfb
502b01f677Szinovik #define SIZE_LEFT(b) (b->cb_size - b->cb_len)
512b01f677Szinovik
522b01f677Szinovik static void buf_grow(BUF *, size_t);
532b01f677Szinovik
54959ebdeaSnicm /*
55959ebdeaSnicm * Create a new buffer structure and return a pointer to it. This structure
56959ebdeaSnicm * uses dynamically-allocated memory and must be freed with buf_free(), once
57959ebdeaSnicm * the buffer is no longer needed.
58959ebdeaSnicm */
596c121f58Sjfb BUF *
buf_alloc(size_t len)607bb3ddb0Sray buf_alloc(size_t len)
616c121f58Sjfb {
626c121f58Sjfb BUF *b;
636c121f58Sjfb
643b4c5c25Sray b = xmalloc(sizeof(*b));
657bb3ddb0Sray /* Postpone creation of zero-sized buffers */
66e07f18f3Sray if (len > 0)
67e07f18f3Sray b->cb_buf = xcalloc(1, len);
68e07f18f3Sray else
690ea6c03aSniallo b->cb_buf = NULL;
706c121f58Sjfb
716c121f58Sjfb b->cb_size = len;
726c121f58Sjfb b->cb_len = 0;
736c121f58Sjfb
746c121f58Sjfb return (b);
756c121f58Sjfb }
766c121f58Sjfb
77959ebdeaSnicm /*
78959ebdeaSnicm * Open the file specified by <path> and load all of its contents into a
79959ebdeaSnicm * buffer.
80959ebdeaSnicm * Returns the loaded buffer.
81959ebdeaSnicm */
826c121f58Sjfb BUF *
buf_load(const char * path)837bb3ddb0Sray buf_load(const char *path)
846c121f58Sjfb {
856c121f58Sjfb int fd;
86f2a4882aSjoris BUF *bp;
87f2a4882aSjoris
88*b7041c07Sderaadt if ((fd = open(path, O_RDONLY)) == -1)
897bb3ddb0Sray fatal("buf_load: failed to load '%s' : %s", path,
90f2a4882aSjoris strerror(errno));
91f2a4882aSjoris
927bb3ddb0Sray bp = buf_load_fd(fd);
93f2a4882aSjoris (void)close(fd);
94f2a4882aSjoris return (bp);
95f2a4882aSjoris }
96f2a4882aSjoris
97f2a4882aSjoris BUF *
buf_load_fd(int fd)987bb3ddb0Sray buf_load_fd(int fd)
99f2a4882aSjoris {
1006c121f58Sjfb struct stat st;
1016c121f58Sjfb BUF *buf;
1026c121f58Sjfb
103296ae42bSxsa if (fstat(fd, &st) == -1)
1047bb3ddb0Sray fatal("buf_load_fd: fstat: %s", strerror(errno));
105f2a4882aSjoris
106f2a4882aSjoris if (lseek(fd, 0, SEEK_SET) == -1)
1077bb3ddb0Sray fatal("buf_load_fd: lseek: %s", strerror(errno));
1086c121f58Sjfb
109ae886706Smillert if ((uintmax_t)st.st_size > SIZE_MAX)
1107bb3ddb0Sray fatal("buf_load_fd: file size too big");
1117bb3ddb0Sray buf = buf_alloc(st.st_size);
112bb59aeccStobias if (atomicio(read, fd, buf->cb_buf, buf->cb_size) != buf->cb_size)
1137bb3ddb0Sray fatal("buf_load_fd: read: %s", strerror(errno));
114bb59aeccStobias buf->cb_len = buf->cb_size;
1156c121f58Sjfb
1166c121f58Sjfb return (buf);
1176c121f58Sjfb }
1186c121f58Sjfb
1196c121f58Sjfb void
buf_free(BUF * b)1207bb3ddb0Sray buf_free(BUF *b)
1216c121f58Sjfb {
122397ddb8aSnicm if (b == NULL)
123397ddb8aSnicm return;
124397ddb8aSnicm free(b->cb_buf);
125397ddb8aSnicm free(b);
1266c121f58Sjfb }
1276c121f58Sjfb
128959ebdeaSnicm /*
129959ebdeaSnicm * Free the buffer <b>'s structural information but do not free the contents
130397ddb8aSnicm * of the buffer. Instead, they are returned and should be freed later.
131959ebdeaSnicm */
132bac54d96Szinovik void *
buf_release(BUF * b)1337bb3ddb0Sray buf_release(BUF *b)
1346c121f58Sjfb {
135bac54d96Szinovik void *tmp;
136a176bc3eStedu
1376c121f58Sjfb tmp = b->cb_buf;
138397ddb8aSnicm free(b);
1396c121f58Sjfb return (tmp);
1406c121f58Sjfb }
1416c121f58Sjfb
142959ebdeaSnicm /*
143959ebdeaSnicm * Append a single character <c> to the end of the buffer <b>.
144959ebdeaSnicm */
145f4da4256Sxsa void
buf_putc(BUF * b,int c)1467bb3ddb0Sray buf_putc(BUF *b, int c)
1476c121f58Sjfb {
1486c121f58Sjfb u_char *bp;
1496c121f58Sjfb
1504b4f7720Stobias if (SIZE_LEFT(b) == 0)
1512b01f677Szinovik buf_grow(b, BUF_INCR);
1529096025bSray bp = b->cb_buf + b->cb_len;
1536c121f58Sjfb *bp = (u_char)c;
1546c121f58Sjfb b->cb_len++;
1556c121f58Sjfb }
1566c121f58Sjfb
157959ebdeaSnicm /*
158959ebdeaSnicm * Append a C-string <str> to the end of the buffer <b>.
159959ebdeaSnicm */
160b034d592Sjoris void
buf_puts(BUF * b,const char * str)1617bb3ddb0Sray buf_puts(BUF *b, const char *str)
162b034d592Sjoris {
1637bb3ddb0Sray buf_append(b, str, strlen(str));
164b034d592Sjoris }
165b034d592Sjoris
166959ebdeaSnicm /*
167959ebdeaSnicm * Append <len> bytes of data pointed to by <data> to the buffer <b>. If the
168959ebdeaSnicm * buffer is too small to accept all data, it will get resized to an
169959ebdeaSnicm * appropriate size to accept all data.
170959ebdeaSnicm */
1719aad96bcStobias void
buf_append(BUF * b,const void * data,size_t len)1727bb3ddb0Sray buf_append(BUF *b, const void *data, size_t len)
1736c121f58Sjfb {
1749aad96bcStobias size_t left;
1752b01f677Szinovik u_char *bp;
1762b01f677Szinovik
1772b01f677Szinovik left = SIZE_LEFT(b);
1782b01f677Szinovik
1792b01f677Szinovik if (left < len)
1802b01f677Szinovik buf_grow(b, len - left);
1816c121f58Sjfb
1829096025bSray bp = b->cb_buf + b->cb_len;
1839aad96bcStobias memcpy(bp, data, len);
1849aad96bcStobias b->cb_len += len;
1856c121f58Sjfb }
1866c121f58Sjfb
187959ebdeaSnicm /*
188959ebdeaSnicm * Returns the size of the buffer that is being used.
189959ebdeaSnicm */
1906c121f58Sjfb size_t
buf_len(BUF * b)1917bb3ddb0Sray buf_len(BUF *b)
1926c121f58Sjfb {
1936c121f58Sjfb return (b->cb_len);
1946c121f58Sjfb }
1956c121f58Sjfb
196959ebdeaSnicm /*
197959ebdeaSnicm * Write the contents of the buffer <b> to the specified <fd>
198959ebdeaSnicm */
1996c121f58Sjfb int
buf_write_fd(BUF * b,int fd)2007bb3ddb0Sray buf_write_fd(BUF *b, int fd)
2016c121f58Sjfb {
202bb59aeccStobias if (atomicio(vwrite, fd, b->cb_buf, b->cb_len) != b->cb_len)
203aaffe046Sxsa return (-1);
2046c121f58Sjfb return (0);
2056c121f58Sjfb }
2066c121f58Sjfb
207959ebdeaSnicm /*
208959ebdeaSnicm * Write the contents of the buffer <b> to the file whose path is given in
209959ebdeaSnicm * <path>. If the file does not exist, it is created with mode <mode>.
210959ebdeaSnicm */
211946f6157Sdjm int
buf_write(BUF * b,const char * path,mode_t mode)2127bb3ddb0Sray buf_write(BUF *b, const char *path, mode_t mode)
213946f6157Sdjm {
214aaffe046Sxsa int fd;
21568361489Sniallo open:
21668361489Sniallo if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
217d593696fSderaadt if (errno == EACCES && unlink(path) != -1)
21868361489Sniallo goto open;
21968361489Sniallo else
220296ae42bSxsa fatal("open: `%s': %s", path, strerror(errno));
22168361489Sniallo }
222946f6157Sdjm
2237bb3ddb0Sray if (buf_write_fd(b, fd) == -1) {
224946f6157Sdjm (void)unlink(path);
2257bb3ddb0Sray fatal("buf_write: buf_write_fd: `%s'", path);
226946f6157Sdjm }
227946f6157Sdjm
2283aaa63ebSderaadt if (fchmod(fd, mode) == -1)
2293ad3fb45Sjoris cvs_log(LP_ERR, "permissions not set on file %s", path);
23062482c4aSniallo
23162482c4aSniallo (void)close(fd);
2320b196f7eSjoris
233aaffe046Sxsa return (0);
234946f6157Sdjm }
235946f6157Sdjm
236959ebdeaSnicm /*
237959ebdeaSnicm * Write the contents of the buffer <b> to a temporary file whose path is
238959ebdeaSnicm * specified using <template> (see mkstemp.3). If <tv> is specified file
239959ebdeaSnicm * access and modification time is set to <tv>.
240959ebdeaSnicm * NB. This function will modify <template>, as per mkstemp
241959ebdeaSnicm */
2422e0d696aSjoris int
buf_write_stmp(BUF * b,char * template,struct timeval * tv)2437bb3ddb0Sray buf_write_stmp(BUF *b, char *template, struct timeval *tv)
244946f6157Sdjm {
245aaffe046Sxsa int fd;
246946f6157Sdjm
247296ae42bSxsa if ((fd = mkstemp(template)) == -1)
248296ae42bSxsa fatal("mkstemp: `%s': %s", template, strerror(errno));
249946f6157Sdjm
2507bb3ddb0Sray if (buf_write_fd(b, fd) == -1) {
251946f6157Sdjm (void)unlink(template);
2527bb3ddb0Sray fatal("buf_write_stmp: buf_write_fd: `%s'", template);
253946f6157Sdjm }
2543a8d8a4eSjoris
2553ad3fb45Sjoris if (tv != NULL) {
2563ad3fb45Sjoris if (futimes(fd, tv) == -1)
2577bb3ddb0Sray fatal("buf_write_stmp: futimes failed");
2583ad3fb45Sjoris }
2593ad3fb45Sjoris
2607a9e6d11Sray worklist_add(template, &temp_files);
2612e0d696aSjoris
2623aaa63ebSderaadt if (lseek(fd, 0, SEEK_SET) == -1)
2637bb3ddb0Sray fatal("buf_write_stmp: lseek: %s", strerror(errno));
2642e0d696aSjoris
2652e0d696aSjoris return (fd);
266946f6157Sdjm }
2676c121f58Sjfb
268e0b06badSjoris u_char *
buf_get(BUF * bp)2697bb3ddb0Sray buf_get(BUF *bp)
2706c121f58Sjfb {
271e0b06badSjoris return (bp->cb_buf);
2727f75e64cSxsa }
2735f549559Sjoris
2745f549559Sjoris int
buf_differ(const BUF * b1,const BUF * b2)2757bb3ddb0Sray buf_differ(const BUF *b1, const BUF *b2)
2765f549559Sjoris {
277659703fcSray if (b1->cb_len != b2->cb_len)
2785f549559Sjoris return (1);
2795f549559Sjoris
280659703fcSray return (memcmp(b1->cb_buf, b2->cb_buf, b1->cb_len));
2815f549559Sjoris }
2822b01f677Szinovik
2832b01f677Szinovik /*
2842b01f677Szinovik * Grow the buffer <b> by <len> bytes. The contents are unchanged by this
2852b01f677Szinovik * operation regardless of the result.
2862b01f677Szinovik */
2872b01f677Szinovik static void
buf_grow(BUF * b,size_t len)2882b01f677Szinovik buf_grow(BUF *b, size_t len)
2892b01f677Szinovik {
290caa2ffb0Sderaadt b->cb_buf = xreallocarray(b->cb_buf, 1, b->cb_size + len);
2912b01f677Szinovik b->cb_size += len;
2922b01f677Szinovik }
293