xref: /openbsd-src/usr.bin/cvs/buf.c (revision b7041c0781c8668129da8084451ded41b0c43954)
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