Annotation of src/usr.bin/rsync/sender.c, Revision 1.16
1.16 ! benno 1: /* $Id: sender.c,v 1.15 2019/02/18 21:55:27 benno Exp $ */
1.1 benno 2: /*
3: * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
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: */
1.8 florian 17: #include <sys/mman.h>
18: #include <sys/queue.h>
1.1 benno 19: #include <sys/stat.h>
20:
21: #include <assert.h>
1.8 florian 22: #include <fcntl.h>
1.1 benno 23: #include <inttypes.h>
1.8 florian 24: #include <poll.h>
1.1 benno 25: #include <stdlib.h>
26: #include <string.h>
27: #include <unistd.h>
28:
1.9 florian 29: #include <openssl/md4.h>
30:
1.1 benno 31: #include "extern.h"
32:
33: /*
1.8 florian 34: * A request from the receiver to download updated file data.
35: */
36: struct send_dl {
1.15 benno 37: int32_t idx; /* index in our file list */
1.8 florian 38: struct blkset *blks; /* the sender's block information */
39: TAILQ_ENTRY(send_dl) entries;
40: };
41:
42: /*
43: * The current file being "updated": sent from sender to receiver.
44: * If there is no file being uploaded, "cur" is NULL.
45: */
46: struct send_up {
47: struct send_dl *cur; /* file being updated or NULL */
48: struct blkstat stat; /* status of file being updated */
49: };
50:
51: TAILQ_HEAD(send_dlq, send_dl);
52:
53: /*
54: * We have finished updating the receiver's file with sender data.
55: * Deallocate and wipe clean all resources required for that.
56: */
57: static void
58: send_up_reset(struct send_up *p)
59: {
60:
1.13 deraadt 61: assert(p != NULL);
1.8 florian 62:
63: /* Free the download request, if applicable. */
64:
65: if (p->cur != NULL) {
66: free(p->cur->blks);
67: free(p->cur);
68: p->cur = NULL;
69: }
70:
71: /* If we mapped a file for scanning, unmap it and close. */
72:
73: if (p->stat.map != MAP_FAILED)
74: munmap(p->stat.map, p->stat.mapsz);
75:
76: p->stat.map = MAP_FAILED;
77: p->stat.mapsz = 0;
78:
79: if (p->stat.fd != -1)
80: close(p->stat.fd);
81:
82: p->stat.fd = -1;
83:
84: /* Now clear the in-transfer information. */
85:
86: p->stat.offs = 0;
87: p->stat.hint = 0;
1.9 florian 88: p->stat.curst = BLKSTAT_NONE;
1.16 ! benno 89: }
! 90:
! 91: /*
! 92: * This is the bulk of the sender work.
! 93: * Here we tend to an output buffer that responds to receiver requests
! 94: * for data.
! 95: * This does not act upon the output descriptor itself so as to avoid
! 96: * blocking, which otherwise would deadlock the protocol.
! 97: * Returns zero on failure, non-zero on success.
! 98: */
! 99: static int
! 100: send_up_fsm(struct sess *sess, size_t *phase,
! 101: struct send_up *up, void **wb, size_t *wbsz, size_t *wbmax,
! 102: const struct flist *fl)
! 103: {
! 104: size_t pos = 0, isz = sizeof(int32_t),
! 105: dsz = MD4_DIGEST_LENGTH;
! 106: unsigned char fmd[MD4_DIGEST_LENGTH];
! 107: off_t sz;
! 108: char buf[20];
! 109:
! 110: switch (up->stat.curst) {
! 111: case BLKSTAT_DATA:
! 112: /*
! 113: * A data segment to be written: buffer both the length
! 114: * and the data.
! 115: * If we've finished the transfer, move on to the token;
! 116: * otherwise, keep sending data.
! 117: */
! 118:
! 119: sz = MINIMUM(MAX_CHUNK,
! 120: up->stat.curlen - up->stat.curpos);
! 121: if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, isz)) {
! 122: ERRX1(sess, "io_lowbuffer_alloc");
! 123: return 0;
! 124: }
! 125: io_lowbuffer_int(sess, *wb, &pos, *wbsz, sz);
! 126: if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, sz)) {
! 127: ERRX1(sess, "io_lowbuffer_alloc");
! 128: return 0;
! 129: }
! 130: io_lowbuffer_buf(sess, *wb, &pos, *wbsz,
! 131: up->stat.map + up->stat.curpos, sz);
! 132:
! 133: up->stat.curpos += sz;
! 134: if (up->stat.curpos == up->stat.curlen)
! 135: up->stat.curst = BLKSTAT_TOK;
! 136: return 1;
! 137: case BLKSTAT_TOK:
! 138: /*
! 139: * The data token following (maybe) a data segment.
! 140: * These can also come standalone if, say, the file's
! 141: * being fully written.
! 142: * It's followed by a hash or another data segment,
! 143: * depending on the token.
! 144: */
! 145:
! 146: if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, isz)) {
! 147: ERRX1(sess, "io_lowbuffer_alloc");
! 148: return 0;
! 149: }
! 150: io_lowbuffer_int(sess, *wb,
! 151: &pos, *wbsz, up->stat.curtok);
! 152: up->stat.curst = up->stat.curtok ?
! 153: BLKSTAT_NEXT : BLKSTAT_HASH;
! 154: return 1;
! 155: case BLKSTAT_HASH:
! 156: /*
! 157: * The hash following transmission of all file contents.
! 158: * This is always followed by the state that we're
! 159: * finished with the file.
! 160: */
! 161:
! 162: hash_file(up->stat.map, up->stat.mapsz, fmd, sess);
! 163: if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, dsz)) {
! 164: ERRX1(sess, "io_lowbuffer_alloc");
! 165: return 0;
! 166: }
! 167: io_lowbuffer_buf(sess, *wb, &pos, *wbsz, fmd, dsz);
! 168: up->stat.curst = BLKSTAT_DONE;
! 169: return 1;
! 170: case BLKSTAT_DONE:
! 171: /*
! 172: * The data has been written.
! 173: * Clear our current send file and allow the block below
! 174: * to find another.
! 175: */
! 176:
! 177: LOG3(sess, "%s: flushed %jd KB total, %.2f%% uploaded",
! 178: fl[up->cur->idx].path,
! 179: (intmax_t)up->stat.total / 1024,
! 180: 100.0 * up->stat.dirty / up->stat.total);
! 181: send_up_reset(up);
! 182: return 1;
! 183: case BLKSTAT_PHASE:
! 184: /*
! 185: * This is where we actually stop the algorithm: we're
! 186: * already at the second phase.
! 187: */
! 188:
! 189: send_up_reset(up);
! 190: (*phase)++;
! 191: return 1;
! 192: case BLKSTAT_NEXT:
! 193: /*
! 194: * Our last case: we need to find the
! 195: * next block (and token) to transmit to
! 196: * the receiver.
! 197: * These will drive the finite state
! 198: * machine in the first few conditional
! 199: * blocks of this set.
! 200: */
! 201:
! 202: assert(up->stat.fd != -1);
! 203: blk_match(sess, up->cur->blks,
! 204: fl[up->cur->idx].path, &up->stat);
! 205: return 1;
! 206: case BLKSTAT_NONE:
! 207: break;
! 208: }
! 209:
! 210: assert(BLKSTAT_NONE == up->stat.curst);
! 211:
! 212: /*
! 213: * We've either hit the phase change following the last file (or
! 214: * start, or prior phase change), or we need to prime the next
! 215: * file for transmission.
! 216: * We special-case dry-run mode.
! 217: */
! 218:
! 219: if (up->cur->idx < 0) {
! 220: if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, isz)) {
! 221: ERRX1(sess, "io_lowbuffer_alloc");
! 222: return 0;
! 223: }
! 224: io_lowbuffer_int(sess, *wb, &pos, *wbsz, -1);
! 225:
! 226: if (sess->opts->server && sess->rver > 27) {
! 227: if (!io_lowbuffer_alloc(sess,
! 228: wb, wbsz, wbmax, isz)) {
! 229: ERRX1(sess, "io_lowbuffer_alloc");
! 230: return 0;
! 231: }
! 232: io_lowbuffer_int(sess, *wb, &pos, *wbsz, -1);
! 233: }
! 234: up->stat.curst = BLKSTAT_PHASE;
! 235: } else if (sess->opts->dry_run) {
! 236: if (!sess->opts->server)
! 237: LOG1(sess, "%s", fl[up->cur->idx].wpath);
! 238:
! 239: if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, isz)) {
! 240: ERRX1(sess, "io_lowbuffer_alloc");
! 241: return 0;
! 242: }
! 243: io_lowbuffer_int(sess, *wb, &pos, *wbsz, up->cur->idx);
! 244: up->stat.curst = BLKSTAT_NEXT;
! 245: } else {
! 246: assert(up->stat.fd != -1);
! 247:
! 248: /*
! 249: * FIXME: use the nice output of log_file() and so on in
! 250: * downloader.c, which means moving this into
! 251: * BLKSTAT_DONE instead of having it be here.
! 252: */
! 253:
! 254: if (!sess->opts->server)
! 255: LOG1(sess, "%s", fl[up->cur->idx].wpath);
! 256:
! 257: if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, 20)) {
! 258: ERRX1(sess, "io_lowbuffer_alloc");
! 259: return 0;
! 260: }
! 261: assert(sizeof(buf) == 20);
! 262: blk_recv_ack(sess, buf, up->cur->blks, up->cur->idx);
! 263: io_lowbuffer_buf(sess, *wb, &pos, *wbsz, buf, 20);
! 264:
! 265: LOG3(sess, "%s: primed for %jd B total",
! 266: fl[up->cur->idx].path,
! 267: (intmax_t)up->cur->blks->size);
! 268: up->stat.curst = BLKSTAT_NEXT;
! 269: }
! 270:
! 271: return 1;
1.8 florian 272: }
273:
274: /*
275: * Enqueue a download request, getting it off the read channel as
276: * quickly a possible.
277: * This frees up the read channel for further incoming requests.
278: * We'll handle each element in turn, up to and including the last
279: * request (phase change), which is always a -1 idx.
280: * Returns zero on failure, non-zero on success.
281: */
282: static int
283: send_dl_enqueue(struct sess *sess, struct send_dlq *q,
284: int32_t idx, const struct flist *fl, size_t flsz, int fd)
285: {
286: struct send_dl *s;
1.15 benno 287:
1.8 florian 288: /* End-of-phase marker. */
289:
290: if (idx == -1) {
291: if ((s = calloc(1, sizeof(struct send_dl))) == NULL) {
292: ERR(sess, "calloc");
293: return 0;
294: }
295: s->idx = -1;
296: s->blks = NULL;
297: TAILQ_INSERT_TAIL(q, s, entries);
298: return 1;
299: }
300:
301: /* Validate the index. */
1.15 benno 302:
1.8 florian 303: if (idx < 0 || (uint32_t)idx >= flsz) {
1.10 florian 304: ERRX(sess, "file index out of bounds: invalid %"
1.8 florian 305: PRId32 " out of %zu", idx, flsz);
306: return 0;
307: } else if (S_ISDIR(fl[idx].st.mode)) {
308: ERRX(sess, "blocks requested for "
309: "directory: %s", fl[idx].path);
310: return 0;
311: } else if (S_ISLNK(fl[idx].st.mode)) {
312: ERRX(sess, "blocks requested for "
313: "symlink: %s", fl[idx].path);
314: return 0;
315: } else if (!S_ISREG(fl[idx].st.mode)) {
316: ERRX(sess, "blocks requested for "
317: "special: %s", fl[idx].path);
318: return 0;
319: }
320:
321: if ((s = calloc(1, sizeof(struct send_dl))) == NULL) {
322: ERR(sess, "callloc");
323: return 0;
324: }
325: s->idx = idx;
326: s->blks = NULL;
327: TAILQ_INSERT_TAIL(q, s, entries);
328:
1.10 florian 329: /*
1.8 florian 330: * This blocks til the full blockset has been read.
331: * That's ok, because the most important thing is getting data
332: * off the wire.
333: */
334:
335: if (!sess->opts->dry_run) {
336: s->blks = blk_recv(sess, fd, fl[idx].path);
337: if (s->blks == NULL) {
338: ERRX1(sess, "blk_recv");
339: return 0;
340: }
341: }
342: return 1;
343: }
344:
345: /*
1.1 benno 346: * A client sender manages the read-only source files and sends data to
347: * the receiver as requested.
348: * First it sends its list of files, then it waits for the server to
349: * request updates to individual files.
1.8 florian 350: * It queues requests for updates as soon as it receives them.
1.1 benno 351: * Returns zero on failure, non-zero on success.
352: *
353: * Pledges: stdio, rpath, unveil.
354: */
355: int
356: rsync_sender(struct sess *sess, int fdin,
357: int fdout, size_t argc, char **argv)
358: {
1.9 florian 359: struct flist *fl = NULL;
360: const struct flist *f;
361: size_t i, flsz = 0, phase = 0, excl;
362: int rc = 0, c;
363: int32_t idx;
364: struct pollfd pfd[3];
365: struct send_dlq sdlq;
366: struct send_dl *dl;
367: struct send_up up;
368: struct stat st;
369: void *wbuf = NULL;
1.16 ! benno 370: size_t wbufpos = 0, wbufsz = 0, wbufmax = 0;
1.9 florian 371: ssize_t ssz;
1.1 benno 372:
1.6 benno 373: if (pledge("stdio getpw rpath unveil", NULL) == -1) {
1.1 benno 374: ERR(sess, "pledge");
375: return 0;
376: }
377:
1.8 florian 378: memset(&up, 0, sizeof(struct send_up));
379: TAILQ_INIT(&sdlq);
380: up.stat.fd = -1;
381: up.stat.map = MAP_FAILED;
382:
1.1 benno 383: /*
384: * Generate the list of files we want to send from our
385: * command-line input.
386: * This will also remove all invalid files.
387: */
388:
1.4 deraadt 389: if (!flist_gen(sess, argc, argv, &fl, &flsz)) {
1.1 benno 390: ERRX1(sess, "flist_gen");
391: goto out;
392: }
393:
394: /* Client sends zero-length exclusions if deleting. */
395:
1.4 deraadt 396: if (!sess->opts->server && sess->opts->del &&
1.16 ! benno 397: !io_write_int(sess, fdout, 0)) {
1.1 benno 398: ERRX1(sess, "io_write_int");
399: goto out;
1.2 benno 400: }
1.1 benno 401:
1.2 benno 402: /*
1.1 benno 403: * Then the file list in any mode.
404: * Finally, the IO error (always zero for us).
405: */
1.2 benno 406:
1.4 deraadt 407: if (!flist_send(sess, fdin, fdout, fl, flsz)) {
1.1 benno 408: ERRX1(sess, "flist_send");
409: goto out;
1.4 deraadt 410: } else if (!io_write_int(sess, fdout, 0)) {
1.1 benno 411: ERRX1(sess, "io_write_int");
412: goto out;
1.2 benno 413: }
1.1 benno 414:
415: /* Exit if we're the server with zero files. */
416:
1.5 deraadt 417: if (flsz == 0 && sess->opts->server) {
1.1 benno 418: WARNX(sess, "sender has empty file list: exiting");
419: rc = 1;
420: goto out;
1.4 deraadt 421: } else if (!sess->opts->server)
1.1 benno 422: LOG1(sess, "Transfer starting: %zu files", flsz);
423:
424: /*
425: * If we're the server, read our exclusion list.
426: * This is always 0 for now.
427: */
428:
429: if (sess->opts->server) {
1.4 deraadt 430: if (!io_read_size(sess, fdin, &excl)) {
1.1 benno 431: ERRX1(sess, "io_read_size");
432: goto out;
1.5 deraadt 433: } else if (excl != 0) {
1.1 benno 434: ERRX1(sess, "exclusion list is non-empty");
435: goto out;
436: }
437: }
438:
1.10 florian 439: /*
1.8 florian 440: * Set up our poll events.
441: * We start by polling only in receiver requests, enabling other
442: * poll events on demand.
1.1 benno 443: */
444:
1.8 florian 445: pfd[0].fd = fdin; /* from receiver */
446: pfd[0].events = POLLIN;
447: pfd[1].fd = -1; /* to receiver */
448: pfd[1].events = POLLOUT;
449: pfd[2].fd = -1; /* from local file */
450: pfd[2].events = POLLIN;
451:
1.1 benno 452: for (;;) {
1.8 florian 453: assert(pfd[0].fd != -1);
454: if ((c = poll(pfd, 3, POLL_TIMEOUT)) == -1) {
455: ERR(sess, "poll");
456: goto out;
457: } else if (c == 0) {
458: ERRX(sess, "poll: timeout");
1.1 benno 459: goto out;
460: }
1.8 florian 461: for (i = 0; i < 3; i++)
462: if (pfd[i].revents & (POLLERR|POLLNVAL)) {
463: ERRX(sess, "poll: bad fd");
464: goto out;
465: } else if (pfd[i].revents & POLLHUP) {
466: ERRX(sess, "poll: hangup");
467: goto out;
468: }
469:
1.1 benno 470: /*
1.8 florian 471: * If we have a request coming down off the wire, pull
472: * it in as quickly as possible into our buffer.
473: * This unclogs the socket buffers so the data can flow.
1.9 florian 474: * FIXME: if we're multiplexing, we might stall here if
475: * there's only a log message and no actual data.
476: * This can be fixed by doing a conditional test.
1.1 benno 477: */
478:
1.8 florian 479: if (pfd[0].revents & POLLIN)
480: for (;;) {
481: if (!io_read_int(sess, fdin, &idx)) {
482: ERRX1(sess, "io_read_int");
483: goto out;
1.10 florian 484: }
1.8 florian 485: if (!send_dl_enqueue(sess,
486: &sdlq, idx, fl, flsz, fdin)) {
487: ERRX1(sess, "send_dl_enqueue");
488: goto out;
489: }
490: c = io_read_check(sess, fdin);
491: if (c < 0) {
492: ERRX1(sess, "io_read_check");
493: goto out;
494: } else if (c == 0)
495: break;
496: }
497:
498: /*
1.9 florian 499: * One of our local files has been opened in response
500: * to a receiver request and now we can map it.
1.8 florian 501: * We'll respond to the event by looking at the map when
502: * the writer is available.
503: * Here we also enable the poll event for output.
504: */
505:
506: if (pfd[2].revents & POLLIN) {
507: assert(up.cur != NULL);
508: assert(up.stat.fd != -1);
509: assert(up.stat.map == MAP_FAILED);
510: assert(up.stat.mapsz == 0);
1.9 florian 511: f = &fl[up.cur->idx];
1.8 florian 512:
513: if (fstat(up.stat.fd, &st) == -1) {
1.9 florian 514: ERR(sess, "%s: fstat", f->path);
1.1 benno 515: goto out;
516: }
517:
1.8 florian 518: /*
519: * If the file is zero-length, the map will
520: * fail, but either way we want to unset that
1.9 florian 521: * we're waiting for the file to open and set
522: * that we're ready for the output channel.
1.8 florian 523: */
524:
525: if ((up.stat.mapsz = st.st_size) > 0) {
1.9 florian 526: up.stat.map = mmap(NULL,
527: up.stat.mapsz, PROT_READ,
528: MAP_SHARED, up.stat.fd, 0);
1.8 florian 529: if (up.stat.map == MAP_FAILED) {
1.9 florian 530: ERR(sess, "%s: mmap", f->path);
1.8 florian 531: goto out;
532: }
1.9 florian 533: }
534:
1.8 florian 535: pfd[2].fd = -1;
536: pfd[1].fd = fdout;
537: }
538:
539: /*
1.9 florian 540: * If we have buffers waiting to write, write them out
541: * as soon as we can in a non-blocking fashion.
542: * We must not be waiting for any local files.
543: * ALL WRITES MUST HAPPEN HERE.
544: * This keeps the sender deadlock-free.
1.8 florian 545: */
1.1 benno 546:
1.9 florian 547: if ((pfd[1].revents & POLLOUT) && wbufsz > 0) {
548: assert(pfd[2].fd == -1);
549: assert(wbufsz - wbufpos);
550: ssz = write(fdout,
551: wbuf + wbufpos, wbufsz - wbufpos);
552: if (ssz < 0) {
553: ERR(sess, "write");
554: goto out;
555: }
556: wbufpos += ssz;
557: if (wbufpos == wbufsz)
558: wbufpos = wbufsz = 0;
559: pfd[1].revents &= ~POLLOUT;
560:
561: /* This is usually in io.c... */
562:
563: sess->total_write += ssz;
564: }
565:
1.16 ! benno 566: /*
! 567: * Engage the FSM for the current transfer.
! 568: * If our phase changes, stop processing.
! 569: */
! 570:
! 571: if (pfd[1].revents & POLLOUT && up.cur != NULL) {
1.8 florian 572: assert(pfd[2].fd == -1);
1.13 deraadt 573: assert(wbufpos == 0 && wbufsz == 0);
1.16 ! benno 574: if (!send_up_fsm(sess, &phase,
! 575: &up, &wbuf, &wbufsz, &wbufmax, fl)) {
! 576: ERRX1(sess, "send_up_fsm");
! 577: goto out;
! 578: } else if (phase > 1)
! 579: break;
1.1 benno 580: }
581:
582: /*
1.8 florian 583: * Incoming queue management.
584: * If we have no queue component that we're waiting on,
585: * then pull off the receiver-request queue and start
586: * processing the request.
1.1 benno 587: */
588:
1.8 florian 589: if (up.cur == NULL) {
590: assert(pfd[2].fd == -1);
591: assert(up.stat.fd == -1);
592: assert(up.stat.map == MAP_FAILED);
593: assert(up.stat.mapsz == 0);
1.9 florian 594: assert(wbufsz == 0 && wbufpos == 0);
595: pfd[1].fd = -1;
596:
597: /*
598: * If there's nothing in the queue, then keep
599: * the output channel disabled and wait for
600: * whatever comes next from the reader.
601: */
1.8 florian 602:
603: if ((up.cur = TAILQ_FIRST(&sdlq)) == NULL)
604: continue;
1.9 florian 605:
1.8 florian 606: TAILQ_REMOVE(&sdlq, up.cur, entries);
607:
1.10 florian 608: /*
1.9 florian 609: * End of phase: enable channel to receiver.
610: * We'll need our output buffer enabled in order
611: * to process this event.
612: */
1.8 florian 613:
614: if (up.cur->idx == -1) {
615: pfd[1].fd = fdout;
616: continue;
617: }
1.15 benno 618:
1.10 florian 619: /*
1.9 florian 620: * Non-blocking open of file.
621: * This will be picked up in the state machine
622: * block of not being primed.
623: */
1.1 benno 624:
1.9 florian 625: up.stat.fd = open(fl[up.cur->idx].path,
1.8 florian 626: O_RDONLY|O_NONBLOCK, 0);
627: if (up.stat.fd == -1) {
628: ERR(sess, "%s: open", fl[up.cur->idx].path);
629: goto out;
630: }
631: pfd[2].fd = up.stat.fd;
1.1 benno 632: }
633: }
634:
1.9 florian 635: if (!TAILQ_EMPTY(&sdlq)) {
636: ERRX(sess, "phases complete with files still queued");
637: goto out;
638: }
639:
1.4 deraadt 640: if (!sess_stats_send(sess, fdout)) {
1.1 benno 641: ERRX1(sess, "sess_stats_end");
642: goto out;
643: }
644:
645: /* Final "goodbye" message. */
646:
1.4 deraadt 647: if (!io_read_int(sess, fdin, &idx)) {
1.1 benno 648: ERRX1(sess, "io_read_int");
649: goto out;
1.5 deraadt 650: } else if (idx != -1) {
1.1 benno 651: ERRX(sess, "read incorrect update complete ack");
652: goto out;
653: }
654:
655: LOG2(sess, "sender finished updating");
656: rc = 1;
657: out:
1.9 florian 658: send_up_reset(&up);
659: while ((dl = TAILQ_FIRST(&sdlq)) != NULL) {
1.16 ! benno 660: TAILQ_REMOVE(&sdlq, dl, entries);
1.9 florian 661: free(dl->blks);
662: free(dl);
663: }
1.1 benno 664: flist_free(fl, flsz);
1.9 florian 665: free(wbuf);
1.1 benno 666: return rc;
667: }