Annotation of src/usr.bin/cvs/proto.c, Revision 1.2
1.1 jfb 1: /* $OpenBSD$ */
2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: *
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26: /*
27: * CVS client/server protocol
28: * ==========================
29: *
30: * The following code implements the CVS client/server protocol, which is
31: * documented at the following URL:
32: * http://www.loria.fr/~molli/cvs/doc/cvsclient_toc.html
33: *
34: * The protocol is split up into two parts; the first part is the client side
35: * of things and is composed of all the response handlers, which are all named
36: * with a prefix of "cvs_resp_". The second part is the set of request
37: * handlers used by the server. These handlers process the request and
38: * generate the appropriate response to send back. The prefix for request
39: * handlers is "cvs_req_".
40: *
41: */
42:
43: #include <sys/types.h>
44: #include <sys/stat.h>
45:
46: #include <fcntl.h>
47: #include <stdio.h>
48: #include <errno.h>
49: #include <stdlib.h>
50: #include <unistd.h>
51: #include <signal.h>
52: #include <string.h>
53: #include <sysexits.h>
54: #ifdef CVS_ZLIB
55: #include <zlib.h>
56: #endif
57:
58: #include "buf.h"
59: #include "cvs.h"
60: #include "log.h"
61:
62:
63:
64: extern int verbosity;
65: extern int cvs_compress;
66: extern char *cvs_rsh;
67: extern int cvs_trace;
68: extern int cvs_nolog;
69: extern int cvs_readonly;
70:
71: extern struct cvsroot *cvs_root;
72:
73:
74:
75: /*
76: * Local and remote directory used by the `Directory' request.
77: */
78: char cvs_ldir[MAXPATHLEN];
79: char cvs_rdir[MAXPATHLEN];
80:
81:
82:
83: char *cvs_fcksum = NULL;
84:
85: mode_t cvs_lastmode = 0;
86:
87:
88: static int cvs_resp_validreq (int, char *);
89: static int cvs_resp_cksum (int, char *);
90: static int cvs_resp_m (int, char *);
91: static int cvs_resp_ok (int, char *);
92: static int cvs_resp_error (int, char *);
93: static int cvs_resp_statdir (int, char *);
94: static int cvs_resp_newentry (int, char *);
95: static int cvs_resp_updated (int, char *);
96: static int cvs_resp_removed (int, char *);
97: static int cvs_resp_mode (int, char *);
98:
99:
100:
101:
102: struct cvs_req {
103: int req_id;
104: char req_str[32];
105: u_int req_flags;
106: int (*req_hdlr)(int, char *);
107: } cvs_requests[] = {
108: { CVS_REQ_DIRECTORY, "Directory", 0, NULL },
109: { CVS_REQ_MAXDOTDOT, "Max-dotdot", 0, NULL },
110: { CVS_REQ_STATICDIR, "Static-directory", 0, NULL },
111: { CVS_REQ_STICKY, "Sticky", 0, NULL },
112: { CVS_REQ_ENTRY, "Entry", 0, NULL },
113: { CVS_REQ_ENTRYEXTRA, "EntryExtra", 0, NULL },
114: { CVS_REQ_CHECKINTIME, "Checkin-time", 0, NULL },
115: { CVS_REQ_MODIFIED, "Modified", 0, NULL },
116: { CVS_REQ_ISMODIFIED, "Is-modified", 0, NULL },
117: { CVS_REQ_UNCHANGED, "Unchanged", 0, NULL },
118: { CVS_REQ_USEUNCHANGED, "UseUnchanged", 0, NULL },
119: { CVS_REQ_NOTIFY, "Notify", 0, NULL },
120: { CVS_REQ_NOTIFYUSER, "NotifyUser", 0, NULL },
121: { CVS_REQ_QUESTIONABLE, "Questionable", 0, NULL },
122: { CVS_REQ_CASE, "Case", 0, NULL },
123: { CVS_REQ_UTF8, "Utf8", 0, NULL },
124: { CVS_REQ_ARGUMENT, "Argument", 0, NULL },
125: { CVS_REQ_ARGUMENTX, "Argumentx", 0, NULL },
126: { CVS_REQ_GLOBALOPT, "Global_option", 0, NULL },
127: { CVS_REQ_GZIPSTREAM, "Gzip-stream", 0, NULL },
128: { CVS_REQ_READCVSRC2, "read-cvsrc2", 0, NULL },
129: { CVS_REQ_READWRAP, "read-cvswrappers", 0, NULL },
130: { CVS_REQ_READIGNORE, "read-cvsignore", 0, NULL },
131: { CVS_REQ_ERRIFREADER, "Error-If-Reader", 0, NULL },
132: { CVS_REQ_VALIDRCSOPT, "Valid-RcsOptions", 0, NULL },
133: { CVS_REQ_SET, "Set", 0, NULL },
134: { CVS_REQ_XPANDMOD, "expand-modules", 0, NULL },
135: { CVS_REQ_LOG, "log", 0, NULL },
136: { CVS_REQ_CO, "co", 0, NULL },
137: { CVS_REQ_EXPORT, "export", 0, NULL },
138: { CVS_REQ_RANNOTATE, "rannotate", 0, NULL },
139: { CVS_REQ_RDIFF, "rdiff", 0, NULL },
140: { CVS_REQ_RLOG, "rlog", 0, NULL },
141: { CVS_REQ_RTAG, "rtag", 0, NULL },
142: { CVS_REQ_INIT, "init", 0, NULL },
143: { CVS_REQ_UPDATE, "update", 0, NULL },
144: { CVS_REQ_HISTORY, "history", 0, NULL },
145: { CVS_REQ_IMPORT, "import", 0, NULL },
146: { CVS_REQ_ADD, "add", 0, NULL },
147: { CVS_REQ_REMOVE, "remove", 0, NULL },
148: { CVS_REQ_RELEASE, "release", 0, NULL },
149: { CVS_REQ_ROOT, "Root", 0, NULL },
150: { CVS_REQ_VALIDRESP, "Valid-responses", 0, NULL },
151: { CVS_REQ_VALIDREQ, "valid-requests", 0, NULL },
152: { CVS_REQ_VERSION, "version", 0, NULL },
153: { CVS_REQ_NOOP, "noop", 0, NULL },
154: { CVS_REQ_DIFF, "diff", 0, NULL },
155: };
156:
157:
158: struct cvs_resp {
159: u_int resp_id;
160: char resp_str[32];
161: int (*resp_hdlr)(int, char *);
162: } cvs_responses[] = {
163: { CVS_RESP_OK, "ok", cvs_resp_ok },
164: { CVS_RESP_ERROR, "error", cvs_resp_error },
165: { CVS_RESP_VALIDREQ, "Valid-requests", cvs_resp_validreq },
166: { CVS_RESP_M, "M", cvs_resp_m },
167: { CVS_RESP_MBINARY, "Mbinary", cvs_resp_m },
168: { CVS_RESP_MT, "MT", cvs_resp_m },
169: { CVS_RESP_E, "E", cvs_resp_m },
170: { CVS_RESP_F, "F", cvs_resp_m },
171: { CVS_RESP_CREATED, "Created", cvs_resp_updated },
172: { CVS_RESP_UPDATED, "Updated", cvs_resp_updated },
173: { CVS_RESP_UPDEXIST, "Update-existing", cvs_resp_updated },
174: { CVS_RESP_REMOVED, "Removed", cvs_resp_removed },
175: { CVS_RESP_MERGED, "Merged", NULL },
176: { CVS_RESP_CKSUM, "Checksum", cvs_resp_cksum },
177: { CVS_RESP_CLRSTATDIR, "Clear-static-directory", cvs_resp_statdir },
178: { CVS_RESP_SETSTATDIR, "Set-static-directory", cvs_resp_statdir },
179: { CVS_RESP_NEWENTRY, "New-entry", cvs_resp_newentry },
180: { CVS_RESP_CHECKEDIN, "Checked-in", cvs_resp_newentry },
181: { CVS_RESP_MODE, "Mode", cvs_resp_mode },
182: };
183:
184:
185: #define CVS_NBREQ (sizeof(cvs_requests)/sizeof(cvs_requests[0]))
186: #define CVS_NBRESP (sizeof(cvs_responses)/sizeof(cvs_responses[0]))
187:
188: /* mask of requets supported by server */
189: static u_char cvs_server_validreq[CVS_REQ_MAX + 1];
190:
191:
192: /*
193: * cvs_req_getbyid()
194: *
195: */
196:
197: const char*
198: cvs_req_getbyid(int reqid)
199: {
200: u_int i;
201:
202: for (i = 0; i < CVS_NBREQ; i++)
203: if (cvs_requests[i].req_id == reqid)
204: return (cvs_requests[i].req_str);
205: return (NULL);
206: }
207:
208:
209: /*
210: * cvs_req_getbyname()
211: */
212:
213: int
214: cvs_req_getbyname(const char *rname)
215: {
216: u_int i;
217:
218: for (i = 0; i < CVS_NBREQ; i++)
219: if (strcmp(cvs_requests[i].req_str, rname) == 0)
220: return (cvs_requests[i].req_id);
221:
222: return (-1);
223: }
224:
225:
226: /*
227: * cvs_req_getvalid()
228: *
229: * Build a space-separated list of all the requests that this protocol
230: * implementation supports.
231: */
232:
233: char*
234: cvs_req_getvalid(void)
235: {
236: u_int i;
237: size_t len;
238: char *vrstr;
239: BUF *buf;
240:
241: buf = cvs_buf_alloc(512, BUF_AUTOEXT);
242: if (buf == NULL)
243: return (NULL);
244:
245: cvs_buf_set(buf, cvs_requests[0].req_str,
246: strlen(cvs_requests[0].req_str), 0);
247:
248: for (i = 1; i < CVS_NBREQ; i++) {
249: if ((cvs_buf_putc(buf, ' ') < 0) ||
250: (cvs_buf_append(buf, cvs_requests[i].req_str,
251: strlen(cvs_requests[i].req_str)) < 0)) {
252: cvs_buf_free(buf);
253: return (NULL);
254: }
255: }
256:
257: /* NUL-terminate */
258: if (cvs_buf_putc(buf, '\0') < 0) {
259: cvs_buf_free(buf);
260: return (NULL);
261: }
262:
263: len = cvs_buf_size(buf);
264: vrstr = (char *)malloc(len);
265:
266: cvs_buf_copy(buf, 0, vrstr, len);
267:
268: cvs_buf_free(buf);
269:
270: return (vrstr);
271: }
272:
273:
274: /*
275: * cvs_req_handle()
276: *
277: * Generic request handler dispatcher.
278: */
279:
280: int
281: cvs_req_handle(char *line)
282: {
283: return (0);
284: }
285:
286:
287: /*
288: * cvs_resp_getbyid()
289: *
290: */
291:
292: const char*
293: cvs_resp_getbyid(int respid)
294: {
295: u_int i;
296:
297: for (i = 0; i < CVS_NBREQ; i++)
298: if (cvs_responses[i].resp_id == respid)
299: return (cvs_responses[i].resp_str);
300: return (NULL);
301: }
302:
303:
304: /*
305: * cvs_resp_getbyname()
306: */
307:
308: int
309: cvs_resp_getbyname(const char *rname)
310: {
311: u_int i;
312:
313: for (i = 0; i < CVS_NBREQ; i++)
314: if (strcmp(cvs_responses[i].resp_str, rname) == 0)
315: return (cvs_responses[i].resp_id);
316:
317: return (-1);
318: }
319:
320:
321: /*
322: * cvs_resp_getvalid()
323: *
324: * Build a space-separated list of all the responses that this protocol
325: * implementation supports.
326: */
327:
328: char*
329: cvs_resp_getvalid(void)
330: {
331: u_int i;
332: size_t len;
333: char *vrstr;
334: BUF *buf;
335:
336: buf = cvs_buf_alloc(512, BUF_AUTOEXT);
337: if (buf == NULL)
338: return (NULL);
339:
340: cvs_buf_set(buf, cvs_responses[0].resp_str,
341: strlen(cvs_responses[0].resp_str), 0);
342:
343: for (i = 1; i < CVS_NBRESP; i++) {
344: if ((cvs_buf_putc(buf, ' ') < 0) ||
345: (cvs_buf_append(buf, cvs_responses[i].resp_str,
346: strlen(cvs_responses[i].resp_str)) < 0)) {
347: cvs_buf_free(buf);
348: return (NULL);
349: }
350: }
351:
352: /* NUL-terminate */
353: if (cvs_buf_putc(buf, '\0') < 0) {
354: cvs_buf_free(buf);
355: return (NULL);
356: }
357:
358: len = cvs_buf_size(buf);
359: vrstr = (char *)malloc(len);
360:
361: cvs_buf_copy(buf, 0, vrstr, len);
362: cvs_buf_free(buf);
363:
364: return (vrstr);
365: }
366:
367:
368: /*
369: * cvs_resp_handle()
370: *
371: * Generic response handler dispatcher. The handler expects the first line
372: * of the command as single argument.
373: * Returns the return value of the command on success, or -1 on failure.
374: */
375:
376: int
377: cvs_resp_handle(char *line)
378: {
379: u_int i;
380: size_t len;
381: char *cp, *cmd;
382:
383: cmd = line;
384:
385: cp = strchr(cmd, ' ');
386: if (cp != NULL)
387: *(cp++) = '\0';
388:
389: for (i = 0; i < CVS_NBRESP; i++) {
390: if (strcmp(cvs_responses[i].resp_str, cmd) == 0)
391: return (*cvs_responses[i].resp_hdlr)
392: (cvs_responses[i].resp_id, cp);
393: }
394:
395: /* unhandled */
396: return (-1);
397: }
398:
399:
400: /*
401: * cvs_client_sendinfo()
402: *
403: * Initialize the connection status by first requesting the list of
404: * supported requests from the server. Then, we send the CVSROOT variable
405: * with the `Root' request.
406: * Returns 0 on success, or -1 on failure.
407: */
408:
409: static int
410: cvs_client_sendinfo(void)
411: {
412: /* first things first, get list of valid requests from server */
413: if (cvs_client_sendreq(CVS_REQ_VALIDREQ, NULL, 1) < 0) {
414: cvs_log(LP_ERR, "failed to get valid requests from server");
415: return (-1);
416: }
417:
418: /* now share our global options with the server */
419: if (verbosity == 1)
420: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-q", 0);
421: else if (verbosity == 0)
422: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-Q", 0);
423:
424: if (cvs_nolog)
425: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-l", 0);
426: if (cvs_readonly)
427: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-r", 0);
428: if (cvs_trace)
429: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-t", 0);
430:
431: /* now send the CVSROOT to the server */
432: if (cvs_client_sendreq(CVS_REQ_ROOT, cvs_root->cr_dir, 0) < 0)
433: return (-1);
434:
435: /* not sure why, but we have to send this */
436: if (cvs_client_sendreq(CVS_REQ_USEUNCHANGED, NULL, 0) < 0)
437: return (-1);
438:
439: return (0);
440: }
441:
442:
443: /*
444: * cvs_resp_validreq()
445: *
446: * Handler for the `Valid-requests' response. The list of valid requests is
447: * split on spaces and each request's entry in the valid request array is set
448: * to 1 to indicate the validity.
449: * Returns 0 on success, or -1 on failure.
450: */
451:
452: static int
453: cvs_resp_validreq(int type, char *line)
454: {
455: int i;
456: char *sp, *ep;
457:
458: /* parse the requests */
459: sp = line;
460: do {
461: ep = strchr(sp, ' ');
462: if (ep != NULL)
463: *ep = '\0';
464:
465: i = cvs_req_getbyname(sp);
466: if (i != -1)
467: cvs_server_validreq[i] = 1;
468:
469: if (ep != NULL)
470: sp = ep + 1;
471: } while (ep != NULL);
472:
473: return (0);
474: }
475:
476:
477: /*
478: * cvs_resp_m()
479: *
480: * Handler for the `M', `F' and `E' responses.
481: */
482:
483: static int
484: cvs_resp_m(int type, char *line)
485: {
486: FILE *stream;
487:
488: stream = NULL;
489:
490: switch (type) {
491: case CVS_RESP_F:
492: fflush(stderr);
493: return (0);
494: case CVS_RESP_M:
495: stream = stdout;
496: break;
497: case CVS_RESP_E:
498: stream = stderr;
499: break;
500: case CVS_RESP_MT:
501: case CVS_RESP_MBINARY:
502: cvs_log(LP_WARN, "MT and Mbinary not supported in client yet");
503: break;
504: }
505:
506: fputs(line, stream);
507: fputc('\n', stream);
508:
509: return (0);
510: }
511:
512:
513: /*
514: * cvs_resp_ok()
515: *
516: * Handler for the `ok' response. This handler's job is to
517: */
518:
519: static int
520: cvs_resp_ok(int type, char *line)
521: {
522: return (1);
523: }
524:
525:
526: /*
527: * cvs_resp_error()
528: *
529: * Handler for the `error' response. This handler's job is to
530: */
531:
532: static int
533: cvs_resp_error(int type, char *line)
534: {
535: return (1);
536: }
537:
538:
539: /*
540: * cvs_resp_statdir()
541: *
542: * Handler for the `Clear-static-directory' and `Set-static-directory'
543: * responses.
544: */
545:
546: static int
547: cvs_resp_statdir(int type, char *line)
548: {
549: int fd;
550: char statpath[MAXPATHLEN];
551:
552: snprintf(statpath, sizeof(statpath), "%s/%s", line,
553: CVS_PATH_STATICENTRIES);
554:
555: if ((type == CVS_RESP_CLRSTATDIR) &&
556: (unlink(statpath) == -1)) {
557: cvs_log(LP_ERRNO, "failed to unlink %s file",
558: CVS_PATH_STATICENTRIES);
559: return (-1);
560: }
561: else if (type == CVS_RESP_SETSTATDIR) {
562: fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0400);
563: if (fd == -1) {
564: cvs_log(LP_ERRNO, "failed to create %s file",
565: CVS_PATH_STATICENTRIES);
566: return (-1);
567: }
568: (void)close(fd);
569: }
570:
571: return (0);
572: }
573:
574:
575: /*
576: * cvs_resp_newentry()
577: *
578: * Handler for the `New-entry' response and `Checked-in' responses.
579: */
580:
581: static int
582: cvs_resp_newentry(int type, char *line)
583: {
584: char entbuf[128], path[MAXPATHLEN];
585: struct cvs_ent *entp;
586: CVSENTRIES *entfile;
587:
1.2 ! jfb 588: snprintf(path, sizeof(path), "%s/" CVS_PATH_ENTRIES, line);
1.1 jfb 589:
590: /* get the remote path */
591: cvs_client_getln(entbuf, sizeof(entbuf));
592:
593: /* get the new Entries line */
594: if (cvs_client_getln(entbuf, sizeof(entbuf)) < 0)
595: return (-1);
596:
597: entp = cvs_ent_parse(entbuf);
598: if (entp == NULL)
599: return (-1);
600:
1.2 ! jfb 601: entfile = cvs_ent_open(path, O_WRONLY);
1.1 jfb 602: if (entfile == NULL)
603: return (-1);
604:
605: cvs_ent_add(entfile, entp);
606:
607: cvs_ent_close(entfile);
608:
609: return (0);
610: }
611:
612:
613: /*
614: * cvs_resp_cksum()
615: *
616: * Handler for the `Checksum' response. We store the checksum received for
617: * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>.
618: * Upon next file reception, the handler checks to see if there is a stored
619: * checksum.
620: * The file handler must make sure that the checksums match and free the
621: * checksum buffer once it's done to indicate there is no further checksum.
622: */
623:
624: static int
625: cvs_resp_cksum(int type, char *line)
626: {
627: if (cvs_fcksum != NULL) {
628: cvs_log(LP_WARN, "unused checksum");
629: free(cvs_fcksum);
630: }
631:
632: cvs_fcksum = strdup(line);
633: if (cvs_fcksum == NULL) {
634: cvs_log(LP_ERRNO, "failed to copy checksum string");
635: return (-1);
636: }
637:
638: return (0);
639: }
640:
641:
642: /*
643: * cvs_resp_updated()
644: *
645: * Handler for the `Updated' response.
646: */
647:
648: static int
649: cvs_resp_updated(int type, char *line)
650: {
651: char cksum_buf[CVS_CKSUM_LEN];
652:
653: if (type == CVS_RESP_CREATED) {
654: }
655: else if (type == CVS_RESP_UPDEXIST) {
656: }
657: else if (type == CVS_RESP_UPDATED) {
658: }
659:
660: if (cvs_recvfile(line) < 0) {
661: return (-1);
662: }
663:
664: /* now see if there is a checksum */
665: if (cvs_cksum(line, cksum_buf, sizeof(cksum_buf)) < 0) {
666: }
667:
668: if (strcmp(cksum_buf, cvs_fcksum) != 0) {
669: cvs_log(LP_ERR, "checksum error on received file");
670: (void)unlink(line);
671: }
672:
673: free(cvs_fcksum);
674: cvs_fcksum = NULL;
675:
676: return (0);
677: }
678:
679:
680: /*
681: * cvs_resp_removed()
682: *
683: * Handler for the `Updated' response.
684: */
685:
686: static int
687: cvs_resp_removed(int type, char *line)
688: {
689: return (0);
690: }
691:
692:
693: /*
694: * cvs_resp_mode()
695: *
696: * Handler for the `Mode' response.
697: */
698:
699: static int
700: cvs_resp_mode(int type, char *line)
701: {
702: if (cvs_strtomode(line, &cvs_lastmode) < 0) {
703: return (-1);
704: }
705: return (0);
706: }
707:
708:
709: /*
710: * cvs_sendfile()
711: *
712: * Send the mode and size of a file followed by the file's contents.
713: * Returns 0 on success, or -1 on failure.
714: */
715:
716: int
717: cvs_sendfile(const char *path)
718: {
719: int fd;
720: ssize_t ret;
721: char buf[4096];
722: struct stat st;
723:
724: if (stat(path, &st) == -1) {
725: cvs_log(LP_ERRNO, "failed to stat `%s'", path);
726: return (-1);
727: }
728:
729: fd = open(path, O_RDONLY, 0);
730: if (fd == -1) {
731: return (-1);
732: }
733:
734: if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
735: return (-1);
736:
737: cvs_client_sendln(buf);
738: snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
739: cvs_client_sendln(buf);
740:
741: while ((ret = read(fd, buf, sizeof(buf))) != 0) {
742: if (ret == -1) {
743: cvs_log(LP_ERRNO, "failed to read file `%s'", path);
744: return (-1);
745: }
746:
747: cvs_client_sendraw(buf, (size_t)ret);
748:
749: }
750:
751: (void)close(fd);
752:
753: return (0);
754: }
755:
756:
757: /*
758: * cvs_recvfile()
759: *
760: * Receive the mode and size of a file followed the file's contents and
761: * create or update the file whose path is <path> with the received
762: * information.
763: */
764:
765: int
766: cvs_recvfile(const char *path)
767: {
768: int fd;
769: mode_t mode;
770: size_t len;
771: ssize_t ret;
772: off_t fsz, cnt;
773: char buf[4096], *ep;
774:
775: if ((cvs_client_getln(buf, sizeof(buf)) < 0) ||
776: (cvs_strtomode(buf, &mode) < 0)) {
777: return (-1);
778: }
779:
780: cvs_client_getln(buf, sizeof(buf));
781:
782: fsz = (off_t)strtol(buf, &ep, 10);
783: if (*ep != '\0') {
784: cvs_log(LP_ERR, "parse error in file size transmission");
785: return (-1);
786: }
787:
788: fd = open(path, O_RDONLY, mode);
789: if (fd == -1) {
790: cvs_log(LP_ERRNO, "failed to open `%s'", path);
791: return (-1);
792: }
793:
794: cnt = 0;
795: do {
796: len = MIN(sizeof(buf), (size_t)(fsz - cnt));
797: ret = cvs_client_recvraw(buf, len);
798: if (ret == -1) {
799: (void)close(fd);
800: (void)unlink(path);
801: return (-1);
802: }
803:
804: if (write(fd, buf, (size_t)ret) == -1) {
805: cvs_log(LP_ERRNO,
806: "failed to write contents to file `%s'", path);
807: (void)close(fd);
808: (void)unlink(path);
809: return (-1);
810: }
811:
812: cnt += (off_t)ret;
813: } while (cnt < fsz);
814:
815: (void)close(fd);
816:
817: return (0);
818: }