Changeset View
Standalone View
flist.c
Show All 9 Lines | |||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
#include <sys/capsicum.h> | |||||
cem: Should this stuff be `#ifdef __FreeBSD__`? | |||||
emasteUnsubmitted Not Done Inline ActionsSee earlier comment - we'll need to work with Kristaps to determine what sort of portability goo is appropriate upstream. emaste: See earlier comment - we'll need to work with Kristaps to determine what sort of portability… | |||||
#include <sys/nv.h> | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <fts.h> | #include <fts.h> | ||||
#include <inttypes.h> | #include <inttypes.h> | ||||
#include <search.h> | #include <search.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <libcasper.h> | |||||
#include <casper/cap_fileargs.h> | |||||
#include "extern.h" | #include "extern.h" | ||||
/* | /* | ||||
* We allocate our file list in chunk sizes so as not to do it one by | * We allocate our file list in chunk sizes so as not to do it one by | ||||
* one. | * one. | ||||
* Preferrably we get one or two allocation. | * Preferrably we get one or two allocation. | ||||
*/ | */ | ||||
#define FLIST_CHUNK_SIZE (1024) | #define FLIST_CHUNK_SIZE (1024) | ||||
▲ Show 20 Lines • Show All 477 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
void *pp; | void *pp; | ||||
if (*sz + 1 <= *max) { | if (*sz + 1 <= *max) { | ||||
(*sz)++; | (*sz)++; | ||||
return 1; | return 1; | ||||
} | } | ||||
pp = recallocarray(*fl, *max, | pp = calloc(*max + FLIST_CHUNK_SIZE, sizeof(struct flist)); | ||||
*max + FLIST_CHUNK_SIZE, sizeof(struct flist)); | memcpy(pp, *fl, (*sz) * sizeof(struct flist)); | ||||
free(*fl); | |||||
cemUnsubmitted Not Done Inline ActionsWhat's the purpose of this change? It introduces a couple major behavioral differences:
cem: What's the purpose of this change? It introduces a couple major behavioral differences:
1. On… | |||||
emasteUnsubmitted Not Done Inline ActionsFor this one we either want to add recallocarray to libc, or just provide it in an openbsd compat lib or local file. emaste: For this one we either want to add `recallocarray` to libc, or just provide it in an openbsd… | |||||
cemUnsubmitted Not Done Inline ActionsWe already have reallocarray in libc cem: We already have reallocarray in libc | |||||
emasteUnsubmitted Not Done Inline ActionsRight, this one is recallocarray - or perhaps you're pointing out the precedent of importing these sort of functions into libc? emaste: Right, this one is re**c**allocarray - or perhaps you're pointing out the precedent of… | |||||
cemUnsubmitted Not Done Inline ActionsDoh, I missed that c both times. Mea culpa. Yeah, +1 to importing it or otherwise implementing a shim, instead of this. cem: Doh, I missed that `c` both times. Mea culpa. Yeah, +1 to importing it or otherwise… | |||||
if (pp == NULL) { | if (pp == NULL) { | ||||
ERR(sess, "recallocarray"); | ERR(sess, "recallocarray"); | ||||
return 0; | return 0; | ||||
} | } | ||||
*fl = pp; | *fl = pp; | ||||
*max += FLIST_CHUNK_SIZE; | *max += FLIST_CHUNK_SIZE; | ||||
(*sz)++; | (*sz)++; | ||||
return 1; | return 1; | ||||
▲ Show 20 Lines • Show All 261 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Generate a flist possibly-recursively given a file root, which may | * Generate a flist possibly-recursively given a file root, which may | ||||
* also be a regular file or symlink. | * also be a regular file or symlink. | ||||
* On success, augments the generated list in "flp" of length "sz". | * On success, augments the generated list in "flp" of length "sz". | ||||
* Returns zero on failure, non-zero on success. | * Returns zero on failure, non-zero on success. | ||||
*/ | */ | ||||
static int | static int | ||||
flist_gen_dirent(struct sess *sess, char *root, struct flist **fl, size_t *sz, | flist_gen_dirent(struct sess *sess, char *root, struct flist **fl, size_t *sz, | ||||
size_t *max) | size_t *max, fileargs_t *fa) | ||||
{ | { | ||||
char *cargv[2], *cp; | char *cargv[2], *cp; | ||||
int rc = 0; | int rc = 0, fd; | ||||
FTS *fts; | FTS *fts; | ||||
FTSENT *ent; | FTSENT *ent; | ||||
struct flist *f; | struct flist *f; | ||||
size_t flsz = 0, stripdir; | size_t flsz = 0, stripdir; | ||||
struct stat st; | struct stat st; | ||||
cargv[0] = root; | cargv[0] = root; | ||||
cargv[1] = NULL; | cargv[1] = NULL; | ||||
/* | /* | ||||
* If we're a file, then revert to the same actions we use for | * If we're a file, then revert to the same actions we use for | ||||
* the non-recursive scan. | * the non-recursive scan. | ||||
*/ | */ | ||||
if (lstat(root, &st) == -1) { | if ((fd = fileargs_open(fa, root)) < 0) { | ||||
cemUnsubmitted Not Done Inline ActionsSo, open + fstat and lstat are slightly different, right? Chiefly, lstat doesn't follow symlinks, but open() does. AFAIK we can't actually open symlinks as pseudo-files, even with openat + O_NOFOLLOW. So we're breaking the S_ISLNK() handling here (and in other places). It seems what we need is a fileargs_stat() (with flags), or _lstat(). cem: So, open + fstat and lstat are slightly different, right? Chiefly, `lstat` doesn't follow… | |||||
ERR(sess, "%s: fileargs_open", root); | |||||
return 0; | |||||
} else if (fstat(fd, &st) == -1) { | |||||
ERR(sess, "%s: lstat", root); | ERR(sess, "%s: lstat", root); | ||||
return 0; | return 0; | ||||
} else if (S_ISREG(st.st_mode)) { | } else if (S_ISREG(st.st_mode)) { | ||||
cemUnsubmitted Not Done Inline ActionsRather than adding close(fd); to every early return path after this, I'd suggest breaking the if/else chain: if ((fd = fileargs...) < 0) { ... return 0; } rc = fstat(fd, ...); close(fd); if (rc == -1) { ... } else if ( ... ) { cem: Rather than adding `close(fd);` to every early return path after this, I'd suggest breaking the… | |||||
if (!flist_realloc(sess, fl, sz, max)) { | if (!flist_realloc(sess, fl, sz, max)) { | ||||
ERRX1(sess, "flist_realloc"); | ERRX1(sess, "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(sess, "flist_append"); | ||||
return 0; | return 0; | ||||
} | } | ||||
if (unveil(root, "r") == -1) { | |||||
ERR(sess, "%s: unveil", root); | |||||
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(sess, "%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(sess, "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(sess, "flist_append"); | ||||
return 0; | return 0; | ||||
} | } | ||||
if (unveil(root, "r") == -1) { | |||||
ERR(sess, "%s: unveil", root); | |||||
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(sess, "%s: skipping special", root); | ||||
return 1; | return 1; | ||||
} | } | ||||
/* | /* | ||||
* If we end with a slash, it means that we're not supposed to | * If we end with a slash, it means that we're not supposed to | ||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | while ((ent = fts_read(fts)) != NULL) { | ||||
/* Reset errno for next fts_read() call. */ | /* Reset errno for next fts_read() call. */ | ||||
errno = 0; | errno = 0; | ||||
} | } | ||||
if (errno) { | if (errno) { | ||||
ERR(sess, "fts_read"); | ERR(sess, "fts_read"); | ||||
goto out; | goto out; | ||||
} | } | ||||
if (unveil(root, "r") == -1) { | |||||
ERR(sess, "%s: unveil", root); | |||||
goto out; | |||||
} | |||||
LOG3(sess, "generated %zu filenames: %s", flsz, root); | LOG3(sess, "generated %zu filenames: %s", flsz, root); | ||||
rc = 1; | rc = 1; | ||||
out: | out: | ||||
fts_close(fts); | fts_close(fts); | ||||
return rc; | return rc; | ||||
} | } | ||||
/* | /* | ||||
* Generate a flist recursively given the array of directories (or | * Generate a flist recursively given the array of directories (or | ||||
* files, symlinks, doesn't matter) specified in argv (argc >0). | * files, symlinks, doesn't matter) specified in argv (argc >0). | ||||
* On success, stores the generated list in "flp" with length "sz", | * On success, stores the generated list in "flp" with length "sz", | ||||
* which may be zero. | * which may be zero. | ||||
* Returns zero on failure, non-zero on success. | * Returns zero on failure, non-zero on success. | ||||
*/ | */ | ||||
static int | static int | ||||
flist_gen_dirs(struct sess *sess, size_t argc, char **argv, struct flist **flp, | flist_gen_dirs(struct sess *sess, size_t argc, char **argv, struct flist **flp, | ||||
size_t *sz) | size_t *sz, fileargs_t *fa) | ||||
{ | { | ||||
size_t i, max = 0; | size_t i, max = 0; | ||||
for (i = 0; i < argc; i++) | for (i = 0; i < argc; i++) | ||||
if (!flist_gen_dirent(sess, argv[i], flp, sz, &max)) | if (!flist_gen_dirent(sess, argv[i], flp, sz, &max, fa)) | ||||
break; | break; | ||||
if (i == argc) { | if (i == argc) { | ||||
LOG2(sess, "recursively generated %zu filenames", *sz); | LOG2(sess, "recursively generated %zu filenames", *sz); | ||||
return 1; | return 1; | ||||
} | } | ||||
ERRX1(sess, "flist_gen_dirent"); | ERRX1(sess, "flist_gen_dirent"); | ||||
flist_free(*flp, max); | flist_free(*flp, max); | ||||
*flp = NULL; | *flp = NULL; | ||||
*sz = 0; | *sz = 0; | ||||
return 0; | return 0; | ||||
} | } | ||||
/* | /* | ||||
* Generate list of files from the command-line argc (>0) and argv. | * Generate list of files from the command-line argc (>0) and argv. | ||||
* On success, stores the generated list in "flp" with length "sz", | * On success, stores the generated list in "flp" with length "sz", | ||||
* which may be zero. | * which may be zero. | ||||
* Returns zero on failure, non-zero on success. | * Returns zero on failure, non-zero on success. | ||||
*/ | */ | ||||
static int | static int | ||||
flist_gen_files(struct sess *sess, size_t argc, char **argv, | flist_gen_files(struct sess *sess, size_t argc, char **argv, | ||||
struct flist **flp, size_t *sz) | struct flist **flp, size_t *sz, fileargs_t *fa) | ||||
{ | { | ||||
struct flist *fl = NULL, *f; | struct flist *fl = NULL, *f; | ||||
size_t i, flsz = 0; | size_t i, flsz = 0; | ||||
struct stat st; | struct stat st; | ||||
int fd; | |||||
assert(argc); | assert(argc); | ||||
if ((fl = calloc(argc, sizeof(struct flist))) == NULL) { | if ((fl = calloc(argc, sizeof(struct flist))) == NULL) { | ||||
ERR(sess, "calloc"); | ERR(sess, "calloc"); | ||||
return 0; | return 0; | ||||
} | } | ||||
for (i = 0; i < argc; i++) { | for (i = 0; i < argc; i++) { | ||||
if ('\0' == argv[i][0]) | if ('\0' == argv[i][0]) | ||||
continue; | continue; | ||||
if (lstat(argv[i], &st) == -1) { | |||||
ERR(sess, "%s: lstat", argv[i]); | if ((fd = fileargs_open(fa, argv[i])) < 0) { | ||||
cemUnsubmitted Not Done Inline Actionssame issue with lstat != open+fstat here cem: same issue with lstat != open+fstat here | |||||
ERR(sess, "%s: fileargs_open", argv[i]); | |||||
goto out; | goto out; | ||||
} | } | ||||
if (fstat(fd, &st) == -1) { | |||||
ERR(sess, "%s: fstat", argv[i]); | |||||
goto out; | |||||
} | |||||
cemUnsubmitted Not Done Inline ActionsSame issue with fd leakage here. cem: Same issue with `fd` leakage here. | |||||
/* | /* | ||||
* File type checks. | * File type checks. | ||||
* In non-recursive mode, we don't accept directories. | * In non-recursive mode, we don't accept directories. | ||||
* We also skip symbolic links without -l. | * We also skip symbolic links without -l. | ||||
* Beyond that, we only accept regular files. | * Beyond that, we only accept regular files. | ||||
*/ | */ | ||||
if (S_ISDIR(st.st_mode)) { | if (S_ISDIR(st.st_mode)) { | ||||
Show All 9 Lines | if (S_ISDIR(st.st_mode)) { | ||||
WARNX(sess, "%s: skipping special", argv[i]); | WARNX(sess, "%s: skipping special", argv[i]); | ||||
continue; | continue; | ||||
} | } | ||||
f = &fl[flsz++]; | f = &fl[flsz++]; | ||||
assert(f != NULL); | assert(f != NULL); | ||||
/* Add this file to our file-system worldview. */ | |||||
if (unveil(argv[i], "r") == -1) { | |||||
ERR(sess, "%s: unveil", argv[i]); | |||||
goto out; | |||||
} | |||||
if (!flist_append(sess, f, &st, argv[i])) { | if (!flist_append(sess, f, &st, argv[i])) { | ||||
ERRX1(sess, "flist_append"); | ERRX1(sess, "flist_append"); | ||||
goto out; | goto out; | ||||
} | } | ||||
} | } | ||||
LOG2(sess, "non-recursively generated %zu filenames", flsz); | LOG2(sess, "non-recursively generated %zu filenames", flsz); | ||||
*sz = flsz; | *sz = flsz; | ||||
Show All 11 Lines | |||||
* In non-recursive mode (the default), we use only the files we're | * In non-recursive mode (the default), we use only the files we're | ||||
* given. | * given. | ||||
* Otherwise, directories are recursively examined. | * Otherwise, directories are recursively examined. | ||||
* Returns zero on failure, non-zero on success. | * Returns zero on failure, non-zero on success. | ||||
* On success, "fl" will need to be freed with flist_free(). | * On success, "fl" will need to be freed with flist_free(). | ||||
*/ | */ | ||||
int | int | ||||
flist_gen(struct sess *sess, size_t argc, char **argv, struct flist **flp, | flist_gen(struct sess *sess, size_t argc, char **argv, struct flist **flp, | ||||
size_t *sz) | size_t *sz, fileargs_t *fa) | ||||
{ | { | ||||
int rc; | int rc; | ||||
assert(argc > 0); | assert(argc > 0); | ||||
rc = sess->opts->recursive ? | rc = sess->opts->recursive ? | ||||
flist_gen_dirs(sess, argc, argv, flp, sz) : | flist_gen_dirs(sess, argc, argv, flp, sz, fa) : | ||||
flist_gen_files(sess, argc, argv, flp, sz); | flist_gen_files(sess, argc, argv, flp, sz, fa); | ||||
/* After scanning, lock our file-system view. */ | |||||
if (unveil(NULL, NULL) == -1) { | |||||
ERR(sess, "unveil"); | |||||
return 0; | |||||
} | |||||
if (!rc) | if (!rc) | ||||
return 0; | return 0; | ||||
qsort(*flp, *sz, sizeof(struct flist), flist_cmp); | qsort(*flp, *sz, sizeof(struct flist), flist_cmp); | ||||
if (flist_dedupe(sess, flp, sz)) { | if (flist_dedupe(sess, flp, sz)) { | ||||
flist_topdirs(sess, *flp, *sz); | flist_topdirs(sess, *flp, *sz); | ||||
return 1; | return 1; | ||||
▲ Show 20 Lines • Show All 221 Lines • Show Last 20 Lines |
Should this stuff be #ifdef __FreeBSD__?