File: 0000-0116.c

package info (click to toggle)
librecast 0.11.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,760 kB
  • sloc: ansic: 31,144; asm: 28,570; sh: 3,164; makefile: 713; python: 70
file content (190 lines) | stat: -rw-r--r-- 6,084 bytes parent folder | download | duplicates (2)
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2023-2024 Brett Sheffield <bacs@librecast.net> */

#define _DEFAULT_SOURCE   /* be64toh */
#define _XOPEN_SOURCE 700 /* for nftw() */
#define _NETBSD_SOURCE    /* required  for scandir() on NetBSD */
#include "test.h"
#include "testdata.h"
#include "testnet.h"
#include <sys/types.h>
#include <errno.h>
#include <dirent.h>
#include <ftw.h>
#include <librecast_pvt.h>
#include <librecast/mdex.h>
#include <librecast/mtree.h>
#include <librecast/net.h>
#include <librecast/sync.h>
#include <unistd.h>

#if HAVE_RQ_OTI
#define MAXFILESZ 1048576
#define MAXFILES 2
#define MAXDIRS 2
#define DEPTH 2
#define TIMEOUT_SECONDS 3

/*
 * test 0116
 *
 * create a tree of files and directories and use lc_syncfile() to sync
 * the top-level directory non-recursively into the destination.
 *
 * Compare the resulting directories and ensure they match.
 */

static sem_t sem_recv;

struct pkg_s {
	char *src;
	char *dst;
};

void *thread_recv(void *arg)
{
	struct pkg_s *pkg = (struct pkg_s *)arg;
	unsigned char hash[HASHSIZE];
	const int flags =
		SYNC_SUBDIR | SYNC_ATIME | SYNC_MTIME | SYNC_OWNER | SYNC_GROUP | SYNC_MODE;
	ssize_t rc;
	mdex_aliashash(pkg->src, hash, sizeof hash);
	fprintf(stderr, "%s() ", __func__);
	hash_hex_debug(stderr, hash, sizeof hash);
	lc_ctx_t *lctx = lc_ctx_new();
	pthread_cleanup_push((void (*)(void *))lc_ctx_free, lctx);
	rc = lc_syncfile(lctx, hash, pkg->dst, NULL, NULL, NULL, flags);
	test_assert(rc > 0, "lc_syncfile returned %zi", rc);
	pthread_cleanup_pop(1); /* lc_ctx_free */
	sem_post(&sem_recv);
	return NULL;
}

static int scandirfilter(const struct dirent *dir)
{
	/* filter current and parent directories */
	return !(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."));
}

/* check mode, owner, group, times */
static int statcmp(const char *src, const char *dst)
{
	struct stat ssb, dsb;
	int rc;
	rc = stat(src, &ssb);
	if (!test_assert(rc == 0, "stat '%s'", src)) return -1;
	rc = stat(dst, &dsb);
	if (!test_assert(rc == 0, "stat '%s'", dst)) return -1;
	test_assert(ssb.st_mode == dsb.st_mode, "st_mode: '%s' %o %o", dst, ssb.st_mode, dsb.st_mode);
	test_assert(ssb.st_uid == dsb.st_uid, "st_uid:  '%s'", dst);
	test_assert(ssb.st_gid == dsb.st_gid, "st_gid:  '%s'", dst);
	test_assert(ssb.st_size == dsb.st_size, "st_size: '%s'", dst);
#ifndef HAVE_UTIMENSAT
	/* without utimensat(), we only have microsecond precision */
	ssb.st_mtim.tv_nsec /= 1000; ssb.st_mtim.tv_nsec *= 1000;
	ssb.st_atim.tv_nsec /= 1000; ssb.st_atim.tv_nsec *= 1000;
#endif
	/* No point testing atime unless the filesystem is mounted noatime */
	/* test_assert(ssb.st_atim.tv_nsec == dsb.st_atim.tv_nsec, "st_atim: '%s'", dst); */
	test_assert(ssb.st_mtim.tv_nsec == dsb.st_mtim.tv_nsec, "st_mtim: '%s'", dst);
	return 0;
}
#endif /* HAVE_RQ_OTI */

int main(int argc, char *argv[])
{
	(void)argc, (void)argv;
	char name[] = "lc_syncfile() - sync directory (not recursive)";
#if HAVE_RQ_OTI
	struct dirent **namelist;
	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 *src = NULL, *dst = NULL;
	int rc;

	test_cap_require(CAP_NET_ADMIN);
	test_name(name);
	test_require_net(TEST_NET_BASIC);

	/* create source directory tree and files */
	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;

	/* index source tree */
	mdex = mdex_init(512);
	if (!test_assert(mdex != NULL, "mdex_init()")) goto err_free_src_dst;
	rc = mdex_addfile(mdex, src, NULL, MDEX_RECURSE);
	if (!test_assert(rc == 0, "mdex_addfile() returned %i", rc)) goto err_mdex_free;

	/* 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;
	test_assert(share != NULL, "lc_share()");

	/* start receive thread, sync files */
	test_log("syncing '%s' => '%s'\n", src, dst);
	rc = sem_init(&sem_recv, 0, 0);
	if (!test_assert(rc == 0, "sem_init(sem_recv)")) goto err_unshare;
	pkg_recv.src = src;
	pkg_recv.dst = 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 */

	/* src has no trailing slash, so the directory must be created inside dst */
	struct stat ssb, dsb;
	char *destdir;
	int len;
	len = snprintf(NULL, 0, "%s/%s", dst, src);
	destdir = malloc(len + 1);
	if (!test_assert(destdir != NULL, "malloc destdir")) goto err_sem_destroy;
	if (!test_assert(len == snprintf(destdir, len + 1, "%s/%s", dst, src), "build destdir string"))
		goto err_free_destdir;
	rc = stat(destdir, &dsb);
	if (!test_assert(rc == 0, "%s exists", destdir)) goto err_free_destdir;
	rc = stat(src, &ssb);
	if (!test_assert(rc == 0, "stat src")) goto err_free_destdir;

	/* check mode, owner, group, times */
	test_assert(statcmp(src, destdir) == 0, "statcmp");

	/* we didn't ask for recursion, so make sure directory is empty */
	rc = scandir(destdir, &namelist, scandirfilter, alphasort);
	test_assert(rc == 0, "destination directory exists and is empty");

err_free_destdir:
	free(destdir);
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(src); free(dst);
	return test_status;
#else
	return test_skip(name);
#endif /* HAVE_RQ_OTI */
}