version 1.24, 2019/04/04 04:19:54 |
version 1.25, 2019/05/08 20:00:25 |
|
|
|
|
new = calloc(*sz, sizeof(struct flist)); |
new = calloc(*sz, sizeof(struct flist)); |
if (new == NULL) { |
if (new == NULL) { |
ERR(sess, "calloc"); |
ERR("calloc"); |
return 0; |
return 0; |
} |
} |
|
|
|
|
if (strcmp(f->path, fnext->path) == 0) { |
if (strcmp(f->path, fnext->path) == 0) { |
new[j++] = *f; |
new[j++] = *f; |
i++; |
i++; |
WARNX(sess, "%s: duplicate path: %s", |
WARNX("%s: duplicate path: %s", |
f->wpath, f->path); |
f->wpath, f->path); |
free(fnext->path); |
free(fnext->path); |
free(fnext->link); |
free(fnext->link); |
|
|
continue; |
continue; |
} |
} |
|
|
ERRX(sess, "%s: duplicate working path for " |
ERRX("%s: duplicate working path for " |
"possibly different file: %s, %s", |
"possibly different file: %s, %s", |
f->wpath, f->path, fnext->path); |
f->wpath, f->path, fnext->path); |
free(new); |
free(new); |
|
|
if (cp != NULL && cp[1] != '\0') |
if (cp != NULL && cp[1] != '\0') |
continue; |
continue; |
fl[i].st.flags |= FLSTAT_TOP_DIR; |
fl[i].st.flags |= FLSTAT_TOP_DIR; |
LOG4(sess, "%s: top-level", fl[i].wpath); |
LOG4("%s: top-level", fl[i].wpath); |
} |
} |
} else if (flsz) { |
} else if (flsz) { |
fl[0].st.flags |= FLSTAT_TOP_DIR; |
fl[0].st.flags |= FLSTAT_TOP_DIR; |
LOG4(sess, "%s: top-level", fl[0].wpath); |
LOG4("%s: top-level", fl[0].wpath); |
} |
} |
} |
} |
|
|
|
|
return 1; |
return 1; |
|
|
if (ent->fts_info == FTS_DC) { |
if (ent->fts_info == FTS_DC) { |
WARNX(sess, "%s: directory cycle", ent->fts_path); |
WARNX("%s: directory cycle", ent->fts_path); |
} else if (ent->fts_info == FTS_DNR) { |
} else if (ent->fts_info == FTS_DNR) { |
errno = ent->fts_errno; |
errno = ent->fts_errno; |
WARN(sess, "%s: unreadable directory", ent->fts_path); |
WARN("%s: unreadable directory", ent->fts_path); |
} else if (ent->fts_info == FTS_DOT) { |
} else if (ent->fts_info == FTS_DOT) { |
WARNX(sess, "%s: skipping dot-file", ent->fts_path); |
WARNX("%s: skipping dot-file", ent->fts_path); |
} else if (ent->fts_info == FTS_ERR) { |
} else if (ent->fts_info == FTS_ERR) { |
errno = ent->fts_errno; |
errno = ent->fts_errno; |
WARN(sess, "%s", ent->fts_path); |
WARN("%s", ent->fts_path); |
} else if (ent->fts_info == FTS_DEFAULT) { |
} else if (ent->fts_info == FTS_DEFAULT) { |
if ((sess->opts->devices && (S_ISBLK(ent->fts_statp->st_mode) || |
if ((sess->opts->devices && (S_ISBLK(ent->fts_statp->st_mode) || |
S_ISCHR(ent->fts_statp->st_mode))) || |
S_ISCHR(ent->fts_statp->st_mode))) || |
|
|
S_ISSOCK(ent->fts_statp->st_mode)))) { |
S_ISSOCK(ent->fts_statp->st_mode)))) { |
return 1; |
return 1; |
} |
} |
WARNX(sess, "%s: skipping special", ent->fts_path); |
WARNX("%s: skipping special", ent->fts_path); |
} else if (ent->fts_info == FTS_NS) { |
} else if (ent->fts_info == FTS_NS) { |
errno = ent->fts_errno; |
errno = ent->fts_errno; |
WARN(sess, "%s: could not stat", ent->fts_path); |
WARN("%s: could not stat", ent->fts_path); |
} |
} |
|
|
return 0; |
return 0; |
|
|
|
|
/* Double-check that we've no pending multiplexed data. */ |
/* Double-check that we've no pending multiplexed data. */ |
|
|
LOG2(sess, "sending file metadata list: %zu", flsz); |
LOG2("sending file metadata list: %zu", flsz); |
|
|
for (i = 0; i < flsz; i++) { |
for (i = 0; i < flsz; i++) { |
f = &fl[i]; |
f = &fl[i]; |
|
|
if (sess->mplex_reads && |
if (sess->mplex_reads && |
io_read_check(sess, fdin) && |
io_read_check(sess, fdin) && |
!io_read_flush(sess, fdin)) { |
!io_read_flush(sess, fdin)) { |
ERRX1(sess, "io_read_flush"); |
ERRX1("io_read_flush"); |
goto out; |
goto out; |
} |
} |
|
|
|
|
if ((FLSTAT_TOP_DIR & f->st.flags)) |
if ((FLSTAT_TOP_DIR & f->st.flags)) |
flag |= FLIST_TOP_LEVEL; |
flag |= FLIST_TOP_LEVEL; |
|
|
LOG3(sess, "%s: sending file metadata: " |
LOG3("%s: sending file metadata: " |
"size %jd, mtime %jd, mode %o", |
"size %jd, mtime %jd, mode %o", |
fn, (intmax_t)f->st.size, |
fn, (intmax_t)f->st.size, |
(intmax_t)f->st.mtime, f->st.mode); |
(intmax_t)f->st.mtime, f->st.mode); |
|
|
/* FIXME: buffer this. */ |
/* FIXME: buffer this. */ |
|
|
if (!io_write_byte(sess, fdout, flag)) { |
if (!io_write_byte(sess, fdout, flag)) { |
ERRX1(sess, "io_write_byte"); |
ERRX1("io_write_byte"); |
goto out; |
goto out; |
} else if (!io_write_int(sess, fdout, sz)) { |
} else if (!io_write_int(sess, fdout, sz)) { |
ERRX1(sess, "io_write_int"); |
ERRX1("io_write_int"); |
goto out; |
goto out; |
} else if (!io_write_buf(sess, fdout, fn, sz)) { |
} else if (!io_write_buf(sess, fdout, fn, sz)) { |
ERRX1(sess, "io_write_buf"); |
ERRX1("io_write_buf"); |
goto out; |
goto out; |
} else if (!io_write_long(sess, fdout, f->st.size)) { |
} else if (!io_write_long(sess, fdout, f->st.size)) { |
ERRX1(sess, "io_write_long"); |
ERRX1("io_write_long"); |
goto out; |
goto out; |
} else if (!io_write_uint(sess, fdout, (uint32_t)f->st.mtime)) { |
} else if (!io_write_uint(sess, fdout, (uint32_t)f->st.mtime)) { |
ERRX1(sess, "io_write_uint"); |
ERRX1("io_write_uint"); |
goto out; |
goto out; |
} else if (!io_write_uint(sess, fdout, f->st.mode)) { |
} else if (!io_write_uint(sess, fdout, f->st.mode)) { |
ERRX1(sess, "io_write_uint"); |
ERRX1("io_write_uint"); |
goto out; |
goto out; |
} |
} |
|
|
|
|
|
|
if (sess->opts->preserve_uids) { |
if (sess->opts->preserve_uids) { |
if (!io_write_uint(sess, fdout, f->st.uid)) { |
if (!io_write_uint(sess, fdout, f->st.uid)) { |
ERRX1(sess, "io_write_uint"); |
ERRX1("io_write_uint"); |
goto out; |
goto out; |
} |
} |
if (!idents_add(sess, 0, &uids, &uidsz, f->st.uid)) { |
if (!idents_add(sess, 0, &uids, &uidsz, f->st.uid)) { |
ERRX1(sess, "idents_add"); |
ERRX1("idents_add"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
|
|
if (sess->opts->preserve_gids) { |
if (sess->opts->preserve_gids) { |
if (!io_write_uint(sess, fdout, f->st.gid)) { |
if (!io_write_uint(sess, fdout, f->st.gid)) { |
ERRX1(sess, "io_write_uint"); |
ERRX1("io_write_uint"); |
goto out; |
goto out; |
} |
} |
if (!idents_add(sess, 1, &gids, &gidsz, f->st.gid)) { |
if (!idents_add(sess, 1, &gids, &gidsz, f->st.gid)) { |
ERRX1(sess, "idents_add"); |
ERRX1("idents_add"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
(sess->opts->specials && (S_ISFIFO(f->st.mode) || |
(sess->opts->specials && (S_ISFIFO(f->st.mode) || |
S_ISSOCK(f->st.mode)))) { |
S_ISSOCK(f->st.mode)))) { |
if (!io_write_int(sess, fdout, f->st.rdev)) { |
if (!io_write_int(sess, fdout, f->st.rdev)) { |
ERRX1(sess, "io_write_int"); |
ERRX1("io_write_int"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
sz = strlen(f->link); |
sz = strlen(f->link); |
assert(sz < INT32_MAX); |
assert(sz < INT32_MAX); |
if (!io_write_int(sess, fdout, sz)) { |
if (!io_write_int(sess, fdout, sz)) { |
ERRX1(sess, "io_write_int"); |
ERRX1("io_write_int"); |
goto out; |
goto out; |
} |
} |
if (!io_write_buf(sess, fdout, fn, sz)) { |
if (!io_write_buf(sess, fdout, fn, sz)) { |
ERRX1(sess, "io_write_buf"); |
ERRX1("io_write_buf"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
/* Signal end of file list. */ |
/* Signal end of file list. */ |
|
|
if (!io_write_byte(sess, fdout, 0)) { |
if (!io_write_byte(sess, fdout, 0)) { |
ERRX1(sess, "io_write_byte"); |
ERRX1("io_write_byte"); |
goto out; |
goto out; |
} |
} |
|
|
/* Conditionally write identifier lists. */ |
/* Conditionally write identifier lists. */ |
|
|
if (sess->opts->preserve_uids && !sess->opts->numeric_ids) { |
if (sess->opts->preserve_uids && !sess->opts->numeric_ids) { |
LOG2(sess, "sending uid list: %zu", uidsz); |
LOG2("sending uid list: %zu", uidsz); |
if (!idents_send(sess, fdout, uids, uidsz)) { |
if (!idents_send(sess, fdout, uids, uidsz)) { |
ERRX1(sess, "idents_send"); |
ERRX1("idents_send"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
if (sess->opts->preserve_gids && !sess->opts->numeric_ids) { |
if (sess->opts->preserve_gids && !sess->opts->numeric_ids) { |
LOG2(sess, "sending gid list: %zu", gidsz); |
LOG2("sending gid list: %zu", gidsz); |
if (!idents_send(sess, fdout, gids, gidsz)) { |
if (!idents_send(sess, fdout, gids, gidsz)) { |
ERRX1(sess, "idents_send"); |
ERRX1("idents_send"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
|
|
if (FLIST_NAME_SAME & flags) { |
if (FLIST_NAME_SAME & flags) { |
if (!io_read_byte(sess, fd, &bval)) { |
if (!io_read_byte(sess, fd, &bval)) { |
ERRX1(sess, "io_read_byte"); |
ERRX1("io_read_byte"); |
return 0; |
return 0; |
} |
} |
partial = bval; |
partial = bval; |
|
|
|
|
if (FLIST_NAME_LONG & flags) { |
if (FLIST_NAME_LONG & flags) { |
if (!io_read_size(sess, fd, &pathlen)) { |
if (!io_read_size(sess, fd, &pathlen)) { |
ERRX1(sess, "io_read_size"); |
ERRX1("io_read_size"); |
return 0; |
return 0; |
} |
} |
} else { |
} else { |
if (!io_read_byte(sess, fd, &bval)) { |
if (!io_read_byte(sess, fd, &bval)) { |
ERRX1(sess, "io_read_byte"); |
ERRX1("io_read_byte"); |
return 0; |
return 0; |
} |
} |
pathlen = bval; |
pathlen = bval; |
|
|
/* FIXME: maximum pathname length. */ |
/* FIXME: maximum pathname length. */ |
|
|
if ((len = pathlen + partial) == 0) { |
if ((len = pathlen + partial) == 0) { |
ERRX(sess, "security violation: zero-length pathname"); |
ERRX("security violation: zero-length pathname"); |
return 0; |
return 0; |
} |
} |
|
|
if ((f->path = malloc(len + 1)) == NULL) { |
if ((f->path = malloc(len + 1)) == NULL) { |
ERR(sess, "malloc"); |
ERR("malloc"); |
return 0; |
return 0; |
} |
} |
f->path[len] = '\0'; |
f->path[len] = '\0'; |
|
|
memcpy(f->path, last, partial); |
memcpy(f->path, last, partial); |
|
|
if (!io_read_buf(sess, fd, f->path + partial, pathlen)) { |
if (!io_read_buf(sess, fd, f->path + partial, pathlen)) { |
ERRX1(sess, "io_read_buf"); |
ERRX1("io_read_buf"); |
return 0; |
return 0; |
} |
} |
|
|
if (f->path[0] == '/') { |
if (f->path[0] == '/') { |
ERRX(sess, "security violation: absolute pathname: %s", |
ERRX("security violation: absolute pathname: %s", |
f->path); |
f->path); |
return 0; |
return 0; |
} |
} |
|
|
(len > 2 && strcmp(f->path + len - 3, "/..") == 0) || |
(len > 2 && strcmp(f->path + len - 3, "/..") == 0) || |
(len > 2 && strncmp(f->path, "../", 3) == 0) || |
(len > 2 && strncmp(f->path, "../", 3) == 0) || |
strcmp(f->path, "..") == 0) { |
strcmp(f->path, "..") == 0) { |
ERRX(sess, "%s: security violation: backtracking pathname", |
ERRX("%s: security violation: backtracking pathname", |
f->path); |
f->path); |
return 0; |
return 0; |
} |
} |
|
|
pp = recallocarray(*fl, *max, |
pp = recallocarray(*fl, *max, |
*max + FLIST_CHUNK_SIZE, sizeof(struct flist)); |
*max + FLIST_CHUNK_SIZE, sizeof(struct flist)); |
if (pp == NULL) { |
if (pp == NULL) { |
ERR(sess, "recallocarray"); |
ERR("recallocarray"); |
return 0; |
return 0; |
} |
} |
*fl = pp; |
*fl = pp; |
|
|
*/ |
*/ |
|
|
if ((f->path = strdup(path)) == NULL) { |
if ((f->path = strdup(path)) == NULL) { |
ERR(sess, "strdup"); |
ERR("strdup"); |
return 0; |
return 0; |
} |
} |
|
|
|
|
if (S_ISLNK(st->st_mode)) { |
if (S_ISLNK(st->st_mode)) { |
f->link = symlink_read(sess, f->path); |
f->link = symlink_read(sess, f->path); |
if (f->link == NULL) { |
if (f->link == NULL) { |
ERRX1(sess, "symlink_read"); |
ERRX1("symlink_read"); |
return 0; |
return 0; |
} |
} |
} |
} |
|
|
|
|
for (;;) { |
for (;;) { |
if (!io_read_byte(sess, fd, &flag)) { |
if (!io_read_byte(sess, fd, &flag)) { |
ERRX1(sess, "io_read_byte"); |
ERRX1("io_read_byte"); |
goto out; |
goto out; |
} else if (flag == 0) |
} else if (flag == 0) |
break; |
break; |
|
|
if (!flist_realloc(sess, &fl, &flsz, &flmax)) { |
if (!flist_realloc(sess, &fl, &flsz, &flmax)) { |
ERRX1(sess, "flist_realloc"); |
ERRX1("flist_realloc"); |
goto out; |
goto out; |
} |
} |
|
|
|
|
/* Filename first. */ |
/* Filename first. */ |
|
|
if (!flist_recv_name(sess, fd, ff, flag, last)) { |
if (!flist_recv_name(sess, fd, ff, flag, last)) { |
ERRX1(sess, "flist_recv_name"); |
ERRX1("flist_recv_name"); |
goto out; |
goto out; |
} |
} |
|
|
/* Read the file size. */ |
/* Read the file size. */ |
|
|
if (!io_read_long(sess, fd, &lval)) { |
if (!io_read_long(sess, fd, &lval)) { |
ERRX1(sess, "io_read_long"); |
ERRX1("io_read_long"); |
goto out; |
goto out; |
} |
} |
ff->st.size = lval; |
ff->st.size = lval; |
|
|
|
|
if (!(FLIST_TIME_SAME & flag)) { |
if (!(FLIST_TIME_SAME & flag)) { |
if (!io_read_uint(sess, fd, &uival)) { |
if (!io_read_uint(sess, fd, &uival)) { |
ERRX1(sess, "io_read_int"); |
ERRX1("io_read_int"); |
goto out; |
goto out; |
} |
} |
ff->st.mtime = uival; /* beyond 2038 */ |
ff->st.mtime = uival; /* beyond 2038 */ |
} else if (fflast == NULL) { |
} else if (fflast == NULL) { |
ERRX(sess, "same time without last entry"); |
ERRX("same time without last entry"); |
goto out; |
goto out; |
} else |
} else |
ff->st.mtime = fflast->st.mtime; |
ff->st.mtime = fflast->st.mtime; |
|
|
|
|
if (!(FLIST_MODE_SAME & flag)) { |
if (!(FLIST_MODE_SAME & flag)) { |
if (!io_read_uint(sess, fd, &uival)) { |
if (!io_read_uint(sess, fd, &uival)) { |
ERRX1(sess, "io_read_int"); |
ERRX1("io_read_int"); |
goto out; |
goto out; |
} |
} |
ff->st.mode = uival; |
ff->st.mode = uival; |
} else if (fflast == NULL) { |
} else if (fflast == NULL) { |
ERRX(sess, "same mode without last entry"); |
ERRX("same mode without last entry"); |
goto out; |
goto out; |
} else |
} else |
ff->st.mode = fflast->st.mode; |
ff->st.mode = fflast->st.mode; |
|
|
if (sess->opts->preserve_uids) { |
if (sess->opts->preserve_uids) { |
if (!(FLIST_UID_SAME & flag)) { |
if (!(FLIST_UID_SAME & flag)) { |
if (!io_read_uint(sess, fd, &uival)) { |
if (!io_read_uint(sess, fd, &uival)) { |
ERRX1(sess, "io_read_int"); |
ERRX1("io_read_int"); |
goto out; |
goto out; |
} |
} |
ff->st.uid = uival; |
ff->st.uid = uival; |
} else if (fflast == NULL) { |
} else if (fflast == NULL) { |
ERRX(sess, "same uid without last entry"); |
ERRX("same uid without last entry"); |
goto out; |
goto out; |
} else |
} else |
ff->st.uid = fflast->st.uid; |
ff->st.uid = fflast->st.uid; |
|
|
if (sess->opts->preserve_gids) { |
if (sess->opts->preserve_gids) { |
if (!(FLIST_GID_SAME & flag)) { |
if (!(FLIST_GID_SAME & flag)) { |
if (!io_read_uint(sess, fd, &uival)) { |
if (!io_read_uint(sess, fd, &uival)) { |
ERRX1(sess, "io_read_int"); |
ERRX1("io_read_int"); |
goto out; |
goto out; |
} |
} |
ff->st.gid = uival; |
ff->st.gid = uival; |
} else if (fflast == NULL) { |
} else if (fflast == NULL) { |
ERRX(sess, "same gid without last entry"); |
ERRX("same gid without last entry"); |
goto out; |
goto out; |
} else |
} else |
ff->st.gid = fflast->st.gid; |
ff->st.gid = fflast->st.gid; |
|
|
S_ISSOCK(ff->st.mode)))) { |
S_ISSOCK(ff->st.mode)))) { |
if (!(FLIST_RDEV_SAME & flag)) { |
if (!(FLIST_RDEV_SAME & flag)) { |
if (!io_read_int(sess, fd, &ival)) { |
if (!io_read_int(sess, fd, &ival)) { |
ERRX1(sess, "io_read_int"); |
ERRX1("io_read_int"); |
goto out; |
goto out; |
} |
} |
ff->st.rdev = ival; |
ff->st.rdev = ival; |
} else if (fflast == NULL) { |
} else if (fflast == NULL) { |
ERRX(sess, "same device without last entry"); |
ERRX("same device without last entry"); |
goto out; |
goto out; |
} else |
} else |
ff->st.rdev = fflast->st.rdev; |
ff->st.rdev = fflast->st.rdev; |
|
|
if (S_ISLNK(ff->st.mode) && |
if (S_ISLNK(ff->st.mode) && |
sess->opts->preserve_links) { |
sess->opts->preserve_links) { |
if (!io_read_size(sess, fd, &lsz)) { |
if (!io_read_size(sess, fd, &lsz)) { |
ERRX1(sess, "io_read_size"); |
ERRX1("io_read_size"); |
goto out; |
goto out; |
} else if (lsz == 0) { |
} else if (lsz == 0) { |
ERRX(sess, "empty link name"); |
ERRX("empty link name"); |
goto out; |
goto out; |
} |
} |
ff->link = calloc(lsz + 1, 1); |
ff->link = calloc(lsz + 1, 1); |
if (ff->link == NULL) { |
if (ff->link == NULL) { |
ERR(sess, "calloc"); |
ERR("calloc"); |
goto out; |
goto out; |
} |
} |
if (!io_read_buf(sess, fd, ff->link, lsz)) { |
if (!io_read_buf(sess, fd, ff->link, lsz)) { |
ERRX1(sess, "io_read_buf"); |
ERRX1("io_read_buf"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
LOG3(sess, "%s: received file metadata: " |
LOG3("%s: received file metadata: " |
"size %jd, mtime %jd, mode %o, rdev (%d, %d)", |
"size %jd, mtime %jd, mode %o, rdev (%d, %d)", |
ff->path, (intmax_t)ff->st.size, |
ff->path, (intmax_t)ff->st.size, |
(intmax_t)ff->st.mtime, ff->st.mode, |
(intmax_t)ff->st.mtime, ff->st.mode, |
|
|
|
|
if (sess->opts->preserve_uids && !sess->opts->numeric_ids) { |
if (sess->opts->preserve_uids && !sess->opts->numeric_ids) { |
if (!idents_recv(sess, fd, &uids, &uidsz)) { |
if (!idents_recv(sess, fd, &uids, &uidsz)) { |
ERRX1(sess, "idents_recv"); |
ERRX1("idents_recv"); |
goto out; |
goto out; |
} |
} |
LOG2(sess, "received uid list: %zu", uidsz); |
LOG2("received uid list: %zu", uidsz); |
} |
} |
|
|
if (sess->opts->preserve_gids && !sess->opts->numeric_ids) { |
if (sess->opts->preserve_gids && !sess->opts->numeric_ids) { |
if (!idents_recv(sess, fd, &gids, &gidsz)) { |
if (!idents_recv(sess, fd, &gids, &gidsz)) { |
ERRX1(sess, "idents_recv"); |
ERRX1("idents_recv"); |
goto out; |
goto out; |
} |
} |
LOG2(sess, "received gid list: %zu", gidsz); |
LOG2("received gid list: %zu", gidsz); |
} |
} |
|
|
/* Remember to order the received list. */ |
/* Remember to order the received list. */ |
|
|
LOG2(sess, "received file metadata list: %zu", flsz); |
LOG2("received file metadata list: %zu", flsz); |
qsort(fl, flsz, sizeof(struct flist), flist_cmp); |
qsort(fl, flsz, sizeof(struct flist), flist_cmp); |
flist_topdirs(sess, fl, flsz); |
flist_topdirs(sess, fl, flsz); |
*sz = flsz; |
*sz = flsz; |
|
|
*/ |
*/ |
|
|
if (lstat(root, &st) == -1) { |
if (lstat(root, &st) == -1) { |
ERR(sess, "%s: lstat", root); |
ERR("%s: lstat", root); |
return 0; |
return 0; |
} else if (S_ISREG(st.st_mode)) { |
} else if (S_ISREG(st.st_mode)) { |
if (!flist_realloc(sess, fl, sz, max)) { |
if (!flist_realloc(sess, fl, sz, max)) { |
ERRX1(sess, "flist_realloc"); |
ERRX1("flist_realloc"); |
return 0; |
return 0; |
} |
} |
f = &(*fl)[(*sz) - 1]; |
f = &(*fl)[(*sz) - 1]; |
assert(f != NULL); |
assert(f != NULL); |
|
|
if (!flist_append(sess, f, &st, root)) { |
if (!flist_append(sess, f, &st, root)) { |
ERRX1(sess, "flist_append"); |
ERRX1("flist_append"); |
return 0; |
return 0; |
} |
} |
if (unveil(root, "r") == -1) { |
if (unveil(root, "r") == -1) { |
ERR(sess, "%s: unveil", root); |
ERR("%s: unveil", root); |
return 0; |
return 0; |
} |
} |
return 1; |
return 1; |
} else if (S_ISLNK(st.st_mode)) { |
} else if (S_ISLNK(st.st_mode)) { |
if (!sess->opts->preserve_links) { |
if (!sess->opts->preserve_links) { |
WARNX(sess, "%s: skipping symlink", root); |
WARNX("%s: skipping symlink", root); |
return 1; |
return 1; |
} else if (!flist_realloc(sess, fl, sz, max)) { |
} else if (!flist_realloc(sess, fl, sz, max)) { |
ERRX1(sess, "flist_realloc"); |
ERRX1("flist_realloc"); |
return 0; |
return 0; |
} |
} |
f = &(*fl)[(*sz) - 1]; |
f = &(*fl)[(*sz) - 1]; |
assert(f != NULL); |
assert(f != NULL); |
|
|
if (!flist_append(sess, f, &st, root)) { |
if (!flist_append(sess, f, &st, root)) { |
ERRX1(sess, "flist_append"); |
ERRX1("flist_append"); |
return 0; |
return 0; |
} |
} |
if (unveil(root, "r") == -1) { |
if (unveil(root, "r") == -1) { |
ERR(sess, "%s: unveil", root); |
ERR("%s: unveil", root); |
return 0; |
return 0; |
} |
} |
return 1; |
return 1; |
} else if (!S_ISDIR(st.st_mode)) { |
} else if (!S_ISDIR(st.st_mode)) { |
WARNX(sess, "%s: skipping special", root); |
WARNX("%s: skipping special", root); |
return 1; |
return 1; |
} |
} |
|
|
|
|
*/ |
*/ |
|
|
if ((fts = fts_open(cargv, FTS_PHYSICAL, NULL)) == NULL) { |
if ((fts = fts_open(cargv, FTS_PHYSICAL, NULL)) == NULL) { |
ERR(sess, "fts_open"); |
ERR("fts_open"); |
return 0; |
return 0; |
} |
} |
|
|
|
|
assert(ent->fts_statp != NULL); |
assert(ent->fts_statp != NULL); |
if (S_ISLNK(ent->fts_statp->st_mode) && |
if (S_ISLNK(ent->fts_statp->st_mode) && |
!sess->opts->preserve_links) { |
!sess->opts->preserve_links) { |
WARNX(sess, "%s: skipping symlink", ent->fts_path); |
WARNX("%s: skipping symlink", ent->fts_path); |
continue; |
continue; |
} |
} |
|
|
|
|
continue; |
continue; |
|
|
if ((xdev = malloc(sizeof(dev_t))) == NULL) { |
if ((xdev = malloc(sizeof(dev_t))) == NULL) { |
ERRX1(sess, "malloc"); |
ERRX1("malloc"); |
goto out; |
goto out; |
} |
} |
|
|
|
|
if (nxdev) |
if (nxdev) |
if ((xdev = realloc(xdev, sizeof(dev_t))) == |
if ((xdev = realloc(xdev, sizeof(dev_t))) == |
NULL) { |
NULL) { |
ERRX1(sess, "realloc"); |
ERRX1("realloc"); |
goto out; |
goto out; |
} |
} |
xdev[nxdev] = ent->fts_statp->st_dev; |
xdev[nxdev] = ent->fts_statp->st_dev; |
|
|
/* Allocate a new file entry. */ |
/* Allocate a new file entry. */ |
|
|
if (!flist_realloc(sess, fl, sz, max)) { |
if (!flist_realloc(sess, fl, sz, max)) { |
ERRX1(sess, "flist_realloc"); |
ERRX1("flist_realloc"); |
goto out; |
goto out; |
} |
} |
flsz++; |
flsz++; |
|
|
|
|
if (ent->fts_path[stripdir] == '\0') { |
if (ent->fts_path[stripdir] == '\0') { |
if (asprintf(&f->path, "%s.", ent->fts_path) < 0) { |
if (asprintf(&f->path, "%s.", ent->fts_path) < 0) { |
ERR(sess, "asprintf"); |
ERR("asprintf"); |
f->path = NULL; |
f->path = NULL; |
goto out; |
goto out; |
} |
} |
} else { |
} else { |
if ((f->path = strdup(ent->fts_path)) == NULL) { |
if ((f->path = strdup(ent->fts_path)) == NULL) { |
ERR(sess, "strdup"); |
ERR("strdup"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
if (S_ISLNK(ent->fts_statp->st_mode)) { |
if (S_ISLNK(ent->fts_statp->st_mode)) { |
f->link = symlink_read(sess, f->path); |
f->link = symlink_read(sess, f->path); |
if (f->link == NULL) { |
if (f->link == NULL) { |
ERRX1(sess, "symlink_read"); |
ERRX1("symlink_read"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
errno = 0; |
errno = 0; |
} |
} |
if (errno) { |
if (errno) { |
ERR(sess, "fts_read"); |
ERR("fts_read"); |
goto out; |
goto out; |
} |
} |
if (unveil(root, "r") == -1) { |
if (unveil(root, "r") == -1) { |
ERR(sess, "%s: unveil", root); |
ERR("%s: unveil", root); |
goto out; |
goto out; |
} |
} |
|
|
LOG3(sess, "generated %zu filenames: %s", flsz, root); |
LOG3("generated %zu filenames: %s", flsz, root); |
rc = 1; |
rc = 1; |
out: |
out: |
fts_close(fts); |
fts_close(fts); |
|
|
break; |
break; |
|
|
if (i == argc) { |
if (i == argc) { |
LOG2(sess, "recursively generated %zu filenames", *sz); |
LOG2("recursively generated %zu filenames", *sz); |
return 1; |
return 1; |
} |
} |
|
|
ERRX1(sess, "flist_gen_dirent"); |
ERRX1("flist_gen_dirent"); |
flist_free(*flp, max); |
flist_free(*flp, max); |
*flp = NULL; |
*flp = NULL; |
*sz = 0; |
*sz = 0; |
|
|
assert(argc); |
assert(argc); |
|
|
if ((fl = calloc(argc, sizeof(struct flist))) == NULL) { |
if ((fl = calloc(argc, sizeof(struct flist))) == NULL) { |
ERR(sess, "calloc"); |
ERR("calloc"); |
return 0; |
return 0; |
} |
} |
|
|
|
|
if (argv[i][0] == '\0') |
if (argv[i][0] == '\0') |
continue; |
continue; |
if (lstat(argv[i], &st) == -1) { |
if (lstat(argv[i], &st) == -1) { |
ERR(sess, "%s: lstat", argv[i]); |
ERR("%s: lstat", argv[i]); |
goto out; |
goto out; |
} |
} |
|
|
|
|
*/ |
*/ |
|
|
if (S_ISDIR(st.st_mode)) { |
if (S_ISDIR(st.st_mode)) { |
WARNX(sess, "%s: skipping directory", argv[i]); |
WARNX("%s: skipping directory", argv[i]); |
continue; |
continue; |
} else if (S_ISLNK(st.st_mode)) { |
} else if (S_ISLNK(st.st_mode)) { |
if (!sess->opts->preserve_links) { |
if (!sess->opts->preserve_links) { |
WARNX(sess, "%s: skipping symlink", argv[i]); |
WARNX("%s: skipping symlink", argv[i]); |
continue; |
continue; |
} |
} |
} else if (!S_ISREG(st.st_mode)) { |
} else if (!S_ISREG(st.st_mode)) { |
WARNX(sess, "%s: skipping special", argv[i]); |
WARNX("%s: skipping special", argv[i]); |
continue; |
continue; |
} |
} |
|
|
|
|
/* Add this file to our file-system worldview. */ |
/* Add this file to our file-system worldview. */ |
|
|
if (unveil(argv[i], "r") == -1) { |
if (unveil(argv[i], "r") == -1) { |
ERR(sess, "%s: unveil", argv[i]); |
ERR("%s: unveil", argv[i]); |
goto out; |
goto out; |
} |
} |
if (!flist_append(sess, f, &st, argv[i])) { |
if (!flist_append(sess, f, &st, argv[i])) { |
ERRX1(sess, "flist_append"); |
ERRX1("flist_append"); |
goto out; |
goto out; |
} |
} |
} |
} |
|
|
LOG2(sess, "non-recursively generated %zu filenames", flsz); |
LOG2("non-recursively generated %zu filenames", flsz); |
*sz = flsz; |
*sz = flsz; |
*flp = fl; |
*flp = fl; |
return 1; |
return 1; |
|
|
/* After scanning, lock our file-system view. */ |
/* After scanning, lock our file-system view. */ |
|
|
if (unveil(NULL, NULL) == -1) { |
if (unveil(NULL, NULL) == -1) { |
ERR(sess, "unveil"); |
ERR("unveil"); |
return 0; |
return 0; |
} |
} |
if (!rc) |
if (!rc) |
|
|
return 1; |
return 1; |
} |
} |
|
|
ERRX1(sess, "flist_dedupe"); |
ERRX1("flist_dedupe"); |
flist_free(*flp, *sz); |
flist_free(*flp, *sz); |
*flp = NULL; |
*flp = NULL; |
*sz = 0; |
*sz = 0; |
|
|
return 1; |
return 1; |
|
|
if ((cargv = calloc(cargvs + 1, sizeof(char *))) == NULL) { |
if ((cargv = calloc(cargvs + 1, sizeof(char *))) == NULL) { |
ERR(sess, "calloc"); |
ERR("calloc"); |
return 0; |
return 0; |
} |
} |
|
|
|
|
assert(cargvs == 1); |
assert(cargvs == 1); |
assert(S_ISDIR(wfl[0].st.mode)); |
assert(S_ISDIR(wfl[0].st.mode)); |
if (asprintf(&cargv[0], "%s/", root) < 0) { |
if (asprintf(&cargv[0], "%s/", root) < 0) { |
ERR(sess, "asprintf"); |
ERR("asprintf"); |
cargv[0] = NULL; |
cargv[0] = NULL; |
goto out; |
goto out; |
} |
} |
|
|
assert(strcmp(wfl[i].wpath, ".")); |
assert(strcmp(wfl[i].wpath, ".")); |
c = asprintf(&cargv[j], "%s/%s", root, wfl[i].wpath); |
c = asprintf(&cargv[j], "%s/%s", root, wfl[i].wpath); |
if (c < 0) { |
if (c < 0) { |
ERR(sess, "asprintf"); |
ERR("asprintf"); |
cargv[j] = NULL; |
cargv[j] = NULL; |
goto out; |
goto out; |
} |
} |
LOG4(sess, "%s: will scan for deletions", cargv[j]); |
LOG4("%s: will scan for deletions", cargv[j]); |
j++; |
j++; |
} |
} |
assert(j == cargvs); |
assert(j == cargvs); |
cargv[j] = NULL; |
cargv[j] = NULL; |
} |
} |
|
|
LOG2(sess, "delete from %zu directories", cargvs); |
LOG2("delete from %zu directories", cargvs); |
|
|
/* |
/* |
* Next, use the standard hcreate(3) hashtable interface to hash |
* Next, use the standard hcreate(3) hashtable interface to hash |
|
|
*/ |
*/ |
|
|
if (!hcreate(wflsz)) { |
if (!hcreate(wflsz)) { |
ERR(sess, "hcreate"); |
ERR("hcreate"); |
goto out; |
goto out; |
} |
} |
|
|
for (i = 0; i < wflsz; i++) { |
for (i = 0; i < wflsz; i++) { |
memset(&hent, 0, sizeof(ENTRY)); |
memset(&hent, 0, sizeof(ENTRY)); |
if ((hent.key = strdup(wfl[i].wpath)) == NULL) { |
if ((hent.key = strdup(wfl[i].wpath)) == NULL) { |
ERR(sess, "strdup"); |
ERR("strdup"); |
goto out; |
goto out; |
} |
} |
if ((hentp = hsearch(hent, ENTER)) == NULL) { |
if ((hentp = hsearch(hent, ENTER)) == NULL) { |
ERR(sess, "hsearch"); |
ERR("hsearch"); |
goto out; |
goto out; |
} else if (hentp->key != hent.key) { |
} else if (hentp->key != hent.key) { |
ERRX(sess, "%s: duplicate", wfl[i].wpath); |
ERRX("%s: duplicate", wfl[i].wpath); |
free(hent.key); |
free(hent.key); |
goto out; |
goto out; |
} |
} |
|
|
*/ |
*/ |
|
|
if ((fts = fts_open(cargv, FTS_PHYSICAL, NULL)) == NULL) { |
if ((fts = fts_open(cargv, FTS_PHYSICAL, NULL)) == NULL) { |
ERR(sess, "fts_open"); |
ERR("fts_open"); |
goto out; |
goto out; |
} |
} |
|
|
|
|
flag = 0; |
flag = 0; |
for (i = 0; i < wflsz; i++) { |
for (i = 0; i < wflsz; i++) { |
if (stat(wfl[i].path, &st) == -1) { |
if (stat(wfl[i].path, &st) == -1) { |
ERR(sess, "%s: stat", wfl[i].path); |
ERR("%s: stat", wfl[i].path); |
goto out; |
goto out; |
} |
} |
if (ent->fts_statp->st_dev == st.st_dev) { |
if (ent->fts_statp->st_dev == st.st_dev) { |
|
|
/* Not found: we'll delete it. */ |
/* Not found: we'll delete it. */ |
|
|
if (!flist_realloc(sess, fl, sz, &max)) { |
if (!flist_realloc(sess, fl, sz, &max)) { |
ERRX1(sess, "flist_realloc"); |
ERRX1("flist_realloc"); |
goto out; |
goto out; |
} |
} |
f = &(*fl)[*sz - 1]; |
f = &(*fl)[*sz - 1]; |
|
|
if ((f->path = strdup(ent->fts_path)) == NULL) { |
if ((f->path = strdup(ent->fts_path)) == NULL) { |
ERR(sess, "strdup"); |
ERR("strdup"); |
goto out; |
goto out; |
} |
} |
f->wpath = f->path + stripdir; |
f->wpath = f->path + stripdir; |
|
|
} |
} |
|
|
if (errno) { |
if (errno) { |
ERR(sess, "fts_read"); |
ERR("fts_read"); |
goto out; |
goto out; |
} |
} |
|
|
|
|
assert(sess->opts->recursive); |
assert(sess->opts->recursive); |
|
|
for (i = flsz - 1; i >= 0; i--) { |
for (i = flsz - 1; i >= 0; i--) { |
LOG1(sess, "%s: deleting", fl[i].wpath); |
LOG1("%s: deleting", fl[i].wpath); |
if (sess->opts->dry_run) |
if (sess->opts->dry_run) |
continue; |
continue; |
assert(root != -1); |
assert(root != -1); |
flag = S_ISDIR(fl[i].st.mode) ? AT_REMOVEDIR : 0; |
flag = S_ISDIR(fl[i].st.mode) ? AT_REMOVEDIR : 0; |
if (unlinkat(root, fl[i].wpath, flag) == -1 && |
if (unlinkat(root, fl[i].wpath, flag) == -1 && |
errno != ENOENT) { |
errno != ENOENT) { |
ERR(sess, "%s: unlinkat", fl[i].wpath); |
ERR("%s: unlinkat", fl[i].wpath); |
return 0; |
return 0; |
} |
} |
} |
} |