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 * 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 * 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 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 * 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 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 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 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 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 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