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