xref: /openbsd-src/usr.bin/rsync/symlinks.c (revision 173dc8749401a03b492e3d8eaa9358ba3cce4e24)
1 /*	$OpenBSD: symlinks.c,v 1.7 2021/09/02 21:06:06 deraadt Exp $ */
2 /*
3  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <assert.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <limits.h>
22 #include <unistd.h>
23 
24 #include "extern.h"
25 
26 /*
27  * Allocate space for a readlink(2) invocation.
28  * Returns NULL on failure or a buffer otherwise.
29  * The buffer must be passed to free() by the caller.
30  */
31 char *
symlink_read(const char * path)32 symlink_read(const char *path)
33 {
34 	char	*buf = NULL;
35 	size_t	 sz;
36 	ssize_t	 nsz = 0;
37 	void	*pp;
38 
39 	for (sz = PATH_MAX; ; sz *= 2) {
40 		if ((pp = realloc(buf, sz + 1)) == NULL) {
41 			ERR("realloc");
42 			free(buf);
43 			return NULL;
44 		}
45 		buf = pp;
46 
47 		if ((nsz = readlink(path, buf, sz)) == -1) {
48 			ERR("%s: readlink", path);
49 			free(buf);
50 			return NULL;
51 		} else if (nsz == 0) {
52 			ERRX("%s: empty link", path);
53 			free(buf);
54 			return NULL;
55 		} else if ((size_t)nsz < sz)
56 			break;
57 	}
58 
59 	assert(buf != NULL);
60 	assert(nsz > 0);
61 	buf[nsz] = '\0';
62 	return buf;
63 }
64 
65 /*
66  * Allocate space for a readlinkat(2) invocation.
67  * Returns NULL on failure or a buffer otherwise.
68  * The buffer must be passed to free() by the caller.
69  */
70 char *
symlinkat_read(int fd,const char * path)71 symlinkat_read(int fd, const char *path)
72 {
73 	char	*buf = NULL;
74 	size_t	 sz;
75 	ssize_t	 nsz = 0;
76 	void	*pp;
77 
78 	for (sz = PATH_MAX; ; sz *= 2) {
79 		if ((pp = realloc(buf, sz + 1)) == NULL) {
80 			ERR("realloc");
81 			free(buf);
82 			return NULL;
83 		}
84 		buf = pp;
85 
86 		if ((nsz = readlinkat(fd, path, buf, sz)) == -1) {
87 			ERR("%s: readlinkat", path);
88 			free(buf);
89 			return NULL;
90 		} else if (nsz == 0) {
91 			ERRX("%s: empty link", path);
92 			free(buf);
93 			return NULL;
94 		} else if ((size_t)nsz < sz)
95 			break;
96 	}
97 
98 	assert(buf != NULL);
99 	assert(nsz > 0);
100 	buf[nsz] = '\0';
101 	return buf;
102 }
103