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