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