Annotation of src/usr.bin/rsync/flist.c, Revision 1.22
1.22 ! deraadt 1: /* $Id: flist.c,v 1.21 2019/03/23 16:04:28 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.22 ! deraadt 598: uint32_t uival;
1.13 florian 599: struct ident *gids = NULL, *uids = NULL;
1.1 benno 600:
601: last[0] = '\0';
602:
603: for (;;) {
1.4 deraadt 604: if (!io_read_byte(sess, fd, &flag)) {
1.1 benno 605: ERRX1(sess, "io_read_byte");
606: goto out;
1.5 deraadt 607: } else if (flag == 0)
1.1 benno 608: break;
609:
1.4 deraadt 610: if (!flist_realloc(sess, &fl, &flsz, &flmax)) {
1.1 benno 611: ERRX1(sess, "flist_realloc");
612: goto out;
613: }
614:
615: ff = &fl[flsz - 1];
616: fflast = flsz > 1 ? &fl[flsz - 2] : NULL;
617:
618: /* Filename first. */
619:
1.4 deraadt 620: if (!flist_recv_name(sess, fd, ff, flag, last)) {
1.1 benno 621: ERRX1(sess, "flist_recv_name");
622: goto out;
623: }
624:
625: /* Read the file size. */
626:
1.4 deraadt 627: if (!io_read_ulong(sess, fd, &lval)) {
1.1 benno 628: ERRX1(sess, "io_read_ulong");
629: goto out;
630: }
631: ff->st.size = lval;
632:
633: /* Read the modification time. */
634:
1.4 deraadt 635: if (!(FLIST_TIME_SAME & flag)) {
1.22 ! deraadt 636: if (!io_read_int(sess, fd, &uival)) {
1.1 benno 637: ERRX1(sess, "io_read_int");
638: goto out;
639: }
1.22 ! deraadt 640: ff->st.mtime = uival; /* beyond 2038 */
1.5 deraadt 641: } else if (fflast == NULL) {
1.1 benno 642: ERRX(sess, "same time without last entry");
643: goto out;
644: } else
645: ff->st.mtime = fflast->st.mtime;
646:
647: /* Read the file mode. */
648:
1.4 deraadt 649: if (!(FLIST_MODE_SAME & flag)) {
650: if (!io_read_int(sess, fd, &ival)) {
1.1 benno 651: ERRX1(sess, "io_read_int");
652: goto out;
653: }
654: ff->st.mode = ival;
1.5 deraadt 655: } else if (fflast == NULL) {
1.1 benno 656: ERRX(sess, "same mode without last entry");
657: goto out;
658: } else
659: ff->st.mode = fflast->st.mode;
660:
1.13 florian 661: /* Conditional part: uid. */
662:
663: if (sess->opts->preserve_uids) {
664: if (!(FLIST_UID_SAME & flag)) {
665: if (!io_read_int(sess, fd, &ival)) {
666: ERRX1(sess, "io_read_int");
667: goto out;
668: }
669: ff->st.uid = ival;
670: } else if (fflast == NULL) {
1.21 deraadt 671: ERRX(sess, "same uid without last entry");
1.13 florian 672: goto out;
673: } else
674: ff->st.uid = fflast->st.uid;
675: }
676:
1.6 benno 677: /* Conditional part: gid. */
678:
679: if (sess->opts->preserve_gids) {
1.11 benno 680: if (!(FLIST_GID_SAME & flag)) {
681: if (!io_read_int(sess, fd, &ival)) {
1.6 benno 682: ERRX1(sess, "io_read_int");
683: goto out;
684: }
685: ff->st.gid = ival;
1.11 benno 686: } else if (fflast == NULL) {
1.21 deraadt 687: ERRX(sess, "same gid without last entry");
1.6 benno 688: goto out;
689: } else
690: ff->st.gid = fflast->st.gid;
691: }
692:
1.17 florian 693: /* Conditional part: devices & special files. */
1.15 florian 694:
695: if ((sess->opts->devices && (S_ISBLK(ff->st.mode) ||
1.17 florian 696: S_ISCHR(ff->st.mode))) ||
1.15 florian 697: (sess->opts->specials && (S_ISFIFO(ff->st.mode) ||
698: S_ISSOCK(ff->st.mode)))) {
699: if (!(FLIST_RDEV_SAME & flag)) {
700: if (!io_read_int(sess, fd, &ival)) {
701: ERRX1(sess, "io_read_int");
702: goto out;
703: }
704: ff->st.rdev = ival;
705: } else if (fflast == NULL) {
1.17 florian 706: ERRX(sess, "same device without last entry");
1.15 florian 707: goto out;
708: } else
709: ff->st.rdev = fflast->st.rdev;
710: }
711:
1.6 benno 712: /* Conditional part: link. */
1.1 benno 713:
714: if (S_ISLNK(ff->st.mode) &&
715: sess->opts->preserve_links) {
1.4 deraadt 716: if (!io_read_size(sess, fd, &lsz)) {
1.1 benno 717: ERRX1(sess, "io_read_size");
718: goto out;
1.5 deraadt 719: } else if (lsz == 0) {
1.1 benno 720: ERRX(sess, "empty link name");
721: goto out;
722: }
723: ff->link = calloc(lsz + 1, 1);
1.5 deraadt 724: if (ff->link == NULL) {
1.1 benno 725: ERR(sess, "calloc");
726: goto out;
727: }
1.4 deraadt 728: if (!io_read_buf(sess, fd, ff->link, lsz)) {
1.1 benno 729: ERRX1(sess, "io_read_buf");
730: goto out;
731: }
732: }
733:
734: LOG3(sess, "%s: received file metadata: "
1.15 florian 735: "size %jd, mtime %jd, mode %o, rdev (%d, %d)",
1.1 benno 736: ff->path, (intmax_t)ff->st.size,
1.15 florian 737: (intmax_t)ff->st.mtime, ff->st.mode,
738: major(ff->st.rdev), minor(ff->st.rdev));
1.1 benno 739:
740: if (S_ISREG(ff->st.mode))
741: sess->total_size += ff->st.size;
742: }
743:
1.13 florian 744: /* Conditionally read the user/group list. */
745:
1.18 benno 746: if (sess->opts->preserve_uids && !sess->opts->numeric_ids) {
1.13 florian 747: if (!idents_recv(sess, fd, &uids, &uidsz)) {
748: ERRX1(sess, "idents_recv");
749: goto out;
750: }
751: LOG2(sess, "received uid list: %zu", uidsz);
752: }
1.7 benno 753:
1.18 benno 754: if (sess->opts->preserve_gids && !sess->opts->numeric_ids) {
1.8 benno 755: if (!idents_recv(sess, fd, &gids, &gidsz)) {
756: ERRX1(sess, "idents_recv");
1.7 benno 757: goto out;
758: }
759: LOG2(sess, "received gid list: %zu", gidsz);
760: }
761:
1.1 benno 762: /* Remember to order the received list. */
763:
764: LOG2(sess, "received file metadata list: %zu", flsz);
765: qsort(fl, flsz, sizeof(struct flist), flist_cmp);
766: flist_topdirs(sess, fl, flsz);
767: *sz = flsz;
768: *flp = fl;
1.7 benno 769:
1.13 florian 770: /* Conditionally remap and reassign identifiers. */
771:
1.18 benno 772: if (sess->opts->preserve_uids && !sess->opts->numeric_ids) {
1.13 florian 773: idents_remap(sess, 0, uids, uidsz);
774: idents_assign_uid(sess, fl, flsz, uids, uidsz);
775: }
1.7 benno 776:
1.18 benno 777: if (sess->opts->preserve_gids && !sess->opts->numeric_ids) {
1.13 florian 778: idents_remap(sess, 1, gids, gidsz);
779: idents_assign_gid(sess, fl, flsz, gids, gidsz);
1.7 benno 780: }
781:
1.8 benno 782: idents_free(gids, gidsz);
1.13 florian 783: idents_free(uids, uidsz);
1.1 benno 784: return 1;
785: out:
786: flist_free(fl, flsz);
1.8 benno 787: idents_free(gids, gidsz);
1.13 florian 788: idents_free(uids, uidsz);
1.1 benno 789: *sz = 0;
790: *flp = NULL;
791: return 0;
792: }
793:
794: /*
795: * Generate a flist possibly-recursively given a file root, which may
796: * also be a regular file or symlink.
797: * On success, augments the generated list in "flp" of length "sz".
798: * Returns zero on failure, non-zero on success.
799: */
800: static int
1.3 benno 801: flist_gen_dirent(struct sess *sess, char *root, struct flist **fl, size_t *sz,
802: size_t *max)
1.1 benno 803: {
804: char *cargv[2], *cp;
805: int rc = 0;
806: FTS *fts;
807: FTSENT *ent;
808: struct flist *f;
809: size_t flsz = 0, stripdir;
810: struct stat st;
811:
812: cargv[0] = root;
813: cargv[1] = NULL;
814:
815: /*
816: * If we're a file, then revert to the same actions we use for
817: * the non-recursive scan.
818: */
819:
1.5 deraadt 820: if (lstat(root, &st) == -1) {
1.1 benno 821: ERR(sess, "%s: lstat", root);
822: return 0;
823: } else if (S_ISREG(st.st_mode)) {
1.4 deraadt 824: if (!flist_realloc(sess, fl, sz, max)) {
1.1 benno 825: ERRX1(sess, "flist_realloc");
826: return 0;
827: }
828: f = &(*fl)[(*sz) - 1];
1.5 deraadt 829: assert(f != NULL);
1.1 benno 830:
1.4 deraadt 831: if (!flist_append(sess, f, &st, root)) {
1.1 benno 832: ERRX1(sess, "flist_append");
833: return 0;
1.10 benno 834: }
835: if (unveil(root, "r") == -1) {
1.1 benno 836: ERR(sess, "%s: unveil", root);
837: return 0;
838: }
839: return 1;
840: } else if (S_ISLNK(st.st_mode)) {
1.4 deraadt 841: if (!sess->opts->preserve_links) {
1.1 benno 842: WARNX(sess, "%s: skipping symlink", root);
843: return 1;
1.4 deraadt 844: } else if (!flist_realloc(sess, fl, sz, max)) {
1.1 benno 845: ERRX1(sess, "flist_realloc");
846: return 0;
847: }
848: f = &(*fl)[(*sz) - 1];
1.5 deraadt 849: assert(f != NULL);
1.1 benno 850:
1.4 deraadt 851: if (!flist_append(sess, f, &st, root)) {
1.1 benno 852: ERRX1(sess, "flist_append");
853: return 0;
1.10 benno 854: }
855: if (unveil(root, "r") == -1) {
1.1 benno 856: ERR(sess, "%s: unveil", root);
857: return 0;
858: }
859: return 1;
1.4 deraadt 860: } else if (!S_ISDIR(st.st_mode)) {
1.1 benno 861: WARNX(sess, "%s: skipping special", root);
862: return 1;
863: }
864:
865: /*
866: * If we end with a slash, it means that we're not supposed to
867: * copy the directory part itself---only the contents.
868: * So set "stripdir" to be what we take out.
869: */
870:
871: stripdir = strlen(root);
872: assert(stripdir > 0);
1.5 deraadt 873: if (root[stripdir - 1] != '/')
1.1 benno 874: stripdir = 0;
875:
876: /*
877: * If we're not stripping anything, then see if we need to strip
878: * out the leading material in the path up to and including the
879: * last directory component.
880: */
881:
1.5 deraadt 882: if (stripdir == 0)
883: if ((cp = strrchr(root, '/')) != NULL)
1.1 benno 884: stripdir = cp - root + 1;
885:
886: /*
887: * If we're recursive, then we need to take down all of the
888: * files and directory components, so use fts(3).
889: * Copying the information file-by-file into the flstat.
890: * We'll make sense of it in flist_send.
891: */
892:
1.5 deraadt 893: if ((fts = fts_open(cargv, FTS_PHYSICAL, NULL)) == NULL) {
1.1 benno 894: ERR(sess, "fts_open");
895: return 0;
896: }
897:
898: errno = 0;
1.5 deraadt 899: while ((ent = fts_read(fts)) != NULL) {
1.4 deraadt 900: if (!flist_fts_check(sess, ent)) {
1.1 benno 901: errno = 0;
902: continue;
903: }
904:
905: /* We don't allow symlinks without -l. */
906:
1.5 deraadt 907: assert(ent->fts_statp != NULL);
1.1 benno 908: if (S_ISLNK(ent->fts_statp->st_mode) &&
1.4 deraadt 909: !sess->opts->preserve_links) {
1.21 deraadt 910: WARNX(sess, "%s: skipping symlink", ent->fts_path);
1.1 benno 911: continue;
912: }
913:
914: /* Allocate a new file entry. */
915:
1.4 deraadt 916: if (!flist_realloc(sess, fl, sz, max)) {
1.1 benno 917: ERRX1(sess, "flist_realloc");
918: goto out;
919: }
920: flsz++;
921: f = &(*fl)[*sz - 1];
922:
923: /* Our path defaults to "." for the root. */
924:
1.20 deraadt 925: if (ent->fts_path[stripdir] == '\0') {
1.1 benno 926: if (asprintf(&f->path, "%s.", ent->fts_path) < 0) {
927: ERR(sess, "asprintf");
928: f->path = NULL;
929: goto out;
930: }
931: } else {
1.5 deraadt 932: if ((f->path = strdup(ent->fts_path)) == NULL) {
1.1 benno 933: ERR(sess, "strdup");
934: goto out;
935: }
936: }
937:
938: f->wpath = f->path + stripdir;
939: flist_copy_stat(f, ent->fts_statp);
940:
941: /* Optionally copy link information. */
942:
943: if (S_ISLNK(ent->fts_statp->st_mode)) {
944: f->link = symlink_read(sess, f->path);
1.5 deraadt 945: if (f->link == NULL) {
1.1 benno 946: ERRX1(sess, "symlink_read");
947: goto out;
948: }
949: }
950:
951: /* Reset errno for next fts_read() call. */
952: errno = 0;
953: }
954: if (errno) {
955: ERR(sess, "fts_read");
956: goto out;
1.10 benno 957: }
958: if (unveil(root, "r") == -1) {
1.1 benno 959: ERR(sess, "%s: unveil", root);
960: goto out;
961: }
962:
963: LOG3(sess, "generated %zu filenames: %s", flsz, root);
964: rc = 1;
965: out:
966: fts_close(fts);
967: return rc;
968: }
969:
970: /*
971: * Generate a flist recursively given the array of directories (or
972: * files, symlinks, doesn't matter) specified in argv (argc >0).
973: * On success, stores the generated list in "flp" with length "sz",
974: * which may be zero.
975: * Returns zero on failure, non-zero on success.
976: */
977: static int
1.3 benno 978: flist_gen_dirs(struct sess *sess, size_t argc, char **argv, struct flist **flp,
979: size_t *sz)
1.1 benno 980: {
981: size_t i, max = 0;
982:
983: for (i = 0; i < argc; i++)
1.4 deraadt 984: if (!flist_gen_dirent(sess, argv[i], flp, sz, &max))
1.1 benno 985: break;
986:
987: if (i == argc) {
988: LOG2(sess, "recursively generated %zu filenames", *sz);
989: return 1;
990: }
991:
992: ERRX1(sess, "flist_gen_dirent");
993: flist_free(*flp, max);
994: *flp = NULL;
995: *sz = 0;
996: return 0;
997: }
998:
999: /*
1000: * Generate list of files from the command-line argc (>0) and argv.
1001: * On success, stores the generated list in "flp" with length "sz",
1002: * which may be zero.
1003: * Returns zero on failure, non-zero on success.
1004: */
1005: static int
1.3 benno 1006: flist_gen_files(struct sess *sess, size_t argc, char **argv,
1007: struct flist **flp, size_t *sz)
1.1 benno 1008: {
1009: struct flist *fl = NULL, *f;
1010: size_t i, flsz = 0;
1011: struct stat st;
1012:
1013: assert(argc);
1014:
1.5 deraadt 1015: if ((fl = calloc(argc, sizeof(struct flist))) == NULL) {
1.1 benno 1016: ERR(sess, "calloc");
1017: return 0;
1018: }
1019:
1020: for (i = 0; i < argc; i++) {
1.20 deraadt 1021: if (argv[i][0] == '\0')
1.1 benno 1022: continue;
1.5 deraadt 1023: if (lstat(argv[i], &st) == -1) {
1.1 benno 1024: ERR(sess, "%s: lstat", argv[i]);
1025: goto out;
1026: }
1027:
1028: /*
1029: * File type checks.
1030: * In non-recursive mode, we don't accept directories.
1031: * We also skip symbolic links without -l.
1032: * Beyond that, we only accept regular files.
1033: */
1034:
1035: if (S_ISDIR(st.st_mode)) {
1036: WARNX(sess, "%s: skipping directory", argv[i]);
1037: continue;
1038: } else if (S_ISLNK(st.st_mode)) {
1.4 deraadt 1039: if (!sess->opts->preserve_links) {
1.21 deraadt 1040: WARNX(sess, "%s: skipping symlink", argv[i]);
1.1 benno 1041: continue;
1042: }
1.4 deraadt 1043: } else if (!S_ISREG(st.st_mode)) {
1.1 benno 1044: WARNX(sess, "%s: skipping special", argv[i]);
1045: continue;
1046: }
1047:
1.3 benno 1048:
1.1 benno 1049: f = &fl[flsz++];
1.5 deraadt 1050: assert(f != NULL);
1.1 benno 1051:
1052: /* Add this file to our file-system worldview. */
1053:
1.5 deraadt 1054: if (unveil(argv[i], "r") == -1) {
1.1 benno 1055: ERR(sess, "%s: unveil", argv[i]);
1056: goto out;
1.10 benno 1057: }
1058: if (!flist_append(sess, f, &st, argv[i])) {
1.1 benno 1059: ERRX1(sess, "flist_append");
1060: goto out;
1061: }
1062: }
1063:
1064: LOG2(sess, "non-recursively generated %zu filenames", flsz);
1065: *sz = flsz;
1066: *flp = fl;
1067: return 1;
1068: out:
1069: flist_free(fl, argc);
1070: *sz = 0;
1071: *flp = NULL;
1072: return 0;
1073: }
1074:
1075: /*
1076: * Generate a sorted, de-duplicated list of file metadata.
1077: * In non-recursive mode (the default), we use only the files we're
1078: * given.
1079: * Otherwise, directories are recursively examined.
1080: * Returns zero on failure, non-zero on success.
1081: * On success, "fl" will need to be freed with flist_free().
1082: */
1083: int
1.3 benno 1084: flist_gen(struct sess *sess, size_t argc, char **argv, struct flist **flp,
1085: size_t *sz)
1.1 benno 1086: {
1087: int rc;
1088:
1089: assert(argc > 0);
1090: rc = sess->opts->recursive ?
1091: flist_gen_dirs(sess, argc, argv, flp, sz) :
1092: flist_gen_files(sess, argc, argv, flp, sz);
1093:
1094: /* After scanning, lock our file-system view. */
1095:
1.5 deraadt 1096: if (unveil(NULL, NULL) == -1) {
1.1 benno 1097: ERR(sess, "unveil");
1098: return 0;
1.10 benno 1099: }
1100: if (!rc)
1.1 benno 1101: return 0;
1102:
1103: qsort(*flp, *sz, sizeof(struct flist), flist_cmp);
1104:
1105: if (flist_dedupe(sess, flp, sz)) {
1106: flist_topdirs(sess, *flp, *sz);
1107: return 1;
1108: }
1109:
1110: ERRX1(sess, "flist_dedupe");
1111: flist_free(*flp, *sz);
1112: *flp = NULL;
1113: *sz = 0;
1114: return 0;
1115: }
1116:
1117: /*
1118: * Generate a list of files in root to delete that are within the
1119: * top-level directories stipulated by "wfl".
1120: * Only handles symbolic links, directories, and regular files.
1121: * Returns zero on failure (fl and flsz will be NULL and zero), non-zero
1122: * on success.
1123: * On success, "fl" will need to be freed with flist_free().
1124: */
1125: int
1.3 benno 1126: flist_gen_dels(struct sess *sess, const char *root, struct flist **fl,
1127: size_t *sz, const struct flist *wfl, size_t wflsz)
1.1 benno 1128: {
1129: char **cargv = NULL;
1130: int rc = 0, c;
1131: FTS *fts = NULL;
1132: FTSENT *ent;
1133: struct flist *f;
1134: size_t cargvs = 0, i, j, max = 0, stripdir;
1135: ENTRY hent;
1136: ENTRY *hentp;
1.2 benno 1137:
1.1 benno 1138: *fl = NULL;
1139: *sz = 0;
1140:
1141: /* Only run this code when we're recursive. */
1142:
1.4 deraadt 1143: if (!sess->opts->recursive)
1.1 benno 1144: return 1;
1145:
1.2 benno 1146: /*
1.1 benno 1147: * Gather up all top-level directories for scanning.
1148: * This is stipulated by rsync's --delete behaviour, where we
1149: * only delete things in the top-level directories given on the
1150: * command line.
1151: */
1152:
1153: assert(wflsz > 0);
1154: for (i = 0; i < wflsz; i++)
1155: if (FLSTAT_TOP_DIR & wfl[i].st.flags)
1156: cargvs++;
1.5 deraadt 1157: if (cargvs == 0)
1.1 benno 1158: return 1;
1159:
1.5 deraadt 1160: if ((cargv = calloc(cargvs + 1, sizeof(char *))) == NULL) {
1.1 benno 1161: ERR(sess, "calloc");
1162: return 0;
1163: }
1164:
1165: /*
1166: * If we're given just a "." as the first entry, that means
1167: * we're doing a relative copy with a trailing slash.
1168: * Special-case this just for the sake of simplicity.
1169: * Otherwise, look through all top-levels.
1170: */
1171:
1.5 deraadt 1172: if (wflsz && strcmp(wfl[0].wpath, ".") == 0) {
1173: assert(cargvs == 1);
1.1 benno 1174: assert(S_ISDIR(wfl[0].st.mode));
1175: if (asprintf(&cargv[0], "%s/", root) < 0) {
1176: ERR(sess, "asprintf");
1177: cargv[0] = NULL;
1178: goto out;
1179: }
1180: cargv[1] = NULL;
1181: } else {
1182: for (i = j = 0; i < wflsz; i++) {
1.4 deraadt 1183: if (!(FLSTAT_TOP_DIR & wfl[i].st.flags))
1.1 benno 1184: continue;
1185: assert(S_ISDIR(wfl[i].st.mode));
1186: assert(strcmp(wfl[i].wpath, "."));
1.5 deraadt 1187: c = asprintf(&cargv[j], "%s/%s", root, wfl[i].wpath);
1.1 benno 1188: if (c < 0) {
1189: ERR(sess, "asprintf");
1190: cargv[j] = NULL;
1191: goto out;
1192: }
1.14 deraadt 1193: LOG4(sess, "%s: will scan for deletions", cargv[j]);
1.1 benno 1194: j++;
1195: }
1196: assert(j == cargvs);
1197: cargv[j] = NULL;
1198: }
1199:
1200: LOG2(sess, "delete from %zu directories", cargvs);
1201:
1202: /*
1203: * Next, use the standard hcreate(3) hashtable interface to hash
1204: * all of the files that we want to synchronise.
1205: * This way, we'll be able to determine which files we want to
1206: * delete in O(n) time instead of O(n * search) time.
1207: * Plus, we can do the scan in-band and only allocate the files
1208: * we want to delete.
1209: */
1210:
1.4 deraadt 1211: if (!hcreate(wflsz)) {
1.1 benno 1212: ERR(sess, "hcreate");
1213: goto out;
1214: }
1215:
1216: for (i = 0; i < wflsz; i++) {
1217: memset(&hent, 0, sizeof(ENTRY));
1.5 deraadt 1218: if ((hent.key = strdup(wfl[i].wpath)) == NULL) {
1.1 benno 1219: ERR(sess, "strdup");
1220: goto out;
1221: }
1.5 deraadt 1222: if ((hentp = hsearch(hent, ENTER)) == NULL) {
1.1 benno 1223: ERR(sess, "hsearch");
1224: goto out;
1225: } else if (hentp->key != hent.key) {
1226: ERRX(sess, "%s: duplicate", wfl[i].wpath);
1227: free(hent.key);
1228: goto out;
1229: }
1230: }
1231:
1.2 benno 1232: /*
1.1 benno 1233: * Now we're going to try to descend into all of the top-level
1234: * directories stipulated by the file list.
1235: * If the directories don't exist, it's ok.
1236: */
1237:
1.5 deraadt 1238: if ((fts = fts_open(cargv, FTS_PHYSICAL, NULL)) == NULL) {
1.1 benno 1239: ERR(sess, "fts_open");
1240: goto out;
1241: }
1242:
1243: stripdir = strlen(root) + 1;
1244: errno = 0;
1.5 deraadt 1245: while ((ent = fts_read(fts)) != NULL) {
1246: if (ent->fts_info == FTS_NS)
1.1 benno 1247: continue;
1.4 deraadt 1248: if (!flist_fts_check(sess, ent)) {
1.1 benno 1249: errno = 0;
1250: continue;
1251: } else if (stripdir >= ent->fts_pathlen)
1252: continue;
1253:
1254: /* Look up in hashtable. */
1255:
1256: memset(&hent, 0, sizeof(ENTRY));
1257: hent.key = ent->fts_path + stripdir;
1.5 deraadt 1258: if (hsearch(hent, FIND) != NULL)
1.1 benno 1259: continue;
1260:
1261: /* Not found: we'll delete it. */
1262:
1.4 deraadt 1263: if (!flist_realloc(sess, fl, sz, &max)) {
1.1 benno 1264: ERRX1(sess, "flist_realloc");
1265: goto out;
1266: }
1267: f = &(*fl)[*sz - 1];
1268:
1.5 deraadt 1269: if ((f->path = strdup(ent->fts_path)) == NULL) {
1.1 benno 1270: ERR(sess, "strdup");
1271: goto out;
1272: }
1273: f->wpath = f->path + stripdir;
1.5 deraadt 1274: assert(ent->fts_statp != NULL);
1.1 benno 1275: flist_copy_stat(f, ent->fts_statp);
1276: errno = 0;
1277: }
1278:
1279: if (errno) {
1280: ERR(sess, "fts_read");
1281: goto out;
1282: }
1283:
1284: qsort(*fl, *sz, sizeof(struct flist), flist_cmp);
1285: rc = 1;
1286: out:
1.5 deraadt 1287: if (fts != NULL)
1.1 benno 1288: fts_close(fts);
1289: for (i = 0; i < cargvs; i++)
1290: free(cargv[i]);
1291: free(cargv);
1292: hdestroy();
1293: return rc;
1294: }
1295:
1296: /*
1297: * Delete all files and directories in "fl".
1298: * If called with a zero-length "fl", does nothing.
1299: * If dry_run is specified, simply write what would be done.
1300: * Return zero on failure, non-zero on success.
1301: */
1302: int
1.3 benno 1303: flist_del(struct sess *sess, int root, const struct flist *fl, size_t flsz)
1.1 benno 1304: {
1305: ssize_t i;
1306: int flag;
1307:
1.5 deraadt 1308: if (flsz == 0)
1.1 benno 1309: return 1;
1310:
1311: assert(sess->opts->del);
1312: assert(sess->opts->recursive);
1313:
1314: for (i = flsz - 1; i >= 0; i--) {
1315: LOG1(sess, "%s: deleting", fl[i].wpath);
1316: if (sess->opts->dry_run)
1317: continue;
1.5 deraadt 1318: assert(root != -1);
1.1 benno 1319: flag = S_ISDIR(fl[i].st.mode) ? AT_REMOVEDIR : 0;
1.5 deraadt 1320: if (unlinkat(root, fl[i].wpath, flag) == -1 &&
1321: errno != ENOENT) {
1.1 benno 1322: ERR(sess, "%s: unlinkat", fl[i].wpath);
1323: return 0;
1324: }
1325: }
1326:
1327: return 1;
1328: }