xref: /openbsd-src/usr.bin/cvs/remote.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: remote.c,v 1.33 2019/06/28 13:35:00 deraadt Exp $	*/
2 /*
3  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
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 <sys/param.h>	/* MAXBSIZE */
19 #include <sys/stat.h>
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "atomicio.h"
28 #include "cvs.h"
29 #include "remote.h"
30 
31 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
32 
33 struct cvs_resp *
34 cvs_remote_get_response_info(const char *response)
35 {
36 	int i;
37 
38 	for (i = 0; cvs_responses[i].supported != -1; i++) {
39 		if (!strcmp(cvs_responses[i].name, response))
40 			return (&(cvs_responses[i]));
41 	}
42 
43 	return (NULL);
44 }
45 
46 struct cvs_req *
47 cvs_remote_get_request_info(const char *request)
48 {
49 	int i;
50 
51 	for (i = 0; cvs_requests[i].supported != -1; i++) {
52 		if (!strcmp(cvs_requests[i].name, request))
53 			return (&(cvs_requests[i]));
54 	}
55 
56 	return (NULL);
57 }
58 
59 void
60 cvs_remote_output(char *data)
61 {
62 	FILE *out;
63 	size_t len;
64 	char nl = '\n';
65 
66 	if (cvs_server_active)
67 		out = stdout;
68 	else
69 		out = current_cvsroot->cr_srvin;
70 
71 	fputs(data, out);
72 	fputs("\n", out);
73 
74 	if (cvs_server_active == 0 && cvs_client_inlog_fd != -1) {
75 		len = strlen(data);
76 		if (atomicio(vwrite, cvs_client_inlog_fd, data, len) != len ||
77 		    atomicio(vwrite, cvs_client_inlog_fd, &nl, 1) != 1)
78 			fatal("failed to write to log file");
79 	}
80 }
81 
82 char *
83 cvs_remote_input(void)
84 {
85 	FILE *in;
86 	size_t len;
87 	char nl = '\n';
88 	char *data, *ldata;
89 
90 	if (cvs_server_active)
91 		in = stdin;
92 	else
93 		in = current_cvsroot->cr_srvout;
94 
95 	data = fgetln(in, &len);
96 	if (data == NULL) {
97 		if (sig_received != 0)
98 			fatal("received signal %d", sig_received);
99 
100 		if (cvs_server_active) {
101 			cvs_cleanup();
102 			exit(0);
103 		}
104 
105 		fatal("the connection has been closed by the server");
106 	}
107 
108 	if (data[len - 1] == '\n') {
109 		data[len - 1] = '\0';
110 		ldata = xstrdup(data);
111 	} else {
112 		ldata = xmalloc(len + 1);
113 		memcpy(ldata, data, len);
114 		ldata[len] = '\0';
115 	}
116 
117 	if (cvs_server_active == 0 && cvs_client_outlog_fd != -1) {
118 		len = strlen(data);
119 		if (atomicio(vwrite, cvs_client_outlog_fd, data, len) != len ||
120 		    atomicio(vwrite, cvs_client_outlog_fd, &nl, 1) != 1)
121 			fatal("failed to write to log file");
122 	}
123 
124 	return (ldata);
125 }
126 
127 void
128 cvs_remote_receive_file(int fd, size_t len)
129 {
130 	FILE *in;
131 	char data[MAXBSIZE];
132 	size_t nread, nleft, toread;
133 
134 	if (cvs_server_active)
135 		in = stdin;
136 	else
137 		in = current_cvsroot->cr_srvout;
138 
139 	nleft = len;
140 
141 	while (nleft > 0) {
142 		toread = MINIMUM(nleft, MAXBSIZE);
143 
144 		nread = fread(data, sizeof(char), toread, in);
145 		if (nread == 0)
146 			fatal("error receiving file");
147 
148 		if (atomicio(vwrite, fd, data, nread) != nread)
149 			fatal("failed to write %zu bytes", nread);
150 
151 		if (cvs_server_active == 0 && cvs_client_outlog_fd != -1 &&
152 		    atomicio(vwrite, cvs_client_outlog_fd, data, nread)
153 		    != nread)
154 			fatal("failed to write to log file");
155 
156 		nleft -= nread;
157 	}
158 }
159 
160 void
161 cvs_remote_send_file(const char *path, int _fd)
162 {
163 	int fd;
164 	FILE *out, *in;
165 	size_t ret, rw;
166 	off_t total;
167 	struct stat st;
168 	char buf[18], data[MAXBSIZE];
169 
170 	if (cvs_server_active)
171 		out = stdout;
172 	else
173 		out = current_cvsroot->cr_srvin;
174 
175 	fd = dup(_fd);
176 	if (fd == -1)
177 		fatal("cvs_remote_send_file: dup: %s", strerror(errno));
178 
179 	if (lseek(fd, 0, SEEK_SET) == -1)
180 		fatal("cvs_remote_send_file: %s: lseek: %s", path,
181 		    strerror(errno));
182 
183 	if (fstat(fd, &st) == -1)
184 		fatal("cvs_remote_send_file: %s: fstat: %s", path,
185 		    strerror(errno));
186 
187 	cvs_modetostr(st.st_mode, buf, sizeof(buf));
188 	cvs_remote_output(buf);
189 
190 	(void)xsnprintf(buf, sizeof(buf), "%lld", st.st_size);
191 	cvs_remote_output(buf);
192 
193 	if ((in = fdopen(fd, "r")) == NULL)
194 		fatal("cvs_remote_send_file: fdopen %s", strerror(errno));
195 
196 	total = 0;
197 	while ((ret = fread(data, sizeof(char), MAXBSIZE, in)) != 0) {
198 		rw = fwrite(data, sizeof(char), ret, out);
199 		if (rw != ret)
200 			fatal("failed to write %zu bytes", ret);
201 
202 		if (cvs_server_active == 0 && cvs_client_inlog_fd != -1 &&
203 		    atomicio(vwrite, cvs_client_inlog_fd, data, ret) != ret)
204 			fatal("failed to write to log file");
205 
206 		total += ret;
207 	}
208 
209 	if (total != st.st_size)
210 		fatal("length mismatch, %lld vs %lld", total, st.st_size);
211 
212 	(void)fclose(in);
213 }
214 
215 void
216 cvs_remote_send_file_buf(char *file, BUF *bp, mode_t mode)
217 {
218 	char buf[18];
219 	u_char *data;
220 	size_t len, ret;
221 
222 	if (cvs_server_active != 1)
223 		fatal("cvs_remote_send_file_buf is server only");
224 
225 	len = buf_len(bp);
226 	data = buf_release(bp);
227 
228 	cvs_modetostr(mode, buf, sizeof(buf));
229 	cvs_remote_output(buf);
230 
231 	(void)xsnprintf(buf, sizeof(buf), "%ld", len);
232 	cvs_remote_output(buf);
233 
234 	ret = fwrite(data, sizeof(char), len, stdout);
235 	if (ret != len)
236 		cvs_log(LP_ERR, "warning: sent %s truncated", file);
237 
238 	if (cvs_server_active == 0 && cvs_client_inlog_fd != -1 &&
239 	    atomicio(vwrite, cvs_client_inlog_fd, data, len) != len)
240 		fatal("failed to write to log file");
241 
242 	free(data);
243 }
244 
245 void
246 cvs_remote_classify_file(struct cvs_file *cf)
247 {
248 	struct stat st;
249 	CVSENTRIES *entlist;
250 
251 	entlist = cvs_ent_open(cf->file_wd);
252 	cf->file_ent = cvs_ent_get(entlist, cf->file_name);
253 
254 	if (cf->file_ent != NULL && cf->file_ent->ce_status != CVS_ENT_REG) {
255 		if (cf->file_ent->ce_status == CVS_ENT_ADDED) {
256 			if (cf->fd != -1)
257 				cf->file_status = FILE_ADDED;
258 			else
259 				cf->file_status = FILE_UNKNOWN;
260 		} else {
261 			cf->file_status = FILE_REMOVED;
262 		}
263 
264 		return;
265 	}
266 
267 	if (cf->file_ent != NULL) {
268 		if (cf->file_ent->ce_type == CVS_ENT_DIR)
269 			cf->file_type = CVS_DIR;
270 		else
271 			cf->file_type = CVS_FILE;
272 	}
273 
274 	if (cf->fd != -1)
275 		cf->file_flags |= FILE_ON_DISK;
276 
277 	if ((cf->file_flags & FILE_ON_DISK) && cf->file_ent != NULL) {
278 		if (fstat(cf->fd, &st) == -1)
279 			fatal("cvs_remote_classify_file(%s): %s", cf->file_path,
280 			    strerror(errno));
281 
282 		if (st.st_mtime != cf->file_ent->ce_mtime ||
283 		    cf->file_ent->ce_conflict != NULL)
284 			cf->file_status = FILE_MODIFIED;
285 		else
286 			cf->file_status = FILE_UPTODATE;
287 	} else if (!(cf->file_flags & FILE_ON_DISK)) {
288 		cf->file_status = FILE_UNKNOWN;
289 	}
290 
291 	if (cvs_cmdop == CVS_OP_IMPORT && cf->file_type == CVS_FILE)
292 		cf->file_status = FILE_MODIFIED;
293 }
294 
295 
296 void
297 cvs_validate_directory(const char *path)
298 {
299 	char *dir, *sp, *dp;
300 
301 	dir = xstrdup(path);
302 
303 	for (sp = dir; sp != NULL; sp = dp) {
304 		dp = strchr(sp, '/');
305 		if (dp != NULL)
306 			*(dp++) = '\0';
307 
308 		if (!strcmp(sp, ".."))
309 			fatal("path validation failed!");
310 	}
311 
312 	free(dir);
313 }
314