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