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