1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2023 Brett Sheffield <bacs@librecast.net> */
#include "test.h"
#include "testdata.h"
#include "testnet.h"
#include "testsync.h"
#include <errno.h>
#include <librecast_pvt.h>
#include <librecast/mdex.h>
#include <librecast/mtree.h>
#include <librecast/net.h>
#include <librecast/sync.h>
#include <sys/resource.h>
#include <unistd.h>
#define LINUX_SRC "/usr/src/linux-6.5.5"
#define MAXFILESZ 1048576
#define MAXFILES 10
#define MAXDIRS 5
#define DEPTH 2
#define TIMEOUT_SECONDS 3600
/*
* test 0122
*
* Sync the Linux kernel using lc_syncfile()
*
* Compare the resulting directories and ensure they match.
*/
static sem_t sem_recv;
struct pkg_s {
char *src;
char *dst;
};
/* append trailing slash to string, return allocated new string */
char *appendslash(char *str)
{
size_t len = strlen(str);
if (str[len - 1] == '/') return strdup(str);
char *newstr = malloc(len + 2);
assert(newstr);
strcpy(newstr, str);
newstr[len] = '/';
newstr[len + 1] = '\0';
return newstr;
}
void *thread_recv(void *arg)
{
struct pkg_s *pkg = (struct pkg_s *)arg;
const int flags =
SYNC_RECURSE | SYNC_ATIME | SYNC_MTIME | SYNC_OWNER | SYNC_GROUP | SYNC_MODE;
ssize_t rc;
lc_ctx_t *lctx = lc_ctx_new();
pthread_cleanup_push((void (*)(void *))lc_ctx_free, lctx);
lc_ctx_stream(lctx, stderr);
lc_ctx_debug(lctx, LCTX_DEBUG_SYNCFILE);
char *slashed = appendslash(pkg->src);
assert(slashed);
pthread_cleanup_push(free, slashed);
test_log("src (with trailing slash appended): '%s'\n", slashed);
rc = lc_syncfile_hash(lctx, slashed, pkg->dst, NULL, NULL, NULL, flags);
test_assert(rc > 0, "lc_syncfile_hash returned %zi", rc);
if (rc == -1) perror("lc_syncfile_hash");
pthread_cleanup_pop(1); /* free(slashed) */
pthread_cleanup_pop(1); /* lc_ctx_free */
sem_post(&sem_recv);
return NULL;
}
int main(int argc, char *argv[])
{
(void)argc;
lc_ctx_t *lctx;
mdex_t *mdex;
lc_share_t *share;
pthread_t tid_recv;
struct pkg_s pkg_recv = {0};
struct timespec timeout = {0};
char *dst = NULL;
char *rpath = NULL;
int rc;
test_cap_require(CAP_NET_ADMIN);
test_name("lc_syncfile() - recursive file syncing");
test_require_net(TEST_NET_BASIC);
/* create source directory tree and files */
#if 0
char *src = NULL;
rc = test_createtestdirs(basename(argv[0]), &src, &dst);
if (!test_assert(rc == 0, "test_createtestdirs()")) return test_status;
test_random_meta(src, TEST_OWN|TEST_MOD);
rc = test_createtesttree(src, MAXFILESZ, MAXFILES, MAXDIRS, DEPTH, TEST_OWN|TEST_MOD);
if (!test_assert(rc == 0, "test_createtesttree()")) goto err_free_src_dst;
#else
char *src = LINUX_SRC;
rc = test_createtestdir(basename(argv[0]), &dst, "dst");
if (!test_assert(rc == 0, "test_createtestdir() - dst directory")) goto err_free_src_dst;
#endif
/* how many open files are we permitted? */
struct rlimit rlim;
rc = getrlimit(RLIMIT_NOFILE, &rlim);
test_log("RLIMIT_NOFILE cur = %i, max = %i\n", rlim.rlim_cur, rlim.rlim_max);
/* use realpath for source */
rpath = realpath(src, NULL);
/* index source tree */
mdex = mdex_init(512);
if (!test_assert(mdex != NULL, "mdex_init()")) goto err_free_src_dst;
mdex->stream = stderr;
mdex->debug = MDEX_DEBUG_FILE;
test_log("mdexing files...\n");
rc = mdex_addfile(mdex, rpath, NULL, MDEX_RECURSE);
test_log("mdexing done.\n");
if (!test_assert(rc == 0, "mdex_addfile() returned %i", rc)) goto err_mdex_free;
#if 0
/* XXX; match src and dst match */
char cmd[128];
//snprintf(cmd, sizeof cmd, "rsync -avq %s/ %s", src, dst);
snprintf(cmd, sizeof cmd, "cp -Rp %s/. %s", src, dst);
test_log("`%s`\n", cmd);
if (system(cmd)) { test_assert(0, "cmd failed"); goto err_free_src_dst; }
#endif
/* share the mdex */
lctx = lc_ctx_new();
if (!test_assert(lctx != NULL, "lc_ctx_new()")) goto err_mdex_free;
share = lc_share(lctx, mdex, 0, NULL, NULL, LC_SHARE_LOOPBACK);
if (!test_assert(share != NULL, "lc_share()")) goto err_free_lctx;
/* start receive thread, sync files */
rc = sem_init(&sem_recv, 0, 0);
if (!test_assert(rc == 0, "sem_init(sem_recv)")) goto err_unshare;
pkg_recv.src = mdex_sharepath(mdex, rpath);
pkg_recv.dst = dst;
test_log("syncing '%s' => '%s'\n", pkg_recv.src, pkg_recv.dst);
rc = pthread_create(&tid_recv, NULL, thread_recv, &pkg_recv);
if (!test_assert(rc == 0, "create recv thread")) goto err_sem_destroy;
/* handle timeout */
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += TIMEOUT_SECONDS;
if ((rc = sem_timedwait(&sem_recv, &timeout)) == -1 && errno == ETIMEDOUT) {
pthread_cancel(tid_recv);
}
test_assert(rc == 0, "timeout waiting for recv thread");
pthread_join(tid_recv, NULL);
/* verify src and dst match */
test_verify_dirs(rpath, dst);
err_sem_destroy:
sem_destroy(&sem_recv);
err_unshare:
lc_unshare(share);
err_free_lctx:
lc_ctx_free(lctx);
err_mdex_free:
mdex_free(mdex);
err_free_src_dst:
free(dst);
free(pkg_recv.src);
free(rpath);
return test_status;
}
|