Annotation of src/usr.bin/rsync/flist.c, Revision 1.14
1.14 ! deraadt 1: /* $Id: flist.c,v 1.13 2019/02/14 18:26:52 florian 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: */
17: #include <sys/param.h>
18: #include <sys/stat.h>
19:
20: #include <assert.h>
21: #include <errno.h>
22: #include <fcntl.h>
1.7 benno 23: #include <fts.h>
1.1 benno 24: #include <inttypes.h>
25: #include <search.h>
26: #include <stdio.h>
27: #include <stdlib.h>
28: #include <string.h>
29: #include <unistd.h>
30:
31: #include "extern.h"
32:
33: /*
34: * We allocate our file list in chunk sizes so as not to do it one by
35: * one.
36: * Preferrably we get one or two allocation.
37: */
38: #define FLIST_CHUNK_SIZE (1024)
39:
40: /*
41: * These flags are part of the rsync protocol.
42: * They are sent as the first byte for a file transmission and encode
43: * information that affects subsequent transmissions.
44: */
45: #define FLIST_MODE_SAME 0x0002 /* mode is repeat */
1.13 florian 46: #define FLIST_UID_SAME 0x0008 /* uid is repeat */
1.6 benno 47: #define FLIST_GID_SAME 0x0010 /* gid is repeat */
1.1 benno 48: #define FLIST_NAME_SAME 0x0020 /* name is repeat */
49: #define FLIST_NAME_LONG 0x0040 /* name >255 bytes */
50: #define FLIST_TIME_SAME 0x0080 /* time is repeat */
51:
52: /*
53: * Requied way to sort a filename list.
54: */
55: static int
56: flist_cmp(const void *p1, const void *p2)
57: {
58: const struct flist *f1 = p1, *f2 = p2;
59:
60: return strcmp(f1->wpath, f2->wpath);
61: }
62:
63: /*
64: * Deduplicate our file list (which may be zero-length).
65: * Returns zero on failure, non-zero on success.
66: */
67: static int
68: flist_dedupe(struct sess *sess, struct flist **fl, size_t *sz)
69: {
1.2 benno 70: size_t i, j;
1.1 benno 71: struct flist *new;
72: struct flist *f, *fnext;
73:
1.5 deraadt 74: if (*sz == 0)
1.1 benno 75: return 1;
76:
77: /* Create a new buffer, "new", and copy. */
78:
79: new = calloc(*sz, sizeof(struct flist));
1.5 deraadt 80: if (new == NULL) {
1.1 benno 81: ERR(sess, "calloc");
82: return 0;
83: }
84:
85: for (i = j = 0; i < *sz - 1; i++) {
86: f = &(*fl)[i];
87: fnext = &(*fl)[i + 1];
88:
89: if (strcmp(f->wpath, fnext->wpath)) {
90: new[j++] = *f;
91: continue;
92: }
93:
94: /*
95: * Our working (destination) paths are the same.
96: * If the actual file is the same (as given on the
97: * command-line), then we can just discard the first.
98: * Otherwise, we need to bail out: it means we have two
99: * different files with the relative path on the
100: * destination side.
101: */
102:
1.5 deraadt 103: if (strcmp(f->path, fnext->path) == 0) {
1.1 benno 104: new[j++] = *f;
105: i++;
106: WARNX(sess, "%s: duplicate path: %s",
1.3 benno 107: f->wpath, f->path);
1.1 benno 108: free(fnext->path);
109: free(fnext->link);
110: fnext->path = fnext->link = NULL;
111: continue;
112: }
113:
114: ERRX(sess, "%s: duplicate working path for "
1.3 benno 115: "possibly different file: %s, %s",
116: f->wpath, f->path, fnext->path);
1.1 benno 117: free(new);
118: return 0;
119: }
120:
121: /* Don't forget the last entry. */
122:
123: if (i == *sz - 1)
124: new[j++] = (*fl)[i];
125:
126: /*
127: * Reassign to the deduplicated array.
128: * If we started out with *sz > 0, which we check for at the
129: * beginning, then we'll always continue having *sz > 0.
130: */
131:
132: free(*fl);
133: *fl = new;
134: *sz = j;
135: assert(*sz);
136: return 1;
137: }
138:
139: /*
140: * We're now going to find our top-level directories.
141: * This only applies to recursive mode.
142: * If we have the first element as the ".", then that's the "top
143: * directory" of our transfer.
144: * Otherwise, mark up all top-level directories in the set.
145: */
146: static void
147: flist_topdirs(struct sess *sess, struct flist *fl, size_t flsz)
148: {
1.2 benno 149: size_t i;
1.1 benno 150: const char *cp;
151:
1.4 deraadt 152: if (!sess->opts->recursive)
1.1 benno 153: return;
154:
155: if (flsz && strcmp(fl[0].wpath, ".")) {
156: for (i = 0; i < flsz; i++) {
1.4 deraadt 157: if (!S_ISDIR(fl[i].st.mode))
1.1 benno 158: continue;
159: cp = strchr(fl[i].wpath, '/');
1.5 deraadt 160: if (cp != NULL && cp[1] != '\0')
1.1 benno 161: continue;
162: fl[i].st.flags |= FLSTAT_TOP_DIR;
163: LOG4(sess, "%s: top-level", fl[i].wpath);
164: }
165: } else if (flsz) {
166: fl[0].st.flags |= FLSTAT_TOP_DIR;
167: LOG4(sess, "%s: top-level", fl[0].wpath);
168: }
169: }
170:
171: /*
172: * Filter through the fts() file information.
173: * We want directories (pre-order), regular files, and symlinks.
174: * Everything else is skipped and possibly warned about.
175: * Return zero to skip, non-zero to examine.
176: */
177: static int
178: flist_fts_check(struct sess *sess, FTSENT *ent)
179: {
180:
1.5 deraadt 181: if (ent->fts_info == FTS_F ||
182: ent->fts_info == FTS_D ||
183: ent->fts_info == FTS_SL ||
184: ent->fts_info == FTS_SLNONE)
1.1 benno 185: return 1;
186:
1.5 deraadt 187: if (ent->fts_info == FTS_DC) {
1.1 benno 188: WARNX(sess, "%s: directory cycle", ent->fts_path);
1.5 deraadt 189: } else if (ent->fts_info == FTS_DNR) {
1.1 benno 190: errno = ent->fts_errno;
191: WARN(sess, "%s: unreadable directory", ent->fts_path);
1.5 deraadt 192: } else if (ent->fts_info == FTS_DOT) {
1.1 benno 193: WARNX(sess, "%s: skipping dot-file", ent->fts_path);
1.5 deraadt 194: } else if (ent->fts_info == FTS_ERR) {
1.1 benno 195: errno = ent->fts_errno;
196: WARN(sess, "%s", ent->fts_path);
1.5 deraadt 197: } else if (ent->fts_info == FTS_DEFAULT) {
1.1 benno 198: WARNX(sess, "%s: skipping special", ent->fts_path);
1.5 deraadt 199: } else if (ent->fts_info == FTS_NS) {
1.1 benno 200: errno = ent->fts_errno;
201: WARN(sess, "%s: could not stat", ent->fts_path);
202: }
203:
204: return 0;
205: }
206:
207: /*
208: * Copy necessary elements in "st" into the fields of "f".
209: */
210: static void
211: flist_copy_stat(struct flist *f, const struct stat *st)
212: {
213: f->st.mode = st->st_mode;
214: f->st.uid = st->st_uid;
215: f->st.gid = st->st_gid;
216: f->st.size = st->st_size;
217: f->st.mtime = st->st_mtime;
218: }
219:
220: void
221: flist_free(struct flist *f, size_t sz)
222: {
223: size_t i;
224:
1.5 deraadt 225: if (f == NULL)
1.1 benno 226: return;
227:
228: for (i = 0; i < sz; i++) {
229: free(f[i].path);
230: free(f[i].link);
231: }
232: free(f);
233: }
234:
235: /*
236: * Serialise our file list (which may be zero-length) to the wire.
237: * Makes sure that the receiver isn't going to block on sending us
238: * return messages on the log channel.
239: * Return zero on failure, non-zero on success.
240: */
241: int
1.3 benno 242: flist_send(struct sess *sess, int fdin, int fdout, const struct flist *fl,
243: size_t flsz)
1.1 benno 244: {
1.13 florian 245: size_t i, sz, gidsz = 0, uidsz = 0;
1.1 benno 246: uint8_t flag;
247: const struct flist *f;
248: const char *fn;
1.13 florian 249: struct ident *gids = NULL, *uids = NULL;
1.7 benno 250: int rc = 0;
1.1 benno 251:
252: /* Double-check that we've no pending multiplexed data. */
253:
254: LOG2(sess, "sending file metadata list: %zu", flsz);
255:
256: for (i = 0; i < flsz; i++) {
257: f = &fl[i];
258: fn = f->wpath;
1.7 benno 259: sz = strlen(f->wpath);
260: assert(sz > 0);
1.1 benno 261:
1.2 benno 262: /*
1.1 benno 263: * If applicable, unclog the read buffer.
264: * This happens when the receiver has a lot of log
265: * messages and all we're doing is sending our file list
266: * without checking for messages.
267: */
268:
269: if (sess->mplex_reads &&
270: io_read_check(sess, fdin) &&
1.4 deraadt 271: !io_read_flush(sess, fdin)) {
1.1 benno 272: ERRX1(sess, "io_read_flush");
1.7 benno 273: goto out;
1.1 benno 274: }
275:
276: /*
277: * For ease, make all of our filenames be "long"
278: * regardless their actual length.
279: * This also makes sure that we don't transmit a zero
280: * byte unintentionally.
281: */
282:
283: flag = FLIST_NAME_LONG;
284:
285: LOG3(sess, "%s: sending file metadata: "
286: "size %jd, mtime %jd, mode %o",
287: fn, (intmax_t)f->st.size,
288: (intmax_t)f->st.mtime, f->st.mode);
289:
290: /* Now write to the wire. */
291: /* FIXME: buffer this. */
292:
1.4 deraadt 293: if (!io_write_byte(sess, fdout, flag)) {
1.1 benno 294: ERRX1(sess, "io_write_byte");
1.7 benno 295: goto out;
296: } else if (!io_write_int(sess, fdout, sz)) {
1.1 benno 297: ERRX1(sess, "io_write_int");
1.7 benno 298: goto out;
299: } else if (!io_write_buf(sess, fdout, fn, sz)) {
1.1 benno 300: ERRX1(sess, "io_write_buf");
1.7 benno 301: goto out;
1.4 deraadt 302: } else if (!io_write_long(sess, fdout, f->st.size)) {
1.1 benno 303: ERRX1(sess, "io_write_long");
1.7 benno 304: goto out;
1.4 deraadt 305: } else if (!io_write_int(sess, fdout, f->st.mtime)) {
1.1 benno 306: ERRX1(sess, "io_write_int");
1.7 benno 307: goto out;
1.4 deraadt 308: } else if (!io_write_int(sess, fdout, f->st.mode)) {
1.1 benno 309: ERRX1(sess, "io_write_int");
1.7 benno 310: goto out;
1.1 benno 311: }
312:
1.13 florian 313: /* Conditional part: uid. */
314:
315: if (sess->opts->preserve_uids) {
316: if (!io_write_int(sess, fdout, f->st.uid)) {
317: ERRX1(sess, "io_write_int");
318: goto out;
319: }
320: if (!idents_add(sess, 0, &uids, &uidsz, f->st.uid)) {
321: ERRX1(sess, "idents_add");
322: goto out;
323: }
324: }
325:
1.6 benno 326: /* Conditional part: gid. */
327:
1.7 benno 328: if (sess->opts->preserve_gids) {
329: if (!io_write_int(sess, fdout, f->st.gid)) {
330: ERRX1(sess, "io_write_int");
331: goto out;
1.12 benno 332: }
1.13 florian 333: if (!idents_add(sess, 1, &gids, &gidsz, f->st.gid)) {
334: ERRX1(sess, "idents_add");
1.7 benno 335: goto out;
336: }
1.6 benno 337: }
338:
339: /* Conditional part: link. */
1.1 benno 340:
341: if (S_ISLNK(f->st.mode) &&
342: sess->opts->preserve_links) {
343: fn = f->link;
1.7 benno 344: sz = strlen(f->link);
345: if (!io_write_int(sess, fdout, sz)) {
1.1 benno 346: ERRX1(sess, "io_write_int");
1.7 benno 347: goto out;
1.1 benno 348: }
1.7 benno 349: if (!io_write_buf(sess, fdout, fn, sz)) {
1.1 benno 350: ERRX1(sess, "io_write_int");
1.7 benno 351: goto out;
1.1 benno 352: }
353: }
354:
355: if (S_ISREG(f->st.mode))
356: sess->total_size += f->st.size;
357: }
358:
1.7 benno 359: /* Signal end of file list. */
360:
1.4 deraadt 361: if (!io_write_byte(sess, fdout, 0)) {
1.1 benno 362: ERRX1(sess, "io_write_byte");
1.7 benno 363: goto out;
364: }
365:
1.13 florian 366: /* Conditionally write identifier lists. */
367:
368: if (sess->opts->preserve_uids) {
369: LOG2(sess, "sending uid list: %zu", uidsz);
370: if (!idents_send(sess, fdout, uids, uidsz)) {
371: ERRX1(sess, "idents_send");
372: goto out;
373: }
374: }
1.7 benno 375:
376: if (sess->opts->preserve_gids) {
377: LOG2(sess, "sending gid list: %zu", gidsz);
1.8 benno 378: if (!idents_send(sess, fdout, gids, gidsz)) {
379: ERRX1(sess, "idents_send");
1.7 benno 380: goto out;
381: }
1.1 benno 382: }
383:
1.7 benno 384: rc = 1;
385: out:
1.8 benno 386: idents_free(gids, gidsz);
1.13 florian 387: idents_free(uids, uidsz);
1.7 benno 388: return rc;
1.1 benno 389: }
390:
391: /*
392: * Read the filename of a file list.
393: * This is the most expensive part of the file list transfer, so a lot
394: * of attention has gone into transmitting as little as possible.
395: * Micro-optimisation, but whatever.
396: * Fills in "f" with the full path on success.
397: * Returns zero on failure, non-zero on success.
398: */
399: static int
1.3 benno 400: flist_recv_name(struct sess *sess, int fd, struct flist *f, uint8_t flags,
401: char last[MAXPATHLEN])
1.1 benno 402: {
403: uint8_t bval;
404: size_t partial = 0;
405: size_t pathlen = 0, len;
406:
407: /*
408: * Read our filename.
409: * If we have FLIST_NAME_SAME, we inherit some of the last
410: * transmitted name.
411: * If we have FLIST_NAME_LONG, then the string length is greater
412: * than byte-size.
413: */
414:
415: if (FLIST_NAME_SAME & flags) {
1.4 deraadt 416: if (!io_read_byte(sess, fd, &bval)) {
1.1 benno 417: ERRX1(sess, "io_read_byte");
418: return 0;
419: }
420: partial = bval;
421: }
422:
423: /* Get the (possibly-remaining) filename length. */
424:
425: if (FLIST_NAME_LONG & flags) {
1.4 deraadt 426: if (!io_read_size(sess, fd, &pathlen)) {
1.1 benno 427: ERRX1(sess, "io_read_size");
428: return 0;
429: }
430: } else {
1.4 deraadt 431: if (!io_read_byte(sess, fd, &bval)) {
1.1 benno 432: ERRX1(sess, "io_read_byte");
433: return 0;
434: }
435: pathlen = bval;
436: }
437:
438: /* Allocate our full filename length. */
439: /* FIXME: maximum pathname length. */
440:
1.5 deraadt 441: if ((len = pathlen + partial) == 0) {
1.1 benno 442: ERRX(sess, "security violation: "
443: "zero-length pathname");
444: return 0;
445: }
446:
1.5 deraadt 447: if ((f->path = malloc(len + 1)) == NULL) {
1.1 benno 448: ERR(sess, "malloc");
449: return 0;
450: }
451: f->path[len] = '\0';
452:
453: if (FLIST_NAME_SAME & flags)
454: memcpy(f->path, last, partial);
455:
1.4 deraadt 456: if (!io_read_buf(sess, fd, f->path + partial, pathlen)) {
1.1 benno 457: ERRX1(sess, "io_read_buf");
458: return 0;
459: }
460:
1.5 deraadt 461: if (f->path[0] == '/') {
1.1 benno 462: ERRX(sess, "security violation: "
463: "absolute pathname: %s", f->path);
464: return 0;
465: }
466:
1.5 deraadt 467: if (strstr(f->path, "/../") != NULL ||
468: (len > 2 && strcmp(f->path + len - 3, "/..") == 0) ||
469: (len > 2 && strncmp(f->path, "../", 3) == 0) ||
470: strcmp(f->path, "..") == 0) {
1.1 benno 471: ERRX(sess, "%s: security violation: "
472: "backtracking pathname", f->path);
473: return 0;
474: }
475:
476: /* Record our last path and construct our filename. */
477:
478: strlcpy(last, f->path, MAXPATHLEN);
479: f->wpath = f->path;
480: return 1;
481: }
482:
483: /*
484: * Reallocate a file list in chunks of FLIST_CHUNK_SIZE;
485: * Returns zero on failure, non-zero on success.
486: */
487: static int
1.3 benno 488: flist_realloc(struct sess *sess, struct flist **fl, size_t *sz, size_t *max)
1.1 benno 489: {
490: void *pp;
491:
492: if (*sz + 1 <= *max) {
493: (*sz)++;
494: return 1;
495: }
496:
497: pp = recallocarray(*fl, *max,
498: *max + FLIST_CHUNK_SIZE, sizeof(struct flist));
1.5 deraadt 499: if (pp == NULL) {
1.1 benno 500: ERR(sess, "recallocarray");
501: return 0;
502: }
503: *fl = pp;
504: *max += FLIST_CHUNK_SIZE;
505: (*sz)++;
506: return 1;
507: }
508:
509: /*
510: * Copy a regular or symbolic link file "path" into "f".
511: * This handles the correct path creation and symbolic linking.
512: * Returns zero on failure, non-zero on success.
513: */
514: static int
1.3 benno 515: flist_append(struct sess *sess, struct flist *f, struct stat *st,
516: const char *path)
1.1 benno 517: {
518:
519: /*
520: * Copy the full path for local addressing and transmit
521: * only the filename part for the receiver.
522: */
523:
1.5 deraadt 524: if ((f->path = strdup(path)) == NULL) {
1.1 benno 525: ERR(sess, "strdup");
526: return 0;
527: }
528:
1.5 deraadt 529: if ((f->wpath = strrchr(f->path, '/')) == NULL)
1.1 benno 530: f->wpath = f->path;
531: else
532: f->wpath++;
533:
534: /*
535: * On the receiving end, we'll strip out all bits on the
536: * mode except for the file permissions.
537: * No need to warn about it here.
538: */
539:
540: flist_copy_stat(f, st);
541:
542: /* Optionally copy link information. */
543:
544: if (S_ISLNK(st->st_mode)) {
545: f->link = symlink_read(sess, f->path);
1.5 deraadt 546: if (f->link == NULL) {
1.1 benno 547: ERRX1(sess, "symlink_read");
548: return 0;
549: }
550: }
551:
552: return 1;
553: }
554:
555: /*
556: * Receive a file list from the wire, filling in length "sz" (which may
557: * possibly be zero) and list "flp" on success.
558: * Return zero on failure, non-zero on success.
559: */
560: int
561: flist_recv(struct sess *sess, int fd, struct flist **flp, size_t *sz)
562: {
563: struct flist *fl = NULL;
564: struct flist *ff;
565: const struct flist *fflast = NULL;
1.13 florian 566: size_t flsz = 0, flmax = 0, lsz, gidsz = 0, uidsz = 0;
1.1 benno 567: uint8_t flag;
568: char last[MAXPATHLEN];
569: uint64_t lval; /* temporary values... */
570: int32_t ival;
1.13 florian 571: struct ident *gids = NULL, *uids = NULL;
1.1 benno 572:
573: last[0] = '\0';
574:
575: for (;;) {
1.4 deraadt 576: if (!io_read_byte(sess, fd, &flag)) {
1.1 benno 577: ERRX1(sess, "io_read_byte");
578: goto out;
1.5 deraadt 579: } else if (flag == 0)
1.1 benno 580: break;
581:
1.4 deraadt 582: if (!flist_realloc(sess, &fl, &flsz, &flmax)) {
1.1 benno 583: ERRX1(sess, "flist_realloc");
584: goto out;
585: }
586:
587: ff = &fl[flsz - 1];
588: fflast = flsz > 1 ? &fl[flsz - 2] : NULL;
589:
590: /* Filename first. */
591:
1.4 deraadt 592: if (!flist_recv_name(sess, fd, ff, flag, last)) {
1.1 benno 593: ERRX1(sess, "flist_recv_name");
594: goto out;
595: }
596:
597: /* Read the file size. */
598:
1.4 deraadt 599: if (!io_read_ulong(sess, fd, &lval)) {
1.1 benno 600: ERRX1(sess, "io_read_ulong");
601: goto out;
602: }
603: ff->st.size = lval;
604:
605: /* Read the modification time. */
606:
1.4 deraadt 607: if (!(FLIST_TIME_SAME & flag)) {
608: if (!io_read_int(sess, fd, &ival)) {
1.1 benno 609: ERRX1(sess, "io_read_int");
610: goto out;
611: }
612: ff->st.mtime = ival;
1.5 deraadt 613: } else if (fflast == NULL) {
1.1 benno 614: ERRX(sess, "same time without last entry");
615: goto out;
616: } else
617: ff->st.mtime = fflast->st.mtime;
618:
619: /* Read the file mode. */
620:
1.4 deraadt 621: if (!(FLIST_MODE_SAME & flag)) {
622: if (!io_read_int(sess, fd, &ival)) {
1.1 benno 623: ERRX1(sess, "io_read_int");
624: goto out;
625: }
626: ff->st.mode = ival;
1.5 deraadt 627: } else if (fflast == NULL) {
1.1 benno 628: ERRX(sess, "same mode without last entry");
629: goto out;
630: } else
631: ff->st.mode = fflast->st.mode;
632:
1.13 florian 633: /* Conditional part: uid. */
634:
635: if (sess->opts->preserve_uids) {
636: if (!(FLIST_UID_SAME & flag)) {
637: if (!io_read_int(sess, fd, &ival)) {
638: ERRX1(sess, "io_read_int");
639: goto out;
640: }
641: ff->st.uid = ival;
642: } else if (fflast == NULL) {
643: ERRX(sess, "same uid "
644: "without last entry");
645: goto out;
646: } else
647: ff->st.uid = fflast->st.uid;
648: }
649:
1.6 benno 650: /* Conditional part: gid. */
651:
652: if (sess->opts->preserve_gids) {
1.11 benno 653: if (!(FLIST_GID_SAME & flag)) {
654: if (!io_read_int(sess, fd, &ival)) {
1.6 benno 655: ERRX1(sess, "io_read_int");
656: goto out;
657: }
658: ff->st.gid = ival;
1.11 benno 659: } else if (fflast == NULL) {
1.6 benno 660: ERRX(sess, "same gid "
661: "without last entry");
662: goto out;
663: } else
664: ff->st.gid = fflast->st.gid;
665: }
666:
667: /* Conditional part: link. */
1.1 benno 668:
669: if (S_ISLNK(ff->st.mode) &&
670: sess->opts->preserve_links) {
1.4 deraadt 671: if (!io_read_size(sess, fd, &lsz)) {
1.1 benno 672: ERRX1(sess, "io_read_size");
673: goto out;
1.5 deraadt 674: } else if (lsz == 0) {
1.1 benno 675: ERRX(sess, "empty link name");
676: goto out;
677: }
678: ff->link = calloc(lsz + 1, 1);
1.5 deraadt 679: if (ff->link == NULL) {
1.1 benno 680: ERR(sess, "calloc");
681: goto out;
682: }
1.4 deraadt 683: if (!io_read_buf(sess, fd, ff->link, lsz)) {
1.1 benno 684: ERRX1(sess, "io_read_buf");
685: goto out;
686: }
687: }
688:
689: LOG3(sess, "%s: received file metadata: "
690: "size %jd, mtime %jd, mode %o",
691: ff->path, (intmax_t)ff->st.size,
692: (intmax_t)ff->st.mtime, ff->st.mode);
693:
694: if (S_ISREG(ff->st.mode))
695: sess->total_size += ff->st.size;
696: }
697:
1.13 florian 698: /* Conditionally read the user/group list. */
699:
700: if (sess->opts->preserve_uids) {
701: if (!idents_recv(sess, fd, &uids, &uidsz)) {
702: ERRX1(sess, "idents_recv");
703: goto out;
704: }
705: LOG2(sess, "received uid list: %zu", uidsz);
706: }
1.7 benno 707:
708: if (sess->opts->preserve_gids) {
1.8 benno 709: if (!idents_recv(sess, fd, &gids, &gidsz)) {
710: ERRX1(sess, "idents_recv");
1.7 benno 711: goto out;
712: }
713: LOG2(sess, "received gid list: %zu", gidsz);
714: }
715:
1.1 benno 716: /* Remember to order the received list. */
717:
718: LOG2(sess, "received file metadata list: %zu", flsz);
719: qsort(fl, flsz, sizeof(struct flist), flist_cmp);
720: flist_topdirs(sess, fl, flsz);
721: *sz = flsz;
722: *flp = fl;
1.7 benno 723:
1.13 florian 724: /* Conditionally remap and reassign identifiers. */
725:
726: if (sess->opts->preserve_uids) {
727: idents_remap(sess, 0, uids, uidsz);
728: idents_assign_uid(sess, fl, flsz, uids, uidsz);
729: }
1.7 benno 730:
731: if (sess->opts->preserve_gids) {
1.13 florian 732: idents_remap(sess, 1, gids, gidsz);
733: idents_assign_gid(sess, fl, flsz, gids, gidsz);
1.7 benno 734: }
735:
1.8 benno 736: idents_free(gids, gidsz);
1.13 florian 737: idents_free(uids, uidsz);
1.1 benno 738: return 1;
739: out:
740: flist_free(fl, flsz);
1.8 benno 741: idents_free(gids, gidsz);
1.13 florian 742: idents_free(uids, uidsz);
1.1 benno 743: *sz = 0;
744: *flp = NULL;
745: return 0;
746: }
747:
748: /*
749: * Generate a flist possibly-recursively given a file root, which may
750: * also be a regular file or symlink.
751: * On success, augments the generated list in "flp" of length "sz".
752: * Returns zero on failure, non-zero on success.
753: */
754: static int
1.3 benno 755: flist_gen_dirent(struct sess *sess, char *root, struct flist **fl, size_t *sz,
756: size_t *max)
1.1 benno 757: {
758: char *cargv[2], *cp;
759: int rc = 0;
760: FTS *fts;
761: FTSENT *ent;
762: struct flist *f;
763: size_t flsz = 0, stripdir;
764: struct stat st;
765:
766: cargv[0] = root;
767: cargv[1] = NULL;
768:
769: /*
770: * If we're a file, then revert to the same actions we use for
771: * the non-recursive scan.
772: */
773:
1.5 deraadt 774: if (lstat(root, &st) == -1) {
1.1 benno 775: ERR(sess, "%s: lstat", root);
776: return 0;
777: } else if (S_ISREG(st.st_mode)) {
1.4 deraadt 778: if (!flist_realloc(sess, fl, sz, max)) {
1.1 benno 779: ERRX1(sess, "flist_realloc");
780: return 0;
781: }
782: f = &(*fl)[(*sz) - 1];
1.5 deraadt 783: assert(f != NULL);
1.1 benno 784:
1.4 deraadt 785: if (!flist_append(sess, f, &st, root)) {
1.1 benno 786: ERRX1(sess, "flist_append");
787: return 0;
1.10 benno 788: }
789: if (unveil(root, "r") == -1) {
1.1 benno 790: ERR(sess, "%s: unveil", root);
791: return 0;
792: }
793: return 1;
794: } else if (S_ISLNK(st.st_mode)) {
1.4 deraadt 795: if (!sess->opts->preserve_links) {
1.1 benno 796: WARNX(sess, "%s: skipping symlink", root);
797: return 1;
1.4 deraadt 798: } else if (!flist_realloc(sess, fl, sz, max)) {
1.1 benno 799: ERRX1(sess, "flist_realloc");
800: return 0;
801: }
802: f = &(*fl)[(*sz) - 1];
1.5 deraadt 803: assert(f != NULL);
1.1 benno 804:
1.4 deraadt 805: if (!flist_append(sess, f, &st, root)) {
1.1 benno 806: ERRX1(sess, "flist_append");
807: return 0;
1.10 benno 808: }
809: if (unveil(root, "r") == -1) {
1.1 benno 810: ERR(sess, "%s: unveil", root);
811: return 0;
812: }
813: return 1;
1.4 deraadt 814: } else if (!S_ISDIR(st.st_mode)) {
1.1 benno 815: WARNX(sess, "%s: skipping special", root);
816: return 1;
817: }
818:
819: /*
820: * If we end with a slash, it means that we're not supposed to
821: * copy the directory part itself---only the contents.
822: * So set "stripdir" to be what we take out.
823: */
824:
825: stripdir = strlen(root);
826: assert(stripdir > 0);
1.5 deraadt 827: if (root[stripdir - 1] != '/')
1.1 benno 828: stripdir = 0;
829:
830: /*
831: * If we're not stripping anything, then see if we need to strip
832: * out the leading material in the path up to and including the
833: * last directory component.
834: */
835:
1.5 deraadt 836: if (stripdir == 0)
837: if ((cp = strrchr(root, '/')) != NULL)
1.1 benno 838: stripdir = cp - root + 1;
839:
840: /*
841: * If we're recursive, then we need to take down all of the
842: * files and directory components, so use fts(3).
843: * Copying the information file-by-file into the flstat.
844: * We'll make sense of it in flist_send.
845: */
846:
1.5 deraadt 847: if ((fts = fts_open(cargv, FTS_PHYSICAL, NULL)) == NULL) {
1.1 benno 848: ERR(sess, "fts_open");
849: return 0;
850: }
851:
852: errno = 0;
1.5 deraadt 853: while ((ent = fts_read(fts)) != NULL) {
1.4 deraadt 854: if (!flist_fts_check(sess, ent)) {
1.1 benno 855: errno = 0;
856: continue;
857: }
858:
859: /* We don't allow symlinks without -l. */
860:
1.5 deraadt 861: assert(ent->fts_statp != NULL);
1.1 benno 862: if (S_ISLNK(ent->fts_statp->st_mode) &&
1.4 deraadt 863: !sess->opts->preserve_links) {
1.1 benno 864: WARNX(sess, "%s: skipping "
865: "symlink", ent->fts_path);
866: continue;
867: }
868:
869: /* Allocate a new file entry. */
870:
1.4 deraadt 871: if (!flist_realloc(sess, fl, sz, max)) {
1.1 benno 872: ERRX1(sess, "flist_realloc");
873: goto out;
874: }
875: flsz++;
876: f = &(*fl)[*sz - 1];
877:
878: /* Our path defaults to "." for the root. */
879:
880: if ('\0' == ent->fts_path[stripdir]) {
881: if (asprintf(&f->path, "%s.", ent->fts_path) < 0) {
882: ERR(sess, "asprintf");
883: f->path = NULL;
884: goto out;
885: }
886: } else {
1.5 deraadt 887: if ((f->path = strdup(ent->fts_path)) == NULL) {
1.1 benno 888: ERR(sess, "strdup");
889: goto out;
890: }
891: }
892:
893: f->wpath = f->path + stripdir;
894: flist_copy_stat(f, ent->fts_statp);
895:
896: /* Optionally copy link information. */
897:
898: if (S_ISLNK(ent->fts_statp->st_mode)) {
899: f->link = symlink_read(sess, f->path);
1.5 deraadt 900: if (f->link == NULL) {
1.1 benno 901: ERRX1(sess, "symlink_read");
902: goto out;
903: }
904: }
905:
906: /* Reset errno for next fts_read() call. */
907: errno = 0;
908: }
909: if (errno) {
910: ERR(sess, "fts_read");
911: goto out;
1.10 benno 912: }
913: if (unveil(root, "r") == -1) {
1.1 benno 914: ERR(sess, "%s: unveil", root);
915: goto out;
916: }
917:
918: LOG3(sess, "generated %zu filenames: %s", flsz, root);
919: rc = 1;
920: out:
921: fts_close(fts);
922: return rc;
923: }
924:
925: /*
926: * Generate a flist recursively given the array of directories (or
927: * files, symlinks, doesn't matter) specified in argv (argc >0).
928: * On success, stores the generated list in "flp" with length "sz",
929: * which may be zero.
930: * Returns zero on failure, non-zero on success.
931: */
932: static int
1.3 benno 933: flist_gen_dirs(struct sess *sess, size_t argc, char **argv, struct flist **flp,
934: size_t *sz)
1.1 benno 935: {
936: size_t i, max = 0;
937:
938: for (i = 0; i < argc; i++)
1.4 deraadt 939: if (!flist_gen_dirent(sess, argv[i], flp, sz, &max))
1.1 benno 940: break;
941:
942: if (i == argc) {
943: LOG2(sess, "recursively generated %zu filenames", *sz);
944: return 1;
945: }
946:
947: ERRX1(sess, "flist_gen_dirent");
948: flist_free(*flp, max);
949: *flp = NULL;
950: *sz = 0;
951: return 0;
952: }
953:
954: /*
955: * Generate list of files from the command-line argc (>0) and argv.
956: * On success, stores the generated list in "flp" with length "sz",
957: * which may be zero.
958: * Returns zero on failure, non-zero on success.
959: */
960: static int
1.3 benno 961: flist_gen_files(struct sess *sess, size_t argc, char **argv,
962: struct flist **flp, size_t *sz)
1.1 benno 963: {
964: struct flist *fl = NULL, *f;
965: size_t i, flsz = 0;
966: struct stat st;
967:
968: assert(argc);
969:
1.5 deraadt 970: if ((fl = calloc(argc, sizeof(struct flist))) == NULL) {
1.1 benno 971: ERR(sess, "calloc");
972: return 0;
973: }
974:
975: for (i = 0; i < argc; i++) {
976: if ('\0' == argv[i][0])
977: continue;
1.5 deraadt 978: if (lstat(argv[i], &st) == -1) {
1.1 benno 979: ERR(sess, "%s: lstat", argv[i]);
980: goto out;
981: }
982:
983: /*
984: * File type checks.
985: * In non-recursive mode, we don't accept directories.
986: * We also skip symbolic links without -l.
987: * Beyond that, we only accept regular files.
988: */
989:
990: if (S_ISDIR(st.st_mode)) {
991: WARNX(sess, "%s: skipping directory", argv[i]);
992: continue;
993: } else if (S_ISLNK(st.st_mode)) {
1.4 deraadt 994: if (!sess->opts->preserve_links) {
1.1 benno 995: WARNX(sess, "%s: skipping "
996: "symlink", argv[i]);
997: continue;
998: }
1.4 deraadt 999: } else if (!S_ISREG(st.st_mode)) {
1.1 benno 1000: WARNX(sess, "%s: skipping special", argv[i]);
1001: continue;
1002: }
1003:
1.3 benno 1004:
1.1 benno 1005: f = &fl[flsz++];
1.5 deraadt 1006: assert(f != NULL);
1.1 benno 1007:
1008: /* Add this file to our file-system worldview. */
1009:
1.5 deraadt 1010: if (unveil(argv[i], "r") == -1) {
1.1 benno 1011: ERR(sess, "%s: unveil", argv[i]);
1012: goto out;
1.10 benno 1013: }
1014: if (!flist_append(sess, f, &st, argv[i])) {
1.1 benno 1015: ERRX1(sess, "flist_append");
1016: goto out;
1017: }
1018: }
1019:
1020: LOG2(sess, "non-recursively generated %zu filenames", flsz);
1021: *sz = flsz;
1022: *flp = fl;
1023: return 1;
1024: out:
1025: flist_free(fl, argc);
1026: *sz = 0;
1027: *flp = NULL;
1028: return 0;
1029: }
1030:
1031: /*
1032: * Generate a sorted, de-duplicated list of file metadata.
1033: * In non-recursive mode (the default), we use only the files we're
1034: * given.
1035: * Otherwise, directories are recursively examined.
1036: * Returns zero on failure, non-zero on success.
1037: * On success, "fl" will need to be freed with flist_free().
1038: */
1039: int
1.3 benno 1040: flist_gen(struct sess *sess, size_t argc, char **argv, struct flist **flp,
1041: size_t *sz)
1.1 benno 1042: {
1043: int rc;
1044:
1045: assert(argc > 0);
1046: rc = sess->opts->recursive ?
1047: flist_gen_dirs(sess, argc, argv, flp, sz) :
1048: flist_gen_files(sess, argc, argv, flp, sz);
1049:
1050: /* After scanning, lock our file-system view. */
1051:
1.5 deraadt 1052: if (unveil(NULL, NULL) == -1) {
1.1 benno 1053: ERR(sess, "unveil");
1054: return 0;
1.10 benno 1055: }
1056: if (!rc)
1.1 benno 1057: return 0;
1058:
1059: qsort(*flp, *sz, sizeof(struct flist), flist_cmp);
1060:
1061: if (flist_dedupe(sess, flp, sz)) {
1062: flist_topdirs(sess, *flp, *sz);
1063: return 1;
1064: }
1065:
1066: ERRX1(sess, "flist_dedupe");
1067: flist_free(*flp, *sz);
1068: *flp = NULL;
1069: *sz = 0;
1070: return 0;
1071: }
1072:
1073: /*
1074: * Generate a list of files in root to delete that are within the
1075: * top-level directories stipulated by "wfl".
1076: * Only handles symbolic links, directories, and regular files.
1077: * Returns zero on failure (fl and flsz will be NULL and zero), non-zero
1078: * on success.
1079: * On success, "fl" will need to be freed with flist_free().
1080: */
1081: int
1.3 benno 1082: flist_gen_dels(struct sess *sess, const char *root, struct flist **fl,
1083: size_t *sz, const struct flist *wfl, size_t wflsz)
1.1 benno 1084: {
1085: char **cargv = NULL;
1086: int rc = 0, c;
1087: FTS *fts = NULL;
1088: FTSENT *ent;
1089: struct flist *f;
1090: size_t cargvs = 0, i, j, max = 0, stripdir;
1091: ENTRY hent;
1092: ENTRY *hentp;
1.2 benno 1093:
1.1 benno 1094: *fl = NULL;
1095: *sz = 0;
1096:
1097: /* Only run this code when we're recursive. */
1098:
1.4 deraadt 1099: if (!sess->opts->recursive)
1.1 benno 1100: return 1;
1101:
1.2 benno 1102: /*
1.1 benno 1103: * Gather up all top-level directories for scanning.
1104: * This is stipulated by rsync's --delete behaviour, where we
1105: * only delete things in the top-level directories given on the
1106: * command line.
1107: */
1108:
1109: assert(wflsz > 0);
1110: for (i = 0; i < wflsz; i++)
1111: if (FLSTAT_TOP_DIR & wfl[i].st.flags)
1112: cargvs++;
1.5 deraadt 1113: if (cargvs == 0)
1.1 benno 1114: return 1;
1115:
1.5 deraadt 1116: if ((cargv = calloc(cargvs + 1, sizeof(char *))) == NULL) {
1.1 benno 1117: ERR(sess, "calloc");
1118: return 0;
1119: }
1120:
1121: /*
1122: * If we're given just a "." as the first entry, that means
1123: * we're doing a relative copy with a trailing slash.
1124: * Special-case this just for the sake of simplicity.
1125: * Otherwise, look through all top-levels.
1126: */
1127:
1.5 deraadt 1128: if (wflsz && strcmp(wfl[0].wpath, ".") == 0) {
1129: assert(cargvs == 1);
1.1 benno 1130: assert(S_ISDIR(wfl[0].st.mode));
1131: if (asprintf(&cargv[0], "%s/", root) < 0) {
1132: ERR(sess, "asprintf");
1133: cargv[0] = NULL;
1134: goto out;
1135: }
1136: cargv[1] = NULL;
1137: } else {
1138: for (i = j = 0; i < wflsz; i++) {
1.4 deraadt 1139: if (!(FLSTAT_TOP_DIR & wfl[i].st.flags))
1.1 benno 1140: continue;
1141: assert(S_ISDIR(wfl[i].st.mode));
1142: assert(strcmp(wfl[i].wpath, "."));
1.5 deraadt 1143: c = asprintf(&cargv[j], "%s/%s", root, wfl[i].wpath);
1.1 benno 1144: if (c < 0) {
1145: ERR(sess, "asprintf");
1146: cargv[j] = NULL;
1147: goto out;
1148: }
1.14 ! deraadt 1149: LOG4(sess, "%s: will scan for deletions", cargv[j]);
1.1 benno 1150: j++;
1151: }
1152: assert(j == cargvs);
1153: cargv[j] = NULL;
1154: }
1155:
1156: LOG2(sess, "delete from %zu directories", cargvs);
1157:
1158: /*
1159: * Next, use the standard hcreate(3) hashtable interface to hash
1160: * all of the files that we want to synchronise.
1161: * This way, we'll be able to determine which files we want to
1162: * delete in O(n) time instead of O(n * search) time.
1163: * Plus, we can do the scan in-band and only allocate the files
1164: * we want to delete.
1165: */
1166:
1.4 deraadt 1167: if (!hcreate(wflsz)) {
1.1 benno 1168: ERR(sess, "hcreate");
1169: goto out;
1170: }
1171:
1172: for (i = 0; i < wflsz; i++) {
1173: memset(&hent, 0, sizeof(ENTRY));
1.5 deraadt 1174: if ((hent.key = strdup(wfl[i].wpath)) == NULL) {
1.1 benno 1175: ERR(sess, "strdup");
1176: goto out;
1177: }
1.5 deraadt 1178: if ((hentp = hsearch(hent, ENTER)) == NULL) {
1.1 benno 1179: ERR(sess, "hsearch");
1180: goto out;
1181: } else if (hentp->key != hent.key) {
1182: ERRX(sess, "%s: duplicate", wfl[i].wpath);
1183: free(hent.key);
1184: goto out;
1185: }
1186: }
1187:
1.2 benno 1188: /*
1.1 benno 1189: * Now we're going to try to descend into all of the top-level
1190: * directories stipulated by the file list.
1191: * If the directories don't exist, it's ok.
1192: */
1193:
1.5 deraadt 1194: if ((fts = fts_open(cargv, FTS_PHYSICAL, NULL)) == NULL) {
1.1 benno 1195: ERR(sess, "fts_open");
1196: goto out;
1197: }
1198:
1199: stripdir = strlen(root) + 1;
1200: errno = 0;
1.5 deraadt 1201: while ((ent = fts_read(fts)) != NULL) {
1202: if (ent->fts_info == FTS_NS)
1.1 benno 1203: continue;
1.4 deraadt 1204: if (!flist_fts_check(sess, ent)) {
1.1 benno 1205: errno = 0;
1206: continue;
1207: } else if (stripdir >= ent->fts_pathlen)
1208: continue;
1209:
1210: /* Look up in hashtable. */
1211:
1212: memset(&hent, 0, sizeof(ENTRY));
1213: hent.key = ent->fts_path + stripdir;
1.5 deraadt 1214: if (hsearch(hent, FIND) != NULL)
1.1 benno 1215: continue;
1216:
1217: /* Not found: we'll delete it. */
1218:
1.4 deraadt 1219: if (!flist_realloc(sess, fl, sz, &max)) {
1.1 benno 1220: ERRX1(sess, "flist_realloc");
1221: goto out;
1222: }
1223: f = &(*fl)[*sz - 1];
1224:
1.5 deraadt 1225: if ((f->path = strdup(ent->fts_path)) == NULL) {
1.1 benno 1226: ERR(sess, "strdup");
1227: goto out;
1228: }
1229: f->wpath = f->path + stripdir;
1.5 deraadt 1230: assert(ent->fts_statp != NULL);
1.1 benno 1231: flist_copy_stat(f, ent->fts_statp);
1232: errno = 0;
1233: }
1234:
1235: if (errno) {
1236: ERR(sess, "fts_read");
1237: goto out;
1238: }
1239:
1240: qsort(*fl, *sz, sizeof(struct flist), flist_cmp);
1241: rc = 1;
1242: out:
1.5 deraadt 1243: if (fts != NULL)
1.1 benno 1244: fts_close(fts);
1245: for (i = 0; i < cargvs; i++)
1246: free(cargv[i]);
1247: free(cargv);
1248: hdestroy();
1249: return rc;
1250: }
1251:
1252: /*
1253: * Delete all files and directories in "fl".
1254: * If called with a zero-length "fl", does nothing.
1255: * If dry_run is specified, simply write what would be done.
1256: * Return zero on failure, non-zero on success.
1257: */
1258: int
1.3 benno 1259: flist_del(struct sess *sess, int root, const struct flist *fl, size_t flsz)
1.1 benno 1260: {
1261: ssize_t i;
1262: int flag;
1263:
1.5 deraadt 1264: if (flsz == 0)
1.1 benno 1265: return 1;
1266:
1267: assert(sess->opts->del);
1268: assert(sess->opts->recursive);
1269:
1270: for (i = flsz - 1; i >= 0; i--) {
1271: LOG1(sess, "%s: deleting", fl[i].wpath);
1272: if (sess->opts->dry_run)
1273: continue;
1.5 deraadt 1274: assert(root != -1);
1.1 benno 1275: flag = S_ISDIR(fl[i].st.mode) ? AT_REMOVEDIR : 0;
1.5 deraadt 1276: if (unlinkat(root, fl[i].wpath, flag) == -1 &&
1277: errno != ENOENT) {
1.1 benno 1278: ERR(sess, "%s: unlinkat", fl[i].wpath);
1279: return 0;
1280: }
1281: }
1282:
1283: return 1;
1284: }