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