mirror of
https://github.com/NationalSecurityAgency/DCP.git
synced 2026-11-07 06:05:59 -10:00
commit
ecfbf3def0
17 files changed
+384
-96
No files matched your search
@@ -23,7 +23,7 @@ PROJECT_NAME = dcp
|
||||
# This could be handy for archiving the generated documentation or
|
||||
# if some version control system is used.
|
||||
|
||||
PROJECT_NUMBER = 1.0.0
|
||||
PROJECT_NUMBER = 1.0.6
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# base path where the generated documentation will be put.
|
||||
|
||||
@@ -21,6 +21,8 @@ option "output" o "where to write output" string typestr="FILE" optional
|
||||
option "input" i "output from a previous run to check for uniqueness"
|
||||
string typestr="FILE" optional multiple
|
||||
|
||||
option "xattr" x "where to write eXtended ATTRibutes" string typestr="FILE" optional
|
||||
|
||||
option "owner" O "username to chown new files/dirs"
|
||||
string typestr="USER" optional
|
||||
|
||||
|
||||
+6
-6
@@ -1,14 +1,14 @@
|
||||
|
||||
bin_PROGRAMS=dcp
|
||||
dcp_SOURCES=main.c digest.c cmdline.c io/io_entry.c io/io_metadata.c \
|
||||
io/pack.c io/io_index.c index/db_index.c io_dcp_processor.c logging.c \
|
||||
fd.c impl/dcp.c impl/process_regular.c impl/process_directory.c \
|
||||
dcp_SOURCES=main.c digest.c cmdline.c io/io_entry.c io/io_metadata.c \
|
||||
io/pack.c io/io_index.c io/io_xattr.c index/db_index.c io_dcp_processor.c \
|
||||
logging.c fd.c impl/dcp.c impl/process_regular.c impl/process_directory.c \
|
||||
impl/process_symlink.c impl/preprocess.c impl/process_special.c
|
||||
dcp_CPPFLAGS=-Wall -Wextra -Werror -fpie -Wno-unused-but-set-variable
|
||||
dcp_LDFLAGS=-lcrypto -ljansson -ldb -pie
|
||||
|
||||
# ensure the headers make it into the dist tarball
|
||||
EXTRA_DIST=digest.h cmdline.h io/io_entry.h io/io_metadata.h io/pack.h \
|
||||
io/io.h io/io_index.h fd.h index/index.h io_dcp_processor.h logging.h \
|
||||
entry.h impl/dcp.h impl/process.h
|
||||
EXTRA_DIST=digest.h cmdline.h io/io_entry.h io/io_metadata.h io/pack.h \
|
||||
io/io.h io/io_index.h io/io_xattr.h fd.h index/index.h io_dcp_processor.h \
|
||||
logging.h entry.h impl/dcp.h impl/process.h
|
||||
|
||||
+12
-8
@@ -120,9 +120,11 @@ int dcp(const char *newpath, const char *src[], size_t srcc,
|
||||
dapath = NULL;
|
||||
destpath = NULL;
|
||||
|
||||
/* dest is the directory to clone every entry in, if newpath is an
|
||||
/*
|
||||
* dest is the directory to clone every entry in, if newpath is an
|
||||
* existing directory dest represents that, otherwise it is the parent
|
||||
* of the file/directory to create */
|
||||
* of the file/directory to create
|
||||
*/
|
||||
if (initdestandpaths(&destroot,path,&destpath,&dapath,sanitized,srcc) != 0)
|
||||
{
|
||||
free(path);
|
||||
@@ -168,7 +170,8 @@ int dcp(const char *newpath, const char *src[], size_t srcc,
|
||||
if (do_append(ent, &destroot, sanitized))
|
||||
strcat(strcat(path, "/"), ent->fts_name);
|
||||
|
||||
/* unfortunatly we have two cases where dapath wont be set right
|
||||
/*
|
||||
* unfortunatly we have two cases where dapath wont be set right
|
||||
* 1. cp a dir to a dir that dne: report dapath as "/"
|
||||
* 2. cp a file to a path that dne: report dapath as "/$filename"
|
||||
*/
|
||||
@@ -374,8 +377,9 @@ void process(file_t *newdir, const char *newpath, FTSENT *ent,
|
||||
state = DCP_DIR_FAILED;
|
||||
}
|
||||
|
||||
popts->callback(state, pathmd5, dapath, ent->fts_statp, NULL, NULL,
|
||||
NULL, NULL, -1, popts->callback_ctx);
|
||||
popts->callback(state, pathmd5, dapath, ent->fts_statp,
|
||||
ent->fts_accpath, NULL, NULL, NULL, NULL, NULL, -1,
|
||||
popts->callback_ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -417,7 +421,7 @@ void process(file_t *newdir, const char *newpath, FTSENT *ent,
|
||||
case FTS_ERR:
|
||||
{
|
||||
popts->callback(DCP_FAILED, pathmd5, dapath, NULL, NULL, NULL, NULL,
|
||||
NULL, -1, popts->callback_ctx);
|
||||
NULL, NULL, NULL, -1, popts->callback_ctx);
|
||||
errno = ent->fts_errno;
|
||||
log_error("fts_read '%s'", ent->fts_path);
|
||||
break;
|
||||
@@ -426,7 +430,7 @@ void process(file_t *newdir, const char *newpath, FTSENT *ent,
|
||||
case FTS_NS:
|
||||
{
|
||||
popts->callback(DCP_FAILED, pathmd5, dapath, NULL, NULL, NULL, NULL,
|
||||
NULL, -1, popts->callback_ctx);
|
||||
NULL, NULL, NULL, -1, popts->callback_ctx);
|
||||
errno = ent->fts_errno;
|
||||
log_error("cannot stat '%s'", ent->fts_path);
|
||||
break;
|
||||
@@ -435,7 +439,7 @@ void process(file_t *newdir, const char *newpath, FTSENT *ent,
|
||||
case FTS_DNR:
|
||||
{
|
||||
popts->callback(DCP_FAILED, pathmd5, dapath, NULL, NULL, NULL, NULL,
|
||||
NULL, -1, popts->callback_ctx);
|
||||
NULL, NULL, NULL, -1, popts->callback_ctx);
|
||||
errno = ent->fts_errno;
|
||||
log_error("cannot read dir '%s'", ent->fts_path);
|
||||
break;
|
||||
|
||||
+2
-1
@@ -53,7 +53,8 @@ typedef enum {
|
||||
* calculated.
|
||||
*/
|
||||
typedef int (*dcp_callback_f)(dcp_state_t state, const void *pathmd5,
|
||||
const char *path, const struct stat *sstat, const void *md5,
|
||||
const char *dapath, const struct stat *sstat, const char *accesspath,
|
||||
const char *symlinkpath, const void *md5,
|
||||
const void *sha1, const void *sha256, const void *sha512,
|
||||
unsigned long process_time, void *context);
|
||||
|
||||
|
||||
+23
-17
@@ -19,6 +19,10 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "process.h"
|
||||
#include "../digest.h"
|
||||
#include "../fd.h"
|
||||
@@ -26,7 +30,6 @@
|
||||
#include "../logging.h"
|
||||
#include "dcp.h"
|
||||
|
||||
|
||||
/* Type Defs ******************************************************************/
|
||||
|
||||
|
||||
@@ -103,7 +106,7 @@ static int copy_fd(int dirfd, const char *pathname, struct stream *stream,
|
||||
*
|
||||
* @return number of bytes that are valid in buf, -1 on error
|
||||
*/
|
||||
static ssize_t cache_n_digest(digesterset_t *set, int fd, void *buf,
|
||||
static ssize_t cache_n_digest(digesterset_t *set, int fd, void *buf,
|
||||
size_t blen);
|
||||
|
||||
/**
|
||||
@@ -160,8 +163,8 @@ int process_regular(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
if ((s = open(oldpath, O_RDONLY)) == -1)
|
||||
{
|
||||
log_error("cannot open '%s'", oldpath);
|
||||
opts->callback(DCP_FAILED, pathmd5, dapath, oldst, NULL, NULL, NULL,
|
||||
NULL, -1, opts->callback_ctx);
|
||||
opts->callback(DCP_FAILED, pathmd5, dapath, oldst, oldpath, NULL, NULL,
|
||||
NULL, NULL, NULL, -1, opts->callback_ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -170,8 +173,10 @@ int process_regular(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
/* ensure we create the hash needed for the and index */
|
||||
digesterset_create(&dgstset, opts->digests | idxkeytype);
|
||||
|
||||
/* there is no index to check against, just copy and digest at the same
|
||||
* time */
|
||||
/*
|
||||
* there is no index to check against, just copy and digest at the same
|
||||
* time
|
||||
*/
|
||||
if (opts->index == NULL)
|
||||
{
|
||||
valid_len = copy_n_digest(newdir->fd, newpath, opts->uid, opts->gid,
|
||||
@@ -180,8 +185,8 @@ int process_regular(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
if (valid_len < 0)
|
||||
{
|
||||
log_debugx("failed copying and hashing '%s'", oldpath);
|
||||
opts->callback(DCP_FAILED, pathmd5, dapath, oldst, NULL, NULL,
|
||||
NULL, NULL, -1, opts->callback_ctx);
|
||||
opts->callback(DCP_FAILED, pathmd5, dapath, oldst, oldpath, NULL,
|
||||
NULL, NULL, NULL, NULL, -1, opts->callback_ctx);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -192,7 +197,7 @@ int process_regular(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
diff = ((clock() - start) * 1000) / CLOCKS_PER_SEC;
|
||||
|
||||
/* finally send the information to the file processor */
|
||||
opts->callback(DCP_FILE_COPIED, pathmd5, dapath, oldst,
|
||||
opts->callback(DCP_FILE_COPIED, pathmd5, dapath, oldst, oldpath, NULL,
|
||||
digesterset_get_value(&dgstset, DGST_MD5),
|
||||
digesterset_get_value(&dgstset, DGST_SHA1),
|
||||
digesterset_get_value(&dgstset, DGST_SHA256),
|
||||
@@ -202,14 +207,13 @@ int process_regular(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* read in the file and calculate the desired digests */
|
||||
if ((valid_len = cache_n_digest(&dgstset, s, opts->buffer,
|
||||
opts->buffer_size)) == -1)
|
||||
{
|
||||
log_debugx("cannot calculate hashes for '%s'", oldpath);
|
||||
opts->callback(DCP_FAILED, pathmd5, dapath, oldst, NULL, NULL,
|
||||
NULL, NULL, -1, opts->callback_ctx);
|
||||
opts->callback(DCP_FAILED, pathmd5, dapath, oldst, oldpath, NULL,
|
||||
NULL, NULL, NULL, NULL, -1, opts->callback_ctx);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -226,8 +230,8 @@ int process_regular(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
|
||||
/* we have seen this file, skip it */
|
||||
case INDEX_SUCCESS:
|
||||
/* we have seen this file, skip it */
|
||||
ret = 0; /* set ret to success */
|
||||
goto cleanup;
|
||||
|
||||
@@ -236,9 +240,11 @@ int process_regular(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
}
|
||||
}
|
||||
|
||||
/* if cache_n_digest was able to store the whole file in the buffer then
|
||||
/*
|
||||
* if cache_n_digest was able to store the whole file in the buffer then
|
||||
* we do not need to seek to the beginning of the fd and reread the
|
||||
* bytes */
|
||||
* bytes
|
||||
*/
|
||||
if (valid_len == oldst->st_size)
|
||||
{
|
||||
datastream.bytes = opts->buffer;
|
||||
@@ -259,7 +265,7 @@ int process_regular(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
diff = ((clock() - start) * 1000) / CLOCKS_PER_SEC;
|
||||
|
||||
/* finally send the information to the file processor */
|
||||
opts->callback(state, pathmd5, dapath, oldst,
|
||||
opts->callback(state, pathmd5, dapath, oldst, oldpath, NULL,
|
||||
digesterset_get_value(&dgstset, DGST_MD5),
|
||||
digesterset_get_value(&dgstset, DGST_SHA1),
|
||||
digesterset_get_value(&dgstset, DGST_SHA256),
|
||||
@@ -280,7 +286,7 @@ int process_regular(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
/* Private Impl ***************************************************************/
|
||||
|
||||
|
||||
int copy_fd(int dirfd, const char *pathname, struct stream *stream, uid_t uid,
|
||||
int copy_fd(int dirfd, const char *pathname, struct stream *stream, uid_t uid,
|
||||
gid_t gid)
|
||||
{
|
||||
int d;
|
||||
|
||||
@@ -40,7 +40,7 @@ int process_special(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
if (r == 0 && fchownat(newdir->fd, newpath, opts->uid, opts->gid, 0) != 0)
|
||||
log_warn("cannot chown '%s'", pathstr(newdir, newpath));
|
||||
|
||||
opts->callback(state, pathmd5, dapath, oldst, NULL, NULL, NULL, NULL, -1,
|
||||
opts->callback(state, pathmd5, dapath, oldst, oldpath, NULL, NULL, NULL, NULL, NULL, -1,
|
||||
opts->callback_ctx);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "process.h"
|
||||
#include "../logging.h"
|
||||
#include "dcp.h"
|
||||
@@ -72,7 +74,7 @@ int process_symlink(file_t *newdir, const char *newpath, const char *oldpath,
|
||||
}
|
||||
}
|
||||
}
|
||||
opts->callback(state, pathmd5, dapath, oldst, NULL, NULL, NULL, NULL, -1,
|
||||
opts->callback(state, pathmd5, dapath, oldst, oldpath, buf, NULL, NULL, NULL, NULL, -1,
|
||||
opts->callback_ctx);
|
||||
|
||||
/* if we allocated a new buffer free it */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "io_entry.h"
|
||||
#include "io_metadata.h"
|
||||
#include "io_index.h"
|
||||
#include "io_xattr.h"
|
||||
|
||||
|
||||
#endif
|
||||
+65
-30
@@ -18,7 +18,7 @@
|
||||
#include <time.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include "jansson.h"
|
||||
#include <jansson.h>
|
||||
|
||||
#include "io_entry.h"
|
||||
#include "../entry.h"
|
||||
@@ -117,7 +117,7 @@ int io_entry_read(entry_t *entry, FILE *in, size_t *line)
|
||||
} while (buf[0] == '#');
|
||||
|
||||
|
||||
/* for jannson's documentation, JSON_REJECT_DUPLICATES issues an error when
|
||||
/* for jansson's documentation, JSON_REJECT_DUPLICATES issues an error when
|
||||
* multiple keys in an object have the same name instead of default
|
||||
* behavior which is to use the last defined value */
|
||||
if ((obj = json_loads(buf, JSON_REJECT_DUPLICATES, &jerr)) == NULL)
|
||||
@@ -311,15 +311,16 @@ int io_entry_read(entry_t *entry, FILE *in, size_t *line)
|
||||
}
|
||||
|
||||
|
||||
/* while jansson can be used for creating a json structure then printing it
|
||||
/*
|
||||
* while jansson can be used for creating a json structure then printing it
|
||||
* out, there is a large overhead cost. Since we control the data only use
|
||||
* jansson to escape the strings we cannot control. */
|
||||
* jansson to escape the strings we cannot control.
|
||||
*/
|
||||
int io_entry_write_fields(const char *state, const char *path,
|
||||
const struct stat *st, const void *pathmd5, const void *md5,
|
||||
const void *sha1, const void *sha256, const void *sha512,
|
||||
long elapsed, FILE *stream)
|
||||
const struct stat *st, const void *pathmd5, const char *symlinkpath,
|
||||
const void *md5, const void *sha1, const void *sha256,
|
||||
const void *sha512, long elapsed, FILE *stream)
|
||||
{
|
||||
|
||||
enum { MAX_LENGTH = PATH_MAX * 4 };
|
||||
|
||||
int ret;
|
||||
@@ -328,14 +329,15 @@ int io_entry_write_fields(const char *state, const char *path,
|
||||
/* use jansson for string character escaping of paths */
|
||||
json_t *escaped;
|
||||
|
||||
/* used to hold hex strings for the hashes and the path if it contains
|
||||
* non utf-8 characters */
|
||||
/*
|
||||
* used to hold hex strings for the hashes and the path if it contains
|
||||
* non utf-8 characters
|
||||
*/
|
||||
char buf[MAX_LENGTH];
|
||||
|
||||
|
||||
ret = 0;
|
||||
|
||||
/* we are writing a json object on the line*/
|
||||
/* we are writing a json object on the line */
|
||||
fputc('{', stream);
|
||||
|
||||
if (md5 != NULL)
|
||||
@@ -362,18 +364,26 @@ int io_entry_write_fields(const char *state, const char *path,
|
||||
fprintf(stream, "\"sha512\":\"%s\",", buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* up till this point we have been appending commas to the end of the
|
||||
* entries, this is because we don't know which hashes are going to be
|
||||
* provided. Now that pathmd5 has been put on the stream we switch to
|
||||
* commas before any new entries, allowing errors to simply exit and not
|
||||
* output malformed json.
|
||||
*/
|
||||
|
||||
unpack(buf, pathmd5, MD5_DIGEST_LENGTH);
|
||||
fprintf(stream, "\"pathmd5\":\"%s\",", buf);
|
||||
fprintf(stream, "\"pathmd5\":\"%s\"", buf); /* no commas before or after */
|
||||
|
||||
if (st != NULL)
|
||||
{
|
||||
/* mode, size and timestamps */
|
||||
fprintf(stream,
|
||||
"\"uid\":%u,\"gid\":%u,"
|
||||
",\"uid\":%u,\"gid\":%u,"
|
||||
"\"mode\":%u,\"size\":%jd,"
|
||||
"\"asec\":%ld,\"ansec\":%ld,"
|
||||
"\"msec\":%ld,\"mnsec\":%ld,"
|
||||
"\"csec\":%ld,\"cnsec\":%ld,",
|
||||
"\"csec\":%ld,\"cnsec\":%ld",
|
||||
st->st_uid, st->st_gid,
|
||||
st->st_mode, (intmax_t) st->st_size,
|
||||
st->st_atim.tv_sec, st->st_atim.tv_nsec,
|
||||
@@ -381,7 +391,7 @@ int io_entry_write_fields(const char *state, const char *path,
|
||||
st->st_ctim.tv_sec, st->st_ctim.tv_nsec);
|
||||
|
||||
/* extract what type of file from the mode */
|
||||
fprintf(stream, "\"type\":\"%s\",",
|
||||
fprintf(stream, ",\"type\":\"%s\"",
|
||||
S_ISREG(st->st_mode)? "reg" : S_ISDIR(st->st_mode)? "dir" :
|
||||
S_ISLNK(st->st_mode)? "lnk" : S_ISCHR(st->st_mode)? "chr" :
|
||||
S_ISBLK(st->st_mode)? "blk" : S_ISFIFO(st->st_mode)? "fifo" :
|
||||
@@ -395,30 +405,54 @@ int io_entry_write_fields(const char *state, const char *path,
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
fputs("\"state\":", stream);
|
||||
fputs(",\"state\":", stream);
|
||||
json_dumpf(escaped, stream, JSON_ENCODE_ANY);
|
||||
json_decref(escaped);
|
||||
fputc(',', stream);
|
||||
|
||||
/* # of secs elapsed while processing */
|
||||
if (elapsed > -1)
|
||||
fprintf(stream, "\"elapsed\":%ld,", elapsed);
|
||||
fprintf(stream, ",\"elapsed\":%ld", elapsed);
|
||||
|
||||
/* PATH and PATHHEX, we prefer to output PATH which is a valid UTF-8 JSON
|
||||
* escaped string, however if we come across a path with invalid utf-8 we
|
||||
* still need a way to output it. */
|
||||
/* if entry is a symlink we include what its target path was */
|
||||
if (symlinkpath != NULL)
|
||||
{
|
||||
/*
|
||||
* symlinkTarget valid utf-8 JSON escaped string where pointing
|
||||
* symlinkTargetHex hex encoding of the target if jansson cannot encode
|
||||
*/
|
||||
if ((escaped = json_string(symlinkpath)) != NULL)
|
||||
{
|
||||
fputs(",\"symlinkTarget\":", stream);
|
||||
json_dumpf(escaped, stream, JSON_ENCODE_ANY);
|
||||
json_decref(escaped);
|
||||
}
|
||||
else /* jansson was unable to handle the string */
|
||||
{
|
||||
len = strlen(symlinkpath);
|
||||
if ((len * 2 + 1) > MAX_LENGTH)
|
||||
{
|
||||
log_errorx("buffer too small, expected string with length < %d,"
|
||||
" for non valid utf-8 string: '%s'", MAX_LENGTH,
|
||||
symlinkpath);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
unpack(buf, symlinkpath, len);
|
||||
fprintf(stream, ",\"symlinkTargetHex\":\"%s\"", buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* path valid utf-8 JSON escaped path to the file
|
||||
* pathHex hex encoding of the path, provided if jansson cannot encode
|
||||
*/
|
||||
if ((escaped = json_string(path)) != NULL)
|
||||
{
|
||||
fputs("\"path\":", stream);
|
||||
|
||||
/*
|
||||
* JSON_ENCODE_ANY tell jansson to write any json type to stream not
|
||||
* just Objects and Arrays
|
||||
*/
|
||||
fputs(",\"path\":", stream);
|
||||
json_dumpf(escaped, stream, JSON_ENCODE_ANY);
|
||||
json_decref(escaped);
|
||||
}
|
||||
else /* jansson was unable to handle the string, */
|
||||
else /* jansson was unable to handle the string */
|
||||
{
|
||||
len = strlen(path);
|
||||
if ((len * 2 + 1) > MAX_LENGTH)
|
||||
@@ -429,9 +463,10 @@ int io_entry_write_fields(const char *state, const char *path,
|
||||
goto cleanup;
|
||||
}
|
||||
unpack(buf, path, len);
|
||||
fprintf(stream, "\"pathhex\":\"%s\",", buf);
|
||||
fprintf(stream, ",\"pathhex\":\"%s\"", buf);
|
||||
}
|
||||
|
||||
|
||||
cleanup:
|
||||
/* print a newline our record separator */
|
||||
fputs("}\n", stream);
|
||||
|
||||
+4
-3
@@ -46,6 +46,7 @@ int io_entry_read(entry_t *entry, FILE *in, size_t *line);
|
||||
* @param path abs path of the file, with src dir treated as root
|
||||
* @param st pointer to the source file's stat struct
|
||||
* @param pathmd5 16 byte md5 of the path
|
||||
* @param symlinkpath if entry is symlink where it's pointing, NULL otherwise
|
||||
* @param md5 the md5 of the file's contents or NULL if not applicable
|
||||
* @param sha1 sha1 of the file's contents or NULL if not applicable
|
||||
* @param sha256 sha256 of the file's contents or NULL if not applicable
|
||||
@@ -56,9 +57,9 @@ int io_entry_read(entry_t *entry, FILE *in, size_t *line);
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int io_entry_write_fields(const char *state, const char *path,
|
||||
const struct stat *st, const void *pathmd5, const void *md5,
|
||||
const void *sha1, const void *sha256, const void *sha512,
|
||||
long process_time, FILE *stream);
|
||||
const struct stat *st, const void *pathmd5, const char *symlinkpath,
|
||||
const void *md5, const void *sha1, const void *sha256,
|
||||
const void *sha512, long process_time, FILE *stream);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @version 1.0
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* Implementation of the io_xattr.h interface. Entries in dcp input and output
|
||||
* are an entry in the filesystem that was copied/duplicated. For each
|
||||
* file and symbolic link we log xattr information about each as a single line
|
||||
* json object.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "io_xattr.h"
|
||||
#include "../digest.h"
|
||||
#include "pack.h"
|
||||
#include "../logging.h"
|
||||
|
||||
|
||||
/* Public Impl ****************************************************************/
|
||||
|
||||
|
||||
int io_entry_write_xattr_fields(const void *pathmd5, const char *name,
|
||||
void *valuebuffer, ssize_t valuesize, FILE *stream)
|
||||
{
|
||||
int ret;
|
||||
int w;
|
||||
|
||||
/* where to store the hex representation of the path md5 sum */
|
||||
char buf[(MD5_DIGEST_LENGTH * 2) + 1];
|
||||
|
||||
/* we store the xattr value as base64 */
|
||||
BIO *bio, *b64;
|
||||
|
||||
/* used to properly escape the utf-8 name of an xattr */
|
||||
json_t *escaped;
|
||||
|
||||
ret = 0;
|
||||
b64 = NULL;
|
||||
|
||||
/* we are writing a json object on the line */
|
||||
fputc('{', stream);
|
||||
|
||||
unpack(buf, pathmd5, MD5_DIGEST_LENGTH);
|
||||
fprintf(stream, "\"pathmd5\":\"%s\",", buf);
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
if ((escaped = json_string(name)) == NULL)
|
||||
{
|
||||
log_errorx("non valid utf-8 xattr string '%s'", name);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
fputs("\"xattrName\":", stream);
|
||||
json_dumpf(escaped, stream, JSON_ENCODE_ANY);
|
||||
json_decref(escaped);
|
||||
}
|
||||
|
||||
if (valuesize > 0)
|
||||
{
|
||||
fputs(",\"xattrValue\":\"", stream);
|
||||
/*
|
||||
* base64 encoding
|
||||
* https://www.openssl.org/docs/manmaster/crypto/BIO_f_base64.html
|
||||
*/
|
||||
b64 = BIO_new(BIO_f_base64());
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); /* do not append newline */
|
||||
bio = BIO_new_fp(stream, BIO_NOCLOSE); /* do not close stream */
|
||||
BIO_push(b64, bio);
|
||||
if ((w = BIO_write(b64, valuebuffer, valuesize)) != valuesize)
|
||||
{
|
||||
log_errorx("cannot base64 encode xattr value for attr: '%s'", name);
|
||||
ret = -1;
|
||||
}
|
||||
BIO_flush(b64);
|
||||
BIO_free_all(b64);
|
||||
fputc('"', stream);
|
||||
}
|
||||
|
||||
|
||||
cleanup:
|
||||
/* print a newline our record separator */
|
||||
fputs("}\n", stream);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @version 1.0
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* API for reading and writing extended attributes to a stream. Each attribute
|
||||
* is written as a JSON Object with the path's md5.
|
||||
*/
|
||||
#ifndef IO_XATTR_H__
|
||||
#define IO_XATTR_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* Public API *****************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* write the following fields as a JSON object to the stream
|
||||
*
|
||||
* @param pathmd5 16 byte md5 of the path
|
||||
* @param name the name of the xattr field
|
||||
* @param valuebuffer the contents of the xattr field
|
||||
* @param valuesize the size of the xattr field
|
||||
* @param stream where to write the json object
|
||||
*
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int io_entry_write_xattr_fields(const void *pathmd5, const char *name,
|
||||
void *valuebuffer, ssize_t valuesize, FILE *stream);
|
||||
|
||||
|
||||
#endif
|
||||
+65
-7
@@ -11,6 +11,8 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include "io_dcp_processor.h"
|
||||
#include "entry.h"
|
||||
@@ -28,32 +30,44 @@
|
||||
*/
|
||||
struct io_dcp_processor_ctx {
|
||||
FILE *out; /**< where to write each file system entry info to */
|
||||
FILE *xattrout; /**< where to write xattr values for paths */
|
||||
};
|
||||
|
||||
|
||||
/* Private API ****************************************************************/
|
||||
|
||||
|
||||
static int process_xattrs(const void *pathmd5, const char *filepath, FILE *out);
|
||||
|
||||
|
||||
/* Public Impl ****************************************************************/
|
||||
|
||||
|
||||
int io_dcp_processor(dcp_state_t state, const void *pathmd5,
|
||||
const char *dapath, const struct stat *st, const void *md5,
|
||||
const void *sha1, const void *sha256, const void *sha512,
|
||||
unsigned long process_time, void *context)
|
||||
const char *dapath, const struct stat *st, const char *accesspath,
|
||||
const char *symlinkpath, const void *md5, const void *sha1,
|
||||
const void *sha256, const void *sha512, unsigned long process_time,
|
||||
void *context)
|
||||
{
|
||||
struct io_dcp_processor_ctx *ctx = context;
|
||||
return io_entry_write_fields(dcp_strstate(state), dapath, st,
|
||||
pathmd5, md5, sha1, sha256, sha512, process_time, ctx->out);
|
||||
process_xattrs(pathmd5, accesspath, ctx->xattrout);
|
||||
|
||||
return io_entry_write_fields(dcp_strstate(state), dapath, st, pathmd5,
|
||||
symlinkpath, md5, sha1, sha256, sha512, process_time, ctx->out);
|
||||
}
|
||||
|
||||
|
||||
int io_dcp_processor_ctx_create(io_dcp_processor_ctx_t **ctx, FILE *stream)
|
||||
int io_dcp_processor_ctx_create(io_dcp_processor_ctx_t **ctx, FILE *stream,
|
||||
FILE *xattrstream)
|
||||
{
|
||||
if (ctx != NULL)
|
||||
{
|
||||
*ctx = malloc(sizeof(struct io_dcp_processor_ctx));
|
||||
(*ctx)->out = stream;
|
||||
(*ctx)->xattrout = xattrstream;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,3 +77,47 @@ int io_dcp_processor_ctx_free(io_dcp_processor_ctx_t *ctx)
|
||||
free(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Private Impl **************************************************************/
|
||||
|
||||
|
||||
int process_xattrs(const void *pathmd5, const char *filepath, FILE *out)
|
||||
{
|
||||
char keys[XATTR_LIST_MAX];
|
||||
size_t keysbuffersize = XATTR_LIST_MAX;
|
||||
uint8_t valuebuffer[XATTR_SIZE_MAX];
|
||||
size_t valuebuffersize = XATTR_SIZE_MAX;
|
||||
ssize_t bufsize, valuesize;
|
||||
char *next, *end;
|
||||
|
||||
if (filepath == NULL)
|
||||
return 0; /* Nothing to do */
|
||||
|
||||
if ((bufsize = llistxattr(filepath, keys, keysbuffersize)) == 0)
|
||||
return 0;
|
||||
else if (bufsize < 0)
|
||||
{
|
||||
/* xattrs not supported or disabled for this file, success */
|
||||
if (errno == ENOTSUP)
|
||||
return 0;
|
||||
log_error("llistxattr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
end = keys + bufsize; /* pointer to the end of the list */
|
||||
|
||||
/* keys is one large string with '\0' delimited names */
|
||||
for (next = keys; next < end; next += (strlen(next) + 1))
|
||||
{
|
||||
memset(valuebuffer, 0, valuebuffersize);
|
||||
valuesize = lgetxattr(filepath, next, valuebuffer, valuebuffersize);
|
||||
if (valuesize < 0)
|
||||
log_error("lgetxattr");
|
||||
else
|
||||
io_entry_write_xattr_fields(pathmd5,next,valuebuffer,valuesize,out);
|
||||
}
|
||||
|
||||
fflush(out);
|
||||
return 0;
|
||||
}
|
||||
+12
-8
@@ -34,11 +34,12 @@ typedef struct io_dcp_processor_ctx io_dcp_processor_ctx_t;
|
||||
* An dcp callback function which writes each file/dir/dev to the configured
|
||||
* FILE stream. Outputs each entry using @see io_entry.h.
|
||||
*
|
||||
* @param state the resulting state of dcp processing,
|
||||
* @see impl/dcp.h
|
||||
* @param path mount relative path to the file to be copied
|
||||
* @param state the resulting state of processing, @see impl/dcp.h
|
||||
* @param pathmd5 the md5 sum of the file's dapath
|
||||
* @param dapath mount relative path to the file to be copied
|
||||
* @param st stat struct for the source file
|
||||
* @param dpath where the file is being copied to
|
||||
* @param accesspath path to the original file
|
||||
* @param symlinkpath if file is a symbolic link, where is it pointing
|
||||
* @param md5 md5 digest of the file
|
||||
* @param sha1 sha1 digest of the file
|
||||
* @param sha256 sha256 digest of the file
|
||||
@@ -50,9 +51,10 @@ typedef struct io_dcp_processor_ctx io_dcp_processor_ctx_t;
|
||||
* @return 0 on success
|
||||
*/
|
||||
int io_dcp_processor(dcp_state_t state, const void *pathmd5,
|
||||
const char *path, const struct stat *st, const void *md5,
|
||||
const void *sha1, const void *sha256, const void *sha512,
|
||||
unsigned long process_time, void *context);
|
||||
const char *dapath, const struct stat *st, const char *accesspath,
|
||||
const char *symlinkpath, const void *md5, const void *sha1,
|
||||
const void *sha256, const void *sha512, unsigned long process_time,
|
||||
void *context);
|
||||
|
||||
|
||||
/**
|
||||
@@ -60,10 +62,12 @@ int io_dcp_processor(dcp_state_t state, const void *pathmd5,
|
||||
*
|
||||
* @param ctx pointer to the context to initialize
|
||||
* @param stream where to serialize the entries to.
|
||||
* @param xattrstream where to serialize the extended attributes to
|
||||
*
|
||||
* @return 0 on success
|
||||
*/
|
||||
int io_dcp_processor_ctx_create(io_dcp_processor_ctx_t **ctx, FILE *stream);
|
||||
int io_dcp_processor_ctx_create(io_dcp_processor_ctx_t **ctx, FILE *stream,
|
||||
FILE *xattrstream);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
+55
-12
@@ -65,6 +65,8 @@ struct mainopts {
|
||||
size_t inputcount; /**< # input files specified */
|
||||
FILE *outputstream; /**< where we should write results to */
|
||||
char *outfilename; /**< the output file that outputstream is writing */
|
||||
FILE *xattroutputstream;/**< where we should write xattr results to */
|
||||
char *xattroutfilename; /**< the output file that xattroutputstream is writing to */
|
||||
|
||||
uid_t uid; /**< id of who will own the copies */
|
||||
gid_t gid; /**< id of what group will own the copies */
|
||||
@@ -95,6 +97,8 @@ static int print_metadata(FILE *out, const char *version, int argc,
|
||||
static int parse_digests(const struct cmdline_info *info);
|
||||
static FILE *parse_outputstream(const struct cmdline_info *info,
|
||||
char **outfilename);
|
||||
static FILE *parse_xattroutputstream(const struct cmdline_info *info,
|
||||
char **xattroutfilename);
|
||||
static gid_t parse_group(const struct cmdline_info *info, char **name);
|
||||
static uid_t parse_owner(const struct cmdline_info *info, char **name);
|
||||
static size_t parse_cache_size(const struct cmdline_info *info);
|
||||
@@ -217,7 +221,42 @@ FILE *parse_outputstream(const struct cmdline_info *info, char **outfilename)
|
||||
else
|
||||
log_crit(EXIT_FAILURE, "cannot create output file '%s'", name);
|
||||
}
|
||||
if ((stream = fdopen(fd, "a")) == NULL)
|
||||
log_crit(EXIT_FAILURE, "cannot create output stream");
|
||||
*outfilename = strdup(name);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
FILE *parse_xattroutputstream(const struct cmdline_info *info, char **outfilename)
|
||||
{
|
||||
int fd;
|
||||
size_t i;
|
||||
char name[38]; /* enough space to hold "dcp(1234567890).xattr.out\0" */
|
||||
FILE *stream;
|
||||
|
||||
if (info->xattr_given)
|
||||
{
|
||||
if ((stream = fopen(info->xattr_arg, "w")) == NULL)
|
||||
log_crit(EXIT_FAILURE,"failed to open output file '%s'",
|
||||
info->xattr_arg);
|
||||
*outfilename = strdup(info->xattr_arg);
|
||||
}
|
||||
|
||||
/* if no output is specified create an dcp.xattr.out in cwd */
|
||||
else
|
||||
{
|
||||
snprintf(name, sizeof(name), "dcp.xattr.out");
|
||||
i = 0;
|
||||
while ((fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1)
|
||||
{
|
||||
if (errno == EEXIST)
|
||||
snprintf(name, sizeof(name), "dcp(%zu).xattr.out", ++i);
|
||||
|
||||
else
|
||||
log_crit(EXIT_FAILURE, "cannot create output file '%s'", name);
|
||||
}
|
||||
if ((stream = fdopen(fd, "a")) == NULL)
|
||||
log_crit(EXIT_FAILURE, "cannot create output stream");
|
||||
*outfilename = strdup(name);
|
||||
@@ -296,6 +335,7 @@ int mainopts_parse(struct mainopts *opts, const struct cmdline_info *info)
|
||||
opts->dest = opts->files[opts->filecount];
|
||||
opts->digests = parse_digests(info);
|
||||
opts->outputstream = parse_outputstream(info, &opts->outfilename);
|
||||
opts->xattroutputstream = parse_xattroutputstream(info, &opts->xattroutfilename);
|
||||
opts->inputs = (const char **) info->input_arg;
|
||||
opts->inputcount = info->input_given;
|
||||
opts->uid = parse_owner(info, &opts->username);
|
||||
@@ -309,9 +349,11 @@ int mainopts_parse(struct mainopts *opts, const struct cmdline_info *info)
|
||||
void mainopts_cleanup(struct mainopts *opts)
|
||||
{
|
||||
if (opts->outfilename != NULL) free(opts->outfilename);
|
||||
if (opts->xattroutfilename != NULL) free(opts->xattroutfilename);
|
||||
if (opts->username != NULL) free(opts->username);
|
||||
if (opts->groupname != NULL) free(opts->groupname);
|
||||
if (opts->outputstream != NULL) fclose(opts->outputstream);
|
||||
if (opts->xattroutputstream != NULL) fclose(opts->xattroutputstream);
|
||||
}
|
||||
|
||||
|
||||
@@ -339,9 +381,10 @@ int dcp_main(const struct mainopts *opts, int argc, const char *argv[])
|
||||
|
||||
/* output information about this run of dcp */
|
||||
print_metadata(opts->outputstream, VERSION, argc, argv, opts, digests);
|
||||
print_metadata(opts->xattroutputstream, VERSION, argc, argv, opts, digests);
|
||||
|
||||
/* setup how and where to send the data gathered during this run */
|
||||
if (io_dcp_processor_ctx_create(&ctx, opts->outputstream) == -1)
|
||||
if (io_dcp_processor_ctx_create(&ctx, opts->outputstream, opts->xattroutputstream) == -1)
|
||||
log_critx(EXIT_FAILURE, "cannot instantiate output context");
|
||||
|
||||
/* set the options struct */
|
||||
@@ -382,10 +425,10 @@ index_t *build_index(int digests, const char *paths[], size_t count)
|
||||
digest_t type;
|
||||
size_t i;
|
||||
|
||||
/* assign type to the first valid one we find, checking md5 then sha1 ... */
|
||||
if ( !(type = digests & DGST_MD5) && !(type = digests & DGST_SHA1) &&
|
||||
!(type = digests & DGST_SHA256) && !(type = digests & DGST_SHA512))
|
||||
log_critx(EXIT_FAILURE, "corrput parsing of digest types from inputs");
|
||||
/* assign type to the first valid one we find, checking md5 then sha1 ... */
|
||||
if ( !(type = digests & DGST_MD5) && !(type = digests & DGST_SHA1) &&
|
||||
!(type = digests & DGST_SHA256) && !(type = digests & DGST_SHA512))
|
||||
log_critx(EXIT_FAILURE, "corrput parsing of digest types from inputs");
|
||||
|
||||
if (index_create(&idx, type) != 0)
|
||||
log_critx(EXIT_FAILURE, "cannot create index");
|
||||
@@ -439,8 +482,8 @@ int prepare(const char **files, size_t count, const char *dest, char **newdest)
|
||||
{
|
||||
name = basename(path);
|
||||
/* build the dest/name path including the slash if needed */
|
||||
if(asprintf(&fulldest, "%s%s%s",
|
||||
dest, dest[strlen(dest)-1] == '/'? "" : "/", name) < 0)
|
||||
if(asprintf(&fulldest, "%s%s%s", dest,
|
||||
dest[strlen(dest)-1] == '/'? "" : "/", name) < 0)
|
||||
{
|
||||
fulldest = NULL;
|
||||
}
|
||||
@@ -491,7 +534,7 @@ int print_metadata(FILE *out, const char *version, int argc,
|
||||
|
||||
/* current working directory */
|
||||
if ((cwd = getcwd(NULL, 0)) == NULL)
|
||||
log_warn("cannot retrieve current working directory");
|
||||
log_warn("cannot retrieve current working directory");
|
||||
|
||||
/* hostname */
|
||||
gethostname(hostname, sizeof(hostname));
|
||||
@@ -506,13 +549,13 @@ int print_metadata(FILE *out, const char *version, int argc,
|
||||
|
||||
if (cwd != NULL)
|
||||
{
|
||||
io_metadata_put_json("cwd ", 1, (const char **) &cwd, out);
|
||||
free(cwd);
|
||||
io_metadata_put_json("cwd ", 1, (const char **) &cwd, out);
|
||||
free(cwd);
|
||||
}
|
||||
|
||||
io_metadata_put_json("sources ", opts->filecount, opts->files, out);
|
||||
io_metadata_put_json("destination", 1, (const char **) &opts->dest, out);
|
||||
io_metadata_put_json("output ", 1,(const char**)&opts->outfilename,out);
|
||||
io_metadata_put_json("destination",1,(const char **) &opts->dest, out);
|
||||
io_metadata_put_json("output ",1,(const char**)&opts->outfilename,out);
|
||||
|
||||
if (opts->username != NULL)
|
||||
io_metadata_put( "data_owner ", opts->username, out);
|
||||
|
||||
Reference in new issue
Block a user