aboutsummaryrefslogtreecommitdiff
path: root/src/fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs')
-rw-r--r--src/fs/.gitignore43
-rw-r--r--src/fs/Makefile.am565
-rw-r--r--src/fs/fs.conf.in62
-rw-r--r--src/fs/fs.h396
-rw-r--r--src/fs/fs_api.c3349
-rw-r--r--src/fs/fs_api.h1940
-rw-r--r--src/fs/fs_directory.c675
-rw-r--r--src/fs/fs_dirmetascan.c503
-rw-r--r--src/fs/fs_download.c2418
-rw-r--r--src/fs/fs_file_information.c475
-rw-r--r--src/fs/fs_getopt.c274
-rw-r--r--src/fs/fs_list_indexed.c223
-rw-r--r--src/fs/fs_misc.c164
-rw-r--r--src/fs/fs_namespace.c818
-rw-r--r--src/fs/fs_publish.c1624
-rw-r--r--src/fs/fs_publish_ksk.c260
-rw-r--r--src/fs/fs_publish_ublock.c324
-rw-r--r--src/fs/fs_publish_ublock.h108
-rw-r--r--src/fs/fs_search.c1835
-rw-r--r--src/fs/fs_sharetree.c456
-rw-r--r--src/fs/fs_test_lib.c659
-rw-r--r--src/fs/fs_test_lib.h102
-rw-r--r--src/fs/fs_test_lib_data.conf17
-rw-r--r--src/fs/fs_tree.c463
-rw-r--r--src/fs/fs_tree.h217
-rw-r--r--src/fs/fs_unindex.c901
-rw-r--r--src/fs/fs_uri.c2061
-rw-r--r--src/fs/gnunet-auto-share.c791
-rw-r--r--src/fs/gnunet-daemon-fsprofiler.c672
-rw-r--r--src/fs/gnunet-directory.c211
-rw-r--r--src/fs/gnunet-download.c384
-rw-r--r--src/fs/gnunet-fs-profiler.c245
-rw-r--r--src/fs/gnunet-fs.c152
-rw-r--r--src/fs/gnunet-helper-fs-publish.c578
-rw-r--r--src/fs/gnunet-publish.c1008
-rw-r--r--src/fs/gnunet-search.c793
-rw-r--r--src/fs/gnunet-service-fs.c1425
-rw-r--r--src/fs/gnunet-service-fs.h306
-rw-r--r--src/fs/gnunet-service-fs_cadet.h168
-rw-r--r--src/fs/gnunet-service-fs_cadet_client.c728
-rw-r--r--src/fs/gnunet-service-fs_cadet_server.c546
-rw-r--r--src/fs/gnunet-service-fs_cp.c1826
-rw-r--r--src/fs/gnunet-service-fs_cp.h415
-rw-r--r--src/fs/gnunet-service-fs_indexing.c522
-rw-r--r--src/fs/gnunet-service-fs_indexing.h121
-rw-r--r--src/fs/gnunet-service-fs_pe.c814
-rw-r--r--src/fs/gnunet-service-fs_pe.h95
-rw-r--r--src/fs/gnunet-service-fs_pr.c1921
-rw-r--r--src/fs/gnunet-service-fs_pr.h422
-rw-r--r--src/fs/gnunet-service-fs_push.c677
-rw-r--r--src/fs/gnunet-service-fs_push.h66
-rw-r--r--src/fs/gnunet-service-fs_put.c296
-rw-r--r--src/fs/gnunet-service-fs_put.h46
-rw-r--r--src/fs/gnunet-unindex.c205
-rw-r--r--src/fs/perf_gnunet_service_fs_p2p.c368
-rw-r--r--src/fs/perf_gnunet_service_fs_p2p.conf7
-rw-r--r--src/fs/perf_gnunet_service_fs_p2p_respect.c479
-rw-r--r--src/fs/plugin_block_fs.c337
-rw-r--r--src/fs/test_fs.c262
-rw-r--r--src/fs/test_fs_data.conf7
-rw-r--r--src/fs/test_fs_defaults.conf47
-rw-r--r--src/fs/test_fs_directory.c186
-rw-r--r--src/fs/test_fs_download.c368
-rw-r--r--src/fs/test_fs_download_data.conf10
-rw-r--r--src/fs/test_fs_download_indexed.conf10
-rw-r--r--src/fs/test_fs_download_persistence.c351
-rw-r--r--src/fs/test_fs_file_information.c163
-rw-r--r--src/fs/test_fs_file_information_data.conf7
-rw-r--r--src/fs/test_fs_getopt.c37
-rw-r--r--src/fs/test_fs_list_indexed.c265
-rw-r--r--src/fs/test_fs_list_indexed_data.conf10
-rw-r--r--src/fs/test_fs_namespace.c320
-rw-r--r--src/fs/test_fs_namespace_data.conf7
-rw-r--r--src/fs/test_fs_namespace_list_updateable.c175
-rw-r--r--src/fs/test_fs_publish.c251
-rw-r--r--src/fs/test_fs_publish_data.conf10
-rw-r--r--src/fs/test_fs_publish_persistence.c322
-rw-r--r--src/fs/test_fs_search.c252
-rw-r--r--src/fs/test_fs_search_data.conf7
-rw-r--r--src/fs/test_fs_search_persistence.c318
-rw-r--r--src/fs/test_fs_search_probes.c258
-rw-r--r--src/fs/test_fs_search_with_and.c272
-rw-r--r--src/fs/test_fs_start_stop.c64
-rw-r--r--src/fs/test_fs_test_lib.c179
-rw-r--r--src/fs/test_fs_unindex.c237
-rw-r--r--src/fs/test_fs_unindex_data.conf7
-rw-r--r--src/fs/test_fs_unindex_persistence.c307
-rw-r--r--src/fs/test_fs_uri.c337
-rwxr-xr-xsrc/fs/test_gnunet_fs_idx.py.in113
-rw-r--r--src/fs/test_gnunet_fs_idx_data.conf7
-rwxr-xr-xsrc/fs/test_gnunet_fs_psd.py.in149
-rw-r--r--src/fs/test_gnunet_fs_psd_data.conf7
-rwxr-xr-xsrc/fs/test_gnunet_fs_rec.py.in171
-rw-r--r--src/fs/test_gnunet_fs_rec_data.conf7
-rw-r--r--src/fs/test_gnunet_fs_rec_data.tgzbin17822 -> 0 bytes
-rw-r--r--src/fs/test_gnunet_service_fs_migration.c221
-rw-r--r--src/fs/test_gnunet_service_fs_migration_data.conf10
-rw-r--r--src/fs/test_gnunet_service_fs_p2p.c166
-rw-r--r--src/fs/test_gnunet_service_fs_p2p_cadet.conf20
-rw-r--r--src/fs/test_plugin_block_fs.c86
-rw-r--r--src/fs/test_pseudonym_data.conf6
101 files changed, 0 insertions, 43565 deletions
diff --git a/src/fs/.gitignore b/src/fs/.gitignore
deleted file mode 100644
index f0e2a4f7b..000000000
--- a/src/fs/.gitignore
+++ /dev/null
@@ -1,43 +0,0 @@
1gnunet-unindex
2gnunet-auto-share
3gnunet-daemon-fsprofiler
4gnunet-directory
5gnunet-download
6gnunet-fs
7gnunet-fs-profiler
8gnunet-helper-fs-publish
9gnunet-publish
10gnunet-search
11gnunet-service-fs
12test_fs_directory
13test_fs_download
14test_fs_download_cadet
15test_fs_download_indexed
16test_fs_download_persistence
17test_fs_file_information
18test_fs_getopt
19test_fs_list_indexed
20test_fs_namespace
21test_fs_namespace_list_updateable
22test_fs_publish
23test_fs_publish_persistence
24test_fs_search
25test_fs_search_persistence
26test_fs_search_probes
27test_fs_search_with_and
28test_fs_start_stop
29test_fs_test_lib
30test_fs_unindex
31test_fs_unindex_persistence
32test_fs_uri
33test_gnunet_fs_idx.py
34test_gnunet_fs_psd.py
35test_gnunet_fs_rec.py
36test_gnunet_service_fs_migration
37test_gnunet_service_fs_p2p
38test_gnunet_service_fs_p2p_cadet
39test_plugin_block_fs
40perf_gnunet_service_fs_p2p
41perf_gnunet_service_fs_p2p_index
42perf_gnunet_service_fs_p2p_respect
43rdir.gnd
diff --git a/src/fs/Makefile.am b/src/fs/Makefile.am
deleted file mode 100644
index 3d8ec2bac..000000000
--- a/src/fs/Makefile.am
+++ /dev/null
@@ -1,565 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9pkgcfgdir= $(pkgdatadir)/config.d/
10
11libexecdir= $(pkglibdir)/libexec/
12
13pkgcfg_DATA = \
14 fs.conf
15
16plugindir = $(libdir)/gnunet
17
18
19lib_LTLIBRARIES = libgnunetfs.la
20
21plugin_LTLIBRARIES = \
22 libgnunet_plugin_block_fs.la
23
24noinst_LIBRARIES = libgnunetfstest.a
25
26libgnunetfs_la_SOURCES = \
27 fs_api.c fs_api.h fs.h \
28 fs_directory.c \
29 fs_dirmetascan.c \
30 fs_download.c \
31 fs_file_information.c \
32 fs_getopt.c \
33 fs_list_indexed.c \
34 fs_publish.c \
35 fs_publish_ksk.c \
36 fs_publish_ublock.c fs_publish_ublock.h \
37 fs_misc.c \
38 fs_namespace.c \
39 fs_search.c \
40 fs_sharetree.c \
41 fs_tree.c fs_tree.h \
42 fs_unindex.c \
43 fs_uri.c
44
45libgnunetfs_la_LIBADD = \
46 $(top_builddir)/src/datastore/libgnunetdatastore.la \
47 $(top_builddir)/src/statistics/libgnunetstatistics.la \
48 $(top_builddir)/src/util/libgnunetutil.la \
49 $(GN_LIBINTL) $(XLIB) $(LIBGCRYPT_LIBS) -lunistring
50
51if HAVE_LIBEXTRACTOR
52libgnunetfs_la_LIBADD += \
53 -lextractor
54endif
55
56libgnunetfs_la_LDFLAGS = \
57 $(GN_LIB_LDFLAGS) \
58 -version-info 3:1:1
59
60
61libgnunetfstest_a_SOURCES = \
62 fs_test_lib.c fs_test_lib.h
63
64libgnunetfstest_a_LIBADD = \
65 $(top_builddir)/src/testing/libgnunettesting.la \
66 $(top_builddir)/src/testbed/libgnunettestbed.la
67
68libexec_PROGRAMS = \
69 gnunet-helper-fs-publish \
70 gnunet-service-fs
71
72noinst_PROGRAMS = \
73 gnunet-fs-profiler \
74 gnunet-daemon-fsprofiler
75
76bin_PROGRAMS = \
77 gnunet-auto-share \
78 gnunet-directory \
79 gnunet-download \
80 gnunet-publish \
81 gnunet-search \
82 gnunet-fs \
83 gnunet-unindex
84
85gnunet_directory_SOURCES = \
86 gnunet-directory.c
87gnunet_directory_LDADD = \
88 libgnunetfs.la \
89 $(top_builddir)/src/util/libgnunetutil.la \
90 $(GN_LIBINTL)
91
92if HAVE_LIBEXTRACTOR
93gnunet_directory_LDADD += \
94 -lextractor
95endif
96
97gnunet_fs_profiler_SOURCES = \
98 gnunet-fs-profiler.c
99gnunet_fs_profiler_LDADD = \
100 $(top_builddir)/src/testing/libgnunettesting.la \
101 $(top_builddir)/src/testbed/libgnunettestbed.la \
102 $(top_builddir)/src/util/libgnunetutil.la \
103 $(GN_LIBINTL)
104
105gnunet_fs_SOURCES = \
106 gnunet-fs.c
107gnunet_fs_LDADD = \
108 libgnunetfs.la \
109 $(top_builddir)/src/util/libgnunetutil.la \
110 $(GN_LIBINTL)
111
112if HAVE_LIBEXTRACTOR
113gnunet_fs_LDADD += \
114 -lextractor
115endif
116
117gnunet_download_SOURCES = \
118 gnunet-download.c
119gnunet_download_LDADD = \
120 libgnunetfs.la \
121 $(top_builddir)/src/util/libgnunetutil.la \
122 $(GN_LIBINTL)
123
124gnunet_publish_SOURCES = \
125 gnunet-publish.c
126gnunet_publish_LDADD = \
127 $(top_builddir)/src/identity/libgnunetidentity.la \
128 libgnunetfs.la \
129 $(top_builddir)/src/util/libgnunetutil.la \
130 $(GN_LIBINTL)
131
132if HAVE_LIBEXTRACTOR
133gnunet_publish_LDADD += \
134 -lextractor
135endif
136
137gnunet_auto_share_SOURCES = \
138 gnunet-auto-share.c
139gnunet_auto_share_LDADD = \
140 $(top_builddir)/src/util/libgnunetutil.la \
141 $(GN_LIBINTL)
142
143if HAVE_LIBEXTRACTOR
144gnunet_auto_share_LDADD += \
145 -lextractor
146endif
147
148gnunet_helper_fs_publish_SOURCES = \
149 gnunet-helper-fs-publish.c
150gnunet_helper_fs_publish_LDADD = \
151 $(top_builddir)/src/util/libgnunetutil.la \
152 $(GN_LIBINTL)
153
154if HAVE_LIBEXTRACTOR
155gnunet_helper_fs_publish_LDADD += \
156 -lextractor
157endif
158
159gnunet_search_SOURCES = \
160 gnunet-search.c
161gnunet_search_LDADD = \
162 libgnunetfs.la \
163 $(top_builddir)/src/util/libgnunetutil.la \
164 $(GN_LIBINTL)
165
166if HAVE_LIBEXTRACTOR
167gnunet_search_LDADD += \
168 -lextractor
169endif
170
171
172gnunet_daemon_fsprofiler_SOURCES = \
173 gnunet-daemon-fsprofiler.c
174gnunet_daemon_fsprofiler_LDADD = \
175 libgnunetfs.la \
176 $(top_builddir)/src/statistics/libgnunetstatistics.la \
177 $(top_builddir)/src/util/libgnunetutil.la \
178 $(GN_LIBINTL)
179
180gnunet_service_fs_SOURCES = \
181 gnunet-service-fs.c gnunet-service-fs.h \
182 gnunet-service-fs_cp.c gnunet-service-fs_cp.h \
183 gnunet-service-fs_indexing.c gnunet-service-fs_indexing.h \
184 gnunet-service-fs_pe.c gnunet-service-fs_pe.h \
185 gnunet-service-fs_pr.c gnunet-service-fs_pr.h \
186 gnunet-service-fs_push.c gnunet-service-fs_push.h \
187 gnunet-service-fs_put.c gnunet-service-fs_put.h \
188 gnunet-service-fs_cadet_client.c gnunet-service-fs_cadet.h \
189 gnunet-service-fs_cadet_server.c
190gnunet_service_fs_LDADD = \
191 libgnunetfs.la \
192 $(top_builddir)/src/dht/libgnunetdht.la \
193 $(top_builddir)/src/block/libgnunetblock.la \
194 $(top_builddir)/src/datastore/libgnunetdatastore.la \
195 $(top_builddir)/src/statistics/libgnunetstatistics.la \
196 $(top_builddir)/src/cadet/libgnunetcadet.la \
197 $(top_builddir)/src/ats/libgnunetats.la \
198 $(top_builddir)/src/core/libgnunetcore.la \
199 $(top_builddir)/src/util/libgnunetutil.la \
200 $(top_builddir)/src/peerstore/libgnunetpeerstore.la \
201 $(GN_LIBINTL) -lm
202
203gnunet_unindex_SOURCES = \
204 gnunet-unindex.c
205gnunet_unindex_LDADD = \
206 libgnunetfs.la \
207 $(top_builddir)/src/util/libgnunetutil.la \
208 $(GN_LIBINTL)
209
210libgnunet_plugin_block_fs_la_SOURCES = \
211 plugin_block_fs.c
212libgnunet_plugin_block_fs_la_LIBADD = \
213 $(top_builddir)/src/block/libgnunetblockgroup.la \
214 $(top_builddir)/src/block/libgnunetblock.la \
215 libgnunetfs.la \
216 $(top_builddir)/src/util/libgnunetutil.la \
217 $(LTLIBINTL)
218libgnunet_plugin_block_fs_la_LDFLAGS = \
219 $(GN_PLUGIN_LDFLAGS)
220
221if HAVE_BENCHMARKS
222 FS_BENCHMARKS = \
223 perf_gnunet_service_fs_p2p \
224 perf_gnunet_service_fs_p2p_dht \
225 perf_gnunet_service_fs_p2p_index \
226 perf_gnunet_service_fs_p2p_respect
227endif
228
229check_PROGRAMS = \
230 test_plugin_block_fs \
231 test_fs_directory \
232 test_fs_download \
233 test_fs_download_cadet \
234 test_fs_download_indexed \
235 test_fs_download_persistence \
236 test_fs_file_information \
237 test_fs_getopt \
238 test_fs_list_indexed \
239 test_fs_namespace \
240 test_fs_namespace_list_updateable \
241 test_fs_publish \
242 test_fs_publish_persistence \
243 test_fs_search \
244 test_fs_search_with_and \
245 test_fs_search_probes \
246 test_fs_search_persistence \
247 test_fs_start_stop \
248 test_fs_test_lib \
249 test_fs_unindex \
250 test_fs_unindex_persistence \
251 test_fs_uri \
252 test_gnunet_service_fs_migration \
253 test_gnunet_service_fs_p2p \
254 test_gnunet_service_fs_p2p_cadet \
255 $(FS_BENCHMARKS)
256
257test_plugin_block_fs_SOURCES = \
258 test_plugin_block_fs.c
259test_plugin_block_fs_LDADD = \
260 $(top_builddir)/src/block/libgnunetblock.la \
261 $(top_builddir)/src/util/libgnunetutil.la
262
263if HAVE_PYTHON
264check_SCRIPTS = \
265 test_gnunet_fs_rec.py \
266 test_gnunet_fs_idx.py
267
268if HAVE_LIBEXTRACTOR
269check_SCRIPTS += \
270 test_gnunet_fs_psd.py
271endif
272endif
273
274
275if ENABLE_TEST_RUN
276AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; $(MONKEY)
277TESTS = \
278 test_fs_directory \
279 test_fs_download \
280 test_fs_download_indexed \
281 test_fs_download_persistence \
282 test_fs_file_information \
283 test_fs_list_indexed \
284 test_fs_namespace \
285 test_fs_namespace_list_updateable \
286 test_fs_publish \
287 test_fs_publish_persistence \
288 test_fs_search \
289 test_fs_search_with_and \
290 test_fs_search_probes \
291 test_fs_search_persistence \
292 test_fs_start_stop \
293 test_fs_unindex \
294 test_fs_unindex_persistence \
295 test_fs_uri \
296 test_fs_test_lib \
297 test_gnunet_service_fs_migration \
298 test_gnunet_service_fs_p2p \
299 test_gnunet_service_fs_p2p_cadet \
300 perf_gnunet_service_fs_p2p \
301 perf_gnunet_service_fs_p2p_index \
302 perf_gnunet_service_fs_p2p_respect \
303 $(check_SCRIPTS)
304endif
305
306
307test_fs_directory_SOURCES = \
308 test_fs_directory.c
309test_fs_directory_LDADD = \
310 libgnunetfs.la \
311 $(top_builddir)/src/util/libgnunetutil.la
312
313if HAVE_LIBEXTRACTOR
314test_fs_directory_LDADD += \
315 -lextractor
316endif
317
318
319test_fs_download_SOURCES = \
320 test_fs_download.c
321test_fs_download_LDADD = \
322 $(top_builddir)/src/testing/libgnunettesting.la \
323 libgnunetfs.la \
324 $(top_builddir)/src/util/libgnunetutil.la
325
326test_fs_download_indexed_SOURCES = \
327 test_fs_download.c
328test_fs_download_indexed_LDADD = \
329 $(top_builddir)/src/testing/libgnunettesting.la \
330 libgnunetfs.la \
331 $(top_builddir)/src/util/libgnunetutil.la
332
333test_fs_download_cadet_SOURCES = \
334 test_fs_download.c
335test_fs_download_cadet_LDADD = \
336 $(top_builddir)/src/testing/libgnunettesting.la \
337 libgnunetfs.la \
338 $(top_builddir)/src/util/libgnunetutil.la
339
340test_fs_download_persistence_SOURCES = \
341 test_fs_download_persistence.c
342test_fs_download_persistence_LDADD = \
343 $(top_builddir)/src/testing/libgnunettesting.la \
344 libgnunetfs.la \
345 $(top_builddir)/src/util/libgnunetutil.la
346
347test_fs_file_information_SOURCES = \
348 test_fs_file_information.c
349test_fs_file_information_LDADD = \
350 libgnunetfs.la \
351 $(top_builddir)/src/util/libgnunetutil.la
352
353if HAVE_LIBEXTRACTOR
354test_fs_file_information_LDADD += \
355 -lextractor
356endif
357
358
359test_fs_getopt_SOURCES = \
360 test_fs_getopt.c
361test_fs_getopt_LDADD = \
362 libgnunetfs.la \
363 $(top_builddir)/src/util/libgnunetutil.la
364
365test_fs_list_indexed_SOURCES = \
366 test_fs_list_indexed.c
367test_fs_list_indexed_LDADD = \
368 $(top_builddir)/src/testing/libgnunettesting.la \
369 libgnunetfs.la \
370 $(top_builddir)/src/util/libgnunetutil.la
371
372test_fs_namespace_SOURCES = \
373 test_fs_namespace.c
374test_fs_namespace_LDADD = \
375 $(top_builddir)/src/testing/libgnunettesting.la \
376 libgnunetfs.la \
377 $(top_builddir)/src/util/libgnunetutil.la
378
379test_fs_namespace_list_updateable_SOURCES = \
380 test_fs_namespace_list_updateable.c
381test_fs_namespace_list_updateable_LDADD = \
382 $(top_builddir)/src/testing/libgnunettesting.la \
383 libgnunetfs.la \
384 $(top_builddir)/src/util/libgnunetutil.la
385
386test_fs_publish_SOURCES = \
387 test_fs_publish.c
388test_fs_publish_LDADD = \
389 $(top_builddir)/src/testing/libgnunettesting.la \
390 libgnunetfs.la \
391 $(top_builddir)/src/util/libgnunetutil.la
392
393test_fs_publish_persistence_SOURCES = \
394 test_fs_publish_persistence.c
395test_fs_publish_persistence_LDADD = \
396 $(top_builddir)/src/testing/libgnunettesting.la \
397 libgnunetfs.la \
398 $(top_builddir)/src/util/libgnunetutil.la
399
400test_fs_search_SOURCES = \
401 test_fs_search.c
402test_fs_search_LDADD = \
403 $(top_builddir)/src/testing/libgnunettesting.la \
404 libgnunetfs.la \
405 $(top_builddir)/src/util/libgnunetutil.la
406
407test_fs_search_with_and_SOURCES = \
408 test_fs_search_with_and.c
409test_fs_search_with_and_LDADD = \
410 $(top_builddir)/src/testing/libgnunettesting.la \
411 libgnunetfs.la \
412 $(top_builddir)/src/util/libgnunetutil.la
413
414test_fs_search_probes_SOURCES = \
415 test_fs_search_probes.c
416test_fs_search_probes_LDADD = \
417 $(top_builddir)/src/testing/libgnunettesting.la \
418 libgnunetfs.la \
419 $(top_builddir)/src/util/libgnunetutil.la
420
421test_fs_search_persistence_SOURCES = \
422 test_fs_search_persistence.c
423test_fs_search_persistence_LDADD = \
424 $(top_builddir)/src/testing/libgnunettesting.la \
425 libgnunetfs.la \
426 $(top_builddir)/src/util/libgnunetutil.la
427
428test_fs_start_stop_SOURCES = \
429 test_fs_start_stop.c
430test_fs_start_stop_LDADD = \
431 $(top_builddir)/src/testing/libgnunettesting.la \
432 libgnunetfs.la \
433 $(top_builddir)/src/util/libgnunetutil.la
434
435test_fs_unindex_SOURCES = \
436 test_fs_unindex.c
437test_fs_unindex_LDADD = \
438 $(top_builddir)/src/testing/libgnunettesting.la \
439 libgnunetfs.la \
440 $(top_builddir)/src/util/libgnunetutil.la
441
442test_fs_unindex_persistence_SOURCES = \
443 test_fs_unindex_persistence.c
444test_fs_unindex_persistence_LDADD = \
445 $(top_builddir)/src/testing/libgnunettesting.la \
446 libgnunetfs.la \
447 $(top_builddir)/src/util/libgnunetutil.la
448
449test_fs_uri_SOURCES = \
450 test_fs_uri.c
451test_fs_uri_LDADD = \
452 libgnunetfs.la \
453 $(top_builddir)/src/util/libgnunetutil.la
454
455test_fs_test_lib_SOURCES = \
456 test_fs_test_lib.c
457test_fs_test_lib_LDADD = \
458 libgnunetfstest.a \
459 $(top_builddir)/src/testbed/libgnunettestbed.la \
460 libgnunetfs.la \
461 $(top_builddir)/src/util/libgnunetutil.la
462
463test_gnunet_service_fs_p2p_SOURCES = \
464 test_gnunet_service_fs_p2p.c
465test_gnunet_service_fs_p2p_LDADD = \
466 libgnunetfstest.a \
467 $(top_builddir)/src/testbed/libgnunettestbed.la \
468 libgnunetfs.la \
469 $(top_builddir)/src/util/libgnunetutil.la
470
471test_gnunet_service_fs_p2p_cadet_SOURCES = \
472 test_gnunet_service_fs_p2p.c
473test_gnunet_service_fs_p2p_cadet_LDADD = \
474 libgnunetfstest.a \
475 $(top_builddir)/src/testbed/libgnunettestbed.la \
476 libgnunetfs.la \
477 $(top_builddir)/src/util/libgnunetutil.la
478
479test_gnunet_service_fs_migration_SOURCES = \
480 test_gnunet_service_fs_migration.c
481test_gnunet_service_fs_migration_LDADD = \
482 libgnunetfstest.a \
483 $(top_builddir)/src/testbed/libgnunettestbed.la \
484 libgnunetfs.la \
485 $(top_builddir)/src/util/libgnunetutil.la
486
487perf_gnunet_service_fs_p2p_SOURCES = \
488 perf_gnunet_service_fs_p2p.c
489perf_gnunet_service_fs_p2p_LDADD = \
490 libgnunetfstest.a \
491 $(top_builddir)/src/statistics/libgnunetstatistics.la \
492 $(top_builddir)/src/testbed/libgnunettestbed.la \
493 libgnunetfs.la \
494 $(top_builddir)/src/util/libgnunetutil.la
495
496perf_gnunet_service_fs_p2p_index_SOURCES = \
497 perf_gnunet_service_fs_p2p.c
498perf_gnunet_service_fs_p2p_index_LDADD = \
499 libgnunetfstest.a \
500 $(top_builddir)/src/statistics/libgnunetstatistics.la \
501 $(top_builddir)/src/testbed/libgnunettestbed.la \
502 libgnunetfs.la \
503 $(top_builddir)/src/util/libgnunetutil.la
504
505perf_gnunet_service_fs_p2p_dht_SOURCES = \
506 perf_gnunet_service_fs_p2p.c
507perf_gnunet_service_fs_p2p_dht_LDADD = \
508 libgnunetfstest.a \
509 $(top_builddir)/src/statistics/libgnunetstatistics.la \
510 $(top_builddir)/src/testbed/libgnunettestbed.la \
511 libgnunetfs.la \
512 $(top_builddir)/src/util/libgnunetutil.la
513
514perf_gnunet_service_fs_p2p_respect_SOURCES = \
515 perf_gnunet_service_fs_p2p_respect.c
516perf_gnunet_service_fs_p2p_respect_LDADD = \
517 libgnunetfstest.a \
518 $(top_builddir)/src/statistics/libgnunetstatistics.la \
519 $(top_builddir)/src/testbed/libgnunettestbed.la \
520 libgnunetfs.la \
521 $(top_builddir)/src/util/libgnunetutil.la
522
523
524test_gnunet_fs_psd.py: test_gnunet_fs_psd.py.in Makefile
525 $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/test_gnunet_fs_psd.py.in > test_gnunet_fs_psd.py
526 chmod +x test_gnunet_fs_psd.py
527
528test_gnunet_fs_rec.py: test_gnunet_fs_rec.py.in Makefile
529 $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/test_gnunet_fs_rec.py.in > test_gnunet_fs_rec.py
530 chmod +x test_gnunet_fs_rec.py
531
532test_gnunet_fs_ns.py: test_gnunet_fs_ns.py.in Makefile
533 $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/test_gnunet_fs_ns.py.in > test_gnunet_fs_ns.py
534 chmod +x test_gnunet_fs_ns.py
535
536test_gnunet_fs_idx.py: test_gnunet_fs_idx.py.in Makefile
537 $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/test_gnunet_fs_idx.py.in > test_gnunet_fs_idx.py
538 chmod +x test_gnunet_fs_idx.py
539
540
541EXTRA_DIST = \
542 fs_test_lib_data.conf \
543 perf_gnunet_service_fs_p2p.conf \
544 test_fs_data.conf \
545 test_fs_defaults.conf \
546 test_fs_download_data.conf \
547 test_fs_download_indexed.conf \
548 test_fs_file_information_data.conf \
549 test_fs_list_indexed_data.conf \
550 test_fs_namespace_data.conf \
551 test_fs_publish_data.conf \
552 test_fs_search_data.conf \
553 test_fs_unindex_data.conf \
554 test_gnunet_fs_idx_data.conf \
555 test_gnunet_fs_psd_data.conf \
556 test_gnunet_fs_rec_data.conf \
557 test_gnunet_fs_rec_data.tgz \
558 test_gnunet_fs_psd.py.in \
559 test_gnunet_fs_rec.py.in \
560 test_gnunet_fs_idx.py.in \
561 test_gnunet_service_fs_migration_data.conf \
562 test_gnunet_service_fs_p2p_cadet.conf \
563 test_pseudonym_data.conf
564
565CLEANFILES = $(check_SCRIPTS)
diff --git a/src/fs/fs.conf.in b/src/fs/fs.conf.in
deleted file mode 100644
index 797109d07..000000000
--- a/src/fs/fs.conf.in
+++ /dev/null
@@ -1,62 +0,0 @@
1[fs]
2START_ON_DEMAND = @START_ON_DEMAND@
3IMMEDIATE_START = YES
4INDEXDB = $GNUNET_DATA_HOME/fs/idxinfo.lst
5RESPECT = $GNUNET_DATA_HOME/fs/credit/
6STATE_DIR = $GNUNET_DATA_HOME/fs/persistence/
7UPDATE_DIR = $GNUNET_DATA_HOME/fs/updates/
8@UNIXONLY@ PORT = 2094
9HOSTNAME = localhost
10BINARY = gnunet-service-fs
11ACCEPT_FROM = 127.0.0.1;
12ACCEPT_FROM6 = ::1;
13
14# PREFIX = valgrind
15
16# Do we introduce artificial delays? (may improve anonymity)
17DELAY = YES
18
19# Do we cache content from other nodes? (may improve anonymity)
20CONTENT_CACHING = YES
21
22# Do we send unsolicited data to other nodes if we have excess bandwidth?
23# (may improve anonymity, probably not a good idea if content_caching is NO)
24CONTENT_PUSHING = YES
25
26UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-fs.sock
27
28# Do we require users that want to access file-sharing to run this process
29# (usually not a good idea)
30UNIX_MATCH_UID = NO
31
32# Do we require users that want to access file-sharing to be in the 'gnunet' group?
33UNIX_MATCH_GID = YES
34
35# Maximum number of requests this peer tracks (important for
36# memory consumption; 2k RAM/request is not unusual)
37MAX_PENDING_REQUESTS = 65536
38
39# How many requests do we have at most waiting in the queue towards
40# the datastore? (important for memory consumption)
41DATASTORE_QUEUE_SIZE = 32
42
43# Maximum frequency we're allowed to poll the datastore
44# for content for migration (can be used to reduce
45# GNUnet's disk-IO rate)
46MIN_MIGRATION_DELAY = 100 ms
47
48# For how many neighbouring peers should we allocate hash maps?
49EXPECTED_NEIGHBOUR_COUNT = 128
50
51# Disable anonymous file-sharing (but keep non-anonymous transfers)?
52# This option is mostly for testing.
53DISABLE_ANON_TRANSFER = NO
54
55# Maximum number of non-anonymous transfers this peer will support
56# at the same time. Excessive values mostly have the problem that
57# the service might use more memory, so we need to bound this at
58# some reasonable level. And if we have a very, very large
59# number, we probably won't have enough bandwidth to support them
60# well anyway, so better have a moderate cap.
61MAX_CADET_CLIENTS = 128
62
diff --git a/src/fs/fs.h b/src/fs/fs.h
deleted file mode 100644
index 4c433e774..000000000
--- a/src/fs/fs.h
+++ /dev/null
@@ -1,396 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003--2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs.h
23 * @brief definitions for the entire fs module
24 * @author Igor Wronsky, Christian Grothoff
25 */
26#ifndef FS_H
27#define FS_H
28
29#include "gnunet_constants.h"
30#include "gnunet_datastore_service.h"
31#include "gnunet_dht_service.h"
32#include "gnunet_fs_service.h"
33#include "gnunet_block_lib.h"
34#include "block_fs.h"
35
36
37/**
38 * Size of the individual blocks used for file-sharing.
39 */
40#define DBLOCK_SIZE (32 * 1024)
41
42/**
43 * Blocksize to use when hashing files for indexing (blocksize for IO,
44 * not for the DBlocks). Larger blocksizes can be more efficient but
45 * will be more disruptive as far as the scheduler is concerned.
46 */
47#define HASHING_BLOCKSIZE (1024 * 128)
48
49
50/**
51 * @brief content hash key
52 */
53struct ContentHashKey
54{
55 /**
56 * Hash of the original content, used for encryption.
57 */
58 struct GNUNET_HashCode key;
59
60 /**
61 * Hash of the encrypted content, used for querying.
62 */
63 struct GNUNET_HashCode query;
64};
65
66
67GNUNET_NETWORK_STRUCT_BEGIN
68
69
70/**
71 * Message sent from a GNUnet (fs) publishing activity to sign
72 * a LOC URI.
73 */
74struct RequestLocSignatureMessage
75{
76 /**
77 * Message type will be #GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGN.
78 */
79 struct GNUNET_MessageHeader header;
80
81 /**
82 * Requested signature purpose. For now, always
83 * #GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT.
84 */
85 uint32_t purpose GNUNET_PACKED;
86
87 /**
88 * Requested expiration time.
89 */
90 struct GNUNET_TIME_AbsoluteNBO expiration_time;
91
92 /**
93 * Information about the shared file (to be signed).
94 */
95 struct ContentHashKey chk;
96
97 /**
98 * Size of the shared file (to be signed).
99 */
100 uint64_t file_length;
101};
102
103
104/**
105 * Message sent from the service with the signed LOC URI.
106 */
107struct ResponseLocSignatureMessage
108{
109 /**
110 * Message type will be
111 * #GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGNATURE.
112 */
113 struct GNUNET_MessageHeader header;
114
115 /**
116 * Purpose of the generated signature. For now, always
117 * #GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT.
118 */
119 uint32_t purpose GNUNET_PACKED;
120
121 /**
122 * Expiration time that was actually used (rounded!).
123 */
124 struct GNUNET_TIME_AbsoluteNBO expiration_time;
125
126 /**
127 * The requested signature.
128 */
129 struct GNUNET_CRYPTO_EddsaSignature signature;
130
131 /**
132 * Identity of the peer sharing the file.
133 */
134 struct GNUNET_PeerIdentity peer;
135};
136
137
138/**
139 * Message sent from a GNUnet (fs) publishing activity to the
140 * gnunet-fs-service to initiate indexing of a file. The service is
141 * supposed to check if the specified file is available and has the
142 * same cryptographic hash. It should then respond with either a
143 * confirmation or a denial.
144 *
145 * On OSes where this works, it is considered acceptable if the
146 * service only checks that the path, device and inode match (it can
147 * then be assumed that the hash will also match without actually
148 * computing it; this is an optimization that should be safe given
149 * that the client is not our adversary).
150 */
151struct IndexStartMessage
152{
153 /**
154 * Message type will be #GNUNET_MESSAGE_TYPE_FS_INDEX_START.
155 */
156 struct GNUNET_MessageHeader header;
157
158 /**
159 * For alignment.
160 */
161 uint32_t reserved GNUNET_PACKED;
162
163 /**
164 * ID of device containing the file, as seen by the client. This
165 * device ID is obtained using a call like "statvfs" (and converting
166 * the "f_fsid" field to a 32-bit big-endian number). Use 0 if the
167 * OS does not support this, in which case the service must do a
168 * full hash recomputation.
169 */
170 uint64_t device GNUNET_PACKED;
171
172 /**
173 * Inode of the file on the given device, as seen by the client
174 * ("st_ino" field from "struct stat"). Use 0 if the OS does not
175 * support this, in which case the service must do a full hash
176 * recomputation.
177 */
178 uint64_t inode GNUNET_PACKED;
179
180 /**
181 * Hash of the file that we would like to index.
182 */
183 struct GNUNET_HashCode file_id;
184
185 /* this is followed by a 0-terminated
186 * filename of a file with the hash
187 * "file_id" as seen by the client */
188};
189
190
191/**
192 * Message send by FS service in response to a request
193 * asking for a list of all indexed files.
194 */
195struct IndexInfoMessage
196{
197 /**
198 * Message type will be
199 * #GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY.
200 */
201 struct GNUNET_MessageHeader header;
202
203 /**
204 * Always zero.
205 */
206 uint32_t reserved GNUNET_PACKED;
207
208 /**
209 * Hash of the indexed file.
210 */
211 struct GNUNET_HashCode file_id;
212
213 /* this is followed by a 0-terminated
214 * filename of a file with the hash
215 * "file_id" as seen by the client */
216};
217
218
219/**
220 * Message sent from a GNUnet (fs) unindexing activity to the
221 * gnunet-service-fs to indicate that a file will be unindexed. The
222 * service is supposed to remove the file from the list of indexed
223 * files and response with a confirmation message (even if the file
224 * was already not on the list).
225 */
226struct UnindexMessage
227{
228 /**
229 * Message type will be #GNUNET_MESSAGE_TYPE_FS_UNINDEX.
230 */
231 struct GNUNET_MessageHeader header;
232
233 /**
234 * Always zero.
235 */
236 uint32_t reserved GNUNET_PACKED;
237
238 /**
239 * Hash of the file that we will unindex.
240 */
241 struct GNUNET_HashCode file_id;
242};
243
244
245/**
246 * No options.
247 */
248#define SEARCH_MESSAGE_OPTION_NONE 0
249
250/**
251 * Only search the local datastore (no network)
252 */
253#define SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY 1
254
255/**
256 * Request is too large to fit in 64k format. The list of
257 * already-known search results will be continued in another message
258 * for the same type/query/target and additional already-known results
259 * following this one).
260 */
261#define SEARCH_MESSAGE_OPTION_CONTINUED 2
262
263
264/**
265 * Message sent from a GNUnet (fs) search activity to the
266 * gnunet-service-fs to start a search.
267 */
268struct SearchMessage
269{
270 /**
271 * Message type will be #GNUNET_MESSAGE_TYPE_FS_START_SEARCH.
272 */
273 struct GNUNET_MessageHeader header;
274
275 /**
276 * Bitmask with options. Zero for no options, one for
277 * loopback-only, two for 'to be continued' (with a second search
278 * message for the same type/query/target and additional
279 * already-known results following this one). See
280 * SEARCH_MESSAGE_OPTION_ defines.
281 *
282 * Other bits are currently not defined.
283 */
284 uint32_t options GNUNET_PACKED;
285
286 /**
287 * Type of the content that we're looking for.
288 */
289 uint32_t type GNUNET_PACKED;
290
291 /**
292 * Desired anonymity level, big-endian.
293 */
294 uint32_t anonymity_level GNUNET_PACKED;
295
296 /**
297 * If the request is for a DBLOCK or IBLOCK, this is the identity of
298 * the peer that is known to have a response. Set to all-zeros if
299 * such a target is not known (note that even if OUR anonymity
300 * level is >0 we may happen to know the responder's identity;
301 * nevertheless, we should probably not use it for a DHT-lookup
302 * or similar blunt actions in order to avoid exposing ourselves).
303 * <p>
304 * Otherwise, "target" must be all zeros.
305 */
306 struct GNUNET_PeerIdentity target;
307
308 /**
309 * Hash of the public key for UBLOCKs; Hash of
310 * the CHK-encoded block for DBLOCKS and IBLOCKS.
311 */
312 struct GNUNET_HashCode query;
313
314 /* this is followed by the hash codes of already-known
315 * results (which should hence be excluded from what
316 * the service returns); naturally, this only applies
317 * to queries that can have multiple results (UBLOCKS).
318 */
319};
320
321
322/**
323 * Response from FS service with a result for a previous FS search.
324 * Note that queries for DBLOCKS and IBLOCKS that have received a
325 * single response are considered done. This message is transmitted
326 * between peers.
327 */
328struct PutMessage
329{
330 /**
331 * Message type will be #GNUNET_MESSAGE_TYPE_FS_PUT.
332 */
333 struct GNUNET_MessageHeader header;
334
335 /**
336 * Type of the block (in big endian). Should never be zero.
337 */
338 uint32_t type GNUNET_PACKED;
339
340 /**
341 * When does this result expire?
342 */
343 struct GNUNET_TIME_AbsoluteNBO expiration;
344
345 /* this is followed by the actual encrypted content */
346};
347
348/**
349 * Response from FS service with a result for a previous FS search.
350 * Note that queries for DBLOCKS and IBLOCKS that have received a
351 * single response are considered done. This message is transmitted
352 * between the service and a client.
353 */
354struct ClientPutMessage
355{
356 /**
357 * Message type will be #GNUNET_MESSAGE_TYPE_FS_PUT.
358 */
359 struct GNUNET_MessageHeader header;
360
361 /**
362 * Type of the block (in big endian). Should never be zero.
363 */
364 uint32_t type GNUNET_PACKED;
365
366 /**
367 * When does this result expire?
368 */
369 struct GNUNET_TIME_AbsoluteNBO expiration;
370
371 /**
372 * When was the last time we've tried to download this block?
373 * (FOREVER if unknown/not relevant)
374 */
375 struct GNUNET_TIME_AbsoluteNBO last_transmission;
376
377 /**
378 * How often did we transmit this query before getting an
379 * answer (estimate).
380 */
381 uint32_t num_transmissions;
382
383 /**
384 * How much respect did we offer (in total) before getting an
385 * answer (estimate).
386 */
387 uint32_t respect_offered;
388
389 /* this is followed by the actual encrypted content */
390};
391GNUNET_NETWORK_STRUCT_END
392
393
394#endif
395
396/* end of fs.h */
diff --git a/src/fs/fs_api.c b/src/fs/fs_api.c
deleted file mode 100644
index 63b17a93e..000000000
--- a/src/fs/fs_api.c
+++ /dev/null
@@ -1,3349 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001--2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_api.c
23 * @brief main FS functions (master initialization, serialization, deserialization, shared code)
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_fs_service.h"
30#include "fs_api.h"
31#include "fs_tree.h"
32
33/**
34 * How many block requests can we have outstanding in parallel at a time by default?
35 */
36#define DEFAULT_MAX_PARALLEL_REQUESTS (1024 * 10)
37
38/**
39 * How many downloads can we have outstanding in parallel at a time by default?
40 */
41#define DEFAULT_MAX_PARALLEL_DOWNLOADS 16
42
43/**
44 * Start the given job (send signal, remove from pending queue, update
45 * counters and state).
46 *
47 * @param qe job to start
48 */
49static void
50start_job (struct GNUNET_FS_QueueEntry *qe)
51{
52 qe->active = GNUNET_YES;
53 qe->start (qe->cls);
54 qe->start_times++;
55 qe->h->active_blocks += qe->blocks;
56 qe->h->active_downloads++;
57 qe->start_time = GNUNET_TIME_absolute_get ();
58 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
59 "Starting job %p (%u active)\n",
60 qe,
61 qe->h->active_downloads);
62 GNUNET_CONTAINER_DLL_remove (qe->h->pending_head, qe->h->pending_tail, qe);
63 GNUNET_CONTAINER_DLL_insert_after (qe->h->running_head,
64 qe->h->running_tail,
65 qe->h->running_tail,
66 qe);
67}
68
69
70/**
71 * Stop the given job (send signal, remove from active queue, update
72 * counters and state).
73 *
74 * @param qe job to stop
75 */
76static void
77stop_job (struct GNUNET_FS_QueueEntry *qe)
78{
79 qe->active = GNUNET_NO;
80 qe->stop (qe->cls);
81 GNUNET_assert (0 < qe->h->active_downloads);
82 qe->h->active_downloads--;
83 qe->h->active_blocks -= qe->blocks;
84 qe->run_time = GNUNET_TIME_relative_add (qe->run_time,
85 GNUNET_TIME_absolute_get_duration (
86 qe->start_time));
87 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
88 "Stopping job %p (%u active)\n",
89 qe,
90 qe->h->active_downloads);
91 GNUNET_CONTAINER_DLL_remove (qe->h->running_head, qe->h->running_tail, qe);
92 GNUNET_CONTAINER_DLL_insert_after (qe->h->pending_head,
93 qe->h->pending_tail,
94 qe->h->pending_tail,
95 qe);
96}
97
98
99/**
100 * Process the jobs in the job queue, possibly starting some
101 * and stopping others.
102 *
103 * @param cls the `struct GNUNET_FS_Handle *`
104 */
105static void
106process_job_queue (void *cls)
107{
108 struct GNUNET_FS_Handle *h = cls;
109 struct GNUNET_FS_QueueEntry *qe;
110 struct GNUNET_FS_QueueEntry *next;
111 struct GNUNET_TIME_Relative run_time;
112 struct GNUNET_TIME_Relative restart_at;
113 struct GNUNET_TIME_Relative rst;
114 struct GNUNET_TIME_Absolute end_time;
115 unsigned int num_downloads_waiting;
116 unsigned int num_downloads_active;
117 unsigned int num_downloads_expired;
118 unsigned int num_probes_active;
119 unsigned int num_probes_waiting;
120 unsigned int num_probes_expired;
121 int num_probes_change;
122 int num_downloads_change;
123 int block_limit_hit;
124
125 h->queue_job = NULL;
126 /* restart_at will be set to the time when it makes sense to
127 re-evaluate the job queue (unless, of course, jobs complete
128 or are added, then we'll be triggered immediately */
129 restart_at = GNUNET_TIME_UNIT_FOREVER_REL;
130 /* first, calculate some basic statistics on pending jobs */
131 num_probes_waiting = 0;
132 num_downloads_waiting = 0;
133 for (qe = h->pending_head; NULL != qe; qe = qe->next)
134 {
135 switch (qe->priority)
136 {
137 case GNUNET_FS_QUEUE_PRIORITY_PROBE:
138 num_probes_waiting++;
139 break;
140
141 case GNUNET_FS_QUEUE_PRIORITY_NORMAL:
142 num_downloads_waiting++;
143 break;
144
145 default:
146 GNUNET_break (0);
147 break;
148 }
149 }
150 /* now, calculate some basic statistics on running jobs */
151 num_probes_active = 0;
152 num_probes_expired = 0;
153 num_downloads_active = 0;
154 num_downloads_expired = 0;
155 next = h->running_head;
156 while (NULL != (qe = next))
157 {
158 next = qe->next;
159 switch (qe->priority)
160 {
161 case GNUNET_FS_QUEUE_PRIORITY_PROBE:
162 run_time = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2);
163 end_time = GNUNET_TIME_absolute_add (qe->start_time, run_time);
164 rst = GNUNET_TIME_absolute_get_remaining (end_time);
165 if (0 == rst.rel_value_us)
166 {
167 num_probes_expired++;
168 stop_job (qe);
169 }
170 else
171 {
172 num_probes_active++;
173 restart_at = GNUNET_TIME_relative_min (rst, restart_at);
174 }
175 break;
176
177 case GNUNET_FS_QUEUE_PRIORITY_NORMAL:
178 run_time =
179 GNUNET_TIME_relative_saturating_multiply (h->avg_block_latency,
180 qe->blocks * qe->start_times);
181 end_time = GNUNET_TIME_absolute_add (qe->start_time, run_time);
182 rst = GNUNET_TIME_absolute_get_remaining (end_time);
183 if (0 == rst.rel_value_us)
184 {
185 num_downloads_expired++;
186 stop_job (qe);
187 }
188 else
189 {
190 num_downloads_active++;
191 restart_at = GNUNET_TIME_relative_min (rst, restart_at);
192 }
193 break;
194
195 default:
196 GNUNET_break (0);
197 break;
198 }
199 }
200 GNUNET_break (h->active_downloads ==
201 num_downloads_active + num_probes_active);
202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
203 "PA: %u, PE: %u, PW: %u; DA: %u, DE: %u, DW: %u\n",
204 num_probes_active,
205 num_probes_expired,
206 num_probes_waiting,
207 num_downloads_active,
208 num_downloads_expired,
209 num_downloads_waiting);
210 GNUNET_break (h->active_downloads + num_probes_active <=
211 h->max_parallel_downloads);
212 /* calculate start/stop decisions */
213 if (h->active_downloads + num_downloads_waiting > h->max_parallel_downloads)
214 {
215 /* stop as many probes as there are downloads and probes */
216 num_probes_change = -GNUNET_MIN (num_probes_active, num_downloads_waiting);
217 /* start as many downloads as there are free slots, including those
218 we just opened up */
219 num_downloads_change =
220 h->max_parallel_downloads - h->active_downloads - num_probes_change;
221 }
222 else
223 {
224 /* start all downloads (we can) */
225 num_downloads_change = num_downloads_waiting;
226 /* also start probes if there is room, but use a lower cap of (mpd/4) + 1 */
227 if (1 + h->max_parallel_downloads / 4 >=
228 (h->active_downloads + num_downloads_change))
229 num_probes_change =
230 GNUNET_MIN (num_probes_waiting,
231 (1 + h->max_parallel_downloads / 4)
232 - (h->active_downloads + num_downloads_change));
233 else
234 num_probes_change = 0;
235 }
236 GNUNET_break (num_downloads_change <= num_downloads_waiting);
237 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
238 "Changing %d probes and %d/%u/%u downloads\n",
239 num_probes_change,
240 num_downloads_change,
241 (unsigned int) h->active_downloads,
242 (unsigned int) h->max_parallel_downloads);
243 /* actually stop probes */
244 next = h->running_head;
245 while (NULL != (qe = next))
246 {
247 next = qe->next;
248 if (GNUNET_FS_QUEUE_PRIORITY_PROBE != qe->priority)
249 continue;
250 if (num_probes_change < 0)
251 {
252 stop_job (qe);
253 num_probes_change++;
254 if (0 == num_probes_change)
255 break;
256 }
257 }
258 GNUNET_break (0 <= num_probes_change);
259
260 /* start some more tasks if we now have empty slots */
261 block_limit_hit = GNUNET_NO;
262 next = h->pending_head;
263 while ((NULL != (qe = next)) &&
264 ((num_probes_change > 0) || (num_downloads_change > 0)))
265 {
266 next = qe->next;
267 switch (qe->priority)
268 {
269 case GNUNET_FS_QUEUE_PRIORITY_PROBE:
270 if (num_probes_change > 0)
271 {
272 start_job (qe);
273 num_probes_change--;
274 run_time = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2);
275 restart_at = GNUNET_TIME_relative_min (run_time, restart_at);
276 }
277 break;
278
279 case GNUNET_FS_QUEUE_PRIORITY_NORMAL:
280 if ((num_downloads_change > 0) &&
281 ((qe->blocks + h->active_blocks <= h->max_parallel_requests) ||
282 ((qe->blocks > h->max_parallel_requests) &&
283 (0 == h->active_downloads))))
284 {
285 start_job (qe);
286 num_downloads_change--;
287 }
288 else if (num_downloads_change > 0)
289 block_limit_hit = GNUNET_YES;
290 break;
291
292 default:
293 GNUNET_break (0);
294 break;
295 }
296 }
297 GNUNET_break ((0 == num_downloads_change) || (GNUNET_YES == block_limit_hit));
298 GNUNET_break (0 == num_probes_change);
299
300 GNUNET_log (
301 GNUNET_ERROR_TYPE_DEBUG,
302 "AD: %u, MP: %u; %d probes and %d downloads to start, will run again in %s\n",
303 h->active_downloads,
304 h->max_parallel_requests,
305 num_probes_change,
306 num_downloads_change,
307 GNUNET_STRINGS_relative_time_to_string (restart_at, GNUNET_YES));
308
309 /* make sure we run again, callbacks might have
310 already re-scheduled the job, so cancel such
311 an operation (if it exists) */
312 if (NULL != h->queue_job)
313 GNUNET_SCHEDULER_cancel (h->queue_job);
314 h->queue_job =
315 GNUNET_SCHEDULER_add_delayed (restart_at, &process_job_queue, h);
316}
317
318
319/**
320 * Add a job to the queue.
321 *
322 * @param h handle to the overall FS state
323 * @param start function to call to begin the job
324 * @param stop function to call to pause the job, or on dequeue (if the job was running)
325 * @param cls closure for start and stop
326 * @param blocks number of blocks this jobs uses
327 * @param priority how important is this download
328 * @return queue handle
329 */
330struct GNUNET_FS_QueueEntry *
331GNUNET_FS_queue_ (struct GNUNET_FS_Handle *h,
332 GNUNET_SCHEDULER_TaskCallback start,
333 GNUNET_SCHEDULER_TaskCallback stop,
334 void *cls,
335 unsigned int blocks,
336 enum GNUNET_FS_QueuePriority priority)
337{
338 struct GNUNET_FS_QueueEntry *qe;
339
340 qe = GNUNET_new (struct GNUNET_FS_QueueEntry);
341 qe->h = h;
342 qe->start = start;
343 qe->stop = stop;
344 qe->cls = cls;
345 qe->queue_time = GNUNET_TIME_absolute_get ();
346 qe->blocks = blocks;
347 qe->priority = priority;
348 GNUNET_CONTAINER_DLL_insert_after (h->pending_head,
349 h->pending_tail,
350 h->pending_tail,
351 qe);
352 if (NULL != h->queue_job)
353 GNUNET_SCHEDULER_cancel (h->queue_job);
354 h->queue_job = GNUNET_SCHEDULER_add_now (&process_job_queue, h);
355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Queueing job %p\n", qe);
356 return qe;
357}
358
359
360/**
361 * Dequeue a job from the queue.
362 *
363 * @param qe handle for the job
364 */
365void
366GNUNET_FS_dequeue_ (struct GNUNET_FS_QueueEntry *qe)
367{
368 struct GNUNET_FS_Handle *h;
369
370 h = qe->h;
371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dequeueing job %p\n", qe);
372 if (GNUNET_YES == qe->active)
373 stop_job (qe);
374 GNUNET_CONTAINER_DLL_remove (h->pending_head, h->pending_tail, qe);
375 GNUNET_free (qe);
376 if (NULL != h->queue_job)
377 GNUNET_SCHEDULER_cancel (h->queue_job);
378 h->queue_job = GNUNET_SCHEDULER_add_now (&process_job_queue, h);
379}
380
381
382/**
383 * Create a top-level activity entry.
384 *
385 * @param h global fs handle
386 * @param ssf suspend signal function to use
387 * @param ssf_cls closure for @a ssf
388 * @return fresh top-level activity handle
389 */
390struct TopLevelActivity *
391GNUNET_FS_make_top (struct GNUNET_FS_Handle *h,
392 SuspendSignalFunction ssf,
393 void *ssf_cls)
394{
395 struct TopLevelActivity *ret;
396
397 ret = GNUNET_new (struct TopLevelActivity);
398 ret->ssf = ssf;
399 ret->ssf_cls = ssf_cls;
400 GNUNET_CONTAINER_DLL_insert (h->top_head, h->top_tail, ret);
401 return ret;
402}
403
404
405/**
406 * Destroy a top-level activity entry.
407 *
408 * @param h global fs handle
409 * @param top top level activity entry
410 */
411void
412GNUNET_FS_end_top (struct GNUNET_FS_Handle *h, struct TopLevelActivity *top)
413{
414 GNUNET_CONTAINER_DLL_remove (h->top_head, h->top_tail, top);
415 GNUNET_free (top);
416}
417
418
419/**
420 * Closure for #GNUNET_FS_data_reader_file_().
421 */
422struct FileInfo
423{
424 /**
425 * Name of the file to read.
426 */
427 char *filename;
428
429 /**
430 * File descriptor, NULL if it has not yet been opened.
431 */
432 struct GNUNET_DISK_FileHandle *fd;
433};
434
435
436/**
437 * Function that provides data by reading from a file.
438 *
439 * @param cls closure with the `struct FileInfo *`
440 * @param offset offset to read from; it is possible
441 * that the caller might need to go backwards
442 * a bit at times; set to `UINT64_MAX` to tell
443 * the reader that we won't be reading for a while
444 * (used to close the file descriptor but NOT fully
445 * clean up the reader's state); in this case,
446 * a value of '0' for @a max should be ignored
447 * @param max maximum number of bytes that should be
448 * copied to @a buf; readers are not allowed
449 * to provide less data unless there is an error;
450 * a value of "0" will be used at the end to allow
451 * the reader to clean up its internal state
452 * @param buf where the reader should write the data
453 * @param emsg location for the reader to store an error message
454 * @return number of bytes written, usually @a max, 0 on error
455 */
456size_t
457GNUNET_FS_data_reader_file_ (void *cls,
458 uint64_t offset,
459 size_t max,
460 void *buf,
461 char **emsg)
462{
463 struct FileInfo *fi = cls;
464 ssize_t ret;
465
466 if (UINT64_MAX == offset)
467 {
468 if (NULL != fi->fd)
469 {
470 GNUNET_DISK_file_close (fi->fd);
471 fi->fd = NULL;
472 }
473 return 0;
474 }
475 if (0 == max)
476 {
477 if (NULL != fi->fd)
478 GNUNET_DISK_file_close (fi->fd);
479 GNUNET_free (fi->filename);
480 GNUNET_free (fi);
481 return 0;
482 }
483 if (NULL == fi->fd)
484 {
485 fi->fd = GNUNET_DISK_file_open (fi->filename,
486 GNUNET_DISK_OPEN_READ,
487 GNUNET_DISK_PERM_NONE);
488 if (NULL == fi->fd)
489 {
490 GNUNET_asprintf (emsg,
491 _ ("Could not open file `%s': %s"),
492 fi->filename,
493 strerror (errno));
494 return 0;
495 }
496 }
497 if ((GNUNET_SYSERR ==
498 GNUNET_DISK_file_seek (fi->fd, offset, GNUNET_DISK_SEEK_SET)) ||
499 (-1 == (ret = GNUNET_DISK_file_read (fi->fd, buf, max))))
500 {
501 GNUNET_asprintf (emsg,
502 _ ("Could not read file `%s': %s"),
503 fi->filename,
504 strerror (errno));
505 return 0;
506 }
507 if (ret != max)
508 {
509 GNUNET_asprintf (emsg,
510 _ ("Short read reading from file `%s'!"),
511 fi->filename);
512 return 0;
513 }
514 return max;
515}
516
517
518/**
519 * Create the closure for the #GNUNET_FS_data_reader_file_() callback.
520 *
521 * @param filename file to read
522 * @return closure to use, NULL on error
523 */
524void *
525GNUNET_FS_make_file_reader_context_ (const char *filename)
526{
527 struct FileInfo *fi;
528
529 fi = GNUNET_new (struct FileInfo);
530 fi->filename = GNUNET_STRINGS_filename_expand (filename);
531 if (NULL == fi->filename)
532 {
533 GNUNET_free (fi);
534 return NULL;
535 }
536 return fi;
537}
538
539
540/**
541 * Function that provides data by copying from a buffer.
542 *
543 * @param cls closure (points to the buffer)
544 * @param offset offset to read from; it is possible
545 * that the caller might need to go backwards
546 * a bit at times; set to `UINT64_MAX` to tell
547 * the reader that we won't be reading for a while
548 * (used to close the file descriptor but NOT fully
549 * clean up the reader's state); in this case,
550 * a value of '0' for @a max should be ignored
551 * @param max maximum number of bytes that should be
552 * copied to @a buf; readers are not allowed
553 * to provide less data unless there is an error;
554 * a value of "0" will be used at the end to allow
555 * the reader to clean up its internal state
556 * @param buf where the reader should write the data
557 * @param emsg location for the reader to store an error message
558 * @return number of bytes written, usually @a max, 0 on error
559 */
560size_t
561GNUNET_FS_data_reader_copy_ (void *cls,
562 uint64_t offset,
563 size_t max,
564 void *buf,
565 char **emsg)
566{
567 char *data = cls;
568
569 if (UINT64_MAX == offset)
570 return 0;
571 if (0 == max)
572 {
573 GNUNET_free (data);
574 return 0;
575 }
576 GNUNET_memcpy (buf, &data[offset], max);
577 return max;
578}
579
580
581/**
582 * Return the full filename where we would store state information
583 * (for serialization/deserialization).
584 *
585 * @param h master context
586 * @param ext component of the path
587 * @param ent entity identifier (or empty string for the directory)
588 * @return NULL on error
589 */
590static char *
591get_serialization_file_name (struct GNUNET_FS_Handle *h,
592 const char *ext,
593 const char *ent)
594{
595 char *basename;
596 char *ret;
597
598 if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE))
599 return NULL; /* persistence not requested */
600 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg,
601 "fs",
602 "STATE_DIR",
603 &basename))
604 return NULL;
605 GNUNET_asprintf (&ret,
606 "%s%s%s%s%s%s%s",
607 basename,
608 DIR_SEPARATOR_STR,
609 h->client_name,
610 DIR_SEPARATOR_STR,
611 ext,
612 DIR_SEPARATOR_STR,
613 ent);
614 GNUNET_free (basename);
615 return ret;
616}
617
618
619/**
620 * Return the full filename where we would store state information
621 * (for serialization/deserialization) that is associated with a
622 * parent operation.
623 *
624 * @param h master context
625 * @param ext component of the path
626 * @param uni name of the parent operation
627 * @param ent entity identifier (or empty string for the directory)
628 * @return NULL on error
629 */
630static char *
631get_serialization_file_name_in_dir (struct GNUNET_FS_Handle *h,
632 const char *ext,
633 const char *uni,
634 const char *ent)
635{
636 char *basename;
637 char *ret;
638
639 if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE))
640 return NULL; /* persistence not requested */
641 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg,
642 "fs",
643 "STATE_DIR",
644 &basename))
645 return NULL;
646 GNUNET_asprintf (&ret,
647 "%s%s%s%s%s%s%s.dir%s%s",
648 basename,
649 DIR_SEPARATOR_STR,
650 h->client_name,
651 DIR_SEPARATOR_STR,
652 ext,
653 DIR_SEPARATOR_STR,
654 uni,
655 DIR_SEPARATOR_STR,
656 ent);
657 GNUNET_free (basename);
658 return ret;
659}
660
661
662/**
663 * Return a read handle for deserialization.
664 *
665 * @param h master context
666 * @param ext component of the path
667 * @param ent entity identifier (or empty string for the directory)
668 * @return NULL on error
669 */
670static struct GNUNET_BIO_ReadHandle *
671get_read_handle (struct GNUNET_FS_Handle *h, const char *ext, const char *ent)
672{
673 char *fn;
674 struct GNUNET_BIO_ReadHandle *ret;
675
676 fn = get_serialization_file_name (h, ext, ent);
677 if (NULL == fn)
678 return NULL;
679 ret = GNUNET_BIO_read_open_file (fn);
680 GNUNET_free (fn);
681 return ret;
682}
683
684
685/**
686 * Return a write handle for serialization.
687 *
688 * @param h master context
689 * @param ext component of the path
690 * @param ent entity identifier (or empty string for the directory)
691 * @return NULL on error
692 */
693static struct GNUNET_BIO_WriteHandle *
694get_write_handle (struct GNUNET_FS_Handle *h, const char *ext, const char *ent)
695{
696 char *fn;
697 struct GNUNET_BIO_WriteHandle *ret;
698
699 fn = get_serialization_file_name (h, ext, ent);
700 if (NULL == fn)
701 return NULL;
702 ret = GNUNET_BIO_write_open_file (fn);
703 GNUNET_break (NULL != ret);
704 GNUNET_free (fn);
705 return ret;
706}
707
708
709/**
710 * Return a write handle for serialization.
711 *
712 * @param h master context
713 * @param ext component of the path
714 * @param uni name of parent
715 * @param ent entity identifier (or empty string for the directory)
716 * @return NULL on error
717 */
718static struct GNUNET_BIO_WriteHandle *
719get_write_handle_in_dir (struct GNUNET_FS_Handle *h,
720 const char *ext,
721 const char *uni,
722 const char *ent)
723{
724 char *fn;
725 struct GNUNET_BIO_WriteHandle *ret;
726
727 fn = get_serialization_file_name_in_dir (h, ext, uni, ent);
728 if (NULL == fn)
729 return NULL;
730 ret = GNUNET_BIO_write_open_file (fn);
731 GNUNET_free (fn);
732 return ret;
733}
734
735
736/**
737 * Remove serialization/deserialization file from disk.
738 *
739 * @param h master context
740 * @param ext component of the path
741 * @param ent entity identifier
742 */
743void
744GNUNET_FS_remove_sync_file_ (struct GNUNET_FS_Handle *h,
745 const char *ext,
746 const char *ent)
747{
748 char *filename;
749
750 if ((NULL == ent) || (0 == strlen (ent)))
751 {
752 GNUNET_break (0);
753 return;
754 }
755 filename = get_serialization_file_name (h, ext, ent);
756 if (NULL != filename)
757 {
758 if ((0 != unlink (filename)) && (ENOENT != errno))
759 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename);
760 GNUNET_free (filename);
761 }
762}
763
764
765/**
766 * Remove serialization/deserialization file from disk.
767 *
768 * @param h master context
769 * @param ext component of the path
770 * @param uni parent name
771 * @param ent entity identifier
772 */
773static void
774remove_sync_file_in_dir (struct GNUNET_FS_Handle *h,
775 const char *ext,
776 const char *uni,
777 const char *ent)
778{
779 char *filename;
780
781 if ((NULL == ent) || (0 == strlen (ent)))
782 {
783 GNUNET_break (0);
784 return;
785 }
786 filename = get_serialization_file_name_in_dir (h, ext, uni, ent);
787 if (NULL == filename)
788 return;
789 if (0 != unlink (filename))
790 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename);
791 GNUNET_free (filename);
792}
793
794
795/**
796 * Remove serialization/deserialization directory from disk.
797 *
798 * @param h master context
799 * @param ext component of the path
800 * @param uni unique name of parent
801 */
802void
803GNUNET_FS_remove_sync_dir_ (struct GNUNET_FS_Handle *h,
804 const char *ext,
805 const char *uni)
806{
807 char *dn;
808
809 if (NULL == uni)
810 return;
811 dn = get_serialization_file_name_in_dir (h, ext, uni, "");
812 if (NULL == dn)
813 return;
814 if ((GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES)) &&
815 (GNUNET_OK != GNUNET_DISK_directory_remove (dn)))
816 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dn);
817 GNUNET_free (dn);
818}
819
820
821/**
822 * Serialize a start-time. Since we use start-times to
823 * calculate the duration of some operation, we actually
824 * do not serialize the absolute time but the (relative)
825 * duration since the start time. When we then
826 * deserialize the start time, we take the current time and
827 * subtract that duration so that we get again an absolute
828 * time stamp that will result in correct performance
829 * calculations.
830 *
831 * @param wh handle for writing
832 * @param timestamp time to serialize
833 * @return #GNUNET_OK on success
834 */
835static int
836write_start_time (struct GNUNET_BIO_WriteHandle *wh,
837 struct GNUNET_TIME_Absolute timestamp)
838{
839 struct GNUNET_TIME_Relative dur;
840
841 dur = GNUNET_TIME_absolute_get_duration (timestamp);
842 return GNUNET_BIO_write_int64 (wh, "start time", dur.rel_value_us);
843}
844
845
846/**
847 * Deserialize a start-time. Since we use start-times to
848 * calculate the duration of some operation, we actually
849 * do not serialize the absolute time but the (relative)
850 * duration since the start time. Thus, when we then
851 * deserialize the start time, we take the current time and
852 * subtract that duration so that we get again an absolute
853 * time stamp that will result in correct performance
854 * calculations.
855 *
856 * @param rh handle for reading
857 * @param timestamp where to write the deserialized timestamp
858 * @return #GNUNET_OK on success
859 */
860static int
861read_start_time (struct GNUNET_BIO_ReadHandle *rh,
862 struct GNUNET_TIME_Absolute *timestamp)
863{
864 struct GNUNET_TIME_Relative dur;
865
866 if (GNUNET_OK != GNUNET_BIO_read_int64 (rh, "start time",
867 (int64_t *) &dur.rel_value_us))
868 return GNUNET_SYSERR;
869 *timestamp = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (), dur);
870 return GNUNET_OK;
871}
872
873
874/**
875 * Using the given serialization filename, try to deserialize
876 * the file-information tree associated with it.
877 *
878 * @param h master context
879 * @param filename name of the file (without directory) with
880 * the information
881 * @return NULL on error
882 */
883static struct GNUNET_FS_FileInformation *
884deserialize_file_information (struct GNUNET_FS_Handle *h, const char *filename);
885
886
887/**
888 * Using the given serialization filename, try to deserialize
889 * the file-information tree associated with it.
890 *
891 * @param h master context
892 * @param fn name of the file (without directory) with
893 * the information
894 * @param rh handle for reading
895 * @return NULL on error
896 */
897static struct GNUNET_FS_FileInformation *
898deserialize_fi_node (struct GNUNET_FS_Handle *h,
899 const char *fn,
900 struct GNUNET_BIO_ReadHandle *rh)
901{
902 struct GNUNET_FS_FileInformation *ret;
903 struct GNUNET_FS_FileInformation *nxt;
904 char b;
905 char *ksks;
906 char *chks;
907 char *skss;
908 char *filename;
909 uint32_t dsize;
910
911 if (GNUNET_OK != GNUNET_BIO_read (rh, "status flag", &b, sizeof(b)))
912 {
913 GNUNET_break (0);
914 return NULL;
915 }
916 ret = GNUNET_new (struct GNUNET_FS_FileInformation);
917 ret->h = h;
918 ksks = NULL;
919 chks = NULL;
920 skss = NULL;
921 filename = NULL;
922 if ((GNUNET_OK != GNUNET_BIO_read_meta_data (rh, "metadata", &ret->meta)) ||
923 (GNUNET_OK != GNUNET_BIO_read_string (rh, "ksk-uri", &ksks, 32 * 1024)) ||
924 ((NULL != ksks) &&
925 ((NULL == (ret->keywords = GNUNET_FS_uri_parse (ksks, NULL))) ||
926 (GNUNET_YES != GNUNET_FS_uri_test_ksk (ret->keywords)))) ||
927 (GNUNET_OK != GNUNET_BIO_read_string (rh, "chk-uri", &chks, 1024)) ||
928 ((NULL != chks) &&
929 ((NULL == (ret->chk_uri = GNUNET_FS_uri_parse (chks, NULL))) ||
930 (GNUNET_YES != GNUNET_FS_uri_test_chk (ret->chk_uri)))) ||
931 (GNUNET_OK != GNUNET_BIO_read_string (rh, "sks-uri", &skss, 1024)) ||
932 ((NULL != skss) &&
933 ((NULL == (ret->sks_uri = GNUNET_FS_uri_parse (skss, NULL))) ||
934 (GNUNET_YES != GNUNET_FS_uri_test_sks (ret->sks_uri)))) ||
935 (GNUNET_OK != read_start_time (rh, &ret->start_time)) ||
936 (GNUNET_OK !=
937 GNUNET_BIO_read_string (rh, "emsg", &ret->emsg, 16 * 1024)) ||
938 (GNUNET_OK !=
939 GNUNET_BIO_read_string (rh, "fn", &ret->filename, 16 * 1024)) ||
940 (GNUNET_OK !=
941 GNUNET_BIO_read_int64 (
942 rh,
943 "expiration time",
944 (int64_t *) &ret->bo.expiration_time.abs_value_us)) ||
945 (GNUNET_OK != GNUNET_BIO_read_int32 (
946 rh,
947 "anonymity level",
948 (int32_t *) &ret->bo.anonymity_level)) ||
949 (GNUNET_OK != GNUNET_BIO_read_int32 (
950 rh,
951 "content priority",
952 (int32_t *) &ret->bo.content_priority)) ||
953 (GNUNET_OK != GNUNET_BIO_read_int32 (
954 rh,
955 "replication level",
956 (int32_t *) &ret->bo.replication_level)))
957 {
958 GNUNET_break (0);
959 goto cleanup;
960 }
961 switch (b)
962 {
963 case 0: /* file-insert */
964 if (GNUNET_OK != GNUNET_BIO_read_int64 (
965 rh,
966 "file size",
967 (int64_t *) &ret->data.file.file_size))
968 {
969 GNUNET_break (0);
970 goto cleanup;
971 }
972 ret->is_directory = GNUNET_NO;
973 ret->data.file.do_index = GNUNET_NO;
974 ret->data.file.have_hash = GNUNET_NO;
975 ret->data.file.index_start_confirmed = GNUNET_NO;
976 if (GNUNET_NO == ret->is_published)
977 {
978 if (NULL == ret->filename)
979 {
980 ret->data.file.reader = &GNUNET_FS_data_reader_copy_;
981 ret->data.file.reader_cls =
982 GNUNET_malloc_large (ret->data.file.file_size);
983 if (ret->data.file.reader_cls == NULL)
984 goto cleanup;
985 if (GNUNET_OK != GNUNET_BIO_read (rh,
986 "file-data",
987 ret->data.file.reader_cls,
988 ret->data.file.file_size))
989 {
990 GNUNET_break (0);
991 goto cleanup;
992 }
993 }
994 else
995 {
996 ret->data.file.reader = &GNUNET_FS_data_reader_file_;
997 ret->data.file.reader_cls =
998 GNUNET_FS_make_file_reader_context_ (ret->filename);
999 }
1000 }
1001 break;
1002
1003 case 1: /* file-index, no hash */
1004 if (NULL == ret->filename)
1005 {
1006 GNUNET_break (0);
1007 goto cleanup;
1008 }
1009 if (GNUNET_OK != GNUNET_BIO_read_int64 (
1010 rh,
1011 "file size",
1012 (int64_t *) &ret->data.file.file_size))
1013 {
1014 GNUNET_break (0);
1015 goto cleanup;
1016 }
1017 ret->is_directory = GNUNET_NO;
1018 ret->data.file.do_index = GNUNET_YES;
1019 ret->data.file.have_hash = GNUNET_NO;
1020 ret->data.file.index_start_confirmed = GNUNET_NO;
1021 ret->data.file.reader = &GNUNET_FS_data_reader_file_;
1022 ret->data.file.reader_cls =
1023 GNUNET_FS_make_file_reader_context_ (ret->filename);
1024 break;
1025
1026 case 2: /* file-index-with-hash */
1027 if (NULL == ret->filename)
1028 {
1029 GNUNET_break (0);
1030 goto cleanup;
1031 }
1032 if ((GNUNET_OK != GNUNET_BIO_read_int64 (
1033 rh,
1034 "file size",
1035 (int64_t *) &ret->data.file.file_size)) ||
1036 (GNUNET_OK != GNUNET_BIO_read (rh,
1037 "fileid",
1038 &ret->data.file.file_id,
1039 sizeof(struct GNUNET_HashCode))))
1040 {
1041 GNUNET_break (0);
1042 goto cleanup;
1043 }
1044 ret->is_directory = GNUNET_NO;
1045 ret->data.file.do_index = GNUNET_YES;
1046 ret->data.file.have_hash = GNUNET_YES;
1047 ret->data.file.index_start_confirmed = GNUNET_NO;
1048 ret->data.file.reader = &GNUNET_FS_data_reader_file_;
1049 ret->data.file.reader_cls =
1050 GNUNET_FS_make_file_reader_context_ (ret->filename);
1051 break;
1052
1053 case 3: /* file-index-with-hash-confirmed */
1054 if (NULL == ret->filename)
1055 {
1056 GNUNET_break (0);
1057 goto cleanup;
1058 }
1059 if ((GNUNET_OK != GNUNET_BIO_read_int64 (
1060 rh,
1061 "file size",
1062 (int64_t *) &ret->data.file.file_size)) ||
1063 (GNUNET_OK != GNUNET_BIO_read (rh,
1064 "fileid",
1065 &ret->data.file.file_id,
1066 sizeof(struct GNUNET_HashCode))))
1067 {
1068 GNUNET_break (0);
1069 goto cleanup;
1070 }
1071 ret->is_directory = GNUNET_NO;
1072 ret->data.file.do_index = GNUNET_YES;
1073 ret->data.file.have_hash = GNUNET_YES;
1074 ret->data.file.index_start_confirmed = GNUNET_YES;
1075 ret->data.file.reader = &GNUNET_FS_data_reader_file_;
1076 ret->data.file.reader_cls =
1077 GNUNET_FS_make_file_reader_context_ (ret->filename);
1078 break;
1079
1080 case 4: /* directory */
1081 ret->is_directory = GNUNET_YES;
1082 if ((GNUNET_OK != GNUNET_BIO_read_int32 (rh, "dsize",
1083 (int32_t *) &dsize)) ||
1084 (GNUNET_OK !=
1085 GNUNET_BIO_read_int64 (
1086 rh,
1087 "contents completed",
1088 (int64_t *) &ret->data.dir.contents_completed)) ||
1089 (GNUNET_OK !=
1090 GNUNET_BIO_read_int64 (
1091 rh,
1092 "contents size",
1093 (int64_t *) &ret->data.dir.contents_size)) ||
1094 (NULL == (ret->data.dir.dir_data = GNUNET_malloc_large (dsize))) ||
1095 (GNUNET_OK !=
1096 GNUNET_BIO_read (rh, "dir-data", ret->data.dir.dir_data, dsize)) ||
1097 (GNUNET_OK !=
1098 GNUNET_BIO_read_string (rh, "ent-filename", &filename, 16 * 1024)))
1099 {
1100 GNUNET_break (0);
1101 goto cleanup;
1102 }
1103 ret->data.dir.dir_size = (uint32_t) dsize;
1104 if (NULL != filename)
1105 {
1106 ret->data.dir.entries = deserialize_file_information (h, filename);
1107 GNUNET_free (filename);
1108 filename = NULL;
1109 nxt = ret->data.dir.entries;
1110 while (NULL != nxt)
1111 {
1112 nxt->dir = ret;
1113 nxt = nxt->next;
1114 }
1115 }
1116 break;
1117
1118 default:
1119 GNUNET_break (0);
1120 goto cleanup;
1121 }
1122 ret->serialization = GNUNET_strdup (fn);
1123 if (GNUNET_OK !=
1124 GNUNET_BIO_read_string (rh, "nxt-filename", &filename, 16 * 1024))
1125 {
1126 GNUNET_break (0);
1127 goto cleanup;
1128 }
1129 if (NULL != filename)
1130 {
1131 ret->next = deserialize_file_information (h, filename);
1132 GNUNET_free (filename);
1133 filename = NULL;
1134 }
1135 GNUNET_free (ksks);
1136 GNUNET_free (skss);
1137 GNUNET_free (chks);
1138 return ret;
1139cleanup:
1140 GNUNET_free (ksks);
1141 GNUNET_free (chks);
1142 GNUNET_free (skss);
1143 GNUNET_free (filename);
1144 GNUNET_FS_file_information_destroy (ret, NULL, NULL);
1145 return NULL;
1146}
1147
1148
1149/**
1150 * Using the given serialization filename, try to deserialize
1151 * the file-information tree associated with it.
1152 *
1153 * @param h master context
1154 * @param filename name of the file (without directory) with
1155 * the information
1156 * @return NULL on error
1157 */
1158static struct GNUNET_FS_FileInformation *
1159deserialize_file_information (struct GNUNET_FS_Handle *h, const char *filename)
1160{
1161 struct GNUNET_FS_FileInformation *ret;
1162 struct GNUNET_BIO_ReadHandle *rh;
1163 char *emsg;
1164 char *fn;
1165
1166 rh = get_read_handle (h, GNUNET_FS_SYNC_PATH_FILE_INFO, filename);
1167 if (NULL == rh)
1168 return NULL;
1169 ret = deserialize_fi_node (h, filename, rh);
1170 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
1171 {
1172 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1173 _ ("Failed to resume publishing information `%s': %s\n"),
1174 filename,
1175 emsg);
1176 GNUNET_free (emsg);
1177 }
1178 if (NULL == ret)
1179 {
1180 fn =
1181 get_serialization_file_name (h, GNUNET_FS_SYNC_PATH_FILE_INFO, filename);
1182 if (NULL != fn)
1183 {
1184 if (0 != unlink (fn))
1185 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
1186 GNUNET_free (fn);
1187 }
1188 }
1189 return ret;
1190}
1191
1192
1193/**
1194 * Given a serialization name (full absolute path), return the
1195 * basename of the file (without the path), which must only
1196 * consist of the 6 random characters.
1197 *
1198 * @param fullname name to extract the basename from
1199 * @return copy of the basename, NULL on error
1200 */
1201static char *
1202get_serialization_short_name (const char *fullname)
1203{
1204 const char *end;
1205 const char *nxt;
1206
1207 end = NULL;
1208 nxt = fullname;
1209 /* FIXME: we could do this faster since we know
1210 * the length of 'end'... */
1211 while ('\0' != *nxt)
1212 {
1213 if (DIR_SEPARATOR == *nxt)
1214 end = nxt + 1;
1215 nxt++;
1216 }
1217 if ((NULL == end) || (0 == strlen (end)))
1218 {
1219 GNUNET_break (0);
1220 return NULL;
1221 }
1222 GNUNET_break (6 == strlen (end));
1223 return GNUNET_strdup (end);
1224}
1225
1226
1227/**
1228 * Create a new random name for serialization. Also checks if persistence
1229 * is enabled and returns NULL if not.
1230 *
1231 * @param h master context
1232 * @param ext component of the path
1233 * @return NULL on error
1234 */
1235static char *
1236make_serialization_file_name (struct GNUNET_FS_Handle *h, const char *ext)
1237{
1238 char *fn;
1239 char *dn;
1240 char *ret;
1241
1242 if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE))
1243 return NULL; /* persistence not requested */
1244 dn = get_serialization_file_name (h, ext, "");
1245 if (NULL == dn)
1246 return NULL;
1247 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dn))
1248 {
1249 GNUNET_free (dn);
1250 return NULL;
1251 }
1252 fn = GNUNET_DISK_mktemp (dn);
1253 GNUNET_free (dn);
1254 if (NULL == fn)
1255 return NULL; /* epic fail */
1256 ret = get_serialization_short_name (fn);
1257 GNUNET_free (fn);
1258 return ret;
1259}
1260
1261
1262/**
1263 * Create a new random name for serialization. Also checks if persistence
1264 * is enabled and returns NULL if not.
1265 *
1266 * @param h master context
1267 * @param ext component of the path
1268 * @param uni name of parent
1269 * @return NULL on error
1270 */
1271static char *
1272make_serialization_file_name_in_dir (struct GNUNET_FS_Handle *h,
1273 const char *ext,
1274 const char *uni)
1275{
1276 char *fn;
1277 char *dn;
1278 char *ret;
1279
1280 if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE))
1281 return NULL; /* persistence not requested */
1282 dn = get_serialization_file_name_in_dir (h, ext, uni, "");
1283 if (NULL == dn)
1284 return NULL;
1285 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dn))
1286 {
1287 GNUNET_free (dn);
1288 return NULL;
1289 }
1290 fn = GNUNET_DISK_mktemp (dn);
1291 GNUNET_free (dn);
1292 if (NULL == fn)
1293 return NULL; /* epic fail */
1294 ret = get_serialization_short_name (fn);
1295 GNUNET_free (fn);
1296 return ret;
1297}
1298
1299
1300/**
1301 * Copy all of the data from the reader to the write handle.
1302 *
1303 * @param wh write handle
1304 * @param fi file with reader
1305 * @return #GNUNET_OK on success
1306 */
1307static int
1308copy_from_reader (struct GNUNET_BIO_WriteHandle *wh,
1309 struct GNUNET_FS_FileInformation *fi)
1310{
1311 char buf[32 * 1024];
1312 uint64_t off;
1313 size_t ret;
1314 size_t left;
1315 char *emsg;
1316
1317 emsg = NULL;
1318 off = 0;
1319 while (off < fi->data.file.file_size)
1320 {
1321 left = GNUNET_MIN (sizeof(buf), fi->data.file.file_size - off);
1322 ret =
1323 fi->data.file.reader (fi->data.file.reader_cls, off, left, buf, &emsg);
1324 if (0 == ret)
1325 {
1326 GNUNET_free (emsg);
1327 return GNUNET_SYSERR;
1328 }
1329 if (GNUNET_OK != GNUNET_BIO_write (wh, "copied from reader", buf, ret))
1330 return GNUNET_SYSERR;
1331 off += ret;
1332 }
1333 return GNUNET_OK;
1334}
1335
1336
1337/**
1338 * Create a temporary file on disk to store the current
1339 * state of @a fi in.
1340 *
1341 * @param fi file information to sync with disk
1342 */
1343void
1344GNUNET_FS_file_information_sync_ (struct GNUNET_FS_FileInformation *fi)
1345{
1346 char *fn;
1347 struct GNUNET_BIO_WriteHandle *wh;
1348 char b;
1349 char *ksks;
1350 char *chks;
1351 char *skss;
1352
1353 if (NULL == fi->serialization)
1354 fi->serialization =
1355 make_serialization_file_name (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO);
1356 if (NULL == fi->serialization)
1357 return;
1358 wh =
1359 get_write_handle (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO, fi->serialization);
1360 if (NULL == wh)
1361 {
1362 GNUNET_free (fi->serialization);
1363 fi->serialization = NULL;
1364 return;
1365 }
1366 if (GNUNET_YES == fi->is_directory)
1367 b = 4;
1368 else if (GNUNET_YES == fi->data.file.index_start_confirmed)
1369 b = 3;
1370 else if (GNUNET_YES == fi->data.file.have_hash)
1371 b = 2;
1372 else if (GNUNET_YES == fi->data.file.do_index)
1373 b = 1;
1374 else
1375 b = 0;
1376 if (NULL != fi->keywords)
1377 ksks = GNUNET_FS_uri_to_string (fi->keywords);
1378 else
1379 ksks = NULL;
1380 if (NULL != fi->chk_uri)
1381 chks = GNUNET_FS_uri_to_string (fi->chk_uri);
1382 else
1383 chks = NULL;
1384 if (NULL != fi->sks_uri)
1385 skss = GNUNET_FS_uri_to_string (fi->sks_uri);
1386 else
1387 skss = NULL;
1388 struct GNUNET_BIO_WriteSpec ws1[] = {
1389 GNUNET_BIO_write_spec_object ("b", &b, sizeof (b)),
1390 GNUNET_BIO_write_spec_meta_data ("meta", fi->meta),
1391 GNUNET_BIO_write_spec_string ("ksks", ksks),
1392 GNUNET_BIO_write_spec_string ("chks", chks),
1393 GNUNET_BIO_write_spec_string ("skss", skss),
1394 GNUNET_BIO_write_spec_end (),
1395 };
1396 struct GNUNET_BIO_WriteSpec ws2[] = {
1397 GNUNET_BIO_write_spec_string ("emsg", fi->emsg),
1398 GNUNET_BIO_write_spec_string ("filename", fi->filename),
1399 GNUNET_BIO_write_spec_int64 (
1400 "expiration time",
1401 (int64_t *) &fi->bo.expiration_time.abs_value_us),
1402 GNUNET_BIO_write_spec_int32 (
1403 "anonymity level",
1404 (int32_t *) &fi->bo.anonymity_level),
1405 GNUNET_BIO_write_spec_int32 (
1406 "content priority",
1407 (int32_t *) &fi->bo.content_priority),
1408 GNUNET_BIO_write_spec_int32 (
1409 "replication level",
1410 (int32_t *) &fi->bo.replication_level),
1411 GNUNET_BIO_write_spec_end (),
1412 };
1413 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws1)) ||
1414 (GNUNET_OK != write_start_time (wh, fi->start_time)) ||
1415 (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws2)))
1416 {
1417 GNUNET_break (0);
1418 goto cleanup;
1419 }
1420 GNUNET_free (chks);
1421 chks = NULL;
1422 GNUNET_free (ksks);
1423 ksks = NULL;
1424 GNUNET_free (skss);
1425 skss = NULL;
1426
1427 switch (b)
1428 {
1429 case 0: /* file-insert */
1430 if (GNUNET_OK != GNUNET_BIO_write_int64 (wh, "file size",
1431 fi->data.file.file_size))
1432 {
1433 GNUNET_break (0);
1434 goto cleanup;
1435 }
1436 if ((GNUNET_NO == fi->is_published) && (NULL == fi->filename))
1437 if (GNUNET_OK != copy_from_reader (wh, fi))
1438 {
1439 GNUNET_break (0);
1440 goto cleanup;
1441 }
1442 break;
1443
1444 case 1: /* file-index, no hash */
1445 if (NULL == fi->filename)
1446 {
1447 GNUNET_break (0);
1448 goto cleanup;
1449 }
1450 if (GNUNET_OK != GNUNET_BIO_write_int64 (wh, "file size",
1451 fi->data.file.file_size))
1452 {
1453 GNUNET_break (0);
1454 goto cleanup;
1455 }
1456 break;
1457
1458 case 2: /* file-index-with-hash */
1459 case 3: /* file-index-with-hash-confirmed */
1460 if (NULL == fi->filename)
1461 {
1462 GNUNET_break (0);
1463 goto cleanup;
1464 }
1465 if ((GNUNET_OK != GNUNET_BIO_write_int64 (wh, "file size",
1466 fi->data.file.file_size)) ||
1467 (GNUNET_OK != GNUNET_BIO_write (wh,
1468 "file id",
1469 &fi->data.file.file_id,
1470 sizeof(struct GNUNET_HashCode))))
1471 {
1472 GNUNET_break (0);
1473 goto cleanup;
1474 }
1475 break;
1476
1477 case 4: /* directory */
1478 if ((NULL != fi->data.dir.entries) &&
1479 (NULL == fi->data.dir.entries->serialization))
1480 GNUNET_FS_file_information_sync_ (fi->data.dir.entries);
1481 struct GNUNET_BIO_WriteSpec ws[] = {
1482 GNUNET_BIO_write_spec_int32 ("dir size",
1483 (int32_t *) &fi->data.dir.dir_size),
1484 GNUNET_BIO_write_spec_int64 (
1485 "contents completed",
1486 (int64_t *) &fi->data.dir.contents_completed),
1487 GNUNET_BIO_write_spec_int64 ("contents size",
1488 (int64_t *) &fi->data.dir.contents_size),
1489 GNUNET_BIO_write_spec_object ("dir data",
1490 fi->data.dir.dir_data,
1491 (uint32_t) fi->data.dir.dir_size),
1492 GNUNET_BIO_write_spec_string ("dir entries",
1493 (fi->data.dir.entries == NULL)
1494 ? NULL
1495 : fi->data.dir.entries->serialization),
1496 GNUNET_BIO_write_spec_end (),
1497 };
1498 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)))
1499 {
1500 GNUNET_break (0);
1501 goto cleanup;
1502 }
1503 break;
1504
1505 default:
1506 GNUNET_assert (0);
1507 goto cleanup;
1508 }
1509 if ((NULL != fi->next) && (NULL == fi->next->serialization))
1510 GNUNET_FS_file_information_sync_ (fi->next);
1511 if (GNUNET_OK != GNUNET_BIO_write_string (wh,
1512 "serialization",
1513 (fi->next != NULL)
1514 ? fi->next->serialization
1515 : NULL))
1516 {
1517 GNUNET_break (0);
1518 goto cleanup;
1519 }
1520 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
1521 {
1522 wh = NULL;
1523 GNUNET_break (0);
1524 goto cleanup;
1525 }
1526 return; /* done! */
1527cleanup:
1528 if (NULL != wh)
1529 (void) GNUNET_BIO_write_close (wh, NULL);
1530 GNUNET_free (chks);
1531 GNUNET_free (ksks);
1532 GNUNET_free (skss);
1533 fn = get_serialization_file_name (fi->h,
1534 GNUNET_FS_SYNC_PATH_FILE_INFO,
1535 fi->serialization);
1536 if (NULL != fn)
1537 {
1538 if (0 != unlink (fn))
1539 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
1540 GNUNET_free (fn);
1541 }
1542 GNUNET_free (fi->serialization);
1543 fi->serialization = NULL;
1544}
1545
1546
1547/**
1548 * Find the entry in the file information struct where the
1549 * serialization filename matches the given name.
1550 *
1551 * @param pos file information to search
1552 * @param srch filename to search for
1553 * @return NULL if srch was not found in this subtree
1554 */
1555static struct GNUNET_FS_FileInformation *
1556find_file_position (struct GNUNET_FS_FileInformation *pos, const char *srch)
1557{
1558 struct GNUNET_FS_FileInformation *r;
1559
1560 while (NULL != pos)
1561 {
1562 if (0 == strcmp (srch, pos->serialization))
1563 return pos;
1564 if ((GNUNET_YES == pos->is_directory) &&
1565 (NULL != (r = find_file_position (pos->data.dir.entries, srch))))
1566 return r;
1567 pos = pos->next;
1568 }
1569 return NULL;
1570}
1571
1572
1573/**
1574 * Signal the FS's progress function that we are resuming
1575 * an upload.
1576 *
1577 * @param cls closure (of type `struct GNUNET_FS_PublishContext *`, for the parent (!))
1578 * @param fi the entry in the publish-structure
1579 * @param length length of the file or directory
1580 * @param meta metadata for the file or directory (can be modified)
1581 * @param uri pointer to the keywords that will be used for this entry (can be modified)
1582 * @param bo block options (can be modified)
1583 * @param do_index should we index?
1584 * @param client_info pointer to client context set upon creation (can be modified)
1585 * @return #GNUNET_OK to continue (always)
1586 */
1587static int
1588fip_signal_resume (void *cls,
1589 struct GNUNET_FS_FileInformation *fi,
1590 uint64_t length,
1591 struct GNUNET_CONTAINER_MetaData *meta,
1592 struct GNUNET_FS_Uri **uri,
1593 struct GNUNET_FS_BlockOptions *bo,
1594 int *do_index,
1595 void **client_info)
1596{
1597 struct GNUNET_FS_PublishContext *pc = cls;
1598 struct GNUNET_FS_ProgressInfo pi;
1599
1600 if (GNUNET_YES == pc->skip_next_fi_callback)
1601 {
1602 pc->skip_next_fi_callback = GNUNET_NO;
1603 return GNUNET_OK;
1604 }
1605 pi.status = GNUNET_FS_STATUS_PUBLISH_RESUME;
1606 pi.value.publish.specifics.resume.message = fi->emsg;
1607 pi.value.publish.specifics.resume.chk_uri = fi->chk_uri;
1608 *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0);
1609 if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1610 {
1611 /* process entries in directory */
1612 pc->skip_next_fi_callback = GNUNET_YES;
1613 GNUNET_FS_file_information_inspect (fi, &fip_signal_resume, pc);
1614 }
1615 return GNUNET_OK;
1616}
1617
1618
1619/**
1620 * Function called with a filename of serialized publishing operation
1621 * to deserialize.
1622 *
1623 * @param cls the `struct GNUNET_FS_Handle *`
1624 * @param filename complete filename (absolute path)
1625 * @return #GNUNET_OK (continue to iterate)
1626 */
1627static int
1628deserialize_publish_file (void *cls, const char *filename)
1629{
1630 struct GNUNET_FS_Handle *h = cls;
1631 struct GNUNET_BIO_ReadHandle *rh;
1632 struct GNUNET_FS_PublishContext *pc;
1633 int32_t options;
1634 int32_t all_done;
1635 int32_t have_ns;
1636 char *fi_root;
1637 struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
1638 char *fi_pos;
1639 char *emsg;
1640
1641 pc = GNUNET_new (struct GNUNET_FS_PublishContext);
1642 pc->h = h;
1643 pc->serialization = get_serialization_short_name (filename);
1644 fi_root = NULL;
1645 fi_pos = NULL;
1646 rh = GNUNET_BIO_read_open_file (filename);
1647 if (NULL == rh)
1648 {
1649 GNUNET_break (0);
1650 goto cleanup;
1651 }
1652 struct GNUNET_BIO_ReadSpec rs[] = {
1653 GNUNET_BIO_read_spec_string ("publish-nid", &pc->nid, 1024),
1654 GNUNET_BIO_read_spec_string ("publish-nuid", &pc->nuid, 1024),
1655 GNUNET_BIO_read_spec_int32 ("options", &options),
1656 GNUNET_BIO_read_spec_int32 ("all done", &all_done),
1657 GNUNET_BIO_read_spec_int32 ("have ns", &have_ns),
1658 GNUNET_BIO_read_spec_string ("publish-firoot", &fi_root, 128),
1659 GNUNET_BIO_read_spec_string ("publish-fipos", &fi_pos, 128),
1660 GNUNET_BIO_read_spec_end (),
1661 };
1662 if ((GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) ||
1663 ((GNUNET_YES == have_ns) &&
1664 (GNUNET_OK != GNUNET_BIO_read (rh, "publish-ns", &ns, sizeof(ns)))))
1665 {
1666 GNUNET_break (0);
1667 goto cleanup;
1668 }
1669 pc->options = options;
1670 pc->all_done = all_done;
1671 if (NULL == fi_root)
1672 {
1673 GNUNET_break (0);
1674 goto cleanup;
1675 }
1676 pc->fi = deserialize_file_information (h, fi_root);
1677 if (NULL == pc->fi)
1678 {
1679 GNUNET_break (0);
1680 goto cleanup;
1681 }
1682 if (GNUNET_YES == have_ns)
1683 {
1684 pc->ns = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
1685 *pc->ns = ns;
1686 }
1687 if ((0 == (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) &&
1688 (GNUNET_YES != pc->all_done))
1689 {
1690 pc->dsh = GNUNET_DATASTORE_connect (h->cfg);
1691 if (NULL == pc->dsh)
1692 goto cleanup;
1693 }
1694 if (NULL != fi_pos)
1695 {
1696 pc->fi_pos = find_file_position (pc->fi, fi_pos);
1697 GNUNET_free (fi_pos);
1698 fi_pos = NULL;
1699 if (NULL == pc->fi_pos)
1700 {
1701 /* failed to find position for resuming, outch! Will start from root! */
1702 GNUNET_break (0);
1703 if (GNUNET_YES != pc->all_done)
1704 pc->fi_pos = pc->fi;
1705 }
1706 }
1707 GNUNET_free (fi_root);
1708 fi_root = NULL;
1709 /* generate RESUME event(s) */
1710 GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_resume, pc);
1711
1712 /* re-start publishing (if needed)... */
1713 if (GNUNET_YES != pc->all_done)
1714 {
1715 GNUNET_assert (NULL == pc->upload_task);
1716 pc->upload_task =
1717 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1718 &GNUNET_FS_publish_main_,
1719 pc);
1720 }
1721 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
1722 {
1723 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1724 _ ("Failure while resuming publishing operation `%s': %s\n"),
1725 filename,
1726 emsg);
1727 GNUNET_free (emsg);
1728 }
1729 pc->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, pc);
1730 return GNUNET_OK;
1731cleanup:
1732 GNUNET_free (pc->nid);
1733 GNUNET_free (pc->nuid);
1734 GNUNET_free (fi_root);
1735 GNUNET_free (fi_pos);
1736 if ((NULL != rh) && (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)))
1737 {
1738 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1739 _ ("Failed to resume publishing operation `%s': %s\n"),
1740 filename,
1741 emsg);
1742 GNUNET_free (emsg);
1743 }
1744 if (NULL != pc->fi)
1745 GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL);
1746 if (0 != unlink (filename))
1747 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename);
1748 GNUNET_free (pc->serialization);
1749 GNUNET_free (pc);
1750 return GNUNET_OK;
1751}
1752
1753
1754/**
1755 * Synchronize this publishing struct with its mirror
1756 * on disk. Note that all internal FS-operations that change
1757 * publishing structs should already call "sync" internally,
1758 * so this function is likely not useful for clients.
1759 *
1760 * @param pc the struct to sync
1761 */
1762void
1763GNUNET_FS_publish_sync_ (struct GNUNET_FS_PublishContext *pc)
1764{
1765 struct GNUNET_BIO_WriteHandle *wh;
1766 int32_t have_ns;
1767
1768 if (NULL == pc->serialization)
1769 pc->serialization =
1770 make_serialization_file_name (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH);
1771 if (NULL == pc->serialization)
1772 return;
1773 if (NULL == pc->fi)
1774 return;
1775 if (NULL == pc->fi->serialization)
1776 {
1777 GNUNET_break (0);
1778 return;
1779 }
1780 wh = get_write_handle (pc->h,
1781 GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1782 pc->serialization);
1783 if (NULL == wh)
1784 {
1785 GNUNET_break (0);
1786 goto cleanup;
1787 }
1788 have_ns = (NULL != pc->ns) ? GNUNET_YES : GNUNET_NO;
1789 struct GNUNET_BIO_WriteSpec ws[] = {
1790 GNUNET_BIO_write_spec_string ("nid", pc->nid),
1791 GNUNET_BIO_write_spec_string ("nuid", pc->nuid),
1792 GNUNET_BIO_write_spec_int32 ("options", (int32_t *) &pc->options),
1793 GNUNET_BIO_write_spec_int32 ("all done", &pc->all_done),
1794 GNUNET_BIO_write_spec_int32 ("have ns", &have_ns),
1795 GNUNET_BIO_write_spec_string ("serialization", pc->fi->serialization),
1796 GNUNET_BIO_write_spec_string ("pos serialization", (NULL == pc->fi_pos)
1797 ? NULL
1798 : pc->fi_pos->serialization),
1799 GNUNET_BIO_read_spec_end ()
1800 };
1801 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)) ||
1802 ((NULL != pc->ns) &&
1803 (GNUNET_OK !=
1804 GNUNET_BIO_write (wh,
1805 "ns",
1806 pc->ns,
1807 sizeof(struct GNUNET_CRYPTO_EcdsaPrivateKey)))))
1808 {
1809 GNUNET_break (0);
1810 goto cleanup;
1811 }
1812 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
1813 {
1814 wh = NULL;
1815 GNUNET_break (0);
1816 goto cleanup;
1817 }
1818 return;
1819cleanup:
1820 if (NULL != wh)
1821 (void) GNUNET_BIO_write_close (wh, NULL);
1822 GNUNET_FS_remove_sync_file_ (pc->h,
1823 GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1824 pc->serialization);
1825 GNUNET_free (pc->serialization);
1826 pc->serialization = NULL;
1827}
1828
1829
1830/**
1831 * Synchronize this unindex struct with its mirror
1832 * on disk. Note that all internal FS-operations that change
1833 * publishing structs should already call "sync" internally,
1834 * so this function is likely not useful for clients.
1835 *
1836 * @param uc the struct to sync
1837 */
1838void
1839GNUNET_FS_unindex_sync_ (struct GNUNET_FS_UnindexContext *uc)
1840{
1841 struct GNUNET_BIO_WriteHandle *wh;
1842 char *uris;
1843
1844 if (NULL == uc->serialization)
1845 uc->serialization =
1846 make_serialization_file_name (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX);
1847 if (NULL == uc->serialization)
1848 return;
1849 wh = get_write_handle (uc->h,
1850 GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
1851 uc->serialization);
1852 if (NULL == wh)
1853 {
1854 GNUNET_break (0);
1855 goto cleanup;
1856 }
1857 if (NULL != uc->ksk_uri)
1858 uris = GNUNET_FS_uri_to_string (uc->ksk_uri);
1859 else
1860 uris = NULL;
1861 struct GNUNET_BIO_WriteSpec ws1[] = {
1862 GNUNET_BIO_write_spec_string ("filename", uc->filename),
1863 GNUNET_BIO_write_spec_int64 ("file size", (int64_t *) &uc->file_size),
1864 GNUNET_BIO_write_spec_end (),
1865 };
1866 struct GNUNET_BIO_WriteSpec ws2[] = {
1867 GNUNET_BIO_write_spec_int32 ("state", (int32_t *) &uc->state),
1868 GNUNET_BIO_write_spec_object ("hashkey", &uc->chk,
1869 sizeof (struct ContentHashKey)),
1870 GNUNET_BIO_write_spec_string ("uris", uris),
1871 GNUNET_BIO_write_spec_int32 ("ksk offset", (int32_t *) &uc->ksk_offset),
1872 GNUNET_BIO_write_spec_end (),
1873 };
1874 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws1)) ||
1875 (GNUNET_OK != write_start_time (wh, uc->start_time)) ||
1876 (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws2)) ||
1877 ((uc->state == UNINDEX_STATE_FS_NOTIFY) &&
1878 (GNUNET_OK != GNUNET_BIO_write (wh,
1879 "file id",
1880 &uc->file_id,
1881 sizeof(struct GNUNET_HashCode)))) ||
1882 ((uc->state == UNINDEX_STATE_ERROR) &&
1883 (GNUNET_OK != GNUNET_BIO_write_string (wh, "emsg", uc->emsg))))
1884 {
1885 GNUNET_break (0);
1886 goto cleanup;
1887 }
1888 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
1889 {
1890 wh = NULL;
1891 GNUNET_break (0);
1892 goto cleanup;
1893 }
1894 return;
1895cleanup:
1896 if (NULL != wh)
1897 (void) GNUNET_BIO_write_close (wh, NULL);
1898 GNUNET_FS_remove_sync_file_ (uc->h,
1899 GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
1900 uc->serialization);
1901 GNUNET_free (uc->serialization);
1902 uc->serialization = NULL;
1903}
1904
1905
1906/**
1907 * Serialize a download request.
1908 *
1909 * @param wh handle for writing the download request to disk
1910 * @param dr the the request to write to disk
1911 * @return #GNUNET_YES on success, #GNUNET_NO on error
1912 */
1913static int
1914write_download_request (struct GNUNET_BIO_WriteHandle *wh,
1915 struct DownloadRequest *dr)
1916{
1917 unsigned int i;
1918 struct GNUNET_BIO_WriteSpec ws[] = {
1919 GNUNET_BIO_write_spec_int32 ("state", (int32_t *) &dr->state),
1920 GNUNET_BIO_write_spec_int64 ("offset", (int64_t *) &dr->offset),
1921 GNUNET_BIO_write_spec_int32 ("num children", (int32_t *) &dr->num_children),
1922 GNUNET_BIO_write_spec_int32 ("depth", (int32_t *) &dr->depth),
1923 GNUNET_BIO_write_spec_end (),
1924 };
1925
1926 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)))
1927 return GNUNET_NO;
1928 if ((BRS_CHK_SET == dr->state) &&
1929 (GNUNET_OK !=
1930 GNUNET_BIO_write (wh, "hashkey",
1931 &dr->chk, sizeof(struct ContentHashKey))))
1932 return GNUNET_NO;
1933 for (i = 0; i < dr->num_children; i++)
1934 if (GNUNET_NO == write_download_request (wh, dr->children[i]))
1935 return GNUNET_NO;
1936 return GNUNET_YES;
1937}
1938
1939
1940/**
1941 * Read a download request tree.
1942 *
1943 * @param rh cadet to read from
1944 * @return value the download request read from disk, NULL on error
1945 */
1946static struct DownloadRequest *
1947read_download_request (struct GNUNET_BIO_ReadHandle *rh)
1948{
1949 struct DownloadRequest *dr;
1950 unsigned int i;
1951
1952 dr = GNUNET_new (struct DownloadRequest);
1953 struct GNUNET_BIO_ReadSpec rs[] = {
1954 GNUNET_BIO_read_spec_int32 ("state", (int32_t *) &dr->state),
1955 GNUNET_BIO_read_spec_int64 ("offset", (int64_t *) &dr->offset),
1956 GNUNET_BIO_read_spec_int32 ("num children", (int32_t *) &dr->num_children),
1957 GNUNET_BIO_read_spec_end (),
1958 };
1959 if ((GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) ||
1960 (dr->num_children > CHK_PER_INODE) ||
1961 (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "depth",
1962 (int32_t *) &dr->depth)) ||
1963 ((0 == dr->depth) && (dr->num_children > 0)) ||
1964 ((dr->depth > 0) && (0 == dr->num_children)))
1965 {
1966 GNUNET_break (0);
1967 dr->num_children = 0;
1968 goto cleanup;
1969 }
1970 if (dr->num_children > 0)
1971 dr->children =
1972 GNUNET_malloc (dr->num_children * sizeof(struct DownloadRequest *));
1973 switch (dr->state)
1974 {
1975 case BRS_INIT:
1976 case BRS_RECONSTRUCT_DOWN:
1977 case BRS_RECONSTRUCT_META_UP:
1978 case BRS_RECONSTRUCT_UP:
1979 break;
1980
1981 case BRS_CHK_SET:
1982 if (GNUNET_OK !=
1983 GNUNET_BIO_read (rh, "chk", &dr->chk, sizeof(struct ContentHashKey)))
1984 goto cleanup;
1985 break;
1986
1987 case BRS_DOWNLOAD_DOWN:
1988 case BRS_DOWNLOAD_UP:
1989 case BRS_ERROR:
1990 break;
1991
1992 default:
1993 GNUNET_break (0);
1994 goto cleanup;
1995 }
1996 for (i = 0; i < dr->num_children; i++)
1997 {
1998 if (NULL == (dr->children[i] = read_download_request (rh)))
1999 goto cleanup;
2000 dr->children[i]->parent = dr;
2001 }
2002 return dr;
2003cleanup:
2004 GNUNET_FS_free_download_request_ (dr);
2005 return NULL;
2006}
2007
2008
2009/**
2010 * Compute the name of the sync file (or directory) for the given download
2011 * context.
2012 *
2013 * @param dc download context to compute for
2014 * @param uni unique filename to use, use "" for the directory name
2015 * @param ext extension to use, use ".dir" for our own subdirectory
2016 * @return the expanded file name, NULL for none
2017 */
2018static char *
2019get_download_sync_filename (struct GNUNET_FS_DownloadContext *dc,
2020 const char *uni,
2021 const char *ext)
2022{
2023 char *par;
2024 char *epar;
2025
2026 if (dc->parent == NULL)
2027 return get_serialization_file_name (dc->h,
2028 (dc->search != NULL)
2029 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2030 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2031 uni);
2032 if (NULL == dc->parent->serialization)
2033 return NULL;
2034 par = get_download_sync_filename (dc->parent, dc->parent->serialization, "");
2035 if (NULL == par)
2036 return NULL;
2037 GNUNET_asprintf (&epar, "%s.dir%s%s%s", par, DIR_SEPARATOR_STR, uni, ext);
2038 GNUNET_free (par);
2039 return epar;
2040}
2041
2042
2043/**
2044 * Synchronize this download struct with its mirror
2045 * on disk. Note that all internal FS-operations that change
2046 * publishing structs should already call "sync" internally,
2047 * so this function is likely not useful for clients.
2048 *
2049 * @param dc the struct to sync
2050 */
2051void
2052GNUNET_FS_download_sync_ (struct GNUNET_FS_DownloadContext *dc)
2053{
2054 struct GNUNET_BIO_WriteHandle *wh;
2055 char *uris;
2056 char *fn;
2057 char *dir;
2058
2059 if (0 != (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
2060 return; /* we don't sync probes */
2061 if (NULL == dc->serialization)
2062 {
2063 dir = get_download_sync_filename (dc, "", "");
2064 if (NULL == dir)
2065 return;
2066 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dir))
2067 {
2068 GNUNET_free (dir);
2069 return;
2070 }
2071 fn = GNUNET_DISK_mktemp (dir);
2072 GNUNET_free (dir);
2073 if (NULL == fn)
2074 return;
2075 dc->serialization = get_serialization_short_name (fn);
2076 }
2077 else
2078 {
2079 fn = get_download_sync_filename (dc, dc->serialization, "");
2080 if (NULL == fn)
2081 {
2082 GNUNET_free (dc->serialization);
2083 dc->serialization = NULL;
2084 GNUNET_free (fn);
2085 return;
2086 }
2087 }
2088 wh = GNUNET_BIO_write_open_file (fn);
2089 if (NULL == wh)
2090 {
2091 GNUNET_free (dc->serialization);
2092 dc->serialization = NULL;
2093 GNUNET_free (fn);
2094 return;
2095 }
2096 GNUNET_assert ((GNUNET_YES == GNUNET_FS_uri_test_chk (dc->uri)) ||
2097 (GNUNET_YES == GNUNET_FS_uri_test_loc (dc->uri)));
2098 uris = GNUNET_FS_uri_to_string (dc->uri);
2099 struct GNUNET_BIO_WriteSpec ws1[] = {
2100 GNUNET_BIO_write_spec_string ("uris", uris),
2101 GNUNET_BIO_write_spec_meta_data ("metadata", dc->meta),
2102 GNUNET_BIO_write_spec_string ("emsg", dc->emsg),
2103 GNUNET_BIO_write_spec_string ("filename", dc->filename),
2104 GNUNET_BIO_write_spec_string ("temp filename", dc->temp_filename),
2105 GNUNET_BIO_write_spec_int64 ("old file size",
2106 (int64_t *) &dc->old_file_size),
2107 GNUNET_BIO_write_spec_int64 ("offset", (int64_t *) &dc->offset),
2108 GNUNET_BIO_write_spec_int64 ("length", (int64_t *) &dc->length),
2109 GNUNET_BIO_write_spec_int64 ("completed", (int64_t *) &dc->completed),
2110 GNUNET_BIO_write_spec_end (),
2111 };
2112 struct GNUNET_BIO_WriteSpec ws2[] = {
2113 GNUNET_BIO_write_spec_int32 ("anonymity", (int32_t *) &dc->anonymity),
2114 GNUNET_BIO_write_spec_int32 ("options", (int32_t *) &dc->options),
2115 GNUNET_BIO_write_spec_int32 ("has finished", (int32_t *) &dc->has_finished),
2116 GNUNET_BIO_write_spec_end (),
2117 };
2118 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws1)) ||
2119 (GNUNET_OK != write_start_time (wh, dc->start_time)) ||
2120 (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws2)))
2121 {
2122 GNUNET_break (0);
2123 goto cleanup;
2124 }
2125 if (NULL == dc->emsg)
2126 {
2127 GNUNET_assert (dc->top_request != NULL);
2128 if (GNUNET_YES != write_download_request (wh, dc->top_request))
2129 {
2130 GNUNET_break (0);
2131 goto cleanup;
2132 }
2133 }
2134 GNUNET_free (uris);
2135 uris = NULL;
2136 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
2137 {
2138 wh = NULL;
2139 GNUNET_break (0);
2140 goto cleanup;
2141 }
2142 GNUNET_free (fn);
2143 return;
2144cleanup:
2145 if (NULL != wh)
2146 (void) GNUNET_BIO_write_close (wh, NULL);
2147 GNUNET_free (uris);
2148 if (0 != unlink (fn))
2149 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
2150 GNUNET_free (fn);
2151 GNUNET_free (dc->serialization);
2152 dc->serialization = NULL;
2153}
2154
2155
2156/**
2157 * Synchronize this search result with its mirror
2158 * on disk. Note that all internal FS-operations that change
2159 * publishing structs should already call "sync" internally,
2160 * so this function is likely not useful for clients.
2161 *
2162 * @param sr the struct to sync
2163 */
2164void
2165GNUNET_FS_search_result_sync_ (struct GNUNET_FS_SearchResult *sr)
2166{
2167 struct GNUNET_BIO_WriteHandle *wh;
2168 char *uris;
2169
2170 if (NULL == sr->sc)
2171 return;
2172 uris = NULL;
2173 if (NULL == sr->serialization)
2174 sr->serialization =
2175 make_serialization_file_name_in_dir (sr->h,
2176 (sr->sc->psearch_result == NULL)
2177 ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2178 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2179 sr->sc->serialization);
2180 if (NULL == sr->serialization)
2181 return;
2182 wh = get_write_handle_in_dir (sr->h,
2183 (sr->sc->psearch_result == NULL)
2184 ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2185 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2186 sr->sc->serialization,
2187 sr->serialization);
2188 if (NULL == wh)
2189 {
2190 GNUNET_break (0);
2191 goto cleanup;
2192 }
2193 uris = GNUNET_FS_uri_to_string (sr->uri);
2194 struct GNUNET_BIO_WriteSpec ws[] = {
2195 GNUNET_BIO_write_spec_string ("uris", uris),
2196 GNUNET_BIO_write_spec_string ("download serialization",
2197 (sr->download != NULL)
2198 ? sr->download->serialization
2199 : NULL),
2200 GNUNET_BIO_write_spec_string ("update search serialization",
2201 (sr->update_search != NULL)
2202 ? sr->update_search->serialization
2203 : NULL),
2204 GNUNET_BIO_write_spec_meta_data ("metadata", sr->meta),
2205 GNUNET_BIO_write_spec_object ("key", &sr->key,
2206 sizeof(struct GNUNET_HashCode)),
2207 GNUNET_BIO_write_spec_int32 ("mandatory missing",
2208 (int32_t *) &sr->mandatory_missing),
2209 GNUNET_BIO_write_spec_int32 ("optional support",
2210 (int32_t *) &sr->optional_support),
2211 GNUNET_BIO_write_spec_int32 ("availability success",
2212 (int32_t *) &sr->availability_success),
2213 GNUNET_BIO_write_spec_int32 ("availability trials",
2214 (int32_t *) &sr->availability_trials),
2215 GNUNET_BIO_write_spec_end (),
2216 };
2217 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)))
2218 {
2219 GNUNET_break (0);
2220 goto cleanup;
2221 }
2222 if ((NULL != sr->uri) && (GNUNET_FS_URI_KSK == sr->sc->uri->type) &&
2223 (GNUNET_OK !=
2224 GNUNET_BIO_write (wh,
2225 "keyword bitmap",
2226 sr->keyword_bitmap,
2227 (sr->sc->uri->data.ksk.keywordCount + 7) / 8)))
2228 {
2229 GNUNET_break (0);
2230 goto cleanup;
2231 }
2232 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
2233 {
2234 wh = NULL;
2235 GNUNET_break (0);
2236 goto cleanup;
2237 }
2238 GNUNET_free (uris);
2239 return;
2240cleanup:
2241 GNUNET_free (uris);
2242 if (NULL != wh)
2243 (void) GNUNET_BIO_write_close (wh, NULL);
2244 remove_sync_file_in_dir (sr->h,
2245 (NULL == sr->sc->psearch_result)
2246 ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2247 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2248 sr->sc->serialization,
2249 sr->serialization);
2250 GNUNET_free (sr->serialization);
2251 sr->serialization = NULL;
2252}
2253
2254
2255/**
2256 * Synchronize this search struct with its mirror
2257 * on disk. Note that all internal FS-operations that change
2258 * publishing structs should already call "sync" internally,
2259 * so this function is likely not useful for clients.
2260 *
2261 * @param sc the struct to sync
2262 */
2263void
2264GNUNET_FS_search_sync_ (struct GNUNET_FS_SearchContext *sc)
2265{
2266 struct GNUNET_BIO_WriteHandle *wh;
2267 char *uris;
2268 char in_pause;
2269 const char *category;
2270
2271 category = (NULL == sc->psearch_result) ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2272 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH;
2273 if (NULL == sc->serialization)
2274 sc->serialization = make_serialization_file_name (sc->h, category);
2275 if (NULL == sc->serialization)
2276 return;
2277 uris = NULL;
2278 wh = get_write_handle (sc->h, category, sc->serialization);
2279 if (NULL == wh)
2280 {
2281 GNUNET_break (0);
2282 goto cleanup;
2283 }
2284 GNUNET_assert ((GNUNET_YES == GNUNET_FS_uri_test_ksk (sc->uri)) ||
2285 (GNUNET_YES == GNUNET_FS_uri_test_sks (sc->uri)));
2286 uris = GNUNET_FS_uri_to_string (sc->uri);
2287 in_pause = (sc->task != NULL) ? 'r' : '\0';
2288 if ((GNUNET_OK != GNUNET_BIO_write_string (wh, "uris", uris)) ||
2289 (GNUNET_OK != write_start_time (wh, sc->start_time)) ||
2290 (GNUNET_OK != GNUNET_BIO_write_string (wh, "emsg", sc->emsg)) ||
2291 (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "options",
2292 (uint32_t) sc->options)) ||
2293 (GNUNET_OK != GNUNET_BIO_write (wh, "in pause",
2294 &in_pause, sizeof(in_pause))) ||
2295 (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "anonymity", sc->anonymity)))
2296 {
2297 GNUNET_break (0);
2298 goto cleanup;
2299 }
2300 GNUNET_free (uris);
2301 uris = NULL;
2302 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
2303 {
2304 wh = NULL;
2305 GNUNET_break (0);
2306 goto cleanup;
2307 }
2308 return;
2309cleanup:
2310 if (NULL != wh)
2311 (void) GNUNET_BIO_write_close (wh, NULL);
2312 GNUNET_free (uris);
2313 GNUNET_FS_remove_sync_file_ (sc->h, category, sc->serialization);
2314 GNUNET_free (sc->serialization);
2315 sc->serialization = NULL;
2316}
2317
2318
2319/**
2320 * Function called with a filename of serialized unindexing operation
2321 * to deserialize.
2322 *
2323 * @param cls the `struct GNUNET_FS_Handle *`
2324 * @param filename complete filename (absolute path)
2325 * @return #GNUNET_OK (continue to iterate)
2326 */
2327static int
2328deserialize_unindex_file (void *cls, const char *filename)
2329{
2330 struct GNUNET_FS_Handle *h = cls;
2331 struct GNUNET_BIO_ReadHandle *rh;
2332 struct GNUNET_FS_UnindexContext *uc;
2333 struct GNUNET_FS_ProgressInfo pi;
2334 char *emsg;
2335 char *uris;
2336 uint32_t state;
2337
2338 uc = GNUNET_new (struct GNUNET_FS_UnindexContext);
2339 uc->h = h;
2340 uc->serialization = get_serialization_short_name (filename);
2341 rh = GNUNET_BIO_read_open_file (filename);
2342 if (NULL == rh)
2343 {
2344 GNUNET_break (0);
2345 goto cleanup;
2346 }
2347 uris = NULL;
2348 if ((GNUNET_OK !=
2349 GNUNET_BIO_read_string (rh, "unindex-fn", &uc->filename, 10 * 1024)) ||
2350 (GNUNET_OK != GNUNET_BIO_read_int64 (rh, "file size",
2351 (int64_t *) &uc->file_size)) ||
2352 (GNUNET_OK != read_start_time (rh, &uc->start_time)) ||
2353 (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "state",
2354 (int32_t *) &state)) ||
2355 (GNUNET_OK !=
2356 GNUNET_BIO_read (rh, "uri", &uc->chk, sizeof(struct ContentHashKey))) ||
2357 (GNUNET_OK !=
2358 GNUNET_BIO_read_string (rh, "unindex-kskuri", &uris, 10 * 1024)) ||
2359 (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "ksk offset",
2360 (int32_t *) &uc->ksk_offset)))
2361 {
2362 GNUNET_free (uris);
2363 GNUNET_break (0);
2364 goto cleanup;
2365 }
2366 if (NULL != uris)
2367 {
2368 uc->ksk_uri = GNUNET_FS_uri_parse (uris, &emsg);
2369 GNUNET_free (uris);
2370 if (NULL == uc->ksk_uri)
2371 {
2372 GNUNET_break (0);
2373 GNUNET_free (emsg);
2374 goto cleanup;
2375 }
2376 }
2377 if ((uc->ksk_offset > 0) &&
2378 ((NULL == uc->ksk_uri) ||
2379 (uc->ksk_offset > uc->ksk_uri->data.ksk.keywordCount)))
2380 {
2381 GNUNET_break (0);
2382 goto cleanup;
2383 }
2384 uc->state = (enum UnindexState) state;
2385 switch (state)
2386 {
2387 case UNINDEX_STATE_HASHING:
2388 break;
2389
2390 case UNINDEX_STATE_FS_NOTIFY:
2391 if (GNUNET_OK != GNUNET_BIO_read (rh,
2392 "unindex-hash",
2393 &uc->file_id,
2394 sizeof(struct GNUNET_HashCode)))
2395 {
2396 GNUNET_break (0);
2397 goto cleanup;
2398 }
2399 break;
2400
2401 case UNINDEX_STATE_DS_REMOVE:
2402 case UNINDEX_STATE_EXTRACT_KEYWORDS:
2403 case UNINDEX_STATE_DS_REMOVE_KBLOCKS:
2404 break;
2405
2406 case UNINDEX_STATE_COMPLETE:
2407 break;
2408
2409 case UNINDEX_STATE_ERROR:
2410 if (GNUNET_OK !=
2411 GNUNET_BIO_read_string (rh, "unindex-emsg", &uc->emsg, 10 * 1024))
2412 {
2413 GNUNET_break (0);
2414 goto cleanup;
2415 }
2416 break;
2417
2418 default:
2419 GNUNET_break (0);
2420 goto cleanup;
2421 }
2422 uc->top = GNUNET_FS_make_top (h, &GNUNET_FS_unindex_signal_suspend_, uc);
2423 pi.status = GNUNET_FS_STATUS_UNINDEX_RESUME;
2424 pi.value.unindex.specifics.resume.message = uc->emsg;
2425 GNUNET_FS_unindex_make_status_ (&pi,
2426 uc,
2427 (uc->state == UNINDEX_STATE_COMPLETE)
2428 ? uc->file_size
2429 : 0);
2430 switch (uc->state)
2431 {
2432 case UNINDEX_STATE_HASHING:
2433 uc->fhc = GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE,
2434 uc->filename,
2435 HASHING_BLOCKSIZE,
2436 &GNUNET_FS_unindex_process_hash_,
2437 uc);
2438 break;
2439
2440 case UNINDEX_STATE_FS_NOTIFY:
2441 uc->state = UNINDEX_STATE_HASHING;
2442 GNUNET_FS_unindex_process_hash_ (uc, &uc->file_id);
2443 break;
2444
2445 case UNINDEX_STATE_DS_REMOVE:
2446 GNUNET_FS_unindex_do_remove_ (uc);
2447 break;
2448
2449 case UNINDEX_STATE_EXTRACT_KEYWORDS:
2450 GNUNET_FS_unindex_do_extract_keywords_ (uc);
2451 break;
2452
2453 case UNINDEX_STATE_DS_REMOVE_KBLOCKS:
2454 GNUNET_FS_unindex_do_remove_kblocks_ (uc);
2455 break;
2456
2457 case UNINDEX_STATE_COMPLETE:
2458 case UNINDEX_STATE_ERROR:
2459 /* no need to resume any operation, we were done */
2460 break;
2461
2462 default:
2463 break;
2464 }
2465 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
2466 {
2467 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2468 _ ("Failure while resuming unindexing operation `%s': %s\n"),
2469 filename,
2470 emsg);
2471 GNUNET_free (emsg);
2472 }
2473 return GNUNET_OK;
2474cleanup:
2475 GNUNET_free (uc->filename);
2476 if ((NULL != rh) && (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)))
2477 {
2478 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2479 _ ("Failed to resume unindexing operation `%s': %s\n"),
2480 filename,
2481 emsg);
2482 GNUNET_free (emsg);
2483 }
2484 if (NULL != uc->serialization)
2485 GNUNET_FS_remove_sync_file_ (h,
2486 GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
2487 uc->serialization);
2488 GNUNET_free (uc->serialization);
2489 GNUNET_free (uc);
2490 return GNUNET_OK;
2491}
2492
2493
2494/**
2495 * Deserialize a download.
2496 *
2497 * @param h overall context
2498 * @param rh file to deserialize from
2499 * @param parent parent download
2500 * @param search associated search
2501 * @param serialization name under which the search was serialized
2502 */
2503static void
2504deserialize_download (struct GNUNET_FS_Handle *h,
2505 struct GNUNET_BIO_ReadHandle *rh,
2506 struct GNUNET_FS_DownloadContext *parent,
2507 struct GNUNET_FS_SearchResult *search,
2508 const char *serialization);
2509
2510
2511/**
2512 * Deserialize a search.
2513 *
2514 * @param h overall context
2515 * @param rh file to deserialize from
2516 * @param psearch_result parent search result
2517 * @param serialization name under which the search was serialized
2518 */
2519static struct GNUNET_FS_SearchContext *
2520deserialize_search (struct GNUNET_FS_Handle *h,
2521 struct GNUNET_BIO_ReadHandle *rh,
2522 struct GNUNET_FS_SearchResult *psearch_result,
2523 const char *serialization);
2524
2525
2526/**
2527 * Function called with a filename of serialized search result
2528 * to deserialize.
2529 *
2530 * @param cls the `struct GNUNET_FS_SearchContext *`
2531 * @param filename complete filename (absolute path)
2532 * @return #GNUNET_OK (continue to iterate)
2533 */
2534static int
2535deserialize_search_result (void *cls, const char *filename)
2536{
2537 struct GNUNET_FS_SearchContext *sc = cls;
2538 char *serialized;
2539 char *uris;
2540 char *emsg;
2541 char *download;
2542 char *update_srch;
2543 struct GNUNET_BIO_ReadHandle *rh;
2544 struct GNUNET_BIO_ReadHandle *drh;
2545 struct GNUNET_FS_SearchResult *sr;
2546
2547 serialized = get_serialization_short_name (filename);
2548 rh = GNUNET_BIO_read_open_file (filename);
2549 if (NULL == rh)
2550 {
2551 if (NULL != serialized)
2552 {
2553 remove_sync_file_in_dir (sc->h,
2554 (NULL == sc->psearch_result)
2555 ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2556 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2557 sc->serialization,
2558 serialized);
2559 GNUNET_free (serialized);
2560 }
2561 return GNUNET_OK;
2562 }
2563 emsg = NULL;
2564 uris = NULL;
2565 download = NULL;
2566 update_srch = NULL;
2567 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
2568 sr->h = sc->h;
2569 sr->sc = sc;
2570 sr->serialization = serialized;
2571 if ((GNUNET_OK !=
2572 GNUNET_BIO_read_string (rh, "result-uri", &uris, 10 * 1024)) ||
2573 (NULL == (sr->uri = GNUNET_FS_uri_parse (uris, &emsg))) ||
2574 (GNUNET_OK !=
2575 GNUNET_BIO_read_string (rh, "download-lnk", &download, 16)) ||
2576 (GNUNET_OK !=
2577 GNUNET_BIO_read_string (rh, "search-lnk", &update_srch, 16)) ||
2578 (GNUNET_OK != GNUNET_BIO_read_meta_data (rh, "result-meta", &sr->meta)) ||
2579 (GNUNET_OK != GNUNET_BIO_read (rh,
2580 "result-key",
2581 &sr->key,
2582 sizeof(struct GNUNET_HashCode))) ||
2583 (GNUNET_OK != GNUNET_BIO_read_int32 (
2584 rh,
2585 "mandatory missing",
2586 (int32_t *) &sr->mandatory_missing)) ||
2587 (GNUNET_OK != GNUNET_BIO_read_int32 (
2588 rh,
2589 "optional support",
2590 (int32_t *) &sr->optional_support)) ||
2591 (GNUNET_OK != GNUNET_BIO_read_int32 (
2592 rh,
2593 "availability success",
2594 (int32_t *) &sr->availability_success)) ||
2595 (GNUNET_OK != GNUNET_BIO_read_int32 (
2596 rh,
2597 "availability trials",
2598 (int32_t *) &sr->availability_trials)))
2599 {
2600 GNUNET_break (0);
2601 goto cleanup;
2602 }
2603 if (GNUNET_FS_URI_KSK == sr->sc->uri->type)
2604 {
2605 sr->keyword_bitmap = GNUNET_malloc (
2606 (sr->sc->uri->data.ksk.keywordCount + 7) / 8); /* round up, count bits */
2607 if (GNUNET_OK !=
2608 GNUNET_BIO_read (rh,
2609 "keyword-bitmap",
2610 sr->keyword_bitmap,
2611 (sr->sc->uri->data.ksk.keywordCount + 7) / 8))
2612 {
2613 GNUNET_break (0);
2614 goto cleanup;
2615 }
2616 }
2617 GNUNET_free (uris);
2618 if (NULL != download)
2619 {
2620 drh = get_read_handle (sc->h, GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD, download);
2621 if (NULL != drh)
2622 {
2623 deserialize_download (sc->h, drh, NULL, sr, download);
2624 if (GNUNET_OK != GNUNET_BIO_read_close (drh, &emsg))
2625 {
2626 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2627 _ ("Failed to resume sub-download `%s': %s\n"),
2628 download,
2629 emsg);
2630 GNUNET_free (emsg);
2631 }
2632 }
2633 GNUNET_free (download);
2634 }
2635 if (NULL != update_srch)
2636 {
2637 drh =
2638 get_read_handle (sc->h, GNUNET_FS_SYNC_PATH_CHILD_SEARCH, update_srch);
2639 if (NULL != drh)
2640 {
2641 deserialize_search (sc->h, drh, sr, update_srch);
2642 if (GNUNET_OK != GNUNET_BIO_read_close (drh, &emsg))
2643 {
2644 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2645 _ ("Failed to resume sub-search `%s': %s\n"),
2646 update_srch,
2647 emsg);
2648 GNUNET_free (emsg);
2649 }
2650 }
2651 GNUNET_free (update_srch);
2652 }
2653 GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multihashmap_put (
2654 sc->master_result_map,
2655 &sr->key,
2656 sr,
2657 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
2658 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
2659 {
2660 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2661 _ ("Failure while resuming search operation `%s': %s\n"),
2662 filename,
2663 emsg);
2664 GNUNET_free (emsg);
2665 }
2666 return GNUNET_OK;
2667cleanup:
2668 GNUNET_free (download);
2669 GNUNET_free (emsg);
2670 GNUNET_free (uris);
2671 GNUNET_free (update_srch);
2672 if (NULL != sr->uri)
2673 GNUNET_FS_uri_destroy (sr->uri);
2674 if (NULL != sr->meta)
2675 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
2676 GNUNET_free (sr->serialization);
2677 GNUNET_free (sr);
2678 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
2679 {
2680 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2681 _ ("Failure while resuming search operation `%s': %s\n"),
2682 filename,
2683 emsg);
2684 GNUNET_free (emsg);
2685 }
2686 return GNUNET_OK;
2687}
2688
2689
2690/**
2691 * Send the 'resume' signal to the callback; also actually
2692 * resume the download (put it in the queue). Does this
2693 * recursively for the top-level download and all child
2694 * downloads.
2695 *
2696 * @param dc download to resume
2697 */
2698static void
2699signal_download_resume (struct GNUNET_FS_DownloadContext *dc)
2700{
2701 struct GNUNET_FS_DownloadContext *dcc;
2702 struct GNUNET_FS_ProgressInfo pi;
2703
2704 pi.status = GNUNET_FS_STATUS_DOWNLOAD_RESUME;
2705 pi.value.download.specifics.resume.meta = dc->meta;
2706 pi.value.download.specifics.resume.message = dc->emsg;
2707 GNUNET_FS_download_make_status_ (&pi, dc);
2708 dcc = dc->child_head;
2709 while (NULL != dcc)
2710 {
2711 signal_download_resume (dcc);
2712 dcc = dcc->next;
2713 }
2714}
2715
2716
2717/**
2718 * Signal resuming of a search to our clients (for the
2719 * top level search and all sub-searches).
2720 *
2721 * @param sc search being resumed
2722 */
2723static void
2724signal_search_resume (struct GNUNET_FS_SearchContext *sc);
2725
2726
2727/**
2728 * Iterator over search results signaling resume to the client for
2729 * each result.
2730 *
2731 * @param cls closure, the `struct GNUNET_FS_SearchContext *`
2732 * @param key current key code
2733 * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *`
2734 * @return #GNUNET_YES (we should continue to iterate)
2735 */
2736static int
2737signal_result_resume (void *cls, const struct GNUNET_HashCode *key, void *value)
2738{
2739 struct GNUNET_FS_SearchContext *sc = cls;
2740 struct GNUNET_FS_ProgressInfo pi;
2741 struct GNUNET_FS_SearchResult *sr = value;
2742
2743 if (0 == sr->mandatory_missing)
2744 {
2745 pi.status = GNUNET_FS_STATUS_SEARCH_RESUME_RESULT;
2746 pi.value.search.specifics.resume_result.meta = sr->meta;
2747 pi.value.search.specifics.resume_result.uri = sr->uri;
2748 pi.value.search.specifics.resume_result.result = sr;
2749 pi.value.search.specifics.resume_result.availability_rank =
2750 2 * sr->availability_success - sr->availability_trials;
2751 pi.value.search.specifics.resume_result.availability_certainty =
2752 sr->availability_trials;
2753 pi.value.search.specifics.resume_result.applicability_rank =
2754 sr->optional_support;
2755 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
2756 }
2757 if (NULL != sr->download)
2758 {
2759 signal_download_resume (sr->download);
2760 }
2761 else
2762 {
2763 GNUNET_FS_search_start_probe_ (sr);
2764 }
2765 if (NULL != sr->update_search)
2766 signal_search_resume (sr->update_search);
2767 return GNUNET_YES;
2768}
2769
2770
2771/**
2772 * Free memory allocated by the search context and its children
2773 *
2774 * @param sc search context to free
2775 */
2776static void
2777free_search_context (struct GNUNET_FS_SearchContext *sc);
2778
2779
2780/**
2781 * Iterator over search results freeing each.
2782 *
2783 * @param cls closure, the `struct GNUNET_FS_SearchContext *`
2784 * @param key current key code
2785 * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *`
2786 * @return #GNUNET_YES (we should continue to iterate)
2787 */
2788static int
2789free_result (void *cls, const struct GNUNET_HashCode *key, void *value)
2790{
2791 struct GNUNET_FS_SearchResult *sr = value;
2792
2793 if (NULL != sr->update_search)
2794 {
2795 free_search_context (sr->update_search);
2796 GNUNET_assert (NULL == sr->update_search);
2797 }
2798 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
2799 GNUNET_FS_uri_destroy (sr->uri);
2800 GNUNET_free (sr);
2801 return GNUNET_YES;
2802}
2803
2804
2805/**
2806 * Free memory allocated by the search context and its children
2807 *
2808 * @param sc search context to free
2809 */
2810static void
2811free_search_context (struct GNUNET_FS_SearchContext *sc)
2812{
2813 if (NULL != sc->serialization)
2814 {
2815 GNUNET_FS_remove_sync_file_ (sc->h,
2816 (sc->psearch_result == NULL)
2817 ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2818 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2819 sc->serialization);
2820 GNUNET_FS_remove_sync_dir_ (sc->h,
2821 (sc->psearch_result == NULL)
2822 ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2823 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2824 sc->serialization);
2825 }
2826 GNUNET_free (sc->serialization);
2827 GNUNET_free (sc->emsg);
2828 if (NULL != sc->uri)
2829 GNUNET_FS_uri_destroy (sc->uri);
2830 if (NULL != sc->master_result_map)
2831 {
2832 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
2833 &free_result,
2834 sc);
2835 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
2836 }
2837 GNUNET_free (sc);
2838}
2839
2840
2841/**
2842 * Function called with a filename of serialized sub-download
2843 * to deserialize.
2844 *
2845 * @param cls the `struct GNUNET_FS_DownloadContext *` (parent)
2846 * @param filename complete filename (absolute path)
2847 * @return #GNUNET_OK (continue to iterate)
2848 */
2849static int
2850deserialize_subdownload (void *cls, const char *filename)
2851{
2852 struct GNUNET_FS_DownloadContext *parent = cls;
2853 char *serialized;
2854 char *emsg;
2855 struct GNUNET_BIO_ReadHandle *rh;
2856
2857 serialized = get_serialization_short_name (filename);
2858 rh = GNUNET_BIO_read_open_file (filename);
2859 if (NULL == rh)
2860 {
2861 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2862 _ (
2863 "Failed to resume sub-download `%s': could not open file `%s'\n"),
2864 serialized,
2865 filename);
2866 GNUNET_free (serialized);
2867 return GNUNET_OK;
2868 }
2869 deserialize_download (parent->h, rh, parent, NULL, serialized);
2870 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
2871 {
2872 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2873 _ ("Failed to resume sub-download `%s': %s\n"),
2874 serialized,
2875 emsg);
2876 GNUNET_free (emsg);
2877 }
2878 GNUNET_free (serialized);
2879 return GNUNET_OK;
2880}
2881
2882
2883/**
2884 * Free this download context and all of its descendants.
2885 * (only works during deserialization since not all possible
2886 * state it taken care of).
2887 *
2888 * @param dc context to free
2889 */
2890static void
2891free_download_context (struct GNUNET_FS_DownloadContext *dc)
2892{
2893 struct GNUNET_FS_DownloadContext *dcc;
2894
2895 if (NULL != dc->meta)
2896 GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2897 if (NULL != dc->uri)
2898 GNUNET_FS_uri_destroy (dc->uri);
2899 GNUNET_free (dc->temp_filename);
2900 GNUNET_free (dc->emsg);
2901 GNUNET_free (dc->filename);
2902 GNUNET_free (dc->serialization);
2903 while (NULL != (dcc = dc->child_head))
2904 {
2905 GNUNET_CONTAINER_DLL_remove (dc->child_head, dc->child_tail, dcc);
2906 free_download_context (dcc);
2907 }
2908 GNUNET_FS_free_download_request_ (dc->top_request);
2909 if (NULL != dc->active)
2910 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2911 GNUNET_free (dc);
2912}
2913
2914
2915/**
2916 * Deserialize a download.
2917 *
2918 * @param h overall context
2919 * @param rh file to deserialize from
2920 * @param parent parent download
2921 * @param search associated search
2922 * @param serialization name under which the search was serialized
2923 */
2924static void
2925deserialize_download (struct GNUNET_FS_Handle *h,
2926 struct GNUNET_BIO_ReadHandle *rh,
2927 struct GNUNET_FS_DownloadContext *parent,
2928 struct GNUNET_FS_SearchResult *search,
2929 const char *serialization)
2930{
2931 struct GNUNET_FS_DownloadContext *dc;
2932 char *emsg;
2933 char *uris;
2934 char *dn;
2935 uint32_t options;
2936 uint32_t status;
2937
2938 uris = NULL;
2939 emsg = NULL;
2940 dc = GNUNET_new (struct GNUNET_FS_DownloadContext);
2941 dc->parent = parent;
2942 dc->h = h;
2943 dc->serialization = GNUNET_strdup (serialization);
2944 struct GNUNET_BIO_ReadSpec rs[] = {
2945 GNUNET_BIO_read_spec_meta_data ("download-meta", &dc->meta),
2946 GNUNET_BIO_read_spec_string ("download-emsg", &dc->emsg, 10 * 1024),
2947 GNUNET_BIO_read_spec_string ("download-fn", &dc->filename, 10 * 1024),
2948 GNUNET_BIO_read_spec_string ("download-tfn",
2949 &dc->temp_filename, 10 * 1024),
2950 GNUNET_BIO_read_spec_int64 ("old file size",
2951 (int64_t *) &dc->old_file_size),
2952 GNUNET_BIO_read_spec_int64 ("offset",
2953 (int64_t *) &dc->offset),
2954 GNUNET_BIO_read_spec_int64 ("length",
2955 (int64_t *) &dc->length),
2956 GNUNET_BIO_read_spec_int64 ("completed",
2957 (int64_t *) &dc->completed),
2958 GNUNET_BIO_read_spec_end (),
2959 };
2960 if ((GNUNET_OK !=
2961 GNUNET_BIO_read_string (rh, "download-uri", &uris, 10 * 1024)) ||
2962 (NULL == (dc->uri = GNUNET_FS_uri_parse (uris, &emsg))) ||
2963 ((GNUNET_YES != GNUNET_FS_uri_test_chk (dc->uri)) &&
2964 (GNUNET_YES != GNUNET_FS_uri_test_loc (dc->uri))) ||
2965 (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) ||
2966 (GNUNET_OK != read_start_time (rh, &dc->start_time)) ||
2967 (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "anonymity",
2968 (int32_t *) &dc->anonymity)) ||
2969 (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "options",
2970 (int32_t *) &options)) ||
2971 (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "status",
2972 (int32_t *) &status)))
2973 {
2974 GNUNET_break (0);
2975 goto cleanup;
2976 }
2977 dc->options = (enum GNUNET_FS_DownloadOptions) options;
2978 dc->active =
2979 GNUNET_CONTAINER_multihashmap_create (1 + 2 * (dc->length / DBLOCK_SIZE),
2980 GNUNET_NO);
2981 dc->has_finished = (int) status;
2982 dc->treedepth =
2983 GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri));
2984 if (GNUNET_FS_uri_test_loc (dc->uri))
2985 GNUNET_assert (GNUNET_OK ==
2986 GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target));
2987 if (NULL == dc->emsg)
2988 {
2989 dc->top_request = read_download_request (rh);
2990 if (NULL == dc->top_request)
2991 {
2992 GNUNET_break (0);
2993 goto cleanup;
2994 }
2995 }
2996 dn = get_download_sync_filename (dc, dc->serialization, ".dir");
2997 if (NULL != dn)
2998 {
2999 if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES))
3000 GNUNET_DISK_directory_scan (dn, &deserialize_subdownload, dc);
3001 GNUNET_free (dn);
3002 }
3003 if (NULL != parent)
3004 {
3005 GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc);
3006 }
3007 if (NULL != search)
3008 {
3009 dc->search = search;
3010 search->download = dc;
3011 }
3012 if ((NULL == parent) && (NULL == search))
3013 {
3014 dc->top =
3015 GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc);
3016 signal_download_resume (dc);
3017 }
3018 GNUNET_free (uris);
3019 GNUNET_assert (NULL == dc->job_queue);
3020 dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
3021 return;
3022cleanup:
3023 GNUNET_free (uris);
3024 GNUNET_free (emsg);
3025 free_download_context (dc);
3026}
3027
3028
3029/**
3030 * Signal resuming of a search to our clients (for the
3031 * top level search and all sub-searches).
3032 *
3033 * @param sc search being resumed
3034 */
3035static void
3036signal_search_resume (struct GNUNET_FS_SearchContext *sc)
3037{
3038 struct GNUNET_FS_ProgressInfo pi;
3039
3040 pi.status = GNUNET_FS_STATUS_SEARCH_RESUME;
3041 pi.value.search.specifics.resume.message = sc->emsg;
3042 pi.value.search.specifics.resume.is_paused =
3043 (NULL == sc->mq) ? GNUNET_YES : GNUNET_NO;
3044 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
3045 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
3046 &signal_result_resume,
3047 sc);
3048}
3049
3050
3051/**
3052 * Deserialize a search.
3053 *
3054 * @param h overall context
3055 * @param rh file to deserialize from
3056 * @param psearch_result parent search result
3057 * @param serialization name under which the search was serialized
3058 */
3059static struct GNUNET_FS_SearchContext *
3060deserialize_search (struct GNUNET_FS_Handle *h,
3061 struct GNUNET_BIO_ReadHandle *rh,
3062 struct GNUNET_FS_SearchResult *psearch_result,
3063 const char *serialization)
3064{
3065 struct GNUNET_FS_SearchContext *sc;
3066 char *emsg;
3067 char *uris;
3068 char *dn;
3069 uint32_t options;
3070 char in_pause;
3071
3072 if ((NULL != psearch_result) && (NULL != psearch_result->update_search))
3073 {
3074 GNUNET_break (0);
3075 return NULL;
3076 }
3077 uris = NULL;
3078 emsg = NULL;
3079 sc = GNUNET_new (struct GNUNET_FS_SearchContext);
3080 if (NULL != psearch_result)
3081 {
3082 sc->psearch_result = psearch_result;
3083 psearch_result->update_search = sc;
3084 }
3085 sc->h = h;
3086 sc->serialization = GNUNET_strdup (serialization);
3087 if ((GNUNET_OK !=
3088 GNUNET_BIO_read_string (rh, "search-uri", &uris, 10 * 1024)) ||
3089 (NULL == (sc->uri = GNUNET_FS_uri_parse (uris, &emsg))) ||
3090 ((GNUNET_YES != GNUNET_FS_uri_test_ksk (sc->uri)) &&
3091 (GNUNET_YES != GNUNET_FS_uri_test_sks (sc->uri))) ||
3092 (GNUNET_OK != read_start_time (rh, &sc->start_time)) ||
3093 (GNUNET_OK !=
3094 GNUNET_BIO_read_string (rh, "search-emsg", &sc->emsg, 10 * 1024)) ||
3095 (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "options",
3096 (int32_t *) &options)) ||
3097 (GNUNET_OK !=
3098 GNUNET_BIO_read (rh, "search-pause", &in_pause, sizeof(in_pause))) ||
3099 (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "anonymity",
3100 (int32_t *) &sc->anonymity)))
3101 {
3102 GNUNET_break (0);
3103 goto cleanup;
3104 }
3105 sc->options = (enum GNUNET_FS_SearchOptions) options;
3106 sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
3107 dn = get_serialization_file_name_in_dir (h,
3108 (NULL == sc->psearch_result)
3109 ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
3110 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
3111 sc->serialization,
3112 "");
3113 if (NULL != dn)
3114 {
3115 if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES))
3116 GNUNET_DISK_directory_scan (dn, &deserialize_search_result, sc);
3117 GNUNET_free (dn);
3118 }
3119 if (('\0' == in_pause) &&
3120 (GNUNET_OK != GNUNET_FS_search_start_searching_ (sc)))
3121 {
3122 GNUNET_log (
3123 GNUNET_ERROR_TYPE_WARNING,
3124 _ ("Could not resume running search, will resume as paused search\n"));
3125 }
3126 signal_search_resume (sc);
3127 GNUNET_free (uris);
3128 return sc;
3129cleanup:
3130 GNUNET_free (emsg);
3131 free_search_context (sc);
3132 GNUNET_free (uris);
3133 return NULL;
3134}
3135
3136
3137/**
3138 * Function called with a filename of serialized search operation
3139 * to deserialize.
3140 *
3141 * @param cls the `struct GNUNET_FS_Handle *`
3142 * @param filename complete filename (absolute path)
3143 * @return #GNUNET_OK (continue to iterate)
3144 */
3145static int
3146deserialize_search_file (void *cls, const char *filename)
3147{
3148 struct GNUNET_FS_Handle *h = cls;
3149 char *set;
3150 char *emsg;
3151 struct GNUNET_BIO_ReadHandle *rh;
3152 struct GNUNET_FS_SearchContext *sc;
3153 struct stat buf;
3154
3155 if (0 != stat (filename, &buf))
3156 {
3157 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
3158 return GNUNET_OK;
3159 }
3160 if (S_ISDIR (buf.st_mode))
3161 return GNUNET_OK; /* skip directories */
3162 set = get_serialization_short_name (filename);
3163 rh = GNUNET_BIO_read_open_file (filename);
3164 if (NULL == rh)
3165 {
3166 if (NULL != set)
3167 {
3168 GNUNET_FS_remove_sync_file_ (h, GNUNET_FS_SYNC_PATH_MASTER_SEARCH, set);
3169 GNUNET_free (set);
3170 }
3171 return GNUNET_OK;
3172 }
3173 sc = deserialize_search (h, rh, NULL, set);
3174 if (NULL != sc)
3175 sc->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, sc);
3176 GNUNET_free (set);
3177 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
3178 {
3179 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3180 _ ("Failure while resuming search operation `%s': %s\n"),
3181 filename,
3182 emsg);
3183 GNUNET_free (emsg);
3184 }
3185 return GNUNET_OK;
3186}
3187
3188
3189/**
3190 * Function called with a filename of serialized download operation
3191 * to deserialize.
3192 *
3193 * @param cls the `struct GNUNET_FS_Handle *`
3194 * @param filename complete filename (absolute path)
3195 * @return #GNUNET_OK (continue to iterate)
3196 */
3197static int
3198deserialize_download_file (void *cls, const char *filename)
3199{
3200 struct GNUNET_FS_Handle *h = cls;
3201 char *set;
3202 char *emsg;
3203 struct GNUNET_BIO_ReadHandle *rh;
3204
3205 set = get_serialization_short_name (filename);
3206 rh = GNUNET_BIO_read_open_file (filename);
3207 if (NULL == rh)
3208 {
3209 if (0 != unlink (filename))
3210 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename);
3211 GNUNET_free (set);
3212 return GNUNET_OK;
3213 }
3214 deserialize_download (h, rh, NULL, NULL, set);
3215 GNUNET_free (set);
3216 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
3217 {
3218 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3219 _ ("Failure while resuming download operation `%s': %s\n"),
3220 filename,
3221 emsg);
3222 GNUNET_free (emsg);
3223 }
3224 return GNUNET_OK;
3225}
3226
3227
3228/**
3229 * Deserialize information about pending operations.
3230 *
3231 * @param master_path which master directory should be scanned
3232 * @param proc function to call for each entry (will get @a h for 'cls')
3233 * @param h the `struct GNUNET_FS_Handle *`
3234 */
3235static void
3236deserialization_master (const char *master_path,
3237 GNUNET_FileNameCallback proc,
3238 struct GNUNET_FS_Handle *h)
3239{
3240 char *dn;
3241
3242 dn = get_serialization_file_name (h, master_path, "");
3243 if (NULL == dn)
3244 return;
3245 if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES))
3246 GNUNET_DISK_directory_scan (dn, proc, h);
3247 GNUNET_free (dn);
3248}
3249
3250
3251/**
3252 * Setup a connection to the file-sharing service.
3253 *
3254 * @param cfg configuration to use
3255 * @param client_name unique identifier for this client
3256 * @param upcb function to call to notify about FS actions
3257 * @param upcb_cls closure for @a upcb
3258 * @param flags specific attributes for fs-operations
3259 * @param ... list of optional options, terminated with #GNUNET_FS_OPTIONS_END
3260 * @return NULL on error
3261 */
3262struct GNUNET_FS_Handle *
3263GNUNET_FS_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
3264 const char *client_name,
3265 GNUNET_FS_ProgressCallback upcb,
3266 void *upcb_cls,
3267 enum GNUNET_FS_Flags flags,
3268 ...)
3269{
3270 struct GNUNET_FS_Handle *ret;
3271 enum GNUNET_FS_OPTIONS opt;
3272 va_list ap;
3273
3274 ret = GNUNET_new (struct GNUNET_FS_Handle);
3275 ret->cfg = cfg;
3276 ret->client_name = GNUNET_strdup (client_name);
3277 ret->upcb = upcb;
3278 ret->upcb_cls = upcb_cls;
3279 ret->flags = flags;
3280 ret->max_parallel_downloads = DEFAULT_MAX_PARALLEL_DOWNLOADS;
3281 ret->max_parallel_requests = DEFAULT_MAX_PARALLEL_REQUESTS;
3282 ret->avg_block_latency =
3283 GNUNET_TIME_UNIT_MINUTES; /* conservative starting point */
3284 va_start (ap, flags);
3285 while (GNUNET_FS_OPTIONS_END !=
3286 (opt = GNUNET_VA_ARG_ENUM (ap, GNUNET_FS_OPTIONS)))
3287 {
3288 switch (opt)
3289 {
3290 case GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM:
3291 ret->max_parallel_downloads = va_arg (ap, unsigned int);
3292
3293 break;
3294
3295 case GNUNET_FS_OPTIONS_REQUEST_PARALLELISM:
3296 ret->max_parallel_requests = va_arg (ap, unsigned int);
3297
3298 break;
3299
3300 default:
3301 GNUNET_break (0);
3302 GNUNET_free (ret->client_name);
3303 GNUNET_free (ret);
3304 va_end (ap);
3305 return NULL;
3306 }
3307 }
3308 va_end (ap);
3309 if (0 != (GNUNET_FS_FLAGS_PERSISTENCE & flags))
3310 {
3311 deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
3312 &deserialize_publish_file,
3313 ret);
3314 deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
3315 &deserialize_search_file,
3316 ret);
3317 deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
3318 &deserialize_download_file,
3319 ret);
3320 deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
3321 &deserialize_unindex_file,
3322 ret);
3323 }
3324 return ret;
3325}
3326
3327
3328/**
3329 * Close our connection with the file-sharing service.
3330 * The callback given to #GNUNET_FS_start() will no longer be
3331 * called after this function returns.
3332 * This function MUST NOT be called from within the
3333 * callback itself.
3334 *
3335 * @param h handle that was returned from #GNUNET_FS_start()
3336 */
3337void
3338GNUNET_FS_stop (struct GNUNET_FS_Handle *h)
3339{
3340 while (NULL != h->top_head)
3341 h->top_head->ssf (h->top_head->ssf_cls);
3342 if (NULL != h->queue_job)
3343 GNUNET_SCHEDULER_cancel (h->queue_job);
3344 GNUNET_free (h->client_name);
3345 GNUNET_free (h);
3346}
3347
3348
3349/* end of fs_api.c */
diff --git a/src/fs/fs_api.h b/src/fs/fs_api.h
deleted file mode 100644
index 050d5f46c..000000000
--- a/src/fs/fs_api.h
+++ /dev/null
@@ -1,1940 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_api.h
23 * @brief shared definitions for the FS library
24 * @author Igor Wronsky, Christian Grothoff
25 */
26#ifndef FS_API_H
27#define FS_API_H
28
29#include "gnunet_constants.h"
30#include "gnunet_datastore_service.h"
31#include "gnunet_dht_service.h"
32#include "gnunet_fs_service.h"
33#include "gnunet_block_lib.h"
34#include "block_fs.h"
35#include "fs.h"
36
37/**
38 * Pick a multiple of 2 here to achieve 8-byte alignment! We also
39 * probably want DBlocks to have (roughly) the same size as IBlocks.
40 * With SHA-512, the optimal value is 32768 byte / 128 byte = 256 (128
41 * byte = 2 * 512 bits). DO NOT CHANGE!
42 */
43#define CHK_PER_INODE 256
44
45/**
46 * Maximum size for a file to be considered for inlining in a
47 * directory.
48 */
49#define MAX_INLINE_SIZE 65536
50
51/**
52 * Name of the directory with top-level searches.
53 */
54#define GNUNET_FS_SYNC_PATH_MASTER_SEARCH "search"
55
56/**
57 * Name of the directory with sub-searches (namespace-updates).
58 */
59#define GNUNET_FS_SYNC_PATH_CHILD_SEARCH "search-child"
60
61/**
62 * Name of the directory with master downloads (not associated
63 * with search or part of another download).
64 */
65#define GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD "download"
66
67/**
68 * Name of the directory with downloads that are part of another
69 * download or a search.
70 */
71#define GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD "download-child"
72
73/**
74 * Name of the directory with publishing operations.
75 */
76#define GNUNET_FS_SYNC_PATH_MASTER_PUBLISH "publish"
77
78/**
79 * Name of the directory with files that are being published
80 */
81#define GNUNET_FS_SYNC_PATH_FILE_INFO "publish-file"
82
83/**
84 * Name of the directory with unindex operations.
85 */
86#define GNUNET_FS_SYNC_PATH_MASTER_UNINDEX "unindex"
87
88
89/**
90 * @brief complete information needed
91 * to download a file.
92 */
93struct FileIdentifier
94{
95 /**
96 * Total size of the file in bytes. (network byte order (!))
97 */
98 uint64_t file_length;
99
100 /**
101 * Query and key of the top GNUNET_EC_IBlock.
102 */
103 struct ContentHashKey chk;
104};
105
106
107/**
108 * Information about a file and its location
109 * (peer claiming to share the file).
110 */
111struct Location
112{
113 /**
114 * Information about the shared file.
115 */
116 struct FileIdentifier fi;
117
118 /**
119 * Identity of the peer sharing the file.
120 */
121 struct GNUNET_PeerIdentity peer;
122
123 /**
124 * Time when this location URI expires.
125 */
126 struct GNUNET_TIME_Absolute expirationTime;
127
128 /**
129 * Signature over the GNUNET_EC_FileIdentifier,
130 * peer identity and expiration time.
131 */
132 struct GNUNET_CRYPTO_EddsaSignature contentSignature;
133};
134
135/**
136 * Types of URIs.
137 */
138enum GNUNET_FS_UriType
139{
140 /**
141 * Content-hash-key (simple file).
142 */
143 GNUNET_FS_URI_CHK,
144
145 /**
146 * Signed key space (file in namespace).
147 */
148 GNUNET_FS_URI_SKS,
149
150 /**
151 * Keyword search key (query with keywords).
152 */
153 GNUNET_FS_URI_KSK,
154
155 /**
156 * Location (chk with identity of hosting peer).
157 */
158 GNUNET_FS_URI_LOC
159};
160
161
162/**
163 * A Universal Resource Identifier (URI), opaque.
164 */
165struct GNUNET_FS_Uri
166{
167 /**
168 * Type of the URI.
169 */
170 enum GNUNET_FS_UriType type;
171
172 union
173 {
174 struct
175 {
176 /**
177 * Keywords start with a '+' if they are mandatory (in which
178 * case the '+' is NOT part of the keyword) and with a simple
179 * space if they are optional (in which case the space is ALSO
180 * not part of the actual keyword).
181 *
182 * Double-quotes to protect spaces and %-encoding are NOT used
183 * internally (only in URI-strings).
184 */
185 char **keywords;
186
187 /**
188 * Size of the keywords array.
189 */
190 unsigned int keywordCount;
191 } ksk;
192
193 struct
194 {
195 /**
196 * Identifier of the namespace.
197 */
198 struct GNUNET_CRYPTO_EcdsaPublicKey ns;
199
200 /**
201 * Human-readable identifier chosen for this entry in the
202 * namespace.
203 */
204 char *identifier;
205 } sks;
206
207 /**
208 * Information needed to retrieve a file (content-hash-key
209 * plus file size).
210 */
211 struct FileIdentifier chk;
212
213 /**
214 * Information needed to retrieve a file including signed
215 * location (identity of a peer) of the content.
216 */
217 struct Location loc;
218 } data;
219};
220
221
222/**
223 * Information for a file or directory that is
224 * about to be published.
225 */
226struct GNUNET_FS_FileInformation
227{
228 /**
229 * Files in a directory are kept as a linked list.
230 */
231 struct GNUNET_FS_FileInformation *next;
232
233 /**
234 * If this is a file in a directory, "dir" refers to
235 * the directory; otherwise NULL.
236 */
237 struct GNUNET_FS_FileInformation *dir;
238
239 /**
240 * Handle to the master context.
241 */
242 struct GNUNET_FS_Handle *h;
243
244 /**
245 * Pointer kept for the client.
246 */
247 void *client_info;
248
249 /**
250 * Metadata to use for the file.
251 */
252 struct GNUNET_CONTAINER_MetaData *meta;
253
254 /**
255 * Keywords to use for KBlocks.
256 */
257 struct GNUNET_FS_Uri *keywords;
258
259 /**
260 * CHK for this file or directory. NULL if
261 * we have not yet computed it.
262 */
263 struct GNUNET_FS_Uri *chk_uri;
264
265 /**
266 * SKS URI for this file or directory. NULL if
267 * we have not yet computed it.
268 */
269 struct GNUNET_FS_Uri *sks_uri;
270
271 /**
272 * Block options for the file.
273 */
274 struct GNUNET_FS_BlockOptions bo;
275
276 /**
277 * At what time did we start this upload?
278 */
279 struct GNUNET_TIME_Absolute start_time;
280
281 /**
282 * Under what filename is this struct serialized
283 * (for operational persistence). Should be determined
284 * using 'mktemp'.
285 */
286 char *serialization;
287
288 /**
289 * Encoder being used to publish this file.
290 */
291 struct GNUNET_FS_TreeEncoder *te;
292
293 /**
294 * Error message (non-NULL if this operation failed).
295 */
296 char *emsg;
297
298 /**
299 * Name of the file or directory (must be an absolute path).
300 */
301 char *filename;
302
303 /**
304 * Data describing either the file or the directory.
305 */
306 union
307 {
308 /**
309 * Data for a file.
310 */
311 struct
312 {
313 /**
314 * Function that can be used to read the data for the file.
315 */
316 GNUNET_FS_DataReader reader;
317
318 /**
319 * Closure for reader.
320 */
321 void *reader_cls;
322
323 /**
324 * If this file is being indexed, this value is set to the hash
325 * over the entire file (when the indexing process is started).
326 * Otherwise this field is not used.
327 */
328 struct GNUNET_HashCode file_id;
329
330 /**
331 * Size of the file (in bytes).
332 */
333 uint64_t file_size;
334
335 /**
336 * Should the file be indexed or inserted?
337 */
338 int do_index;
339
340 /**
341 * Is "file_id" already valid? Set to #GNUNET_YES once the hash
342 * has been calculated.
343 */
344 int have_hash;
345
346 /**
347 * Has the service confirmed our INDEX_START request?
348 * #GNUNET_YES if this step has been completed.
349 */
350 int index_start_confirmed;
351 } file;
352
353 /**
354 * Data for a directory.
355 */
356 struct
357 {
358 /**
359 * Linked list of entries in the directory.
360 */
361 struct GNUNET_FS_FileInformation *entries;
362
363 /**
364 * Size of the directory itself (in bytes); 0 if the
365 * size has not yet been calculated.
366 */
367 size_t dir_size;
368
369 /**
370 * Pointer to the data for the directory (or NULL if not
371 * available).
372 */
373 void *dir_data;
374
375 /**
376 * How much of the directory have we published (relative to @e contents_size).
377 */
378 uint64_t contents_completed;
379
380 /**
381 * Sum of all of the sizes of all of the files in the directory.
382 */
383 uint64_t contents_size;
384 } dir;
385 } data;
386
387 /**
388 * Is this struct for a file or directory?
389 */
390 int is_directory;
391
392 /**
393 * Are we done publishing this file?
394 */
395 int is_published;
396};
397
398
399/**
400 * Priorities for the queue.
401 */
402enum GNUNET_FS_QueuePriority
403{
404 /**
405 * This is a probe (low priority).
406 */
407 GNUNET_FS_QUEUE_PRIORITY_PROBE,
408
409 /**
410 * Default priority.
411 */
412 GNUNET_FS_QUEUE_PRIORITY_NORMAL
413};
414
415
416/**
417 * Entry in the job queue.
418 */
419struct GNUNET_FS_QueueEntry
420{
421 /**
422 * This is a linked list.
423 */
424 struct GNUNET_FS_QueueEntry *next;
425
426 /**
427 * This is a linked list.
428 */
429 struct GNUNET_FS_QueueEntry *prev;
430
431 /**
432 * Function to call when the job is started.
433 */
434 GNUNET_SCHEDULER_TaskCallback start;
435
436 /**
437 * Function to call when the job needs to stop (or is done / dequeued).
438 */
439 GNUNET_SCHEDULER_TaskCallback stop;
440
441 /**
442 * Closure for start and stop.
443 */
444 void *cls;
445
446 /**
447 * Handle to FS primary context.
448 */
449 struct GNUNET_FS_Handle *h;
450
451 /**
452 * Message queue handle, or NULL if job is not running.
453 */
454 struct GNUNET_MQ_Handle *mq;
455
456 /**
457 * Time the job was originally queued.
458 */
459 struct GNUNET_TIME_Absolute queue_time;
460
461 /**
462 * Time the job was started last.
463 */
464 struct GNUNET_TIME_Absolute start_time;
465
466 /**
467 * Total amount of time the job has been running (except for the
468 * current run).
469 */
470 struct GNUNET_TIME_Relative run_time;
471
472 /**
473 * How many blocks do the active downloads have?
474 */
475 unsigned int blocks;
476
477 /**
478 * How important is this download?
479 */
480 enum GNUNET_FS_QueuePriority priority;
481
482 /**
483 * How often have we (re)started this download?
484 */
485 unsigned int start_times;
486
487 /**
488 * #GNUNET_YES if the job is active now.
489 */
490 int active;
491};
492
493
494/**
495 * Information we store for each search result.
496 */
497struct GNUNET_FS_SearchResult
498{
499 /**
500 * File-sharing context this result belongs to.
501 */
502 struct GNUNET_FS_Handle *h;
503
504 /**
505 * Kept in a DLL while probing.
506 */
507 struct GNUNET_FS_SearchResult *next;
508
509 /**
510 * Kept in a DLL while probing.
511 */
512 struct GNUNET_FS_SearchResult *prev;
513
514 /**
515 * Search context this result belongs to; can be NULL
516 * for probes that come from a directory result.
517 */
518 struct GNUNET_FS_SearchContext *sc;
519
520 /**
521 * URI to which this search result refers to.
522 */
523 struct GNUNET_FS_Uri *uri;
524
525 /**
526 * Metadata for the search result.
527 */
528 struct GNUNET_CONTAINER_MetaData *meta;
529
530 /**
531 * Client info for this search result.
532 */
533 void *client_info;
534
535 /**
536 * ID of a job that is currently probing this results' availability
537 * (NULL if we are not currently probing).
538 */
539 struct GNUNET_FS_DownloadContext *probe_ctx;
540
541 /**
542 * ID of an associated download based on this search result (or
543 * NULL for none).
544 */
545 struct GNUNET_FS_DownloadContext *download;
546
547 /**
548 * If this search result triggered an update search, this field
549 * links to the update search.
550 */
551 struct GNUNET_FS_SearchContext *update_search;
552
553 /**
554 * Name under which this search result is stored on disk.
555 */
556 char *serialization;
557
558 /**
559 * Bitmap that specifies precisely which keywords have been matched already.
560 */
561 uint8_t *keyword_bitmap;
562
563 /**
564 * Key for the search result based on the URI.
565 */
566 struct GNUNET_HashCode key;
567
568 /**
569 * ID of the task that will clean up the probe_ctx should it not
570 * complete on time (and that will need to be cancelled if we clean
571 * up the search result before then).
572 */
573 struct GNUNET_SCHEDULER_Task *probe_cancel_task;
574
575 /**
576 * When did the current probe become active?
577 */
578 struct GNUNET_TIME_Absolute probe_active_time;
579
580 /**
581 * How much longer should we run the current probe before giving up?
582 */
583 struct GNUNET_TIME_Relative remaining_probe_time;
584
585 /**
586 * Anonymity level to use for probes using this search result.
587 */
588 uint32_t anonymity;
589
590 /**
591 * Number of mandatory keywords for which we have NOT yet found the
592 * search result; when this value hits zero, the search result is
593 * given to the callback.
594 */
595 uint32_t mandatory_missing;
596
597 /**
598 * Number of optional keywords under which this result was also
599 * found.
600 */
601 uint32_t optional_support;
602
603 /**
604 * Number of availability tests that have succeeded for this result.
605 */
606 uint32_t availability_success;
607
608 /**
609 * Number of availability trials that we have performed for this
610 * search result.
611 */
612 uint32_t availability_trials;
613};
614
615
616/**
617 * Add a job to the queue.
618 *
619 * @param h handle to the overall FS state
620 * @param start function to call to begin the job
621 * @param stop function to call to pause the job, or on dequeue (if the job was running)
622 * @param cls closure for start and stop
623 * @param blocks number of blocks this download has
624 * @param priority how important is this download
625 * @return queue handle
626 */
627struct GNUNET_FS_QueueEntry *
628GNUNET_FS_queue_ (struct GNUNET_FS_Handle *h,
629 GNUNET_SCHEDULER_TaskCallback start,
630 GNUNET_SCHEDULER_TaskCallback stop,
631 void *cls,
632 unsigned int blocks,
633 enum GNUNET_FS_QueuePriority priority);
634
635
636/**
637 * Dequeue a job from the queue.
638 *
639 * @param qe handle for the job
640 */
641void
642GNUNET_FS_dequeue_ (struct GNUNET_FS_QueueEntry *qe);
643
644
645/**
646 * Function that provides data by reading from a file.
647 *
648 * @param cls closure (points to the file information)
649 * @param offset offset to read from; it is possible
650 * that the caller might need to go backwards
651 * a bit at times
652 * @param max maximum number of bytes that should be
653 * copied to @a buf; readers are not allowed
654 * to provide less data unless there is an error;
655 * a value of "0" will be used at the end to allow
656 * the reader to clean up its internal state
657 * @param buf where the reader should write the data
658 * @param emsg location for the reader to store an error message
659 * @return number of bytes written, usually "max", 0 on error
660 */
661size_t
662GNUNET_FS_data_reader_file_ (void *cls,
663 uint64_t offset,
664 size_t max,
665 void *buf,
666 char **emsg);
667
668
669/**
670 * Create the closure for the #GNUNET_FS_data_reader_file_() callback.
671 *
672 * @param filename file to read
673 * @return closure to use
674 */
675void *
676GNUNET_FS_make_file_reader_context_ (const char *filename);
677
678
679/**
680 * Function that provides data by copying from a buffer.
681 *
682 * @param cls closure (points to the buffer)
683 * @param offset offset to read from; it is possible
684 * that the caller might need to go backwards
685 * a bit at times
686 * @param max maximum number of bytes that should be
687 * copied to @a buf; readers are not allowed
688 * to provide less data unless there is an error;
689 * a value of "0" will be used at the end to allow
690 * the reader to clean up its internal state
691 * @param buf where the reader should write the data
692 * @param emsg location for the reader to store an error message
693 * @return number of bytes written, usually @a max, 0 on error
694 */
695size_t
696GNUNET_FS_data_reader_copy_ (void *cls,
697 uint64_t offset,
698 size_t max,
699 void *buf,
700 char **emsg);
701
702
703/**
704 * Notification of FS that a search probe has made progress.
705 * This function is used INSTEAD of the client's event handler
706 * for downloads where the #GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
707 *
708 * @param cls closure, always NULL (!), actual closure
709 * is in the client-context of the info struct
710 * @param info details about the event, specifying the event type
711 * and various bits about the event
712 * @return client-context (for the next progress call
713 * for this operation; should be set to NULL for
714 * SUSPEND and STOPPED events). The value returned
715 * will be passed to future callbacks in the respective
716 * field in the `struct GNUNET_FS_ProgressInfo`.
717 */
718void *
719GNUNET_FS_search_probe_progress_ (void *cls,
720 const struct GNUNET_FS_ProgressInfo *info);
721
722
723/**
724 * Main function that performs the upload.
725 *
726 * @param cls `struct GNUNET_FS_PublishContext` identifies the upload
727 */
728void
729GNUNET_FS_publish_main_ (void *cls);
730
731
732/**
733 * Function called once the hash of the file
734 * that is being unindexed has been computed.
735 *
736 * @param cls closure, unindex context
737 * @param file_id computed hash, NULL on error
738 */
739void
740GNUNET_FS_unindex_process_hash_ (void *cls,
741 const struct GNUNET_HashCode *file_id);
742
743
744/**
745 * Extract the keywords for KBlock removal
746 *
747 * @param uc context for the unindex operation.
748 */
749void
750GNUNET_FS_unindex_do_extract_keywords_ (struct GNUNET_FS_UnindexContext *uc);
751
752
753/**
754 * If necessary, connect to the datastore and remove the KBlocks.
755 *
756 * @param uc context for the unindex operation.
757 */
758void
759GNUNET_FS_unindex_do_remove_kblocks_ (struct GNUNET_FS_UnindexContext *uc);
760
761
762/**
763 * Fill in all of the generic fields for a publish event and call the
764 * callback.
765 *
766 * @param pi structure to fill in
767 * @param pc overall publishing context
768 * @param p file information for the file being published
769 * @param offset where in the file are we so far
770 * @return value returned from callback
771 */
772void *
773GNUNET_FS_publish_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
774 struct GNUNET_FS_PublishContext *pc,
775 const struct GNUNET_FS_FileInformation *p,
776 uint64_t offset);
777
778
779/**
780 * Fill in all of the generic fields for a download event and call the
781 * callback.
782 *
783 * @param pi structure to fill in
784 * @param dc overall download context
785 */
786void
787GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
788 struct GNUNET_FS_DownloadContext *dc);
789
790
791/**
792 * Task that creates the initial (top-level) download
793 * request for the file.
794 *
795 * @param cls the 'struct GNUNET_FS_DownloadContext'
796 */
797void
798GNUNET_FS_download_start_task_ (void *cls);
799
800
801/**
802 * Fill in all of the generic fields for
803 * an unindex event and call the callback.
804 *
805 * @param pi structure to fill in
806 * @param uc overall unindex context
807 * @param offset where we are in the file (for progress)
808 */
809void
810GNUNET_FS_unindex_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
811 struct GNUNET_FS_UnindexContext *uc,
812 uint64_t offset);
813
814/**
815 * Fill in all of the generic fields for a search event and
816 * call the callback.
817 *
818 * @param pi structure to fill in
819 * @param h file-sharing handle
820 * @param sc overall search context
821 * @return value returned by the callback
822 */
823void *
824GNUNET_FS_search_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
825 struct GNUNET_FS_Handle *h,
826 struct GNUNET_FS_SearchContext *sc);
827
828
829/**
830 * Connect to the datastore and remove the blocks.
831 *
832 * @param uc context for the unindex operation.
833 */
834void
835GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc);
836
837/**
838 * Build the request and actually initiate the search using the
839 * GNUnet FS service.
840 *
841 * @param sc search context
842 * @return GNUNET_OK on success, GNUNET_SYSERR on error
843 */
844int
845GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc);
846
847/**
848 * Start the downloading process (by entering the queue).
849 *
850 * @param dc our download context
851 */
852void
853GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc);
854
855
856/**
857 * Start download probes for the given search result.
858 *
859 * @param sr the search result
860 */
861void
862GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr);
863
864
865/**
866 * Remove serialization/deserialization file from disk.
867 *
868 * @param h master context
869 * @param ext component of the path
870 * @param ent entity identifier
871 */
872void
873GNUNET_FS_remove_sync_file_ (struct GNUNET_FS_Handle *h,
874 const char *ext,
875 const char *ent);
876
877
878/**
879 * Remove serialization/deserialization directory from disk.
880 *
881 * @param h master context
882 * @param ext component of the path
883 * @param uni unique name of parent
884 */
885void
886GNUNET_FS_remove_sync_dir_ (struct GNUNET_FS_Handle *h,
887 const char *ext,
888 const char *uni);
889
890
891/**
892 * Synchronize this file-information struct with its mirror
893 * on disk. Note that all internal FS-operations that change
894 * file information data should already call "sync" internally,
895 * so this function is likely not useful for clients.
896 *
897 * @param fi the struct to sync
898 */
899void
900GNUNET_FS_file_information_sync_ (struct GNUNET_FS_FileInformation *f);
901
902
903/**
904 * Synchronize this publishing struct with its mirror
905 * on disk. Note that all internal FS-operations that change
906 * publishing structs should already call "sync" internally,
907 * so this function is likely not useful for clients.
908 *
909 * @param pc the struct to sync
910 */
911void
912GNUNET_FS_publish_sync_ (struct GNUNET_FS_PublishContext *pc);
913
914
915/**
916 * Synchronize this unindex struct with its mirror
917 * on disk. Note that all internal FS-operations that change
918 * publishing structs should already call "sync" internally,
919 * so this function is likely not useful for clients.
920 *
921 * @param uc the struct to sync
922 */
923void
924GNUNET_FS_unindex_sync_ (struct GNUNET_FS_UnindexContext *uc);
925
926
927/**
928 * Synchronize this search struct with its mirror
929 * on disk. Note that all internal FS-operations that change
930 * publishing structs should already call "sync" internally,
931 * so this function is likely not useful for clients.
932 *
933 * @param sc the struct to sync
934 */
935void
936GNUNET_FS_search_sync_ (struct GNUNET_FS_SearchContext *sc);
937
938
939/**
940 * Synchronize this search result with its mirror
941 * on disk. Note that all internal FS-operations that change
942 * publishing structs should already call "sync" internally,
943 * so this function is likely not useful for clients.
944 *
945 * @param sr the struct to sync
946 */
947void
948GNUNET_FS_search_result_sync_ (struct GNUNET_FS_SearchResult *sr);
949
950
951/**
952 * Synchronize this download struct with its mirror
953 * on disk. Note that all internal FS-operations that change
954 * publishing structs should already call "sync" internally,
955 * so this function is likely not useful for clients.
956 *
957 * @param dc the struct to sync
958 */
959void
960GNUNET_FS_download_sync_ (struct GNUNET_FS_DownloadContext *dc);
961
962
963/**
964 * Create SUSPEND event for the given publish operation
965 * and then clean up our state (without stop signal).
966 *
967 * @param cls the `struct GNUNET_FS_PublishContext` to signal for
968 */
969void
970GNUNET_FS_publish_signal_suspend_ (void *cls);
971
972
973/**
974 * Create SUSPEND event for the given search operation
975 * and then clean up our state (without stop signal).
976 *
977 * @param cls the 'struct GNUNET_FS_SearchContext' to signal for
978 */
979void
980GNUNET_FS_search_signal_suspend_ (void *cls);
981
982
983/**
984 * Create SUSPEND event for the given download operation
985 * and then clean up our state (without stop signal).
986 *
987 * @param cls the `struct GNUNET_FS_DownloadContext` to signal for
988 */
989void
990GNUNET_FS_download_signal_suspend_ (void *cls);
991
992
993/**
994 * Create SUSPEND event for the given unindex operation
995 * and then clean up our state (without stop signal).
996 *
997 * @param cls the `struct GNUNET_FS_UnindexContext` to signal for
998 */
999void
1000GNUNET_FS_unindex_signal_suspend_ (void *cls);
1001
1002
1003/**
1004 * Function signature of the functions that can be called
1005 * to trigger suspend signals and clean-up for top-level
1006 * activities.
1007 *
1008 * @param cls closure
1009 */
1010typedef void (*SuspendSignalFunction) (void *cls);
1011
1012/**
1013 * We track all of the top-level activities of FS
1014 * so that we can signal 'suspend' on shutdown.
1015 */
1016struct TopLevelActivity
1017{
1018 /**
1019 * This is a doubly-linked list.
1020 */
1021 struct TopLevelActivity *next;
1022
1023 /**
1024 * This is a doubly-linked list.
1025 */
1026 struct TopLevelActivity *prev;
1027
1028 /**
1029 * Function to call for suspend-signalling and clean up.
1030 */
1031 SuspendSignalFunction ssf;
1032
1033 /**
1034 * Closure for 'ssf' (some struct GNUNET_FS_XXXHandle*)
1035 */
1036 void *ssf_cls;
1037};
1038
1039
1040/**
1041 * Create a top-level activity entry.
1042 *
1043 * @param h global fs handle
1044 * @param ssf suspend signal function to use
1045 * @param ssf_cls closure for @a ssf
1046 * @return fresh top-level activity handle
1047 */
1048struct TopLevelActivity *
1049GNUNET_FS_make_top (struct GNUNET_FS_Handle *h,
1050 SuspendSignalFunction ssf,
1051 void *ssf_cls);
1052
1053
1054/**
1055 * Destroy a top-level activity entry.
1056 *
1057 * @param h global fs handle
1058 * @param top top level activity entry
1059 */
1060void
1061GNUNET_FS_end_top (struct GNUNET_FS_Handle *h,
1062 struct TopLevelActivity *top);
1063
1064
1065/**
1066 * Master context for most FS operations.
1067 */
1068struct GNUNET_FS_Handle
1069{
1070 /**
1071 * Configuration to use.
1072 */
1073 const struct GNUNET_CONFIGURATION_Handle *cfg;
1074
1075 /**
1076 * Name of our client.
1077 */
1078 char *client_name;
1079
1080 /**
1081 * Function to call with updates on our progress.
1082 */
1083 GNUNET_FS_ProgressCallback upcb;
1084
1085 /**
1086 * Closure for upcb.
1087 */
1088 void *upcb_cls;
1089
1090 /**
1091 * Head of DLL of top-level activities.
1092 */
1093 struct TopLevelActivity *top_head;
1094
1095 /**
1096 * Tail of DLL of top-level activities.
1097 */
1098 struct TopLevelActivity *top_tail;
1099
1100 /**
1101 * Head of DLL of running jobs.
1102 */
1103 struct GNUNET_FS_QueueEntry *running_head;
1104
1105 /**
1106 * Tail of DLL of running jobs.
1107 */
1108 struct GNUNET_FS_QueueEntry *running_tail;
1109
1110 /**
1111 * Head of DLL of pending jobs.
1112 */
1113 struct GNUNET_FS_QueueEntry *pending_head;
1114
1115 /**
1116 * Tail of DLL of pending jobs.
1117 */
1118 struct GNUNET_FS_QueueEntry *pending_tail;
1119
1120 /**
1121 * Head of active probes.
1122 */
1123 struct GNUNET_FS_SearchResult *probes_head;
1124
1125 /**
1126 * Tail of active probes.
1127 */
1128 struct GNUNET_FS_SearchResult *probes_tail;
1129
1130 /**
1131 * Task that processes the jobs in the running and pending queues
1132 * (and moves jobs around as needed).
1133 */
1134 struct GNUNET_SCHEDULER_Task *queue_job;
1135
1136 /**
1137 * Task we use to report periodically to the application that
1138 * certain search probes (from @e probes_head) are still running.
1139 */
1140 struct GNUNET_SCHEDULER_Task *probe_ping_task;
1141
1142 /**
1143 * Average time we take for a single request to be satisfied.
1144 * FIXME: not yet calculated properly...
1145 */
1146 struct GNUNET_TIME_Relative avg_block_latency;
1147
1148 /**
1149 * How many actual downloads do we have running right now?
1150 */
1151 unsigned int active_downloads;
1152
1153 /**
1154 * How many blocks do the active downloads have?
1155 */
1156 unsigned int active_blocks;
1157
1158 /**
1159 * General flags.
1160 */
1161 enum GNUNET_FS_Flags flags;
1162
1163 /**
1164 * Maximum number of parallel downloads.
1165 */
1166 unsigned int max_parallel_downloads;
1167
1168 /**
1169 * Maximum number of parallel requests.
1170 */
1171 unsigned int max_parallel_requests;
1172};
1173
1174
1175/**
1176 * Handle for controlling a publication process.
1177 */
1178struct GNUNET_FS_PublishContext
1179{
1180 /**
1181 * Handle to the global fs context.
1182 */
1183 struct GNUNET_FS_Handle *h;
1184
1185 /**
1186 * Our top-level activity entry (if we are top-level, otherwise NULL).
1187 */
1188 struct TopLevelActivity *top;
1189
1190 /**
1191 * File-structure that is being shared.
1192 */
1193 struct GNUNET_FS_FileInformation *fi;
1194
1195 /**
1196 * Namespace that we are publishing in, NULL if we have no namespace.
1197 */
1198 struct GNUNET_CRYPTO_EcdsaPrivateKey *ns;
1199
1200 /**
1201 * ID of the content in the namespace, NULL if we have no namespace.
1202 */
1203 char *nid;
1204
1205 /**
1206 * ID for future updates, NULL if we have no namespace or no updates.
1207 */
1208 char *nuid;
1209
1210 /**
1211 * Filename used for serializing information about this operation
1212 * (should be determined using 'mktemp').
1213 */
1214 char *serialization;
1215
1216 /**
1217 * Our own message queue for the FS service; only briefly used when
1218 * we start to index a file, otherwise NULL.
1219 */
1220 struct GNUNET_MQ_Handle *mq;
1221
1222 /**
1223 * Current position in the file-tree for the upload.
1224 */
1225 struct GNUNET_FS_FileInformation *fi_pos;
1226
1227 /**
1228 * Non-null if we are currently hashing a file.
1229 */
1230 struct GNUNET_CRYPTO_FileHashContext *fhc;
1231
1232 /**
1233 * Connection to the datastore service.
1234 */
1235 struct GNUNET_DATASTORE_Handle *dsh;
1236
1237 /**
1238 * Queue entry for reservation/unreservation.
1239 */
1240 struct GNUNET_DATASTORE_QueueEntry *qre;
1241
1242 /**
1243 * Context for SKS publishing operation that is part of this publishing operation
1244 * (NULL if not active).
1245 */
1246 struct GNUNET_FS_PublishSksContext *sks_pc;
1247
1248 /**
1249 * Context for KSK publishing operation that is part of this publishing operation
1250 * (NULL if not active).
1251 */
1252 struct GNUNET_FS_PublishKskContext *ksk_pc;
1253
1254 /**
1255 * ID of the task performing the upload. NO_TASK if the upload has
1256 * completed.
1257 */
1258 struct GNUNET_SCHEDULER_Task *upload_task;
1259
1260 /**
1261 * Storage space to reserve for the operation.
1262 */
1263 uint64_t reserve_space;
1264
1265 /**
1266 * Overall number of entries to reserve for the
1267 * publish operation.
1268 */
1269 uint32_t reserve_entries;
1270
1271 /**
1272 * Options for publishing.
1273 */
1274 enum GNUNET_FS_PublishOptions options;
1275
1276 /**
1277 * Space reservation ID with datastore service
1278 * for this upload.
1279 */
1280 int rid;
1281
1282 /**
1283 * Set to #GNUNET_YES if we were able to publish any block.
1284 * (and thus unindexing on error might make sense).
1285 */
1286 int any_done;
1287
1288 /**
1289 * Set to #GNUNET_YES if all processing has completed.
1290 */
1291 int all_done;
1292
1293 /**
1294 * Flag set to #GNUNET_YES if the next callback from
1295 * #GNUNET_FS_file_information_inspect should be skipped because it
1296 * is for the directory which was already processed with the parent.
1297 */
1298 int skip_next_fi_callback;
1299};
1300
1301
1302/**
1303 * Phases of unindex processing (state machine).
1304 */
1305enum UnindexState
1306{
1307 /**
1308 * We're currently hashing the file.
1309 */
1310 UNINDEX_STATE_HASHING = 0,
1311
1312 /**
1313 * We're telling the datastore to delete
1314 * the respective DBlocks and IBlocks.
1315 */
1316 UNINDEX_STATE_DS_REMOVE = 1,
1317
1318 /**
1319 * Find out which keywords apply.
1320 */
1321 UNINDEX_STATE_EXTRACT_KEYWORDS = 2,
1322
1323 /**
1324 * We're telling the datastore to remove KBlocks.
1325 */
1326 UNINDEX_STATE_DS_REMOVE_KBLOCKS = 3,
1327
1328 /**
1329 * We're notifying the FS service about
1330 * the unindexing.
1331 */
1332 UNINDEX_STATE_FS_NOTIFY = 4,
1333
1334 /**
1335 * We're done.
1336 */
1337 UNINDEX_STATE_COMPLETE = 5,
1338
1339 /**
1340 * We've encountered a fatal error.
1341 */
1342 UNINDEX_STATE_ERROR = 6
1343};
1344
1345
1346/**
1347 * Handle for controlling an unindexing operation.
1348 */
1349struct GNUNET_FS_UnindexContext
1350{
1351 /**
1352 * The content hash key of the last block we processed, will in the
1353 * end be set to the CHK from the URI. Used to remove the KBlocks.
1354 */
1355 struct ContentHashKey chk;
1356
1357 /**
1358 * Global FS context.
1359 */
1360 struct GNUNET_FS_Handle *h;
1361
1362 /**
1363 * Our top-level activity entry.
1364 */
1365 struct TopLevelActivity *top;
1366
1367 /**
1368 * Directory scanner to find keywords (KBlock removal).
1369 */
1370 struct GNUNET_FS_DirScanner *dscan;
1371
1372 /**
1373 * Keywords found (telling us which KBlocks to remove).
1374 */
1375 struct GNUNET_FS_Uri *ksk_uri;
1376
1377 /**
1378 * Current offset in KSK removal.
1379 */
1380 uint32_t ksk_offset;
1381
1382 /**
1383 * Name of the file that we are unindexing.
1384 */
1385 char *filename;
1386
1387 /**
1388 * Short name under which we are serializing the state of this operation.
1389 */
1390 char *serialization;
1391
1392 /**
1393 * Connection to the FS service, only valid during the
1394 * #UNINDEX_STATE_FS_NOTIFY phase.
1395 */
1396 struct GNUNET_MQ_Handle *mq;
1397
1398 /**
1399 * Connection to the datastore service, only valid during the
1400 * UNINDEX_STATE_DS_NOTIFY phase.
1401 */
1402 struct GNUNET_DATASTORE_Handle *dsh;
1403
1404 /**
1405 * Pointer kept for the client.
1406 */
1407 void *client_info;
1408
1409 /**
1410 * Merkle-ish tree encoder context.
1411 */
1412 struct GNUNET_FS_TreeEncoder *tc;
1413
1414 /**
1415 * Handle used to read the file.
1416 */
1417 struct GNUNET_DISK_FileHandle *fh;
1418
1419 /**
1420 * Handle to datastore 'get_key' operation issued for
1421 * obtaining KBlocks.
1422 */
1423 struct GNUNET_DATASTORE_QueueEntry *dqe;
1424
1425 /**
1426 * Current key for decrypting UBLocks from 'get_key' operation.
1427 */
1428 struct GNUNET_HashCode ukey;
1429
1430 /**
1431 * Current query of 'get_key' operation.
1432 */
1433 struct GNUNET_HashCode uquery;
1434
1435 /**
1436 * Error message, NULL on success.
1437 */
1438 char *emsg;
1439
1440 /**
1441 * Context for hashing of the file.
1442 */
1443 struct GNUNET_CRYPTO_FileHashContext *fhc;
1444
1445 /**
1446 * Overall size of the file.
1447 */
1448 uint64_t file_size;
1449
1450 /**
1451 * When did we start?
1452 */
1453 struct GNUNET_TIME_Absolute start_time;
1454
1455 /**
1456 * Hash of the file's contents (once computed).
1457 */
1458 struct GNUNET_HashCode file_id;
1459
1460 /**
1461 * Current operatinonal phase.
1462 */
1463 enum UnindexState state;
1464};
1465
1466
1467/**
1468 * Information we keep for each keyword in a keyword search.
1469 */
1470struct SearchRequestEntry
1471{
1472 /**
1473 * Hash of the public key, also known as the query.
1474 */
1475 struct GNUNET_HashCode uquery;
1476
1477 /**
1478 * Derived public key, hashes to 'uquery'.
1479 */
1480 struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
1481
1482 /**
1483 * The original keyword, used to derive the
1484 * key (for decrypting the UBlock).
1485 */
1486 char *keyword;
1487
1488 /**
1489 * Map that contains a "struct GNUNET_FS_SearchResult" for each result that
1490 * was found under this keyword. Note that the entries will point
1491 * to the same locations as those in the master result map (in
1492 * "struct GNUNET_FS_SearchContext"), so they should not be freed.
1493 * The key for each entry is the XOR of the key and query in the CHK
1494 * URI (as a unique identifier for the search result).
1495 */
1496 struct GNUNET_CONTAINER_MultiHashMap *results;
1497
1498 /**
1499 * Is this keyword a mandatory keyword
1500 * (started with '+')?
1501 */
1502 int mandatory;
1503};
1504
1505
1506/**
1507 * Handle for controlling a search.
1508 */
1509struct GNUNET_FS_SearchContext
1510{
1511 /**
1512 * Handle to the global FS context.
1513 */
1514 struct GNUNET_FS_Handle *h;
1515
1516 /**
1517 * Our top-level activity entry (if we are top-level, otherwise NULL).
1518 */
1519 struct TopLevelActivity *top;
1520
1521 /**
1522 * List of keywords that we're looking for.
1523 */
1524 struct GNUNET_FS_Uri *uri;
1525
1526 /**
1527 * For update-searches, link to the search result that triggered
1528 * the update search; otherwise NULL.
1529 */
1530 struct GNUNET_FS_SearchResult *psearch_result;
1531
1532 /**
1533 * Connection to the FS service.
1534 */
1535 struct GNUNET_MQ_Handle *mq;
1536
1537 /**
1538 * Pointer we keep for the client.
1539 */
1540 void *client_info;
1541
1542 /**
1543 * Name of the file on disk we use for persistence.
1544 */
1545 char *serialization;
1546
1547 /**
1548 * Error message (non-NULL if this operation failed).
1549 */
1550 char *emsg;
1551
1552 /**
1553 * Map that contains a `struct GNUNET_FS_SearchResult` for each result that
1554 * was found in the search. The key for each entry is the XOR of
1555 * the key and query in the CHK URI (as a unique identifier for the
1556 * search result).
1557 */
1558 struct GNUNET_CONTAINER_MultiHashMap *master_result_map;
1559
1560 /**
1561 * Per-keyword information for a keyword search. This array will
1562 * have exactly as many entries as there were keywords.
1563 */
1564 struct SearchRequestEntry *requests;
1565
1566 /**
1567 * When did we start?
1568 */
1569 struct GNUNET_TIME_Absolute start_time;
1570
1571 /**
1572 * How long to wait before we try to reconnect to FS service?
1573 */
1574 struct GNUNET_TIME_Relative reconnect_backoff;
1575
1576 /**
1577 * ID of a task that is using this struct and that must be cancelled
1578 * when the search is being stopped (if not
1579 * NULL). Used for the task that adds some
1580 * artificial delay when trying to reconnect to the FS service.
1581 */
1582 struct GNUNET_SCHEDULER_Task *task;
1583
1584 /**
1585 * Anonymity level for the search.
1586 */
1587 uint32_t anonymity;
1588
1589 /**
1590 * Number of mandatory keywords in this query.
1591 */
1592 uint32_t mandatory_count;
1593
1594 /**
1595 * Options for the search.
1596 */
1597 enum GNUNET_FS_SearchOptions options;
1598};
1599
1600
1601/**
1602 * FSM for possible states a block can go through. The typical
1603 * order of progression is linear through the states, alternatives
1604 * are documented in the comments.
1605 */
1606enum BlockRequestState
1607{
1608 /**
1609 * Initial state, block has only been allocated (since it is
1610 * relevant to the overall download request).
1611 */
1612 BRS_INIT = 0,
1613
1614 /**
1615 * We've checked the block on the path down the tree, and the
1616 * content on disk did match the desired CHK, but not all
1617 * the way down, so at the bottom some blocks will still
1618 * need to be reconstructed).
1619 */
1620 BRS_RECONSTRUCT_DOWN = 1,
1621
1622 /**
1623 * We've calculated the CHK bottom-up based on the meta data.
1624 * This may work, but if it did we have to write the meta data to
1625 * disk at the end (and we still need to check against the
1626 * CHK set on top).
1627 */
1628 BRS_RECONSTRUCT_META_UP = 2,
1629
1630 /**
1631 * We've calculated the CHK bottom-up based on what we have on
1632 * disk, which may not be what the desired CHK is. If the
1633 * reconstructed CHKs match whatever comes from above, we're
1634 * done with the respective subtree.
1635 */
1636 BRS_RECONSTRUCT_UP = 3,
1637
1638 /**
1639 * We've determined the real, desired CHK for this block
1640 * (full tree reconstruction failed), request is now pending.
1641 * If the CHK that bubbled up through reconstruction did match
1642 * the top-level request, the state machine for the subtree
1643 * would have moved to BRS_DOWNLOAD_UP.
1644 */
1645 BRS_CHK_SET = 4,
1646
1647 /**
1648 * We've successfully downloaded this block, but the children
1649 * still need to be either downloaded or verified (download
1650 * request propagates down). If the download fails, the
1651 * state machine for this block may move to
1652 * BRS_DOWNLOAD_ERROR instead.
1653 */
1654 BRS_DOWNLOAD_DOWN = 5,
1655
1656 /**
1657 * This block and all of its children have been downloaded
1658 * successfully (full completion propagates up).
1659 */
1660 BRS_DOWNLOAD_UP = 6,
1661
1662 /**
1663 * We got a block back that matched the query but did not hash to
1664 * the key (malicious publisher or hash collision); this block
1665 * can never be downloaded (error propagates up).
1666 */
1667 BRS_ERROR = 7
1668};
1669
1670
1671/**
1672 * Information about an active download request.
1673 */
1674struct DownloadRequest
1675{
1676 /**
1677 * Parent in the CHK-tree.
1678 */
1679 struct DownloadRequest *parent;
1680
1681 /**
1682 * Array (!) of child-requests, or NULL for the bottom of the tree.
1683 */
1684 struct DownloadRequest **children;
1685
1686 /**
1687 * CHK for the request for this block (set during reconstruction
1688 * to what we have on disk, later to what we want to have).
1689 */
1690 struct ContentHashKey chk;
1691
1692 /**
1693 * Offset of the corresponding block. Specifically, first (!) byte of
1694 * the first DBLOCK in the subtree induced by block represented by
1695 * this request.
1696 */
1697 uint64_t offset;
1698
1699 /**
1700 * Number of entries in @e children array.
1701 */
1702 unsigned int num_children;
1703
1704 /**
1705 * Depth of the corresponding block in the tree. 0==DBLOCKs.
1706 */
1707 unsigned int depth;
1708
1709 /**
1710 * Offset of the CHK for this block in the parent block
1711 */
1712 unsigned int chk_idx;
1713
1714 /**
1715 * State in the FSM.
1716 */
1717 enum BlockRequestState state;
1718};
1719
1720
1721/**
1722 * (recursively) free download request structure
1723 *
1724 * @param dr request to free
1725 */
1726void
1727GNUNET_FS_free_download_request_ (struct DownloadRequest *dr);
1728
1729
1730/**
1731 * Stop the ping task for this search result.
1732 *
1733 * @param sr result to start pinging for.
1734 */
1735void
1736GNUNET_FS_stop_probe_ping_task_ (struct GNUNET_FS_SearchResult *sr);
1737
1738
1739/**
1740 * Context for controlling a download.
1741 */
1742struct GNUNET_FS_DownloadContext
1743{
1744 /**
1745 * Global FS context.
1746 */
1747 struct GNUNET_FS_Handle *h;
1748
1749 /**
1750 * Our top-level activity entry (if we are top-level, otherwise NULL).
1751 */
1752 struct TopLevelActivity *top;
1753
1754 /**
1755 * Connection to the FS service.
1756 */
1757 struct GNUNET_MQ_Handle *mq;
1758
1759 /**
1760 * Parent download (used when downloading files
1761 * in directories).
1762 */
1763 struct GNUNET_FS_DownloadContext *parent;
1764
1765 /**
1766 * Associated search (used when downloading files
1767 * based on search results), or NULL for none.
1768 */
1769 struct GNUNET_FS_SearchResult *search;
1770
1771 /**
1772 * Head of list of child downloads.
1773 */
1774 struct GNUNET_FS_DownloadContext *child_head;
1775
1776 /**
1777 * Tail of list of child downloads.
1778 */
1779 struct GNUNET_FS_DownloadContext *child_tail;
1780
1781 /**
1782 * Previous download belonging to the same parent.
1783 */
1784 struct GNUNET_FS_DownloadContext *prev;
1785
1786 /**
1787 * Next download belonging to the same parent.
1788 */
1789 struct GNUNET_FS_DownloadContext *next;
1790
1791 /**
1792 * Context kept for the client.
1793 */
1794 void *client_info;
1795
1796 /**
1797 * URI that identifies the file that we are downloading.
1798 */
1799 struct GNUNET_FS_Uri *uri;
1800
1801 /**
1802 * Known meta-data for the file (can be NULL).
1803 */
1804 struct GNUNET_CONTAINER_MetaData *meta;
1805
1806 /**
1807 * Error message, NULL if we're doing OK.
1808 */
1809 char *emsg;
1810
1811 /**
1812 * Random portion of filename we use for syncing state of this
1813 * download.
1814 */
1815 char *serialization;
1816
1817 /**
1818 * Where are we writing the data (name of the
1819 * file, can be NULL!).
1820 */
1821 char *filename;
1822
1823 /**
1824 * Where are we writing the data temporarily (name of the
1825 * file, can be NULL!); used if we do not have a permanent
1826 * name and we are a directory and we do a recursive download.
1827 */
1828 char *temp_filename;
1829
1830 /**
1831 * Our entry in the job queue.
1832 */
1833 struct GNUNET_FS_QueueEntry *job_queue;
1834
1835 /**
1836 * Tree encoder used for the reconstruction.
1837 */
1838 struct GNUNET_FS_TreeEncoder *te;
1839
1840 /**
1841 * File handle for reading data from an existing file
1842 * (to pass to tree encoder).
1843 */
1844 struct GNUNET_DISK_FileHandle *rfh;
1845
1846 /**
1847 * Map of active requests (those waiting for a response). The key
1848 * is the hash of the encryped block (aka query).
1849 */
1850 struct GNUNET_CONTAINER_MultiHashMap *active;
1851
1852 /**
1853 * Top-level download request.
1854 */
1855 struct DownloadRequest *top_request;
1856
1857 /**
1858 * Identity of the peer having the content, or all-zeros
1859 * if we don't know of such a peer.
1860 */
1861 struct GNUNET_PeerIdentity target;
1862
1863 /**
1864 * ID of a task that is using this struct and that must be cancelled
1865 * when the download is being stopped (if not
1866 * NULL). Used for the task that adds some
1867 * artificial delay when trying to reconnect to the FS service or
1868 * the task processing incrementally the data on disk, or the
1869 * task requesting blocks, etc.
1870 */
1871 struct GNUNET_SCHEDULER_Task *task;
1872
1873 /**
1874 * What is the first offset that we're interested
1875 * in?
1876 */
1877 uint64_t offset;
1878
1879 /**
1880 * How many bytes starting from offset are desired?
1881 * This is NOT the overall length of the file!
1882 */
1883 uint64_t length;
1884
1885 /**
1886 * How many bytes have we already received within
1887 * the specified range (DBlocks only).
1888 */
1889 uint64_t completed;
1890
1891 /**
1892 * What was the size of the file on disk that we're downloading
1893 * before we started? Used to detect if there is a point in
1894 * checking an existing block on disk for matching the desired
1895 * content. 0 if the file did not exist already.
1896 */
1897 uint64_t old_file_size;
1898
1899 /**
1900 * Time download was started.
1901 */
1902 struct GNUNET_TIME_Absolute start_time;
1903
1904 /**
1905 * How long to wait before we try to reconnect to FS service?
1906 */
1907 struct GNUNET_TIME_Relative reconnect_backoff;
1908
1909 /**
1910 * Desired level of anonymity.
1911 */
1912 uint32_t anonymity;
1913
1914 /**
1915 * The depth of the file-tree.
1916 */
1917 unsigned int treedepth;
1918
1919 /**
1920 * Options for the download.
1921 */
1922 enum GNUNET_FS_DownloadOptions options;
1923
1924 /**
1925 * Flag set upon transitive completion (includes child downloads).
1926 * This flag is only set to #GNUNET_YES for directories where all
1927 * child-downloads have also completed (and signalled completion).
1928 */
1929 int has_finished;
1930
1931 /**
1932 * Are we ready to issue requests (reconstructions are finished)?
1933 */
1934 int issue_requests;
1935};
1936
1937
1938#endif
1939
1940/* end of fs_api.h */
diff --git a/src/fs/fs_directory.c b/src/fs/fs_directory.c
deleted file mode 100644
index eb7aac5bb..000000000
--- a/src/fs/fs_directory.c
+++ /dev/null
@@ -1,675 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003, 2004, 2006, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_directory.c
23 * @brief Helper functions for building directories.
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - modify directory builder API to support incremental
28 * generation of directories (to allow directories that
29 * would not fit into memory to be created)
30 * - modify directory processor API to support incremental
31 * iteration over FULL directories (without missing entries)
32 * to allow access to directories that do not fit entirely
33 * into memory
34 */
35#include "platform.h"
36#include "gnunet_fs_service.h"
37#include "fs_api.h"
38
39/**
40 * String that is used to indicate that a file
41 * is a GNUnet directory.
42 */
43#define GNUNET_DIRECTORY_MAGIC "\211GND\r\n\032\n"
44
45
46/**
47 * Does the meta-data claim that this is a directory?
48 * Checks if the mime-type is that of a GNUnet directory.
49 *
50 * @return #GNUNET_YES if it is, #GNUNET_NO if it is not, #GNUNET_SYSERR if
51 * we have no mime-type information (treat as #GNUNET_NO)
52 */
53int
54GNUNET_FS_meta_data_test_for_directory (const struct
55 GNUNET_CONTAINER_MetaData *md)
56{
57 char *mime;
58 int ret;
59
60 if (NULL == md)
61 return GNUNET_SYSERR;
62 mime = GNUNET_CONTAINER_meta_data_get_by_type (md,
63 EXTRACTOR_METATYPE_MIMETYPE);
64 if (NULL == mime)
65 return GNUNET_SYSERR;
66 ret = (0 == strcasecmp (mime, GNUNET_FS_DIRECTORY_MIME)) ? GNUNET_YES :
67 GNUNET_NO;
68 GNUNET_free (mime);
69 return ret;
70}
71
72
73/**
74 * Set the MIMETYPE information for the given
75 * metadata to "application/gnunet-directory".
76 *
77 * @param md metadata to add mimetype to
78 */
79void
80GNUNET_FS_meta_data_make_directory (struct GNUNET_CONTAINER_MetaData *md)
81{
82 char *mime;
83
84 mime =
85 GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_METATYPE_MIMETYPE);
86 if (mime != NULL)
87 {
88 GNUNET_break (0 == strcmp (mime, GNUNET_FS_DIRECTORY_MIME));
89 GNUNET_free (mime);
90 return;
91 }
92 GNUNET_CONTAINER_meta_data_insert (md, "<gnunet>",
93 EXTRACTOR_METATYPE_MIMETYPE,
94 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
95 GNUNET_FS_DIRECTORY_MIME,
96 strlen (GNUNET_FS_DIRECTORY_MIME) + 1);
97}
98
99
100/**
101 * Closure for 'find_full_data'.
102 */
103struct GetFullDataClosure
104{
105 /**
106 * Extracted binary meta data.
107 */
108 void *data;
109
110 /**
111 * Number of bytes stored in data.
112 */
113 size_t size;
114};
115
116
117/**
118 * Type of a function that libextractor calls for each
119 * meta data item found.
120 *
121 * @param cls closure (user-defined)
122 * @param plugin_name name of the plugin that produced this value;
123 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
124 * used in the main libextractor library and yielding
125 * meta data).
126 * @param type libextractor-type describing the meta data
127 * @param format basic format information about data
128 * @param data_mime_type mime-type of data (not of the original file);
129 * can be NULL (if mime-type is not known)
130 * @param data actual meta-data found
131 * @param data_len number of bytes in data
132 * @return 0 to continue extracting, 1 to abort
133 */
134static int
135find_full_data (void *cls, const char *plugin_name,
136 enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format,
137 const char *data_mime_type, const char *data, size_t data_len)
138{
139 struct GetFullDataClosure *gfdc = cls;
140
141 if (type == EXTRACTOR_METATYPE_GNUNET_FULL_DATA)
142 {
143 gfdc->size = data_len;
144 if (data_len > 0)
145 {
146 gfdc->data = GNUNET_malloc (data_len);
147 GNUNET_memcpy (gfdc->data, data, data_len);
148 }
149 return 1;
150 }
151 return 0;
152}
153
154
155/**
156 * Iterate over all entries in a directory. Note that directories
157 * are structured such that it is possible to iterate over the
158 * individual blocks as well as over the entire directory. Thus
159 * a client can call this function on the buffer in the
160 * GNUNET_FS_ProgressCallback. Also, directories can optionally
161 * include the contents of (small) files embedded in the directory
162 * itself; for those files, the processor may be given the
163 * contents of the file directly by this function.
164 * <p>
165 *
166 * Note that this function maybe called on parts of directories. Thus
167 * parser errors should not be reported _at all_ (with GNUNET_break).
168 * Still, if some entries can be recovered despite these parsing
169 * errors, the function should try to do this.
170 *
171 * @param size number of bytes in data
172 * @param data pointer to the beginning of the directory
173 * @param offset offset of data in the directory
174 * @param dep function to call on each entry
175 * @param dep_cls closure for @a dep
176 * @return #GNUNET_OK if this could be a block in a directory,
177 * #GNUNET_NO if this could be part of a directory (but not 100% OK)
178 * #GNUNET_SYSERR if @a data does not represent a directory
179 */
180int
181GNUNET_FS_directory_list_contents (size_t size,
182 const void *data,
183 uint64_t offset,
184 GNUNET_FS_DirectoryEntryProcessor dep,
185 void *dep_cls)
186{
187 struct GetFullDataClosure full_data;
188 const char *cdata = data;
189 char *emsg;
190 uint64_t pos;
191 uint64_t align;
192 uint32_t mdSize;
193 uint64_t epos;
194 struct GNUNET_FS_Uri *uri;
195 struct GNUNET_CONTAINER_MetaData *md;
196 char *filename;
197
198 if ((offset == 0) &&
199 ((size < 8 + sizeof(uint32_t)) ||
200 (0 != memcmp (cdata,
201 GNUNET_FS_DIRECTORY_MAGIC,
202 8))))
203 return GNUNET_SYSERR;
204 pos = offset;
205 if (offset == 0)
206 {
207 GNUNET_memcpy (&mdSize,
208 &cdata[8],
209 sizeof(uint32_t));
210 mdSize = ntohl (mdSize);
211 if (mdSize > size - 8 - sizeof(uint32_t))
212 {
213 /* invalid size */
214 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
215 _ ("MAGIC mismatch. This is not a GNUnet directory.\n"));
216 return GNUNET_SYSERR;
217 }
218 md = GNUNET_CONTAINER_meta_data_deserialize (&cdata[8 + sizeof(uint32_t)],
219 mdSize);
220 if (md == NULL)
221 {
222 GNUNET_break (0);
223 return GNUNET_SYSERR; /* malformed ! */
224 }
225 dep (dep_cls,
226 NULL,
227 NULL,
228 md,
229 0,
230 NULL);
231 GNUNET_CONTAINER_meta_data_destroy (md);
232 pos = 8 + sizeof(uint32_t) + mdSize;
233 }
234 while (pos < size)
235 {
236 /* find end of URI */
237 if (cdata[pos] == '\0')
238 {
239 /* URI is never empty, must be end of block,
240 * skip to next alignment */
241 align = ((pos / DBLOCK_SIZE) + 1) * DBLOCK_SIZE;
242 if (align == pos)
243 {
244 /* if we were already aligned, still skip a block! */
245 align += DBLOCK_SIZE;
246 }
247 pos = align;
248 if (pos >= size)
249 {
250 /* malformed - or partial download... */
251 break;
252 }
253 }
254 epos = pos;
255 while ((epos < size) && (cdata[epos] != '\0'))
256 epos++;
257 if (epos >= size)
258 return GNUNET_NO; /* malformed - or partial download */
259
260 uri = GNUNET_FS_uri_parse (&cdata[pos], &emsg);
261 pos = epos + 1;
262 if (NULL == uri)
263 {
264 GNUNET_free (emsg);
265 pos--; /* go back to '\0' to force going to next alignment */
266 continue;
267 }
268 if (GNUNET_FS_uri_test_ksk (uri))
269 {
270 GNUNET_FS_uri_destroy (uri);
271 GNUNET_break (0);
272 return GNUNET_NO; /* illegal in directory! */
273 }
274
275 GNUNET_memcpy (&mdSize,
276 &cdata[pos],
277 sizeof(uint32_t));
278 mdSize = ntohl (mdSize);
279 pos += sizeof(uint32_t);
280 if (pos + mdSize > size)
281 {
282 GNUNET_FS_uri_destroy (uri);
283 return GNUNET_NO; /* malformed - or partial download */
284 }
285
286 md = GNUNET_CONTAINER_meta_data_deserialize (&cdata[pos],
287 mdSize);
288 if (NULL == md)
289 {
290 GNUNET_FS_uri_destroy (uri);
291 GNUNET_break (0);
292 return GNUNET_NO; /* malformed ! */
293 }
294 pos += mdSize;
295 filename =
296 GNUNET_CONTAINER_meta_data_get_by_type (md,
297 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
298 full_data.size = 0;
299 full_data.data = NULL;
300 GNUNET_CONTAINER_meta_data_iterate (md,
301 &find_full_data,
302 &full_data);
303 if (NULL != dep)
304 {
305 dep (dep_cls,
306 filename,
307 uri,
308 md,
309 full_data.size,
310 full_data.data);
311 }
312 GNUNET_free (full_data.data);
313 GNUNET_free (filename);
314 GNUNET_CONTAINER_meta_data_destroy (md);
315 GNUNET_FS_uri_destroy (uri);
316 }
317 return GNUNET_OK;
318}
319
320
321/**
322 * Entries in the directory (builder).
323 */
324struct BuilderEntry
325{
326 /**
327 * This is a linked list.
328 */
329 struct BuilderEntry *next;
330
331 /**
332 * Length of this entry.
333 */
334 size_t len;
335};
336
337/**
338 * Internal state of a directory builder.
339 */
340struct GNUNET_FS_DirectoryBuilder
341{
342 /**
343 * Meta-data for the directory itself.
344 */
345 struct GNUNET_CONTAINER_MetaData *meta;
346
347 /**
348 * Head of linked list of entries.
349 */
350 struct BuilderEntry *head;
351
352 /**
353 * Number of entries in the directory.
354 */
355 unsigned int count;
356};
357
358
359/**
360 * Create a directory builder.
361 *
362 * @param mdir metadata for the directory
363 */
364struct GNUNET_FS_DirectoryBuilder *
365GNUNET_FS_directory_builder_create (const struct GNUNET_CONTAINER_MetaData
366 *mdir)
367{
368 struct GNUNET_FS_DirectoryBuilder *ret;
369
370 ret = GNUNET_new (struct GNUNET_FS_DirectoryBuilder);
371 if (mdir != NULL)
372 ret->meta = GNUNET_CONTAINER_meta_data_duplicate (mdir);
373 else
374 ret->meta = GNUNET_CONTAINER_meta_data_create ();
375 GNUNET_FS_meta_data_make_directory (ret->meta);
376 return ret;
377}
378
379
380/**
381 * Add an entry to a directory.
382 *
383 * @param bld directory to extend
384 * @param uri uri of the entry (must not be a KSK)
385 * @param md metadata of the entry
386 * @param data raw data of the entry, can be NULL, otherwise
387 * data must point to exactly the number of bytes specified
388 * by the uri which must be of type LOC or CHK
389 */
390void
391GNUNET_FS_directory_builder_add (struct GNUNET_FS_DirectoryBuilder *bld,
392 const struct GNUNET_FS_Uri *uri,
393 const struct GNUNET_CONTAINER_MetaData *md,
394 const void *data)
395{
396 struct GNUNET_FS_Uri *curi;
397 struct BuilderEntry *e;
398 uint64_t fsize;
399 uint32_t big;
400 ssize_t ret;
401 size_t mds;
402 size_t mdxs;
403 char *uris;
404 char *serialized;
405 char *sptr;
406 size_t slen;
407 struct GNUNET_CONTAINER_MetaData *meta;
408 const struct GNUNET_CONTAINER_MetaData *meta_use;
409
410 GNUNET_assert (! GNUNET_FS_uri_test_ksk (uri));
411 if (NULL != data)
412 {
413 GNUNET_assert (! GNUNET_FS_uri_test_sks (uri));
414 if (GNUNET_FS_uri_test_chk (uri))
415 {
416 fsize = GNUNET_FS_uri_chk_get_file_size (uri);
417 }
418 else
419 {
420 curi = GNUNET_FS_uri_loc_get_uri (uri);
421 GNUNET_assert (NULL != curi);
422 fsize = GNUNET_FS_uri_chk_get_file_size (curi);
423 GNUNET_FS_uri_destroy (curi);
424 }
425 }
426 else
427 {
428 fsize = 0; /* not given */
429 }
430 if (fsize > MAX_INLINE_SIZE)
431 fsize = 0; /* too large */
432 uris = GNUNET_FS_uri_to_string (uri);
433 slen = strlen (uris) + 1;
434 mds = GNUNET_CONTAINER_meta_data_get_serialized_size (md);
435 meta_use = md;
436 meta = NULL;
437 if (fsize > 0)
438 {
439 meta = GNUNET_CONTAINER_meta_data_duplicate (md);
440 GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>",
441 EXTRACTOR_METATYPE_GNUNET_FULL_DATA,
442 EXTRACTOR_METAFORMAT_BINARY, NULL, data,
443 fsize);
444 mdxs = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
445 if ((slen + sizeof(uint32_t) + mdxs - 1) / DBLOCK_SIZE ==
446 (slen + sizeof(uint32_t) + mds - 1) / DBLOCK_SIZE)
447 {
448 /* adding full data would not cause us to cross
449 * additional blocks, so add it! */
450 meta_use = meta;
451 mds = mdxs;
452 }
453 }
454
455 if (mds > GNUNET_MAX_MALLOC_CHECKED / 2)
456 mds = GNUNET_MAX_MALLOC_CHECKED / 2;
457 e = GNUNET_malloc (sizeof(struct BuilderEntry) + slen + mds
458 + sizeof(uint32_t));
459 serialized = (char *) &e[1];
460 GNUNET_memcpy (serialized, uris, slen);
461 GNUNET_free (uris);
462 sptr = &serialized[slen + sizeof(uint32_t)];
463 ret =
464 GNUNET_CONTAINER_meta_data_serialize (meta_use, &sptr, mds,
465 GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
466 if (NULL != meta)
467 GNUNET_CONTAINER_meta_data_destroy (meta);
468 if (ret == -1)
469 mds = 0;
470 else
471 mds = ret;
472 big = htonl (mds);
473 GNUNET_memcpy (&serialized[slen], &big, sizeof(uint32_t));
474 e->len = slen + sizeof(uint32_t) + mds;
475 e->next = bld->head;
476 bld->head = e;
477 bld->count++;
478}
479
480
481/**
482 * Given the start and end position of a block of
483 * data, return the end position of that data
484 * after alignment to the DBLOCK_SIZE.
485 */
486static size_t
487do_align (size_t start_position, size_t end_position)
488{
489 size_t align;
490
491 align = (end_position / DBLOCK_SIZE) * DBLOCK_SIZE;
492 if ((start_position < align) && (end_position > align))
493 return align + end_position - start_position;
494 return end_position;
495}
496
497
498/**
499 * Compute a permutation of the blocks to
500 * minimize the cost of alignment. Greedy packer.
501 *
502 * @param start starting position for the first block
503 * @param count size of the two arrays
504 * @param sizes the sizes of the individual blocks
505 * @param perm the permutation of the blocks (updated)
506 */
507static void
508block_align (size_t start, unsigned int count, const size_t *sizes,
509 unsigned int *perm)
510{
511 unsigned int i;
512 unsigned int j;
513 unsigned int tmp;
514 unsigned int best;
515 ssize_t badness;
516 size_t cpos;
517 size_t cend;
518 ssize_t cbad;
519 unsigned int cval;
520
521 cpos = start;
522 for (i = 0; i < count; i++)
523 {
524 start = cpos;
525 badness = 0x7FFFFFFF;
526 best = -1;
527 for (j = i; j < count; j++)
528 {
529 cval = perm[j];
530 cend = cpos + sizes[cval];
531 if (cpos % DBLOCK_SIZE == 0)
532 {
533 /* prefer placing the largest blocks first */
534 cbad = -(cend % DBLOCK_SIZE);
535 }
536 else
537 {
538 if (cpos / DBLOCK_SIZE == cend / DBLOCK_SIZE)
539 {
540 /* Data fits into the same block! Prefer small left-overs! */
541 cbad = DBLOCK_SIZE - cend % DBLOCK_SIZE;
542 }
543 else
544 {
545 /* Would have to waste space to re-align, add big factor, this
546 * case is a real loss (proportional to space wasted)! */
547 cbad = DBLOCK_SIZE * (DBLOCK_SIZE - cpos % DBLOCK_SIZE);
548 }
549 }
550 if (cbad < badness)
551 {
552 best = j;
553 badness = cbad;
554 }
555 }
556 GNUNET_assert (best != -1);
557 tmp = perm[i];
558 perm[i] = perm[best];
559 perm[best] = tmp;
560 cpos += sizes[perm[i]];
561 cpos = do_align (start, cpos);
562 }
563}
564
565
566/**
567 * Finish building the directory. Frees the
568 * builder context and returns the directory
569 * in-memory.
570 *
571 * @param bld directory to finish
572 * @param rsize set to the number of bytes needed
573 * @param rdata set to the encoded directory
574 * @return #GNUNET_OK on success
575 */
576int
577GNUNET_FS_directory_builder_finish (struct GNUNET_FS_DirectoryBuilder *bld,
578 size_t *rsize,
579 void **rdata)
580{
581 char *data;
582 char *sptr;
583 size_t *sizes;
584 unsigned int *perm;
585 unsigned int i;
586 unsigned int j;
587 struct BuilderEntry *pos;
588 struct BuilderEntry **bes;
589 size_t size;
590 size_t psize;
591 size_t off;
592 ssize_t ret;
593 uint32_t big;
594
595 size = strlen (GNUNET_DIRECTORY_MAGIC) + sizeof(uint32_t);
596 size += GNUNET_CONTAINER_meta_data_get_serialized_size (bld->meta);
597 sizes = NULL;
598 perm = NULL;
599 bes = NULL;
600 if (0 < bld->count)
601 {
602 sizes = GNUNET_new_array (bld->count,
603 size_t);
604 perm = GNUNET_new_array (bld->count,
605 unsigned int);
606 bes = GNUNET_new_array (bld->count,
607 struct BuilderEntry *);
608 pos = bld->head;
609 for (i = 0; i < bld->count; i++)
610 {
611 perm[i] = i;
612 bes[i] = pos;
613 sizes[i] = pos->len;
614 pos = pos->next;
615 }
616 block_align (size, bld->count, sizes, perm);
617 /* compute final size with alignment */
618 for (i = 0; i < bld->count; i++)
619 {
620 psize = size;
621 size += sizes[perm[i]];
622 size = do_align (psize, size);
623 }
624 }
625 *rsize = size;
626 data = GNUNET_malloc_large (size);
627 if (data == NULL)
628 {
629 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
630 "malloc");
631 *rsize = 0;
632 *rdata = NULL;
633 GNUNET_free (sizes);
634 GNUNET_free (perm);
635 GNUNET_free (bes);
636 return GNUNET_SYSERR;
637 }
638 *rdata = data;
639 GNUNET_memcpy (data,
640 GNUNET_DIRECTORY_MAGIC,
641 strlen (GNUNET_DIRECTORY_MAGIC));
642 off = strlen (GNUNET_DIRECTORY_MAGIC);
643
644 sptr = &data[off + sizeof(uint32_t)];
645 ret =
646 GNUNET_CONTAINER_meta_data_serialize (bld->meta,
647 &sptr,
648 size - off - sizeof(uint32_t),
649 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
650 GNUNET_assert (ret != -1);
651 big = htonl (ret);
652 GNUNET_memcpy (&data[off],
653 &big,
654 sizeof(uint32_t));
655 off += sizeof(uint32_t) + ret;
656 for (j = 0; j < bld->count; j++)
657 {
658 i = perm[j];
659 psize = off;
660 off += sizes[i];
661 off = do_align (psize, off);
662 GNUNET_memcpy (&data[off - sizes[i]], &(bes[i])[1], sizes[i]);
663 GNUNET_free (bes[i]);
664 }
665 GNUNET_free (sizes);
666 GNUNET_free (perm);
667 GNUNET_free (bes);
668 GNUNET_assert (off == size);
669 GNUNET_CONTAINER_meta_data_destroy (bld->meta);
670 GNUNET_free (bld);
671 return GNUNET_OK;
672}
673
674
675/* end of fs_directory.c */
diff --git a/src/fs/fs_dirmetascan.c b/src/fs/fs_dirmetascan.c
deleted file mode 100644
index cb50182f9..000000000
--- a/src/fs/fs_dirmetascan.c
+++ /dev/null
@@ -1,503 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2005-2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_dirmetascan.c
23 * @brief code to asynchronously build a 'struct GNUNET_FS_ShareTreeItem'
24 * from an on-disk directory for publishing; use the 'gnunet-helper-fs-publish'.
25 * @author LRN
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_fs_service.h"
30#include "gnunet_scheduler_lib.h"
31#include <pthread.h>
32
33
34/**
35 * An opaque structure a pointer to which is returned to the
36 * caller to be used to control the scanner.
37 */
38struct GNUNET_FS_DirScanner
39{
40 /**
41 * Helper process.
42 */
43 struct GNUNET_HELPER_Handle *helper;
44
45 /**
46 * Expanded filename (as given by the scan initiator).
47 * The scanner thread stores a copy here, and frees it when it finishes.
48 */
49 char *filename_expanded;
50
51 /**
52 * Second argument to helper process.
53 */
54 char *ex_arg;
55
56 /**
57 * The function that will be called every time there's a progress
58 * message.
59 */
60 GNUNET_FS_DirScannerProgressCallback progress_callback;
61
62 /**
63 * A closure for progress_callback.
64 */
65 void *progress_callback_cls;
66
67 /**
68 * After the scan is finished, it will contain a pointer to the
69 * top-level directory entry in the directory tree built by the
70 * scanner.
71 */
72 struct GNUNET_FS_ShareTreeItem *toplevel;
73
74 /**
75 * Current position during processing.
76 */
77 struct GNUNET_FS_ShareTreeItem *pos;
78
79 /**
80 * Task scheduled when we are done.
81 */
82 struct GNUNET_SCHEDULER_Task *stop_task;
83
84 /**
85 * Arguments for helper.
86 */
87 char *args[4];
88};
89
90
91/**
92 * Abort the scan. Must not be called from within the progress_callback
93 * function.
94 *
95 * @param ds directory scanner structure
96 */
97void
98GNUNET_FS_directory_scan_abort (struct GNUNET_FS_DirScanner *ds)
99{
100 /* terminate helper */
101 if (NULL != ds->helper)
102 GNUNET_HELPER_stop (ds->helper, GNUNET_NO);
103
104 /* free resources */
105 if (NULL != ds->toplevel)
106 GNUNET_FS_share_tree_free (ds->toplevel);
107 if (NULL != ds->stop_task)
108 GNUNET_SCHEDULER_cancel (ds->stop_task);
109 GNUNET_free (ds->ex_arg);
110 GNUNET_free (ds->filename_expanded);
111 GNUNET_free (ds);
112}
113
114
115/**
116 * Obtain the result of the scan after the scan has signalled
117 * completion. Must not be called prior to completion. The 'ds' is
118 * freed as part of this call.
119 *
120 * @param ds directory scanner structure
121 * @return the results of the scan (a directory tree)
122 */
123struct GNUNET_FS_ShareTreeItem *
124GNUNET_FS_directory_scan_get_result (struct GNUNET_FS_DirScanner *ds)
125{
126 struct GNUNET_FS_ShareTreeItem *result;
127
128 /* check that we're actually done */
129 GNUNET_assert (NULL == ds->helper);
130 /* preserve result */
131 result = ds->toplevel;
132 ds->toplevel = NULL;
133 GNUNET_FS_directory_scan_abort (ds);
134 return result;
135}
136
137
138/**
139 * Move in the directory from the given position to the next file
140 * in DFS traversal.
141 *
142 * @param pos current position
143 * @return next file, NULL for none
144 */
145static struct GNUNET_FS_ShareTreeItem *
146advance (struct GNUNET_FS_ShareTreeItem *pos)
147{
148 int moved;
149
150 GNUNET_assert (NULL != pos);
151 moved = 0; /* must not terminate, even on file, otherwise "normal" */
152 while ((pos->is_directory == GNUNET_YES) || (0 == moved))
153 {
154 if ((moved != -1) && (NULL != pos->children_head))
155 {
156 pos = pos->children_head;
157 moved = 1; /* can terminate if file */
158 continue;
159 }
160 if (NULL != pos->next)
161 {
162 pos = pos->next;
163 moved = 1; /* can terminate if file */
164 continue;
165 }
166 if (NULL != pos->parent)
167 {
168 pos = pos->parent;
169 moved = -1; /* force move to 'next' or 'parent' */
170 continue;
171 }
172 /* no more options, end of traversal */
173 return NULL;
174 }
175 return pos;
176}
177
178
179/**
180 * Add another child node to the tree.
181 *
182 * @param parent parent of the child, NULL for top level
183 * @param filename name of the file or directory
184 * @param is_directory GNUNET_YES for directories
185 * @return new entry that was just created
186 */
187static struct GNUNET_FS_ShareTreeItem *
188expand_tree (struct GNUNET_FS_ShareTreeItem *parent,
189 const char *filename,
190 int is_directory)
191{
192 struct GNUNET_FS_ShareTreeItem *chld;
193 size_t slen;
194
195 chld = GNUNET_new (struct GNUNET_FS_ShareTreeItem);
196 chld->parent = parent;
197 chld->filename = GNUNET_strdup (filename);
198 GNUNET_asprintf (&chld->short_filename,
199 "%s%s",
200 GNUNET_STRINGS_get_short_name (filename),
201 is_directory == GNUNET_YES ? "/" : "");
202 /* make sure we do not end with '//' */
203 slen = strlen (chld->short_filename);
204 if ((slen >= 2) && (chld->short_filename[slen - 1] == '/') &&
205 (chld->short_filename[slen - 2] == '/'))
206 chld->short_filename[slen - 1] = '\0';
207 chld->is_directory = is_directory;
208 if (NULL != parent)
209 GNUNET_CONTAINER_DLL_insert (parent->children_head,
210 parent->children_tail,
211 chld);
212 return chld;
213}
214
215
216/**
217 * Task run last to shut everything down.
218 *
219 * @param cls the 'struct GNUNET_FS_DirScanner'
220 */
221static void
222finish_scan (void *cls)
223{
224 struct GNUNET_FS_DirScanner *ds = cls;
225
226 ds->stop_task = NULL;
227 if (NULL != ds->helper)
228 {
229 GNUNET_HELPER_stop (ds->helper, GNUNET_NO);
230 ds->helper = NULL;
231 }
232 ds->progress_callback (ds->progress_callback_cls,
233 NULL,
234 GNUNET_SYSERR,
235 GNUNET_FS_DIRSCANNER_FINISHED);
236}
237
238
239/**
240 * Called every time there is data to read from the scanner.
241 * Calls the scanner progress handler.
242 *
243 * @param cls the closure (directory scanner object)
244 * @param msg message from the helper process
245 * @return #GNUNET_OK on success,
246 * #GNUNET_NO to stop further processing (no error)
247 * #GNUNET_SYSERR to stop further processing with error
248 */
249static int
250process_helper_msgs (void *cls, const struct GNUNET_MessageHeader *msg)
251{
252 struct GNUNET_FS_DirScanner *ds = cls;
253 const char *filename;
254 size_t left;
255
256#if 0
257 fprintf (stderr,
258 "DMS parses %u-byte message of type %u\n",
259 (unsigned int) ntohs (msg->size),
260 (unsigned int) ntohs (msg->type));
261#endif
262 left = ntohs (msg->size) - sizeof(struct GNUNET_MessageHeader);
263 filename = (const char *) &msg[1];
264 switch (ntohs (msg->type))
265 {
266 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_FILE:
267 if (filename[left - 1] != '\0')
268 {
269 GNUNET_break (0);
270 break;
271 }
272 ds->progress_callback (ds->progress_callback_cls,
273 filename,
274 GNUNET_NO,
275 GNUNET_FS_DIRSCANNER_FILE_START);
276 if (NULL == ds->toplevel)
277 {
278 ds->toplevel = expand_tree (ds->pos, filename, GNUNET_NO);
279 }
280 else
281 {
282 GNUNET_assert (NULL != ds->pos);
283 (void) expand_tree (ds->pos, filename, GNUNET_NO);
284 }
285 return GNUNET_OK;
286
287 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_DIRECTORY:
288 if (filename[left - 1] != '\0')
289 {
290 GNUNET_break (0);
291 break;
292 }
293 if (0 == strcmp ("..", filename))
294 {
295 if (NULL == ds->pos)
296 {
297 GNUNET_break (0);
298 break;
299 }
300 ds->pos = ds->pos->parent;
301 return GNUNET_OK;
302 }
303 ds->progress_callback (ds->progress_callback_cls,
304 filename,
305 GNUNET_YES,
306 GNUNET_FS_DIRSCANNER_FILE_START);
307 ds->pos = expand_tree (ds->pos, filename, GNUNET_YES);
308 if (NULL == ds->toplevel)
309 ds->toplevel = ds->pos;
310 return GNUNET_OK;
311
312 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR:
313 break;
314
315 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_SKIP_FILE:
316 if ('\0' != filename[left - 1])
317 break;
318 ds->progress_callback (ds->progress_callback_cls,
319 filename,
320 GNUNET_SYSERR,
321 GNUNET_FS_DIRSCANNER_FILE_IGNORED);
322 return GNUNET_OK;
323
324 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_COUNTING_DONE:
325 if (0 != left)
326 {
327 GNUNET_break (0);
328 break;
329 }
330 if (NULL == ds->toplevel)
331 break;
332 ds->progress_callback (ds->progress_callback_cls,
333 NULL,
334 GNUNET_SYSERR,
335 GNUNET_FS_DIRSCANNER_ALL_COUNTED);
336 ds->pos = ds->toplevel;
337 if (GNUNET_YES == ds->pos->is_directory)
338 ds->pos = advance (ds->pos);
339 return GNUNET_OK;
340
341 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_META_DATA: {
342 size_t nlen;
343 const char *end;
344
345 if (NULL == ds->pos)
346 {
347 GNUNET_break (0);
348 break;
349 }
350 end = memchr (filename, 0, left);
351 if (NULL == end)
352 {
353 GNUNET_break (0);
354 break;
355 }
356 end++;
357 nlen = end - filename;
358 left -= nlen;
359 if (0 != strcmp (filename, ds->pos->filename))
360 {
361 GNUNET_break (0);
362 break;
363 }
364 ds->progress_callback (ds->progress_callback_cls,
365 filename,
366 GNUNET_YES,
367 GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED);
368 if (0 < left)
369 {
370 ds->pos->meta = GNUNET_CONTAINER_meta_data_deserialize (end, left);
371 if (NULL == ds->pos->meta)
372 {
373 GNUNET_break (0);
374 break;
375 }
376 /* having full filenames is too dangerous; always make sure we clean them up */
377 GNUNET_CONTAINER_meta_data_delete (ds->pos->meta,
378 EXTRACTOR_METATYPE_FILENAME,
379 NULL,
380 0);
381 /* instead, put in our 'safer' original filename */
382 GNUNET_CONTAINER_meta_data_insert (ds->pos->meta,
383 "<libgnunetfs>",
384 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
385 EXTRACTOR_METAFORMAT_UTF8,
386 "text/plain",
387 ds->pos->short_filename,
388 strlen (ds->pos->short_filename)
389 + 1);
390 }
391 ds->pos->ksk_uri = GNUNET_FS_uri_ksk_create_from_meta_data (
392 ds->pos->meta);
393 ds->pos = advance (ds->pos);
394 return GNUNET_OK;
395 }
396
397 case GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_FINISHED:
398 if (NULL != ds->pos)
399 {
400 GNUNET_break (0);
401 break;
402 }
403 if (0 != left)
404 {
405 GNUNET_break (0);
406 break;
407 }
408 if (NULL == ds->toplevel)
409 break;
410 ds->stop_task = GNUNET_SCHEDULER_add_now (&finish_scan, ds);
411 return GNUNET_OK;
412
413 default:
414 GNUNET_break (0);
415 break;
416 }
417 ds->progress_callback (ds->progress_callback_cls,
418 NULL,
419 GNUNET_SYSERR,
420 GNUNET_FS_DIRSCANNER_INTERNAL_ERROR);
421 return GNUNET_OK;
422}
423
424
425/**
426 * Function called if our helper process died.
427 *
428 * @param cls the 'struct GNUNET_FS_DirScanner' callback.
429 */
430static void
431helper_died_cb (void *cls)
432{
433 struct GNUNET_FS_DirScanner *ds = cls;
434
435 ds->helper = NULL;
436 if (NULL != ds->stop_task)
437 return; /* normal death, was finished */
438 ds->progress_callback (ds->progress_callback_cls,
439 NULL,
440 GNUNET_SYSERR,
441 GNUNET_FS_DIRSCANNER_INTERNAL_ERROR);
442}
443
444
445/**
446 * Start a directory scanner thread.
447 *
448 * @param filename name of the directory to scan
449 * @param disable_extractor #GNUNET_YES to not run libextractor on files (only
450 * build a tree)
451 * @param ex if not NULL, must be a list of extra plugins for extractor
452 * @param cb the callback to call when there are scanning progress messages
453 * @param cb_cls closure for 'cb'
454 * @return directory scanner object to be used for controlling the scanner
455 */
456struct GNUNET_FS_DirScanner *
457GNUNET_FS_directory_scan_start (const char *filename,
458 int disable_extractor,
459 const char *ex,
460 GNUNET_FS_DirScannerProgressCallback cb,
461 void *cb_cls)
462{
463 struct stat sbuf;
464 char *filename_expanded;
465 struct GNUNET_FS_DirScanner *ds;
466
467 if (0 != stat (filename, &sbuf))
468 return NULL;
469 filename_expanded = GNUNET_STRINGS_filename_expand (filename);
470 if (NULL == filename_expanded)
471 return NULL;
472 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
473 "Starting to scan directory `%s'\n",
474 filename_expanded);
475 ds = GNUNET_new (struct GNUNET_FS_DirScanner);
476 ds->progress_callback = cb;
477 ds->progress_callback_cls = cb_cls;
478 ds->filename_expanded = filename_expanded;
479 if (disable_extractor)
480 ds->ex_arg = GNUNET_strdup ("-");
481 else
482 ds->ex_arg = (NULL != ex) ? GNUNET_strdup (ex) : NULL;
483 ds->args[0] = "gnunet-helper-fs-publish";
484 ds->args[1] = ds->filename_expanded;
485 ds->args[2] = ds->ex_arg;
486 ds->args[3] = NULL;
487 ds->helper = GNUNET_HELPER_start (GNUNET_NO,
488 "gnunet-helper-fs-publish",
489 ds->args,
490 &process_helper_msgs,
491 &helper_died_cb,
492 ds);
493 if (NULL == ds->helper)
494 {
495 GNUNET_free (filename_expanded);
496 GNUNET_free (ds);
497 return NULL;
498 }
499 return ds;
500}
501
502
503/* end of fs_dirmetascan.c */
diff --git a/src/fs/fs_download.c b/src/fs/fs_download.c
deleted file mode 100644
index 5c98d224a..000000000
--- a/src/fs/fs_download.c
+++ /dev/null
@@ -1,2418 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/fs_download.c
22 * @brief download methods
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_constants.h"
27#include "gnunet_fs_service.h"
28#include "fs_api.h"
29#include "fs_tree.h"
30
31
32/**
33 * Determine if the given download (options and meta data) should cause
34 * use to try to do a recursive download.
35 */
36static int
37is_recursive_download (struct GNUNET_FS_DownloadContext *dc)
38{
39 return (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) &&
40 ((GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ||
41 ((NULL == dc->meta) &&
42 ((NULL == dc->filename) ||
43 ((strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
44 (NULL != strstr (dc->filename + strlen (dc->filename)
45 - strlen (GNUNET_FS_DIRECTORY_EXT),
46 GNUNET_FS_DIRECTORY_EXT))))));
47}
48
49
50/**
51 * We're storing the IBLOCKS after the DBLOCKS on disk (so that we
52 * only have to truncate the file once we're done).
53 *
54 * Given the offset of a block (with respect to the DBLOCKS) and its
55 * depth, return the offset where we would store this block in the
56 * file.
57 *
58 * @param fsize overall file size
59 * @param off offset of the block in the file
60 * @param depth depth of the block in the tree, 0 for DBLOCK
61 * @return off for DBLOCKS (depth == treedepth),
62 * otherwise an offset past the end
63 * of the file that does not overlap
64 * with the range for any other block
65 */
66static uint64_t
67compute_disk_offset (uint64_t fsize, uint64_t off, unsigned int depth)
68{
69 unsigned int i;
70 uint64_t lsize; /* what is the size of all IBlocks for depth "i"? */
71 uint64_t loff; /* where do IBlocks for depth "i" start? */
72 unsigned int ioff; /* which IBlock corresponds to "off" at depth "i"? */
73
74 if (0 == depth)
75 return off;
76 /* first IBlocks start at the end of file, rounded up
77 * to full DBLOCK_SIZE */
78 loff = ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * DBLOCK_SIZE;
79 lsize =
80 ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * sizeof(struct ContentHashKey);
81 GNUNET_assert (0 == (off % DBLOCK_SIZE));
82 ioff = (off / DBLOCK_SIZE);
83 for (i = 1; i < depth; i++)
84 {
85 loff += lsize;
86 lsize = (lsize + CHK_PER_INODE - 1) / CHK_PER_INODE;
87 GNUNET_assert (lsize > 0);
88 GNUNET_assert (0 == (ioff % CHK_PER_INODE));
89 ioff /= CHK_PER_INODE;
90 }
91 return loff + ioff * sizeof(struct ContentHashKey);
92}
93
94
95/**
96 * Fill in all of the generic fields for a download event and call the
97 * callback.
98 *
99 * @param pi structure to fill in
100 * @param dc overall download context
101 */
102void
103GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
104 struct GNUNET_FS_DownloadContext *dc)
105{
106 pi->value.download.dc = dc;
107 pi->value.download.cctx = dc->client_info;
108 pi->value.download.pctx =
109 (NULL == dc->parent) ? NULL : dc->parent->client_info;
110 pi->value.download.sctx =
111 (NULL == dc->search) ? NULL : dc->search->client_info;
112 pi->value.download.uri = dc->uri;
113 pi->value.download.filename = dc->filename;
114 pi->value.download.size = dc->length;
115 /* FIXME: Fix duration calculation to account for pauses */
116 pi->value.download.duration =
117 GNUNET_TIME_absolute_get_duration (dc->start_time);
118 pi->value.download.completed = dc->completed;
119 pi->value.download.anonymity = dc->anonymity;
120 pi->value.download.eta =
121 GNUNET_TIME_calculate_eta (dc->start_time, dc->completed, dc->length);
122 pi->value.download.is_active = (NULL == dc->mq) ? GNUNET_NO : GNUNET_YES;
123 pi->fsh = dc->h;
124 if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
125 dc->client_info = dc->h->upcb (dc->h->upcb_cls, pi);
126 else
127 dc->client_info = GNUNET_FS_search_probe_progress_ (NULL, pi);
128}
129
130
131/**
132 * Closure for iterator processing results.
133 */
134struct ProcessResultClosure
135{
136 /**
137 * Hash of data.
138 */
139 struct GNUNET_HashCode query;
140
141 /**
142 * Data found in P2P network.
143 */
144 const void *data;
145
146 /**
147 * Our download context.
148 */
149 struct GNUNET_FS_DownloadContext *dc;
150
151 /**
152 * When did we last transmit the request?
153 */
154 struct GNUNET_TIME_Absolute last_transmission;
155
156 /**
157 * Number of bytes in data.
158 */
159 size_t size;
160
161 /**
162 * Type of data.
163 */
164 enum GNUNET_BLOCK_Type type;
165
166 /**
167 * Flag to indicate if this block should be stored on disk.
168 */
169 int do_store;
170
171 /**
172 * how much respect did we offer to get this reply?
173 */
174 uint32_t respect_offered;
175
176 /**
177 * how often did we transmit the query?
178 */
179 uint32_t num_transmissions;
180};
181
182
183/**
184 * Iterator over entries in the pending requests in the 'active' map for the
185 * reply that we just got.
186 *
187 * @param cls closure (our 'struct ProcessResultClosure')
188 * @param key query for the given value / request
189 * @param value value in the hash map (a 'struct DownloadRequest')
190 * @return #GNUNET_YES (we should continue to iterate); unless serious error
191 */
192static int
193process_result_with_request (void *cls,
194 const struct GNUNET_HashCode *key,
195 void *value);
196
197
198/**
199 * We've found a matching block without downloading it.
200 * Encrypt it and pass it to our "receive" function as
201 * if we had received it from the network.
202 *
203 * @param dc download in question
204 * @param chk request this relates to
205 * @param dr request details
206 * @param block plaintext data matching request
207 * @param len number of bytes in block
208 * @param do_store should we still store the block on disk?
209 * @return GNUNET_OK on success
210 */
211static int
212encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc,
213 const struct ContentHashKey *chk,
214 struct DownloadRequest *dr,
215 const char *block,
216 size_t len,
217 int do_store)
218{
219 struct ProcessResultClosure prc;
220 char enc[len];
221 struct GNUNET_CRYPTO_SymmetricSessionKey sk;
222 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
223 struct GNUNET_HashCode query;
224
225 GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv);
226 if (-1 == GNUNET_CRYPTO_symmetric_encrypt (block, len, &sk, &iv, enc))
227 {
228 GNUNET_break (0);
229 return GNUNET_SYSERR;
230 }
231 GNUNET_CRYPTO_hash (enc, len, &query);
232 if (0 != memcmp (&query, &chk->query, sizeof(struct GNUNET_HashCode)))
233 {
234 GNUNET_break_op (0);
235 return GNUNET_SYSERR;
236 }
237 GNUNET_log (
238 GNUNET_ERROR_TYPE_DEBUG,
239 "Matching %u byte block for `%s' at offset %llu already present, no need for download!\n",
240 (unsigned int) len,
241 dc->filename,
242 (unsigned long long) dr->offset);
243 /* already got it! */
244 prc.dc = dc;
245 prc.data = enc;
246 prc.size = len;
247 prc.type = (0 == dr->depth) ? GNUNET_BLOCK_TYPE_FS_DBLOCK
248 : GNUNET_BLOCK_TYPE_FS_IBLOCK;
249 prc.query = chk->query;
250 prc.do_store = do_store;
251 prc.last_transmission = GNUNET_TIME_UNIT_FOREVER_ABS;
252 process_result_with_request (&prc, &chk->key, dr);
253 return GNUNET_OK;
254}
255
256
257/**
258 * We've lost our connection with the FS service.
259 * Re-establish it and re-transmit all of our
260 * pending requests.
261 *
262 * @param dc download context that is having trouble
263 */
264static void
265try_reconnect (struct GNUNET_FS_DownloadContext *dc);
266
267
268/**
269 * We found an entry in a directory. Check if the respective child
270 * already exists and if not create the respective child download.
271 *
272 * @param cls the parent download
273 * @param filename name of the file in the directory
274 * @param uri URI of the file (CHK or LOC)
275 * @param meta meta data of the file
276 * @param length number of bytes in data
277 * @param data contents of the file (or NULL if they were not inlined)
278 */
279static void
280trigger_recursive_download (void *cls,
281 const char *filename,
282 const struct GNUNET_FS_Uri *uri,
283 const struct GNUNET_CONTAINER_MetaData *meta,
284 size_t length,
285 const void *data);
286
287
288/**
289 * We're done downloading a directory. Open the file and
290 * trigger all of the (remaining) child downloads.
291 *
292 * @param dc context of download that just completed
293 */
294static void
295full_recursive_download (struct GNUNET_FS_DownloadContext *dc)
296{
297 size_t size;
298 uint64_t size64;
299 void *data;
300 struct GNUNET_DISK_FileHandle *h;
301 struct GNUNET_DISK_MapHandle *m;
302
303 size64 = GNUNET_FS_uri_chk_get_file_size (dc->uri);
304 size = (size_t) size64;
305 if (size64 != (uint64_t) size)
306 {
307 GNUNET_log (
308 GNUNET_ERROR_TYPE_ERROR,
309 _ (
310 "Recursive downloads of directories larger than 4 GB are not supported on 32-bit systems\n"));
311 return;
312 }
313 if (NULL != dc->filename)
314 {
315 h = GNUNET_DISK_file_open (dc->filename,
316 GNUNET_DISK_OPEN_READ,
317 GNUNET_DISK_PERM_NONE);
318 }
319 else
320 {
321 GNUNET_assert (NULL != dc->temp_filename);
322 h = GNUNET_DISK_file_open (dc->temp_filename,
323 GNUNET_DISK_OPEN_READ,
324 GNUNET_DISK_PERM_NONE);
325 }
326 if (NULL == h)
327 return; /* oops */
328 data = GNUNET_DISK_file_map (h, &m, GNUNET_DISK_MAP_TYPE_READ, size);
329 if (NULL == data)
330 {
331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
332 _ ("Directory too large for system address space\n"));
333 }
334 else
335 {
336 if (GNUNET_OK !=
337 GNUNET_FS_directory_list_contents (size,
338 data,
339 0,
340 &trigger_recursive_download,
341 dc))
342 {
343 GNUNET_log (
344 GNUNET_ERROR_TYPE_WARNING,
345 _ (
346 "Failed to access full directory contents of `%s' for recursive download\n"),
347 dc->filename);
348 }
349 GNUNET_DISK_file_unmap (m);
350 }
351 GNUNET_DISK_file_close (h);
352 if (NULL == dc->filename)
353 {
354 if (0 != unlink (dc->temp_filename))
355 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
356 "unlink",
357 dc->temp_filename);
358 GNUNET_free (dc->temp_filename);
359 dc->temp_filename = NULL;
360 }
361}
362
363
364/**
365 * Check if all child-downloads have completed (or trigger them if
366 * necessary) and once we're completely done, signal completion (and
367 * possibly recurse to parent). This function MUST be called when the
368 * download of a file itself is done or when the download of a file is
369 * done and then later a direct child download has completed (and
370 * hence this download may complete itself).
371 *
372 * @param dc download to check for completion of children
373 */
374static void
375check_completed (struct GNUNET_FS_DownloadContext *dc)
376{
377 struct GNUNET_FS_ProgressInfo pi;
378 struct GNUNET_FS_DownloadContext *pos;
379
380 /* first, check if we need to download children */
381 if (is_recursive_download (dc))
382 full_recursive_download (dc);
383 /* then, check if children are done already */
384 for (pos = dc->child_head; NULL != pos; pos = pos->next)
385 {
386 if ((NULL == pos->emsg) && (pos->completed < pos->length))
387 return; /* not done yet */
388 if ((NULL != pos->child_head) && (pos->has_finished != GNUNET_YES))
389 return; /* not transitively done yet */
390 }
391 /* All of our children are done, so mark this download done */
392 dc->has_finished = GNUNET_YES;
393 if (NULL != dc->job_queue)
394 {
395 GNUNET_FS_dequeue_ (dc->job_queue);
396 dc->job_queue = NULL;
397 }
398 if (NULL != dc->task)
399 {
400 GNUNET_SCHEDULER_cancel (dc->task);
401 dc->task = NULL;
402 }
403 if (NULL != dc->rfh)
404 {
405 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
406 dc->rfh = NULL;
407 }
408 GNUNET_FS_download_sync_ (dc);
409
410 /* signal completion */
411 pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
412 GNUNET_FS_download_make_status_ (&pi, dc);
413
414 /* let parent know */
415 if (NULL != dc->parent)
416 check_completed (dc->parent);
417}
418
419
420/**
421 * We got a block of plaintext data (from the meta data).
422 * Try it for upward reconstruction of the data. On success,
423 * the top-level block will move to state BRS_DOWNLOAD_UP.
424 *
425 * @param dc context for the download
426 * @param dr download request to match against
427 * @param data plaintext data, starting from the beginning of the file
428 * @param data_len number of bytes in data
429 */
430static void
431try_match_block (struct GNUNET_FS_DownloadContext *dc,
432 struct DownloadRequest *dr,
433 const char *data,
434 size_t data_len)
435{
436 struct GNUNET_FS_ProgressInfo pi;
437 unsigned int i;
438 char enc[DBLOCK_SIZE];
439 struct ContentHashKey chks[CHK_PER_INODE];
440 struct ContentHashKey in_chk;
441 struct GNUNET_CRYPTO_SymmetricSessionKey sk;
442 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
443 size_t dlen;
444 struct DownloadRequest *drc;
445 struct GNUNET_DISK_FileHandle *fh;
446 int complete;
447 const char *fn;
448 const char *odata;
449 size_t odata_len;
450
451 odata = data;
452 odata_len = data_len;
453 if (BRS_DOWNLOAD_UP == dr->state)
454 return;
455 if (dr->depth > 0)
456 {
457 if ((dc->offset > 0) ||
458 (dc->length < GNUNET_ntohll (dc->uri->data.chk.file_length)))
459 {
460 /* NOTE: this test is not tight, but should suffice; the issue
461 here is that 'dr->num_children' may inherently only specify a
462 smaller range than what is in the original file;
463 thus, reconstruction of (some) inner blocks will fail.
464 FIXME: we might eventually want to write a tighter test to
465 maximize the circumstances under which we do succeed with
466 IBlock reconstruction. (need good tests though). */return;
467 }
468 complete = GNUNET_YES;
469 for (i = 0; i < dr->num_children; i++)
470 {
471 drc = dr->children[i];
472 try_match_block (dc, drc, data, data_len);
473 if (drc->state != BRS_RECONSTRUCT_META_UP)
474 complete = GNUNET_NO;
475 else
476 chks[i] = drc->chk;
477 }
478 if (GNUNET_YES != complete)
479 return;
480 data = (const char *) chks;
481 dlen = dr->num_children * sizeof(struct ContentHashKey);
482 }
483 else
484 {
485 if (dr->offset > data_len)
486 return; /* oops */
487 dlen = GNUNET_MIN (data_len - dr->offset, DBLOCK_SIZE);
488 }
489 GNUNET_CRYPTO_hash (&data[dr->offset], dlen, &in_chk.key);
490 GNUNET_CRYPTO_hash_to_aes_key (&in_chk.key, &sk, &iv);
491 if (-1 ==
492 GNUNET_CRYPTO_symmetric_encrypt (&data[dr->offset], dlen, &sk, &iv, enc))
493 {
494 GNUNET_break (0);
495 return;
496 }
497 GNUNET_CRYPTO_hash (enc, dlen, &in_chk.query);
498 switch (dr->state)
499 {
500 case BRS_INIT:
501 dr->chk = in_chk;
502 dr->state = BRS_RECONSTRUCT_META_UP;
503 break;
504
505 case BRS_CHK_SET:
506 if (0 != memcmp (&in_chk, &dr->chk, sizeof(struct ContentHashKey)))
507 {
508 /* other peer provided bogus meta data */
509 GNUNET_break_op (0);
510 break;
511 }
512 /* write block to disk */
513 fn = (NULL != dc->filename) ? dc->filename : dc->temp_filename;
514 if (NULL != fn)
515 {
516 fh = GNUNET_DISK_file_open (fn,
517 GNUNET_DISK_OPEN_READWRITE
518 | GNUNET_DISK_OPEN_CREATE
519 | GNUNET_DISK_OPEN_TRUNCATE,
520 GNUNET_DISK_PERM_USER_READ
521 | GNUNET_DISK_PERM_USER_WRITE
522 | GNUNET_DISK_PERM_GROUP_READ
523 | GNUNET_DISK_PERM_OTHER_READ);
524 if (NULL == fh)
525 {
526 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
527 GNUNET_asprintf (&dc->emsg,
528 _ ("Failed to open file `%s' for writing"),
529 fn);
530 GNUNET_DISK_file_close (fh);
531 dr->state = BRS_ERROR;
532 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
533 pi.value.download.specifics.error.message = dc->emsg;
534 GNUNET_FS_download_make_status_ (&pi, dc);
535 return;
536 }
537 if (data_len != GNUNET_DISK_file_write (fh, odata, odata_len))
538 {
539 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "write", fn);
540 GNUNET_asprintf (&dc->emsg,
541 _ ("Failed to open file `%s' for writing"),
542 fn);
543 GNUNET_DISK_file_close (fh);
544 dr->state = BRS_ERROR;
545 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
546 pi.value.download.specifics.error.message = dc->emsg;
547 GNUNET_FS_download_make_status_ (&pi, dc);
548 return;
549 }
550 GNUNET_DISK_file_close (fh);
551 }
552 /* signal success */
553 dr->state = BRS_DOWNLOAD_UP;
554 dc->completed = dc->length;
555 GNUNET_FS_download_sync_ (dc);
556 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
557 pi.value.download.specifics.progress.data = data;
558 pi.value.download.specifics.progress.offset = 0;
559 pi.value.download.specifics.progress.data_len = dlen;
560 pi.value.download.specifics.progress.depth = 0;
561 pi.value.download.specifics.progress.respect_offered = 0;
562 pi.value.download.specifics.progress.block_download_duration =
563 GNUNET_TIME_UNIT_ZERO;
564 GNUNET_FS_download_make_status_ (&pi, dc);
565 if ((NULL != dc->filename) &&
566 (0 != truncate (dc->filename,
567 GNUNET_ntohll (dc->uri->data.chk.file_length))))
568 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
569 "truncate",
570 dc->filename);
571 check_completed (dc);
572 break;
573
574 default:
575 /* how did we get here? */
576 GNUNET_break (0);
577 break;
578 }
579}
580
581
582/**
583 * Type of a function that libextractor calls for each
584 * meta data item found. If we find full data meta data,
585 * call 'try_match_block' on it.
586 *
587 * @param cls our 'struct GNUNET_FS_DownloadContext*'
588 * @param plugin_name name of the plugin that produced this value;
589 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
590 * used in the main libextractor library and yielding
591 * meta data).
592 * @param type libextractor-type describing the meta data
593 * @param format basic format information about data
594 * @param data_mime_type mime-type of data (not of the original file);
595 * can be NULL (if mime-type is not known)
596 * @param data actual meta-data found
597 * @param data_len number of bytes in data
598 * @return 0 to continue extracting, 1 to abort
599 */
600static int
601match_full_data (void *cls,
602 const char *plugin_name,
603 enum EXTRACTOR_MetaType type,
604 enum EXTRACTOR_MetaFormat format,
605 const char *data_mime_type,
606 const char *data,
607 size_t data_len)
608{
609 struct GNUNET_FS_DownloadContext *dc = cls;
610
611 if (EXTRACTOR_METATYPE_GNUNET_FULL_DATA != type)
612 return 0;
613 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614 "Found %u bytes of FD!\n",
615 (unsigned int) data_len);
616 if (GNUNET_FS_uri_chk_get_file_size (dc->uri) != data_len)
617 {
618 GNUNET_break_op (0);
619 return 1; /* bogus meta data */
620 }
621 try_match_block (dc, dc->top_request, data, data_len);
622 return 1;
623}
624
625
626/**
627 * Set the state of the given download request to
628 * BRS_DOWNLOAD_UP and propagate it up the tree.
629 *
630 * @param dr download request that is done
631 */
632static void
633propagate_up (struct DownloadRequest *dr)
634{
635 unsigned int i;
636
637 do
638 {
639 dr->state = BRS_DOWNLOAD_UP;
640 dr = dr->parent;
641 if (NULL == dr)
642 break;
643 for (i = 0; i < dr->num_children; i++)
644 if (dr->children[i]->state != BRS_DOWNLOAD_UP)
645 break;
646 }
647 while (i == dr->num_children);
648}
649
650
651/**
652 * Try top-down reconstruction. Before, the given request node
653 * must have the state BRS_CHK_SET. Afterwards, more nodes may
654 * have that state or advanced to BRS_DOWNLOAD_DOWN or even
655 * BRS_DOWNLOAD_UP. It is also possible to get BRS_ERROR on the
656 * top level.
657 *
658 * @param dc overall download this block belongs to
659 * @param dr block to reconstruct
660 */
661static void
662try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
663 struct DownloadRequest *dr)
664{
665 uint64_t off;
666 char block[DBLOCK_SIZE];
667 struct GNUNET_HashCode key;
668 uint64_t total;
669 size_t len;
670 unsigned int i;
671 struct DownloadRequest *drc;
672 uint64_t child_block_size;
673 const struct ContentHashKey *chks;
674 int up_done;
675
676 GNUNET_assert (NULL != dc->rfh);
677 GNUNET_assert (BRS_CHK_SET == dr->state);
678 total = GNUNET_FS_uri_chk_get_file_size (dc->uri);
679 GNUNET_assert (dr->depth < dc->treedepth);
680 len = GNUNET_FS_tree_calculate_block_size (total, dr->offset, dr->depth);
681 GNUNET_assert (len <= DBLOCK_SIZE);
682 off = compute_disk_offset (total, dr->offset, dr->depth);
683 if (dc->old_file_size < off + len)
684 return; /* failure */
685 if (off != GNUNET_DISK_file_seek (dc->rfh, off, GNUNET_DISK_SEEK_SET))
686 {
687 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "seek", dc->filename);
688 return; /* failure */
689 }
690 if (len != GNUNET_DISK_file_read (dc->rfh, block, len))
691 {
692 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", dc->filename);
693 return; /* failure */
694 }
695 GNUNET_CRYPTO_hash (block, len, &key);
696 if (0 != memcmp (&key, &dr->chk.key, sizeof(struct GNUNET_HashCode)))
697 return; /* mismatch */
698 if (GNUNET_OK !=
699 encrypt_existing_match (dc, &dr->chk, dr, block, len, GNUNET_NO))
700 {
701 /* hash matches but encrypted block does not, really bad */
702 dr->state = BRS_ERROR;
703 /* propagate up */
704 while (NULL != dr->parent)
705 {
706 dr = dr->parent;
707 dr->state = BRS_ERROR;
708 }
709 return;
710 }
711 /* block matches */
712 dr->state = BRS_DOWNLOAD_DOWN;
713
714 /* set CHKs for children */
715 up_done = GNUNET_YES;
716 chks = (const struct ContentHashKey *) block;
717 for (i = 0; i < dr->num_children; i++)
718 {
719 drc = dr->children[i];
720 GNUNET_assert (drc->offset >= dr->offset);
721 child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth);
722 GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size);
723 if (BRS_INIT == drc->state)
724 {
725 drc->state = BRS_CHK_SET;
726 drc->chk = chks[drc->chk_idx];
727 try_top_down_reconstruction (dc, drc);
728 }
729 if (BRS_DOWNLOAD_UP != drc->state)
730 up_done = GNUNET_NO; /* children not all done */
731 }
732 if (GNUNET_YES == up_done)
733 propagate_up (dr); /* children all done (or no children...) */
734}
735
736
737/**
738 * Add entries to the message queue.
739 *
740 * @param cls our download context
741 * @param key unused
742 * @param entry entry of type `struct DownloadRequest`
743 * @return #GNUNET_OK
744 */
745static int
746retry_entry (void *cls, const struct GNUNET_HashCode *key, void *entry)
747{
748 struct GNUNET_FS_DownloadContext *dc = cls;
749 struct DownloadRequest *dr = entry;
750 struct SearchMessage *sm;
751 struct GNUNET_MQ_Envelope *env;
752
753 env = GNUNET_MQ_msg (sm, GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
754 if (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY))
755 sm->options = htonl (GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY);
756 else
757 sm->options = htonl (GNUNET_FS_SEARCH_OPTION_NONE);
758 if (0 == dr->depth)
759 sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK);
760 else
761 sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK);
762 sm->anonymity_level = htonl (dc->anonymity);
763 sm->target = dc->target;
764 sm->query = dr->chk.query;
765 GNUNET_MQ_send (dc->mq, env);
766 return GNUNET_OK;
767}
768
769
770/**
771 * Schedule the download of the specified block in the tree.
772 *
773 * @param dc overall download this block belongs to
774 * @param dr request to schedule
775 */
776static void
777schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
778 struct DownloadRequest *dr)
779{
780 unsigned int i;
781
782 switch (dr->state)
783 {
784 case BRS_INIT:
785 GNUNET_assert (0);
786 break;
787
788 case BRS_RECONSTRUCT_DOWN:
789 GNUNET_assert (0);
790 break;
791
792 case BRS_RECONSTRUCT_META_UP:
793 GNUNET_assert (0);
794 break;
795
796 case BRS_RECONSTRUCT_UP:
797 GNUNET_assert (0);
798 break;
799
800 case BRS_CHK_SET:
801 /* normal case, start download */
802 break;
803
804 case BRS_DOWNLOAD_DOWN:
805 for (i = 0; i < dr->num_children; i++)
806 schedule_block_download (dc, dr->children[i]);
807 return;
808
809 case BRS_DOWNLOAD_UP:
810 /* We're done! */
811 return;
812
813 case BRS_ERROR:
814 GNUNET_break (0);
815 return;
816 }
817 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
818 "Scheduling download at offset %llu and depth %u for `%s'\n",
819 (unsigned long long) dr->offset,
820 dr->depth,
821 GNUNET_h2s (&dr->chk.query));
822 if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains_value (dc->active,
823 &dr->chk.query,
824 dr))
825 return; /* already active */
826 GNUNET_CONTAINER_multihashmap_put (dc->active,
827 &dr->chk.query,
828 dr,
829 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
830 if (NULL == dc->mq)
831 return; /* download not active */
832 retry_entry (dc, &dr->chk.query, dr);
833}
834
835
836#define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX
837
838/**
839 * We found an entry in a directory. Check if the respective child
840 * already exists and if not create the respective child download.
841 *
842 * @param cls the parent download
843 * @param filename name of the file in the directory
844 * @param uri URI of the file (CHK or LOC)
845 * @param meta meta data of the file
846 * @param length number of bytes in data
847 * @param data contents of the file (or NULL if they were not inlined)
848 */
849static void
850trigger_recursive_download (void *cls,
851 const char *filename,
852 const struct GNUNET_FS_Uri *uri,
853 const struct GNUNET_CONTAINER_MetaData *meta,
854 size_t length,
855 const void *data)
856{
857 struct GNUNET_FS_DownloadContext *dc = cls;
858 struct GNUNET_FS_DownloadContext *cpos;
859 char *temp_name;
860 char *fn;
861 char *us;
862 char *ext;
863 char *dn;
864 char *pos;
865 char *full_name;
866 char *sfn;
867
868 if (NULL == uri)
869 return; /* entry for the directory itself */
870 cpos = dc->child_head;
871 while (NULL != cpos)
872 {
873 if ((GNUNET_FS_uri_test_equal (uri, cpos->uri)) ||
874 ((NULL != filename) && (0 == strcmp (cpos->filename, filename))))
875 break;
876 cpos = cpos->next;
877 }
878 if (NULL != cpos)
879 return; /* already exists */
880 fn = NULL;
881 if (NULL == filename)
882 {
883 fn = GNUNET_FS_meta_data_suggest_filename (meta);
884 if (NULL == fn)
885 {
886 us = GNUNET_FS_uri_to_string (uri);
887 fn = GNUNET_strdup (&us[strlen (GNUNET_FS_URI_CHK_PREFIX)]);
888 GNUNET_free (us);
889 }
890 else if ('.' == fn[0])
891 {
892 ext = fn;
893 us = GNUNET_FS_uri_to_string (uri);
894 GNUNET_asprintf (&fn,
895 "%s%s",
896 &us[strlen (GNUNET_FS_URI_CHK_PREFIX)],
897 ext);
898 GNUNET_free (ext);
899 GNUNET_free (us);
900 }
901 /* change '\' to '/' (this should have happened
902 * during insertion, but malicious peers may
903 * not have done this) */
904 while (NULL != (pos = strstr (fn, "\\")))
905 *pos = '/';
906 /* remove '../' everywhere (again, well-behaved
907 * peers don't do this, but don't trust that
908 * we did not get something nasty) */
909 while (NULL != (pos = strstr (fn, "../")))
910 {
911 pos[0] = '_';
912 pos[1] = '_';
913 pos[2] = '_';
914 }
915 filename = fn;
916 }
917 if (NULL == dc->filename)
918 {
919 full_name = NULL;
920 }
921 else
922 {
923 dn = GNUNET_strdup (dc->filename);
924 GNUNET_break (
925 (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
926 (NULL != strstr (dn + strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT),
927 GNUNET_FS_DIRECTORY_EXT)));
928 sfn = GNUNET_strdup (filename);
929 while ((strlen (sfn) > 0) && ('/' == filename[strlen (sfn) - 1]))
930 sfn[strlen (sfn) - 1] = '\0';
931 if ((strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
932 (NULL != strstr (dn + strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT),
933 GNUNET_FS_DIRECTORY_EXT)))
934 dn[strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT)] = '\0';
935 if ((GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) &&
936 ((strlen (filename) < strlen (GNUNET_FS_DIRECTORY_EXT)) ||
937 (NULL == strstr (filename + strlen (filename)
938 - strlen (GNUNET_FS_DIRECTORY_EXT),
939 GNUNET_FS_DIRECTORY_EXT))))
940 {
941 GNUNET_asprintf (&full_name,
942 "%s%s%s%s",
943 dn,
944 DIR_SEPARATOR_STR,
945 sfn,
946 GNUNET_FS_DIRECTORY_EXT);
947 }
948 else
949 {
950 GNUNET_asprintf (&full_name, "%s%s%s", dn, DIR_SEPARATOR_STR, sfn);
951 }
952 GNUNET_free (sfn);
953 GNUNET_free (dn);
954 }
955 if ((NULL != full_name) &&
956 (GNUNET_OK != GNUNET_DISK_directory_create_for_file (full_name)))
957 {
958 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
959 _ (
960 "Failed to create directory for recursive download of `%s'\n"),
961 full_name);
962 GNUNET_free (full_name);
963 GNUNET_free (fn);
964 return;
965 }
966
967 temp_name = NULL;
968 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
969 "Triggering recursive download of size %llu with %u bytes MD\n",
970 (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri),
971 (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (
972 meta));
973 GNUNET_FS_download_start (dc->h,
974 uri,
975 meta,
976 full_name,
977 temp_name,
978 0,
979 GNUNET_FS_uri_chk_get_file_size (uri),
980 dc->anonymity,
981 dc->options,
982 NULL,
983 dc);
984 GNUNET_free (full_name);
985 GNUNET_free (temp_name);
986 GNUNET_free (fn);
987}
988
989
990/**
991 * (recursively) free download request structure
992 *
993 * @param dr request to free
994 */
995void
996GNUNET_FS_free_download_request_ (struct DownloadRequest *dr)
997{
998 if (NULL == dr)
999 return;
1000 for (unsigned int i = 0; i < dr->num_children; i++)
1001 GNUNET_FS_free_download_request_ (dr->children[i]);
1002 GNUNET_free (dr->children);
1003 GNUNET_free (dr);
1004}
1005
1006
1007/**
1008 * Iterator over entries in the pending requests in the 'active' map for the
1009 * reply that we just got.
1010 *
1011 * @param cls closure (our `struct ProcessResultClosure`)
1012 * @param key query for the given value / request
1013 * @param value value in the hash map (a `struct DownloadRequest`)
1014 * @return #GNUNET_YES (we should continue to iterate); unless serious error
1015 */
1016static int
1017process_result_with_request (void *cls,
1018 const struct GNUNET_HashCode *key,
1019 void *value)
1020{
1021 struct ProcessResultClosure *prc = cls;
1022 struct DownloadRequest *dr = value;
1023 struct GNUNET_FS_DownloadContext *dc = prc->dc;
1024 struct DownloadRequest *drc;
1025 struct GNUNET_DISK_FileHandle *fh = NULL;
1026 struct GNUNET_CRYPTO_SymmetricSessionKey skey;
1027 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
1028 char pt[prc->size];
1029 struct GNUNET_FS_ProgressInfo pi;
1030 uint64_t off;
1031 size_t bs;
1032 size_t app;
1033 int i;
1034 struct ContentHashKey *chkarr;
1035
1036 GNUNET_log (
1037 GNUNET_ERROR_TYPE_DEBUG,
1038 "Received %u byte block `%s' matching pending request at depth %u and offset %llu/%llu\n",
1039 (unsigned int) prc->size,
1040 GNUNET_h2s (key),
1041 dr->depth,
1042 (unsigned long long) dr->offset,
1043 (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1044 bs = GNUNET_FS_tree_calculate_block_size (GNUNET_ntohll (
1045 dc->uri->data.chk.file_length),
1046 dr->offset,
1047 dr->depth);
1048 if (prc->size != bs)
1049 {
1050 GNUNET_asprintf (
1051 &dc->emsg,
1052 _ (
1053 "Internal error or bogus download URI (expected %lu bytes at depth %u and offset %llu/%llu, got %lu bytes)"),
1054 bs,
1055 dr->depth,
1056 (unsigned long long) dr->offset,
1057 (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length),
1058 prc->size);
1059 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s\n", dc->emsg);
1060 while (NULL != dr->parent)
1061 {
1062 dr->state = BRS_ERROR;
1063 dr = dr->parent;
1064 }
1065 dr->state = BRS_ERROR;
1066 goto signal_error;
1067 }
1068
1069 (void) GNUNET_CONTAINER_multihashmap_remove (dc->active, &prc->query, dr);
1070 GNUNET_CRYPTO_hash_to_aes_key (&dr->chk.key, &skey, &iv);
1071 if (-1 ==
1072 GNUNET_CRYPTO_symmetric_decrypt (prc->data, prc->size, &skey, &iv, pt))
1073 {
1074 GNUNET_break (0);
1075 dc->emsg = GNUNET_strdup (_ ("internal error decrypting content"));
1076 goto signal_error;
1077 }
1078 off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length),
1079 dr->offset,
1080 dr->depth);
1081 /* save to disk */
1082 if ((GNUNET_YES == prc->do_store) &&
1083 ((NULL != dc->filename) || (is_recursive_download (dc))) &&
1084 ((dr->depth == dc->treedepth) ||
1085 (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES))))
1086 {
1087 fh = GNUNET_DISK_file_open (NULL != dc->filename ? dc->filename
1088 : dc->temp_filename,
1089 GNUNET_DISK_OPEN_READWRITE
1090 | GNUNET_DISK_OPEN_CREATE,
1091 GNUNET_DISK_PERM_USER_READ
1092 | GNUNET_DISK_PERM_USER_WRITE
1093 | GNUNET_DISK_PERM_GROUP_READ
1094 | GNUNET_DISK_PERM_OTHER_READ);
1095 if (NULL == fh)
1096 {
1097 GNUNET_asprintf (&dc->emsg,
1098 _ ("Download failed: could not open file `%s': %s"),
1099 dc->filename,
1100 strerror (errno));
1101 goto signal_error;
1102 }
1103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1104 "Saving decrypted block to disk at offset %llu\n",
1105 (unsigned long long) off);
1106 if ((off != GNUNET_DISK_file_seek (fh, off, GNUNET_DISK_SEEK_SET)))
1107 {
1108 GNUNET_asprintf (&dc->emsg,
1109 _ ("Failed to seek to offset %llu in file `%s': %s"),
1110 (unsigned long long) off,
1111 dc->filename,
1112 strerror (errno));
1113 goto signal_error;
1114 }
1115 if (prc->size != GNUNET_DISK_file_write (fh, pt, prc->size))
1116 {
1117 GNUNET_asprintf (
1118 &dc->emsg,
1119 _ ("Failed to write block of %u bytes at offset %llu in file `%s': %s"),
1120 (unsigned int) prc->size,
1121 (unsigned long long) off,
1122 dc->filename,
1123 strerror (errno));
1124 goto signal_error;
1125 }
1126 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
1127 fh = NULL;
1128 }
1129
1130 if (0 == dr->depth)
1131 {
1132 /* DBLOCK, update progress and try recursion if applicable */
1133 app = prc->size;
1134 if (dr->offset < dc->offset)
1135 {
1136 /* starting offset begins in the middle of pt,
1137 * do not count first bytes as progress */
1138 GNUNET_assert (app > (dc->offset - dr->offset));
1139 app -= (dc->offset - dr->offset);
1140 }
1141 if (dr->offset + prc->size > dc->offset + dc->length)
1142 {
1143 /* end of block is after relevant range,
1144 * do not count last bytes as progress */
1145 GNUNET_assert (app >
1146 (dr->offset + prc->size) - (dc->offset + dc->length));
1147 app -= (dr->offset + prc->size) - (dc->offset + dc->length);
1148 }
1149 dc->completed += app;
1150
1151 /* do recursive download if option is set and either meta data
1152 * says it is a directory or if no meta data is given AND filename
1153 * ends in '.gnd' (top-level case) */
1154 if (is_recursive_download (dc))
1155 GNUNET_FS_directory_list_contents (prc->size,
1156 pt,
1157 off,
1158 &trigger_recursive_download,
1159 dc);
1160 }
1161 GNUNET_assert (dc->completed <= dc->length);
1162 dr->state = BRS_DOWNLOAD_DOWN;
1163 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1164 pi.value.download.specifics.progress.data = pt;
1165 pi.value.download.specifics.progress.offset = dr->offset;
1166 pi.value.download.specifics.progress.data_len = prc->size;
1167 pi.value.download.specifics.progress.depth = dr->depth;
1168 pi.value.download.specifics.progress.respect_offered = prc->respect_offered;
1169 pi.value.download.specifics.progress.num_transmissions =
1170 prc->num_transmissions;
1171 if (prc->last_transmission.abs_value_us !=
1172 GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
1173 pi.value.download.specifics.progress.block_download_duration =
1174 GNUNET_TIME_absolute_get_duration (prc->last_transmission);
1175 else
1176 pi.value.download.specifics.progress.block_download_duration =
1177 GNUNET_TIME_UNIT_ZERO; /* found locally */
1178 GNUNET_FS_download_make_status_ (&pi, dc);
1179 if (0 == dr->depth)
1180 propagate_up (dr);
1181
1182 if (dc->completed == dc->length)
1183 {
1184 /* download completed, signal */
1185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1186 "Download completed, truncating file to desired length %llu\n",
1187 (unsigned long long) GNUNET_ntohll (
1188 dc->uri->data.chk.file_length));
1189 /* truncate file to size (since we store IBlocks at the end) */
1190 if (NULL != dc->filename)
1191 {
1192 if (0 != truncate (dc->filename,
1193 GNUNET_ntohll (dc->uri->data.chk.file_length)))
1194 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1195 "truncate",
1196 dc->filename);
1197 }
1198 GNUNET_assert (0 == dr->depth);
1199 check_completed (dc);
1200 }
1201 if (0 == dr->depth)
1202 {
1203 /* bottom of the tree, no child downloads possible, just sync */
1204 GNUNET_FS_download_sync_ (dc);
1205 return GNUNET_YES;
1206 }
1207
1208 GNUNET_log (
1209 GNUNET_ERROR_TYPE_DEBUG,
1210 "Triggering downloads of children (this block was at depth %u and offset %llu)\n",
1211 dr->depth,
1212 (unsigned long long) dr->offset);
1213 GNUNET_assert (0 == (prc->size % sizeof(struct ContentHashKey)));
1214 chkarr = (struct ContentHashKey *) pt;
1215 for (i = dr->num_children - 1; i >= 0; i--)
1216 {
1217 drc = dr->children[i];
1218 switch (drc->state)
1219 {
1220 case BRS_INIT:
1221 if ((drc->chk_idx + 1) * sizeof(struct ContentHashKey) > prc->size)
1222 {
1223 /* 'chkarr' does not have enough space for this chk_idx;
1224 internal error! */
1225 GNUNET_break (0);
1226 GNUNET_assert (0);
1227 dc->emsg = GNUNET_strdup (_ ("internal error decoding tree"));
1228 goto signal_error;
1229 }
1230 drc->chk = chkarr[drc->chk_idx];
1231 drc->state = BRS_CHK_SET;
1232 if (GNUNET_YES == dc->issue_requests)
1233 schedule_block_download (dc, drc);
1234 break;
1235
1236 case BRS_RECONSTRUCT_DOWN:
1237 GNUNET_assert (0);
1238 break;
1239
1240 case BRS_RECONSTRUCT_META_UP:
1241 GNUNET_assert (0);
1242 break;
1243
1244 case BRS_RECONSTRUCT_UP:
1245 GNUNET_assert (0);
1246 break;
1247
1248 case BRS_CHK_SET:
1249 GNUNET_assert (0);
1250 break;
1251
1252 case BRS_DOWNLOAD_DOWN:
1253 GNUNET_assert (0);
1254 break;
1255
1256 case BRS_DOWNLOAD_UP:
1257 GNUNET_assert (0);
1258 break;
1259
1260 case BRS_ERROR:
1261 GNUNET_assert (0);
1262 break;
1263
1264 default:
1265 GNUNET_assert (0);
1266 break;
1267 }
1268 }
1269 GNUNET_FS_download_sync_ (dc);
1270 return GNUNET_YES;
1271
1272signal_error:
1273 if (NULL != fh)
1274 GNUNET_DISK_file_close (fh);
1275 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1276 pi.value.download.specifics.error.message = dc->emsg;
1277 GNUNET_FS_download_make_status_ (&pi, dc);
1278 GNUNET_MQ_destroy (dc->mq);
1279 dc->mq = NULL;
1280 GNUNET_FS_free_download_request_ (dc->top_request);
1281 dc->top_request = NULL;
1282 if (NULL != dc->job_queue)
1283 {
1284 GNUNET_FS_dequeue_ (dc->job_queue);
1285 dc->job_queue = NULL;
1286 }
1287 GNUNET_FS_download_sync_ (dc);
1288 return GNUNET_NO;
1289}
1290
1291
1292/**
1293 * Type of a function to call when we check the PUT message
1294 * from the service.
1295 *
1296 * @param cls closure
1297 * @param msg message received
1298 */
1299static int
1300check_put (void *cls, const struct ClientPutMessage *cm)
1301{
1302 /* any varsize length is OK */
1303 return GNUNET_OK;
1304}
1305
1306
1307/**
1308 * Type of a function to call when we receive a message
1309 * from the service.
1310 *
1311 * @param cls closure
1312 * @param msg message received
1313 */
1314static void
1315handle_put (void *cls, const struct ClientPutMessage *cm)
1316{
1317 struct GNUNET_FS_DownloadContext *dc = cls;
1318 uint16_t msize = ntohs (cm->header.size) - sizeof(*cm);
1319 struct ProcessResultClosure prc;
1320
1321 prc.dc = dc;
1322 prc.data = &cm[1];
1323 prc.last_transmission = GNUNET_TIME_absolute_ntoh (cm->last_transmission);
1324 prc.size = msize;
1325 prc.type = ntohl (cm->type);
1326 prc.do_store = GNUNET_YES;
1327 prc.respect_offered = ntohl (cm->respect_offered);
1328 prc.num_transmissions = ntohl (cm->num_transmissions);
1329 GNUNET_CRYPTO_hash (prc.data, msize, &prc.query);
1330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1331 "Received result for query `%s' from FS service\n",
1332 GNUNET_h2s (&prc.query));
1333 GNUNET_CONTAINER_multihashmap_get_multiple (dc->active,
1334 &prc.query,
1335 &process_result_with_request,
1336 &prc);
1337}
1338
1339
1340/**
1341 * Generic error handler, called with the appropriate error code and
1342 * the same closure specified at the creation of the message queue.
1343 * Not every message queue implementation supports an error handler.
1344 *
1345 * @param cls closure with the `struct GNUNET_FS_DownloadContext *`
1346 * @param error error code
1347 */
1348static void
1349download_mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
1350{
1351 struct GNUNET_FS_DownloadContext *dc = cls;
1352
1353 if (NULL != dc->mq)
1354 {
1355 GNUNET_MQ_destroy (dc->mq);
1356 dc->mq = NULL;
1357 }
1358 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1359 "Transmitting download request failed, trying to reconnect\n");
1360 try_reconnect (dc);
1361}
1362
1363
1364/**
1365 * Reconnect to the FS service and transmit our queries NOW.
1366 *
1367 * @param cls our download context
1368 */
1369static void
1370do_reconnect (void *cls)
1371{
1372 struct GNUNET_FS_DownloadContext *dc = cls;
1373 struct GNUNET_MQ_MessageHandler handlers[] =
1374 { GNUNET_MQ_hd_var_size (put,
1375 GNUNET_MESSAGE_TYPE_FS_PUT,
1376 struct ClientPutMessage,
1377 dc),
1378 GNUNET_MQ_handler_end () };
1379
1380 dc->task = NULL;
1381 dc->mq = GNUNET_CLIENT_connect (dc->h->cfg,
1382 "fs",
1383 handlers,
1384 &download_mq_error_handler,
1385 dc);
1386 if (NULL == dc->mq)
1387 {
1388 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1389 "Connecting to `%s'-service failed, will try again.\n",
1390 "FS");
1391 try_reconnect (dc);
1392 return;
1393 }
1394 GNUNET_CONTAINER_multihashmap_iterate (dc->active, &retry_entry, dc);
1395}
1396
1397
1398/**
1399 * We've lost our connection with the FS service.
1400 * Re-establish it and re-transmit all of our
1401 * pending requests.
1402 *
1403 * @param dc download context that is having trouble
1404 */
1405static void
1406try_reconnect (struct GNUNET_FS_DownloadContext *dc)
1407{
1408 if (NULL != dc->mq)
1409 {
1410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1411 "Moving all requests back to pending list\n");
1412 GNUNET_MQ_destroy (dc->mq);
1413 dc->mq = NULL;
1414 }
1415 if (0 == dc->reconnect_backoff.rel_value_us)
1416 dc->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
1417 else
1418 dc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (dc->reconnect_backoff);
1419
1420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1421 "Will try to reconnect in %s\n",
1422 GNUNET_STRINGS_relative_time_to_string (dc->reconnect_backoff,
1423 GNUNET_YES));
1424 GNUNET_break (NULL != dc->job_queue);
1425 dc->task =
1426 GNUNET_SCHEDULER_add_delayed (dc->reconnect_backoff, &do_reconnect, dc);
1427}
1428
1429
1430/**
1431 * We're allowed to ask the FS service for our blocks. Start the download.
1432 *
1433 * @param cls the 'struct GNUNET_FS_DownloadContext'
1434 * @param mq handle to use for communication with FS (we must destroy it!)
1435 */
1436static void
1437activate_fs_download (void *cls)
1438{
1439 struct GNUNET_FS_DownloadContext *dc = cls;
1440 struct GNUNET_FS_ProgressInfo pi;
1441
1442 GNUNET_assert (NULL == dc->mq);
1443 GNUNET_assert (NULL != dc->active);
1444 do_reconnect (dc);
1445 if (NULL != dc->mq)
1446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download activated\n");
1447 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
1448 GNUNET_FS_download_make_status_ (&pi, dc);
1449}
1450
1451
1452/**
1453 * We must stop to ask the FS service for our blocks. Pause the download.
1454 *
1455 * @param cls the `struct GNUNET_FS_DownloadContext`
1456 */
1457static void
1458deactivate_fs_download (void *cls)
1459{
1460 struct GNUNET_FS_DownloadContext *dc = cls;
1461 struct GNUNET_FS_ProgressInfo pi;
1462
1463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download deactivated\n");
1464 if (NULL != dc->mq)
1465 {
1466 GNUNET_MQ_destroy (dc->mq);
1467 dc->mq = NULL;
1468 }
1469 pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
1470 GNUNET_FS_download_make_status_ (&pi, dc);
1471}
1472
1473
1474/**
1475 * (recursively) Create a download request structure.
1476 *
1477 * @param parent parent of the current entry
1478 * @param chk_idx index of the chk for this block in the parent block
1479 * @param depth depth of the current entry, 0 are the DBLOCKs,
1480 * top level block is 'dc->treedepth - 1'
1481 * @param dr_offset offset in the original file this block maps to
1482 * (as in, offset of the first byte of the first DBLOCK
1483 * in the subtree rooted in the returned download request tree)
1484 * @param file_start_offset desired starting offset for the download
1485 * in the original file; requesting tree should not contain
1486 * DBLOCKs prior to the file_start_offset
1487 * @param desired_length desired number of bytes the user wanted to access
1488 * (from file_start_offset). Resulting tree should not contain
1489 * DBLOCKs after file_start_offset + file_length.
1490 * @return download request tree for the given range of DBLOCKs at
1491 * the specified depth
1492 */
1493static struct DownloadRequest *
1494create_download_request (struct DownloadRequest *parent,
1495 unsigned int chk_idx,
1496 unsigned int depth,
1497 uint64_t dr_offset,
1498 uint64_t file_start_offset,
1499 uint64_t desired_length)
1500{
1501 struct DownloadRequest *dr;
1502 unsigned int i;
1503 unsigned int head_skip;
1504 uint64_t child_block_size;
1505
1506 dr = GNUNET_new (struct DownloadRequest);
1507 dr->parent = parent;
1508 dr->depth = depth;
1509 dr->offset = dr_offset;
1510 dr->chk_idx = chk_idx;
1511 if (0 == depth)
1512 return dr;
1513 child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
1514
1515 /* calculate how many blocks at this level are not interesting
1516 * from the start (rounded down), either because of the requested
1517 * file offset or because this IBlock is further along */
1518 if (dr_offset < file_start_offset)
1519 {
1520 head_skip = (file_start_offset - dr_offset) / child_block_size;
1521 }
1522 else
1523 {
1524 head_skip = 0;
1525 }
1526
1527 /* calculate index of last block at this level that is interesting (rounded up) */
1528 dr->num_children =
1529 (file_start_offset + desired_length - dr_offset) / child_block_size;
1530 if (dr->num_children * child_block_size <
1531 file_start_offset + desired_length - dr_offset)
1532 dr->num_children++; /* round up */
1533 GNUNET_assert (dr->num_children > head_skip);
1534 dr->num_children -= head_skip;
1535 if (dr->num_children > CHK_PER_INODE)
1536 dr->num_children = CHK_PER_INODE; /* cap at max */
1537 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1538 "Block at offset %llu and depth %u has %u children\n",
1539 (unsigned long long) dr_offset,
1540 depth,
1541 dr->num_children);
1542
1543 /* now we can get the total number of *interesting* children for this block */
1544
1545 /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
1546 GNUNET_assert (dr->num_children > 0);
1547
1548 dr->children = GNUNET_new_array (dr->num_children, struct DownloadRequest *);
1549 for (i = 0; i < dr->num_children; i++)
1550 {
1551 dr->children[i] =
1552 create_download_request (dr,
1553 i + head_skip,
1554 depth - 1,
1555 dr_offset + (i + head_skip) * child_block_size,
1556 file_start_offset,
1557 desired_length);
1558 }
1559 return dr;
1560}
1561
1562
1563/**
1564 * Continuation after a possible attempt to reconstruct
1565 * the current IBlock from the existing file.
1566 *
1567 * @param cls the 'struct ReconstructContext'
1568 */
1569static void
1570reconstruct_cont (void *cls)
1571{
1572 struct GNUNET_FS_DownloadContext *dc = cls;
1573
1574 /* clean up state from tree encoder */
1575 if (NULL != dc->task)
1576 {
1577 GNUNET_SCHEDULER_cancel (dc->task);
1578 dc->task = NULL;
1579 }
1580 if (NULL != dc->rfh)
1581 {
1582 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
1583 dc->rfh = NULL;
1584 }
1585 /* start "normal" download */
1586 dc->issue_requests = GNUNET_YES;
1587 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting normal download\n");
1588 schedule_block_download (dc, dc->top_request);
1589}
1590
1591
1592/**
1593 * Task requesting the next block from the tree encoder.
1594 *
1595 * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing
1596 */
1597static void
1598get_next_block (void *cls)
1599{
1600 struct GNUNET_FS_DownloadContext *dc = cls;
1601
1602 dc->task = NULL;
1603 GNUNET_FS_tree_encoder_next (dc->te);
1604}
1605
1606
1607/**
1608 * Function called asking for the current (encoded)
1609 * block to be processed. After processing the
1610 * client should either call "GNUNET_FS_tree_encode_next"
1611 * or (on error) "GNUNET_FS_tree_encode_finish".
1612 *
1613 * This function checks if the content on disk matches
1614 * the expected content based on the URI.
1615 *
1616 * @param cls closure
1617 * @param chk content hash key for the block
1618 * @param offset offset of the block
1619 * @param depth depth of the block, 0 for DBLOCK
1620 * @param type type of the block (IBLOCK or DBLOCK)
1621 * @param block the (encrypted) block
1622 * @param block_size size of block (in bytes)
1623 */
1624static void
1625reconstruct_cb (void *cls,
1626 const struct ContentHashKey *chk,
1627 uint64_t offset,
1628 unsigned int depth,
1629 enum GNUNET_BLOCK_Type type,
1630 const void *block,
1631 uint16_t block_size)
1632{
1633 struct GNUNET_FS_DownloadContext *dc = cls;
1634 struct GNUNET_FS_ProgressInfo pi;
1635 struct DownloadRequest *dr;
1636 uint64_t blen;
1637 unsigned int chld;
1638
1639 /* find corresponding request entry */
1640 dr = dc->top_request;
1641 while (dr->depth > depth)
1642 {
1643 GNUNET_assert (dr->num_children > 0);
1644 blen = GNUNET_FS_tree_compute_tree_size (dr->depth - 1);
1645 chld = (offset - dr->offset) / blen;
1646 if (chld < dr->children[0]->chk_idx)
1647 {
1648 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1649 "Block %u < %u irrelevant for our range\n",
1650 chld,
1651 dr->children[0]->chk_idx);
1652 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1653 return; /* irrelevant block */
1654 }
1655 if (chld > dr->children[dr->num_children - 1]->chk_idx)
1656 {
1657 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1658 "Block %u > %u irrelevant for our range\n",
1659 chld,
1660 dr->children[dr->num_children - 1]->chk_idx);
1661 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1662 return; /* irrelevant block */
1663 }
1664 dr = dr->children[chld - dr->children[0]->chk_idx];
1665 }
1666 GNUNET_log (
1667 GNUNET_ERROR_TYPE_DEBUG,
1668 "Matched TE block with request at offset %llu and depth %u in state %d\n",
1669 (unsigned long long) dr->offset,
1670 dr->depth,
1671 dr->state);
1672 /* FIXME: this code needs more testing and might
1673 need to handle more states... */
1674 switch (dr->state)
1675 {
1676 case BRS_INIT:
1677 break;
1678
1679 case BRS_RECONSTRUCT_DOWN:
1680 break;
1681
1682 case BRS_RECONSTRUCT_META_UP:
1683 break;
1684
1685 case BRS_RECONSTRUCT_UP:
1686 break;
1687
1688 case BRS_CHK_SET:
1689 if (0 == memcmp (chk, &dr->chk, sizeof(struct ContentHashKey)))
1690 {
1691 GNUNET_log (
1692 GNUNET_ERROR_TYPE_DEBUG,
1693 "Reconstruction succeeded, can use block at offset %llu, depth %u\n",
1694 (unsigned long long) offset,
1695 depth);
1696 /* block matches, hence tree below matches;
1697 * this request is done! */
1698 dr->state = BRS_DOWNLOAD_UP;
1699 (void) GNUNET_CONTAINER_multihashmap_remove (dc->active,
1700 &dr->chk.query,
1701 dr);
1702 /* calculate how many bytes of payload this block
1703 * corresponds to */
1704 blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1705 /* how many of those bytes are in the requested range? */
1706 blen = GNUNET_MIN (blen, dc->length + dc->offset - dr->offset);
1707 /* signal progress */
1708 dc->completed += blen;
1709 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1710 pi.value.download.specifics.progress.data = NULL;
1711 pi.value.download.specifics.progress.offset = offset;
1712 pi.value.download.specifics.progress.data_len = 0;
1713 pi.value.download.specifics.progress.depth = 0;
1714 pi.value.download.specifics.progress.respect_offered = 0;
1715 pi.value.download.specifics.progress.block_download_duration =
1716 GNUNET_TIME_UNIT_ZERO;
1717 GNUNET_FS_download_make_status_ (&pi, dc);
1718 /* FIXME: duplicated code from 'process_result_with_request - refactor */
1719 if (dc->completed == dc->length)
1720 {
1721 /* download completed, signal */
1722 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1723 "Download completed, truncating file to desired length %llu\n",
1724 (unsigned long long) GNUNET_ntohll (
1725 dc->uri->data.chk.file_length));
1726 /* truncate file to size (since we store IBlocks at the end) */
1727 if (NULL != dc->filename)
1728 {
1729 if (0 != truncate (dc->filename,
1730 GNUNET_ntohll (dc->uri->data.chk.file_length)))
1731 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1732 "truncate",
1733 dc->filename);
1734 }
1735 }
1736 }
1737 else
1738 GNUNET_log (
1739 GNUNET_ERROR_TYPE_DEBUG,
1740 "Reconstruction failed, need to download block at offset %llu, depth %u\n",
1741 (unsigned long long) offset,
1742 depth);
1743 break;
1744
1745 case BRS_DOWNLOAD_DOWN:
1746 break;
1747
1748 case BRS_DOWNLOAD_UP:
1749 break;
1750
1751 case BRS_ERROR:
1752 break;
1753
1754 default:
1755 GNUNET_assert (0);
1756 break;
1757 }
1758 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1759 if ((dr == dc->top_request) && (dr->state == BRS_DOWNLOAD_UP))
1760 check_completed (dc);
1761}
1762
1763
1764/**
1765 * Function called by the tree encoder to obtain a block of plaintext
1766 * data (for the lowest level of the tree).
1767 *
1768 * @param cls our 'struct ReconstructContext'
1769 * @param offset identifies which block to get
1770 * @param max (maximum) number of bytes to get; returning
1771 * fewer will also cause errors
1772 * @param buf where to copy the plaintext buffer
1773 * @param emsg location to store an error message (on error)
1774 * @return number of bytes copied to buf, 0 on error
1775 */
1776static size_t
1777fh_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg)
1778{
1779 struct GNUNET_FS_DownloadContext *dc = cls;
1780 struct GNUNET_DISK_FileHandle *fh = dc->rfh;
1781 ssize_t ret;
1782
1783 if (NULL != emsg)
1784 *emsg = NULL;
1785 if (offset != GNUNET_DISK_file_seek (fh, offset, GNUNET_DISK_SEEK_SET))
1786 {
1787 if (NULL != emsg)
1788 *emsg = GNUNET_strdup (strerror (errno));
1789 return 0;
1790 }
1791 ret = GNUNET_DISK_file_read (fh, buf, max);
1792 if (ret < 0)
1793 {
1794 if (NULL != emsg)
1795 *emsg = GNUNET_strdup (strerror (errno));
1796 return 0;
1797 }
1798 return ret;
1799}
1800
1801
1802/**
1803 * Task that creates the initial (top-level) download
1804 * request for the file.
1805 *
1806 * @param cls the 'struct GNUNET_FS_DownloadContext'
1807 */
1808void
1809GNUNET_FS_download_start_task_ (void *cls)
1810{
1811 struct GNUNET_FS_DownloadContext *dc = cls;
1812 struct GNUNET_FS_ProgressInfo pi;
1813 struct GNUNET_DISK_FileHandle *fh;
1814
1815 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start task running...\n");
1816 dc->task = NULL;
1817 if (0 == dc->length)
1818 {
1819 /* no bytes required! */
1820 if (NULL != dc->filename)
1821 {
1822 fh = GNUNET_DISK_file_open (dc->filename,
1823 GNUNET_DISK_OPEN_READWRITE
1824 | GNUNET_DISK_OPEN_CREATE
1825 | ((0 ==
1826 GNUNET_FS_uri_chk_get_file_size (dc->uri))
1827 ? GNUNET_DISK_OPEN_TRUNCATE
1828 : 0),
1829 GNUNET_DISK_PERM_USER_READ
1830 | GNUNET_DISK_PERM_USER_WRITE
1831 | GNUNET_DISK_PERM_GROUP_READ
1832 | GNUNET_DISK_PERM_OTHER_READ);
1833 GNUNET_DISK_file_close (fh);
1834 }
1835 GNUNET_FS_download_sync_ (dc);
1836 pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
1837 pi.value.download.specifics.start.meta = dc->meta;
1838 GNUNET_FS_download_make_status_ (&pi, dc);
1839 check_completed (dc);
1840 return;
1841 }
1842 if (NULL != dc->emsg)
1843 return;
1844 if (NULL == dc->top_request)
1845 {
1846 dc->top_request = create_download_request (NULL,
1847 0,
1848 dc->treedepth - 1,
1849 0,
1850 dc->offset,
1851 dc->length);
1852 dc->top_request->state = BRS_CHK_SET;
1853 dc->top_request->chk = (dc->uri->type == GNUNET_FS_URI_CHK)
1854 ? dc->uri->data.chk.chk
1855 : dc->uri->data.loc.fi.chk;
1856 /* signal start */
1857 GNUNET_FS_download_sync_ (dc);
1858 if (NULL != dc->search)
1859 GNUNET_FS_search_result_sync_ (dc->search);
1860 pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
1861 pi.value.download.specifics.start.meta = dc->meta;
1862 GNUNET_FS_download_make_status_ (&pi, dc);
1863 }
1864 GNUNET_FS_download_start_downloading_ (dc);
1865 /* attempt reconstruction from disk */
1866 if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename))
1867 dc->rfh = GNUNET_DISK_file_open (dc->filename,
1868 GNUNET_DISK_OPEN_READ,
1869 GNUNET_DISK_PERM_NONE);
1870 if (dc->top_request->state == BRS_CHK_SET)
1871 {
1872 if (NULL != dc->rfh)
1873 {
1874 /* first, try top-down */
1875 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1876 "Trying top-down reconstruction for `%s'\n",
1877 dc->filename);
1878 try_top_down_reconstruction (dc, dc->top_request);
1879 switch (dc->top_request->state)
1880 {
1881 case BRS_CHK_SET:
1882 break; /* normal */
1883
1884 case BRS_DOWNLOAD_DOWN:
1885 break; /* normal, some blocks already down */
1886
1887 case BRS_DOWNLOAD_UP:
1888 /* already done entirely, party! */
1889 if (NULL != dc->rfh)
1890 {
1891 /* avoid hanging on to file handle longer than
1892 * necessary */
1893 GNUNET_DISK_file_close (dc->rfh);
1894 dc->rfh = NULL;
1895 }
1896 return;
1897
1898 case BRS_ERROR:
1899 GNUNET_asprintf (&dc->emsg, _ ("Invalid URI"));
1900 GNUNET_FS_download_sync_ (dc);
1901 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1902 pi.value.download.specifics.error.message = dc->emsg;
1903 GNUNET_FS_download_make_status_ (&pi, dc);
1904 return;
1905
1906 default:
1907 GNUNET_assert (0);
1908 break;
1909 }
1910 }
1911 }
1912 /* attempt reconstruction from meta data */
1913 if ((GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
1914 (NULL != dc->meta))
1915 {
1916 GNUNET_log (
1917 GNUNET_ERROR_TYPE_DEBUG,
1918 "Trying to find embedded meta data for download of size %llu with %u bytes MD\n",
1919 (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri),
1920 (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (dc->meta));
1921 GNUNET_CONTAINER_meta_data_iterate (dc->meta, &match_full_data, dc);
1922 if (BRS_DOWNLOAD_UP == dc->top_request->state)
1923 {
1924 if (NULL != dc->rfh)
1925 {
1926 /* avoid hanging on to file handle longer than
1927 * necessary */
1928 GNUNET_DISK_file_close (dc->rfh);
1929 dc->rfh = NULL;
1930 }
1931 return; /* finished, status update was already done for us */
1932 }
1933 }
1934 if (NULL != dc->rfh)
1935 {
1936 /* finally, actually run bottom-up */
1937 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1938 "Trying bottom-up reconstruction of file `%s'\n",
1939 dc->filename);
1940 dc->te =
1941 GNUNET_FS_tree_encoder_create (dc->h,
1942 GNUNET_FS_uri_chk_get_file_size (dc->uri),
1943 dc,
1944 &fh_reader,
1945 &reconstruct_cb,
1946 NULL,
1947 &reconstruct_cont);
1948 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1949 }
1950 else
1951 {
1952 /* simple, top-level download */
1953 dc->issue_requests = GNUNET_YES;
1954 schedule_block_download (dc, dc->top_request);
1955 }
1956 if (BRS_DOWNLOAD_UP == dc->top_request->state)
1957 check_completed (dc);
1958}
1959
1960
1961/**
1962 * Create SUSPEND event for the given download operation
1963 * and then clean up our state (without stop signal).
1964 *
1965 * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for
1966 */
1967void
1968GNUNET_FS_download_signal_suspend_ (void *cls)
1969{
1970 struct GNUNET_FS_DownloadContext *dc = cls;
1971 struct GNUNET_FS_ProgressInfo pi;
1972
1973 if (NULL != dc->top)
1974 GNUNET_FS_end_top (dc->h, dc->top);
1975 while (NULL != dc->child_head)
1976 GNUNET_FS_download_signal_suspend_ (dc->child_head);
1977 if (NULL != dc->search)
1978 {
1979 dc->search->download = NULL;
1980 dc->search = NULL;
1981 }
1982 if (NULL != dc->job_queue)
1983 {
1984 GNUNET_FS_dequeue_ (dc->job_queue);
1985 dc->job_queue = NULL;
1986 }
1987 if (NULL != dc->parent)
1988 GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
1989 dc->parent->child_tail,
1990 dc);
1991 if (NULL != dc->task)
1992 {
1993 GNUNET_SCHEDULER_cancel (dc->task);
1994 dc->task = NULL;
1995 }
1996 pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
1997 GNUNET_FS_download_make_status_ (&pi, dc);
1998 if (NULL != dc->te)
1999 {
2000 GNUNET_FS_tree_encoder_finish (dc->te, NULL);
2001 dc->te = NULL;
2002 }
2003 if (NULL != dc->rfh)
2004 {
2005 GNUNET_DISK_file_close (dc->rfh);
2006 dc->rfh = NULL;
2007 }
2008 GNUNET_FS_free_download_request_ (dc->top_request);
2009 if (NULL != dc->active)
2010 {
2011 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2012 dc->active = NULL;
2013 }
2014 GNUNET_free (dc->filename);
2015 GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2016 GNUNET_FS_uri_destroy (dc->uri);
2017 GNUNET_free (dc->temp_filename);
2018 GNUNET_free (dc->serialization);
2019 GNUNET_assert (NULL == dc->job_queue);
2020 GNUNET_free (dc);
2021}
2022
2023
2024/**
2025 * Helper function to setup the download context.
2026 *
2027 * @param h handle to the file sharing subsystem
2028 * @param uri the URI of the file (determines what to download); CHK or LOC URI
2029 * @param meta known metadata for the file (can be NULL)
2030 * @param filename where to store the file, maybe NULL (then no file is
2031 * created on disk and data must be grabbed from the callbacks)
2032 * @param tempname where to store temporary file data, not used if filename is non-NULL;
2033 * can be NULL (in which case we will pick a name if needed); the temporary file
2034 * may already exist, in which case we will try to use the data that is there and
2035 * if it is not what is desired, will overwrite it
2036 * @param offset at what offset should we start the download (typically 0)
2037 * @param length how many bytes should be downloaded starting at offset
2038 * @param anonymity anonymity level to use for the download
2039 * @param options various options
2040 * @param cctx initial value for the client context for this download
2041 * @return context that can be used to control this download
2042 */
2043struct GNUNET_FS_DownloadContext *
2044create_download_context (struct GNUNET_FS_Handle *h,
2045 const struct GNUNET_FS_Uri *uri,
2046 const struct GNUNET_CONTAINER_MetaData *meta,
2047 const char *filename,
2048 const char *tempname,
2049 uint64_t offset,
2050 uint64_t length,
2051 uint32_t anonymity,
2052 enum GNUNET_FS_DownloadOptions options,
2053 void *cctx)
2054{
2055 struct GNUNET_FS_DownloadContext *dc;
2056
2057 GNUNET_assert (GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri));
2058 if ((offset + length < offset) ||
2059 (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)))
2060 {
2061 GNUNET_break (0);
2062 return NULL;
2063 }
2064 dc = GNUNET_new (struct GNUNET_FS_DownloadContext);
2065 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2066 "Starting download %p, %u bytes at offset %llu\n",
2067 dc,
2068 (unsigned int) length,
2069 (unsigned long long) offset);
2070 dc->h = h;
2071 dc->uri = GNUNET_FS_uri_dup (uri);
2072 dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
2073 dc->client_info = cctx;
2074 dc->start_time = GNUNET_TIME_absolute_get ();
2075 if (NULL != filename)
2076 {
2077 dc->filename = GNUNET_strdup (filename);
2078 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2079 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_size (filename,
2080 &dc->old_file_size,
2081 GNUNET_YES,
2082 GNUNET_YES));
2083 }
2084 if (GNUNET_FS_uri_test_loc (dc->uri))
2085 GNUNET_assert (GNUNET_OK ==
2086 GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target));
2087 dc->offset = offset;
2088 dc->length = length;
2089 dc->anonymity = anonymity;
2090 dc->options = options;
2091 dc->active =
2092 GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE),
2093 GNUNET_NO);
2094 dc->treedepth =
2095 GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri));
2096 if ((NULL == filename) && (is_recursive_download (dc)))
2097 {
2098 if (NULL != tempname)
2099 dc->temp_filename = GNUNET_strdup (tempname);
2100 else
2101 dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
2102 }
2103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2104 "Starting download `%s' of %llu bytes with tree depth %u\n",
2105 filename,
2106 (unsigned long long) length,
2107 dc->treedepth);
2108 GNUNET_assert (NULL == dc->job_queue);
2109 dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
2110 return dc;
2111}
2112
2113
2114/**
2115 * Download parts of a file. Note that this will store
2116 * the blocks at the respective offset in the given file. Also, the
2117 * download is still using the blocking of the underlying FS
2118 * encoding. As a result, the download may *write* outside of the
2119 * given boundaries (if offset and length do not match the 32k FS
2120 * block boundaries). <p>
2121 *
2122 * This function should be used to focus a download towards a
2123 * particular portion of the file (optimization), not to strictly
2124 * limit the download to exactly those bytes.
2125 *
2126 * @param h handle to the file sharing subsystem
2127 * @param uri the URI of the file (determines what to download); CHK or LOC URI
2128 * @param meta known metadata for the file (can be NULL)
2129 * @param filename where to store the file, maybe NULL (then no file is
2130 * created on disk and data must be grabbed from the callbacks)
2131 * @param tempname where to store temporary file data, not used if filename is non-NULL;
2132 * can be NULL (in which case we will pick a name if needed); the temporary file
2133 * may already exist, in which case we will try to use the data that is there and
2134 * if it is not what is desired, will overwrite it
2135 * @param offset at what offset should we start the download (typically 0)
2136 * @param length how many bytes should be downloaded starting at offset
2137 * @param anonymity anonymity level to use for the download
2138 * @param options various options
2139 * @param cctx initial value for the client context for this download
2140 * @param parent parent download to associate this download with (use NULL
2141 * for top-level downloads; useful for manually-triggered recursive downloads)
2142 * @return context that can be used to control this download
2143 */
2144struct GNUNET_FS_DownloadContext *
2145GNUNET_FS_download_start (struct GNUNET_FS_Handle *h,
2146 const struct GNUNET_FS_Uri *uri,
2147 const struct GNUNET_CONTAINER_MetaData *meta,
2148 const char *filename,
2149 const char *tempname,
2150 uint64_t offset,
2151 uint64_t length,
2152 uint32_t anonymity,
2153 enum GNUNET_FS_DownloadOptions options,
2154 void *cctx,
2155 struct GNUNET_FS_DownloadContext *parent)
2156{
2157 struct GNUNET_FS_DownloadContext *dc;
2158
2159 dc = create_download_context (h,
2160 uri,
2161 meta,
2162 filename,
2163 tempname,
2164 offset,
2165 length,
2166 anonymity,
2167 options,
2168 cctx);
2169 if (NULL == dc)
2170 return NULL;
2171 dc->parent = parent;
2172 if (NULL != parent)
2173 GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc);
2174 else if (0 == (GNUNET_FS_DOWNLOAD_IS_PROBE & options))
2175 dc->top =
2176 GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc);
2177 return dc;
2178}
2179
2180
2181/**
2182 * Download parts of a file based on a search result. The download
2183 * will be associated with the search result (and the association
2184 * will be preserved when serializing/deserializing the state).
2185 * If the search is stopped, the download will not be aborted but
2186 * be 'promoted' to a stand-alone download.
2187 *
2188 * As with the other download function, this will store
2189 * the blocks at the respective offset in the given file. Also, the
2190 * download is still using the blocking of the underlying FS
2191 * encoding. As a result, the download may *write* outside of the
2192 * given boundaries (if offset and length do not match the 32k FS
2193 * block boundaries). <p>
2194 *
2195 * The given range can be used to focus a download towards a
2196 * particular portion of the file (optimization), not to strictly
2197 * limit the download to exactly those bytes.
2198 *
2199 * @param h handle to the file sharing subsystem
2200 * @param sr the search result to use for the download (determines uri and
2201 * meta data and associations)
2202 * @param filename where to store the file, maybe NULL (then no file is
2203 * created on disk and data must be grabbed from the callbacks)
2204 * @param tempname where to store temporary file data, not used if filename is non-NULL;
2205 * can be NULL (in which case we will pick a name if needed); the temporary file
2206 * may already exist, in which case we will try to use the data that is there and
2207 * if it is not what is desired, will overwrite it
2208 * @param offset at what offset should we start the download (typically 0)
2209 * @param length how many bytes should be downloaded starting at offset
2210 * @param anonymity anonymity level to use for the download
2211 * @param options various download options
2212 * @param cctx initial value for the client context for this download
2213 * @return context that can be used to control this download
2214 */
2215struct GNUNET_FS_DownloadContext *
2216GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
2217 struct GNUNET_FS_SearchResult *sr,
2218 const char *filename,
2219 const char *tempname,
2220 uint64_t offset,
2221 uint64_t length,
2222 uint32_t anonymity,
2223 enum GNUNET_FS_DownloadOptions options,
2224 void *cctx)
2225{
2226 struct GNUNET_FS_DownloadContext *dc;
2227
2228 if ((NULL == sr) || (NULL != sr->download))
2229 {
2230 GNUNET_break (0);
2231 return NULL;
2232 }
2233 dc = create_download_context (h,
2234 sr->uri,
2235 sr->meta,
2236 filename,
2237 tempname,
2238 offset,
2239 length,
2240 anonymity,
2241 options,
2242 cctx);
2243 if (NULL == dc)
2244 return NULL;
2245 dc->search = sr;
2246 sr->download = dc;
2247 if (NULL != sr->probe_ctx)
2248 {
2249 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
2250 sr->probe_ctx = NULL;
2251 GNUNET_FS_stop_probe_ping_task_ (sr);
2252 }
2253 return dc;
2254}
2255
2256
2257/**
2258 * Start the downloading process (by entering the queue).
2259 *
2260 * @param dc our download context
2261 */
2262void
2263GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
2264{
2265 if (dc->completed == dc->length)
2266 return;
2267 if (NULL != dc->mq)
2268 return; /* already running */
2269 GNUNET_assert (NULL == dc->job_queue);
2270 GNUNET_assert (NULL == dc->task);
2271 GNUNET_assert (NULL != dc->active);
2272 dc->job_queue =
2273 GNUNET_FS_queue_ (dc->h,
2274 &activate_fs_download,
2275 &deactivate_fs_download,
2276 dc,
2277 (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE,
2278 (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
2279 ? GNUNET_FS_QUEUE_PRIORITY_NORMAL
2280 : GNUNET_FS_QUEUE_PRIORITY_PROBE);
2281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2282 "Download %p put into queue as job %p\n",
2283 dc,
2284 dc->job_queue);
2285}
2286
2287
2288/**
2289 * Suspend a download.
2290 *
2291 * @param dc handle for the download
2292 */
2293void
2294GNUNET_FS_download_suspend (struct GNUNET_FS_DownloadContext *dc)
2295{
2296 deactivate_fs_download (dc);
2297}
2298
2299
2300/**
2301 * Resume a suspended download.
2302 *
2303 * @param dc handle for the download
2304 */
2305void
2306GNUNET_FS_download_resume (struct GNUNET_FS_DownloadContext *dc)
2307{
2308 struct GNUNET_FS_ProgressInfo pi;
2309
2310 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
2311 GNUNET_FS_download_make_status_ (&pi, dc);
2312
2313 GNUNET_assert (NULL == dc->task);
2314 dc->job_queue =
2315 GNUNET_FS_queue_ (dc->h,
2316 &activate_fs_download,
2317 &deactivate_fs_download,
2318 dc,
2319 (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE,
2320 (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
2321 ? GNUNET_FS_QUEUE_PRIORITY_NORMAL
2322 : GNUNET_FS_QUEUE_PRIORITY_PROBE);
2323}
2324
2325
2326/**
2327 * Stop a download (aborts if download is incomplete).
2328 *
2329 * @param dc handle for the download
2330 * @param do_delete delete files of incomplete downloads
2331 */
2332void
2333GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc, int do_delete)
2334{
2335 struct GNUNET_FS_ProgressInfo pi;
2336 int have_children;
2337 int search_was_null;
2338
2339 if (NULL != dc->top)
2340 GNUNET_FS_end_top (dc->h, dc->top);
2341 if (NULL != dc->task)
2342 {
2343 GNUNET_SCHEDULER_cancel (dc->task);
2344 dc->task = NULL;
2345 }
2346 search_was_null = (NULL == dc->search);
2347 if (NULL != dc->search)
2348 {
2349 dc->search->download = NULL;
2350 GNUNET_FS_search_result_sync_ (dc->search);
2351 dc->search = NULL;
2352 }
2353 if (NULL != dc->job_queue)
2354 {
2355 GNUNET_FS_dequeue_ (dc->job_queue);
2356 dc->job_queue = NULL;
2357 }
2358 if (NULL != dc->te)
2359 {
2360 GNUNET_FS_tree_encoder_finish (dc->te, NULL);
2361 dc->te = NULL;
2362 }
2363 have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
2364 while (NULL != dc->child_head)
2365 GNUNET_FS_download_stop (dc->child_head, do_delete);
2366 if (NULL != dc->parent)
2367 GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2368 dc->parent->child_tail,
2369 dc);
2370 if (NULL != dc->serialization)
2371 GNUNET_FS_remove_sync_file_ (dc->h,
2372 ((NULL != dc->parent) || (! search_was_null))
2373 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2374 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2375 dc->serialization);
2376 if ((GNUNET_YES == have_children) && (NULL == dc->parent))
2377 GNUNET_FS_remove_sync_dir_ (dc->h,
2378 (! search_was_null)
2379 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2380 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2381 dc->serialization);
2382 pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
2383 GNUNET_FS_download_make_status_ (&pi, dc);
2384 GNUNET_FS_free_download_request_ (dc->top_request);
2385 dc->top_request = NULL;
2386 if (NULL != dc->active)
2387 {
2388 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2389 dc->active = NULL;
2390 }
2391 if (NULL != dc->filename)
2392 {
2393 if ((dc->completed != dc->length) && (GNUNET_YES == do_delete))
2394 {
2395 if ((0 != unlink (dc->filename)) && (ENOENT != errno))
2396 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2397 "unlink",
2398 dc->filename);
2399 }
2400 GNUNET_free (dc->filename);
2401 }
2402 GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2403 GNUNET_FS_uri_destroy (dc->uri);
2404 if (NULL != dc->temp_filename)
2405 {
2406 if (0 != unlink (dc->temp_filename))
2407 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2408 "unlink",
2409 dc->temp_filename);
2410 GNUNET_free (dc->temp_filename);
2411 }
2412 GNUNET_free (dc->serialization);
2413 GNUNET_assert (NULL == dc->job_queue);
2414 GNUNET_free (dc);
2415}
2416
2417
2418/* end of fs_download.c */
diff --git a/src/fs/fs_file_information.c b/src/fs/fs_file_information.c
deleted file mode 100644
index c5faa14d4..000000000
--- a/src/fs/fs_file_information.c
+++ /dev/null
@@ -1,475 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_file_information.c
23 * @brief Manage information for publishing directory hierarchies
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#if HAVE_EXTRACTOR_H
28#include <extractor.h>
29#endif
30#include "gnunet_fs_service.h"
31#include "fs_api.h"
32#include "fs_tree.h"
33
34
35/**
36 * Obtain the name under which this file information
37 * structure is stored on disk. Only works for top-level
38 * file information structures.
39 *
40 * @param s structure to get the filename for
41 * @return NULL on error, otherwise filename that
42 * can be used to read this fi-struct from disk.
43 */
44const char *
45GNUNET_FS_file_information_get_id (struct GNUNET_FS_FileInformation *s)
46{
47 if (NULL != s->dir)
48 return NULL;
49 return s->serialization;
50}
51
52
53/**
54 * Obtain the filename from the file information structure.
55 *
56 * @param s structure to get the filename for
57 * @return "filename" field of the structure (can be NULL)
58 */
59const char *
60GNUNET_FS_file_information_get_filename (const struct GNUNET_FS_FileInformation *s)
61{
62 return s->filename;
63}
64
65
66/**
67 * Set the filename in the file information structure.
68 * If filename was already set, frees it before setting the new one.
69 * Makes a copy of the argument.
70 *
71 * @param s structure to get the filename for
72 * @param filename filename to set
73 */
74void
75GNUNET_FS_file_information_set_filename (struct GNUNET_FS_FileInformation *s,
76 const char *filename)
77{
78 GNUNET_free (s->filename);
79 if (filename)
80 s->filename = GNUNET_strdup (filename);
81 else
82 s->filename = NULL;
83}
84
85
86/**
87 * Create an entry for a file in a publish-structure.
88 *
89 * @param h handle to the file sharing subsystem
90 * @param client_info initial value for the client-info value for this entry
91 * @param filename name of the file or directory to publish
92 * @param keywords under which keywords should this file be available
93 * directly; can be NULL
94 * @param meta metadata for the file
95 * @param do_index #GNUNET_YES for index, #GNUNET_NO for insertion,
96 * #GNUNET_SYSERR for simulation
97 * @param bo block options
98 * @return publish structure entry for the file
99 */
100struct GNUNET_FS_FileInformation *
101GNUNET_FS_file_information_create_from_file (
102 struct GNUNET_FS_Handle *h,
103 void *client_info,
104 const char *filename,
105 const struct GNUNET_FS_Uri *keywords,
106 const struct GNUNET_CONTAINER_MetaData *meta,
107 int do_index,
108 const struct GNUNET_FS_BlockOptions *bo)
109{
110 struct FileInfo *fi;
111 uint64_t fsize;
112 struct GNUNET_FS_FileInformation *ret;
113 const char *fn;
114 const char *ss;
115
116 /* FIXME: should include_symbolic_links be GNUNET_NO or GNUNET_YES here? */
117 if (GNUNET_OK !=
118 GNUNET_DISK_file_size (filename, &fsize, GNUNET_NO, GNUNET_YES))
119 {
120 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
121 return NULL;
122 }
123 fi = GNUNET_FS_make_file_reader_context_ (filename);
124 if (NULL == fi)
125 {
126 GNUNET_break (0);
127 return NULL;
128 }
129 ret =
130 GNUNET_FS_file_information_create_from_reader (h,
131 client_info,
132 fsize,
133 &GNUNET_FS_data_reader_file_,
134 fi,
135 keywords,
136 meta,
137 do_index,
138 bo);
139 if (ret == NULL)
140 return NULL;
141 ret->h = h;
142 ret->filename = GNUNET_strdup (filename);
143 fn = filename;
144 while (NULL != (ss = strstr (fn, DIR_SEPARATOR_STR)))
145 fn = ss + 1;
146/* FIXME: If we assume that on other platforms CRT is UTF-8-aware, then
147 * this should be changed to EXTRACTOR_METAFORMAT_UTF8
148 */
149 GNUNET_CONTAINER_meta_data_insert (ret->meta,
150 "<gnunet>",
151 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
152 EXTRACTOR_METAFORMAT_C_STRING,
153 "text/plain",
154 fn,
155 strlen (fn) + 1);
156 return ret;
157}
158
159
160/**
161 * Create an entry for a file in a publish-structure.
162 *
163 * @param h handle to the file sharing subsystem
164 * @param client_info initial value for the client-info value for this entry
165 * @param length length of the file
166 * @param data data for the file (should not be used afterwards by
167 * the caller; callee will "free")
168 * @param keywords under which keywords should this file be available
169 * directly; can be NULL
170 * @param meta metadata for the file
171 * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
172 * GNUNET_SYSERR for simulation
173 * @param bo block options
174 * @return publish structure entry for the file
175 */
176struct GNUNET_FS_FileInformation *
177GNUNET_FS_file_information_create_from_data (
178 struct GNUNET_FS_Handle *h,
179 void *client_info,
180 uint64_t length,
181 void *data,
182 const struct GNUNET_FS_Uri *keywords,
183 const struct GNUNET_CONTAINER_MetaData *meta,
184 int do_index,
185 const struct GNUNET_FS_BlockOptions *bo)
186{
187 if (GNUNET_YES == do_index)
188 {
189 GNUNET_break (0);
190 return NULL;
191 }
192 return GNUNET_FS_file_information_create_from_reader (h,
193 client_info,
194 length,
195 &
196 GNUNET_FS_data_reader_copy_,
197 data,
198 keywords,
199 meta,
200 do_index,
201 bo);
202}
203
204
205/**
206 * Create an entry for a file in a publish-structure.
207 *
208 * @param h handle to the file sharing subsystem
209 * @param client_info initial value for the client-info value for this entry
210 * @param length length of the file
211 * @param reader function that can be used to obtain the data for the file
212 * @param reader_cls closure for "reader"
213 * @param keywords under which keywords should this file be available
214 * directly; can be NULL
215 * @param meta metadata for the file
216 * @param do_index #GNUNET_YES for index, #GNUNET_NO for insertion,
217 * #GNUNET_SYSERR for simulation
218 * @param bo block options
219 * @return publish structure entry for the file
220 */
221struct GNUNET_FS_FileInformation *
222GNUNET_FS_file_information_create_from_reader (
223 struct GNUNET_FS_Handle *h,
224 void *client_info,
225 uint64_t length,
226 GNUNET_FS_DataReader reader,
227 void *reader_cls,
228 const struct GNUNET_FS_Uri *keywords,
229 const struct GNUNET_CONTAINER_MetaData *meta,
230 int do_index,
231 const struct GNUNET_FS_BlockOptions *bo)
232{
233 struct GNUNET_FS_FileInformation *ret;
234
235 if ((GNUNET_YES == do_index) && (reader != &GNUNET_FS_data_reader_file_))
236 {
237 GNUNET_break (0);
238 return NULL;
239 }
240 ret = GNUNET_new (struct GNUNET_FS_FileInformation);
241 ret->h = h;
242 ret->client_info = client_info;
243 ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
244 if (ret->meta == NULL)
245 ret->meta = GNUNET_CONTAINER_meta_data_create ();
246 ret->keywords = (keywords == NULL) ? NULL : GNUNET_FS_uri_dup (keywords);
247 ret->data.file.reader = reader;
248 ret->data.file.reader_cls = reader_cls;
249 ret->data.file.do_index = do_index;
250 ret->data.file.file_size = length;
251 ret->bo = *bo;
252 return ret;
253}
254
255
256/**
257 * Test if a given entry represents a directory.
258 *
259 * @param ent check if this FI represents a directory
260 * @return #GNUNET_YES if so, #GNUNET_NO if not
261 */
262int
263GNUNET_FS_file_information_is_directory (
264 const struct GNUNET_FS_FileInformation *ent)
265{
266 return ent->is_directory;
267}
268
269
270/**
271 * Create an entry for an empty directory in a publish-structure.
272 *
273 * @param h handle to the file sharing subsystem
274 * @param client_info initial value for the client-info value for this entry
275 * @param meta metadata for the directory
276 * @param keywords under which keywords should this directory be available
277 * directly; can be NULL
278 * @param bo block options
279 * @param filename name of the directory; can be NULL
280 * @return publish structure entry for the directory , NULL on error
281 */
282struct GNUNET_FS_FileInformation *
283GNUNET_FS_file_information_create_empty_directory (
284 struct GNUNET_FS_Handle *h,
285 void *client_info,
286 const struct GNUNET_FS_Uri *keywords,
287 const struct GNUNET_CONTAINER_MetaData *meta,
288 const struct GNUNET_FS_BlockOptions *bo,
289 const char *filename)
290{
291 struct GNUNET_FS_FileInformation *ret;
292
293 ret = GNUNET_new (struct GNUNET_FS_FileInformation);
294 ret->h = h;
295 ret->client_info = client_info;
296 ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
297 ret->keywords = GNUNET_FS_uri_dup (keywords);
298 ret->bo = *bo;
299 ret->is_directory = GNUNET_YES;
300 if (filename != NULL)
301 ret->filename = GNUNET_strdup (filename);
302 return ret;
303}
304
305
306/**
307 * Add an entry to a directory in a publish-structure. Clients
308 * should never modify publish structures that were passed to
309 * #GNUNET_FS_publish_start already.
310 *
311 * @param dir the directory
312 * @param ent the entry to add; the entry must not have been
313 * added to any other directory at this point and
314 * must not include @a dir in its structure
315 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
316 */
317int
318GNUNET_FS_file_information_add (struct GNUNET_FS_FileInformation *dir,
319 struct GNUNET_FS_FileInformation *ent)
320{
321 if ((ent->dir != NULL) || (ent->next != NULL) ||
322 (dir->is_directory != GNUNET_YES))
323 {
324 GNUNET_break (0);
325 return GNUNET_SYSERR;
326 }
327 ent->dir = dir;
328 ent->next = dir->data.dir.entries;
329 dir->data.dir.entries = ent;
330 dir->data.dir.dir_size = 0;
331 return GNUNET_OK;
332}
333
334
335/**
336 * Inspect a file or directory in a publish-structure. Clients
337 * should never modify publish structures that were passed to
338 * #GNUNET_FS_publish_start already. When called on a directory,
339 * this function will FIRST call @a proc with information about
340 * the directory itself and then for each of the files in the
341 * directory (but not for files in subdirectories). When called
342 * on a file, @a proc will be called exactly once (with information
343 * about the specific file).
344 *
345 * @param dir the directory
346 * @param proc function to call on each entry
347 * @param proc_cls closure for @a proc
348 */
349void
350GNUNET_FS_file_information_inspect (struct GNUNET_FS_FileInformation *dir,
351 GNUNET_FS_FileInformationProcessor proc,
352 void *proc_cls)
353{
354 struct GNUNET_FS_FileInformation *pos;
355 int no;
356
357 no = GNUNET_NO;
358 if (GNUNET_OK !=
359 proc (proc_cls,
360 dir,
361 (dir->is_directory == GNUNET_YES) ? dir->data.dir.dir_size
362 : dir->data.file.file_size,
363 dir->meta,
364 &dir->keywords,
365 &dir->bo,
366 (dir->is_directory == GNUNET_YES) ? &no : &dir->data.file.do_index,
367 &dir->client_info))
368 return;
369 if (dir->is_directory != GNUNET_YES)
370 return;
371 pos = dir->data.dir.entries;
372 while (pos != NULL)
373 {
374 no = GNUNET_NO;
375 if (GNUNET_OK !=
376 proc (proc_cls,
377 pos,
378 (pos->is_directory == GNUNET_YES) ? pos->data.dir.dir_size
379 : pos->data.file.file_size,
380 pos->meta,
381 &pos->keywords,
382 &pos->bo,
383 (pos->is_directory == GNUNET_YES) ? &no
384 : &pos->data.file.do_index,
385 &pos->client_info))
386 break;
387 pos = pos->next;
388 }
389}
390
391
392/**
393 * Destroy publish-structure. Clients should never destroy publish
394 * structures that were passed to #GNUNET_FS_publish_start already.
395 *
396 * @param fi structure to destroy
397 * @param cleaner function to call on each entry in the structure
398 * (useful to clean up client_info); can be NULL; return
399 * values are ignored
400 * @param cleaner_cls closure for @a cleaner
401 */
402void
403GNUNET_FS_file_information_destroy (struct GNUNET_FS_FileInformation *fi,
404 GNUNET_FS_FileInformationProcessor cleaner,
405 void *cleaner_cls)
406{
407 struct GNUNET_FS_FileInformation *pos;
408 int no;
409
410 no = GNUNET_NO;
411 if (GNUNET_YES == fi->is_directory)
412 {
413 /* clean up directory */
414 while (NULL != (pos = fi->data.dir.entries))
415 {
416 fi->data.dir.entries = pos->next;
417 GNUNET_FS_file_information_destroy (pos, cleaner, cleaner_cls);
418 }
419 /* clean up client-info */
420 if (NULL != cleaner)
421 cleaner (cleaner_cls,
422 fi,
423 fi->data.dir.dir_size,
424 fi->meta,
425 &fi->keywords,
426 &fi->bo,
427 &no,
428 &fi->client_info);
429 GNUNET_free (fi->data.dir.dir_data);
430 }
431 else
432 {
433 /* call clean-up function of the reader */
434 if (NULL != fi->data.file.reader)
435 {
436 (void) fi->data.file.reader (fi->data.file.reader_cls, 0, 0, NULL, NULL);
437 fi->data.file.reader = NULL;
438 }
439 /* clean up client-info */
440 if (NULL != cleaner)
441 cleaner (cleaner_cls,
442 fi,
443 fi->data.file.file_size,
444 fi->meta,
445 &fi->keywords,
446 &fi->bo,
447 &fi->data.file.do_index,
448 &fi->client_info);
449 }
450 GNUNET_free (fi->filename);
451 GNUNET_free (fi->emsg);
452 if (NULL != fi->sks_uri)
453 GNUNET_FS_uri_destroy (fi->sks_uri);
454 if (NULL != fi->chk_uri)
455 GNUNET_FS_uri_destroy (fi->chk_uri);
456 /* clean up serialization */
457 if ((NULL != fi->serialization) && (0 != unlink (fi->serialization)))
458 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
459 "unlink",
460 fi->serialization);
461 if (NULL != fi->keywords)
462 GNUNET_FS_uri_destroy (fi->keywords);
463 if (NULL != fi->meta)
464 GNUNET_CONTAINER_meta_data_destroy (fi->meta);
465 GNUNET_free (fi->serialization);
466 if (NULL != fi->te)
467 {
468 GNUNET_FS_tree_encoder_finish (fi->te, NULL);
469 fi->te = NULL;
470 }
471 GNUNET_free (fi);
472}
473
474
475/* end of fs_file_information.c */
diff --git a/src/fs/fs_getopt.c b/src/fs/fs_getopt.c
deleted file mode 100644
index 43a02a9fa..000000000
--- a/src/fs/fs_getopt.c
+++ /dev/null
@@ -1,274 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_getopt.c
23 * @brief helper functions for command-line argument processing
24 * @author Igor Wronsky, Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_fs_service.h"
28#include "gnunet_getopt_lib.h"
29#include "fs_api.h"
30
31/* ******************** command-line option parsing API ******************** */
32
33/**
34 * Command-line option parser function that allows the user
35 * to specify one or more '-k' options with keywords. Each
36 * specified keyword will be added to the URI. A pointer to
37 * the URI must be passed as the "scls" argument.
38 *
39 * @param ctx command line processor context
40 * @param scls must be of type "struct GNUNET_FS_Uri **"
41 * @param option name of the option (typically 'k')
42 * @param value command line argument given
43 * @return #GNUNET_OK on success
44 */
45static int
46getopt_set_keywords (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
47 void *scls,
48 const char *option,
49 const char *value)
50{
51 struct GNUNET_FS_Uri **uri = scls;
52 struct GNUNET_FS_Uri *u = *uri;
53 char *val;
54 size_t slen;
55
56 if (NULL == u)
57 {
58 u = GNUNET_new (struct GNUNET_FS_Uri);
59 *uri = u;
60 u->type = GNUNET_FS_URI_KSK;
61 u->data.ksk.keywordCount = 0;
62 u->data.ksk.keywords = NULL;
63 }
64 else
65 {
66 GNUNET_assert (GNUNET_FS_URI_KSK == u->type);
67 }
68 slen = strlen (value);
69 if (0 == slen)
70 return GNUNET_SYSERR; /* cannot be empty */
71 if (value[0] == '+')
72 {
73 /* simply preserve the "mandatory" flag */
74 if (slen < 2)
75 return GNUNET_SYSERR; /* empty keywords not allowed */
76 if ((value[1] == '"') && (slen > 3) && (value[slen - 1] == '"'))
77 {
78 /* remove the quotes, keep the '+' */
79 val = GNUNET_malloc (slen - 1);
80 val[0] = '+';
81 GNUNET_memcpy (&val[1],
82 &value[2],
83 slen - 3);
84 val[slen - 2] = '\0';
85 }
86 else
87 {
88 /* no quotes, just keep the '+' */
89 val = GNUNET_strdup (value);
90 }
91 }
92 else
93 {
94 if ((value[0] == '"') && (slen > 2) && (value[slen - 1] == '"'))
95 {
96 /* remove the quotes, add a space */
97 val = GNUNET_malloc (slen);
98 val[0] = ' ';
99 GNUNET_memcpy (&val[1],
100 &value[1],
101 slen - 2);
102 val[slen - 1] = '\0';
103 }
104 else
105 {
106 /* add a space to indicate "not mandatory" */
107 val = GNUNET_malloc (slen + 2);
108 strcpy (val, " ");
109 strcat (val, value);
110 }
111 }
112 GNUNET_array_append (u->data.ksk.keywords,
113 u->data.ksk.keywordCount,
114 val);
115 return GNUNET_OK;
116}
117
118
119/**
120 * Allow user to specify keywords.
121 *
122 * @param shortName short name of the option
123 * @param name long name of the option
124 * @param argumentHelp help text for the option argument
125 * @param description long help text for the option
126 * @param[out] topKeywords set to the desired value
127 */
128struct GNUNET_GETOPT_CommandLineOption
129GNUNET_FS_GETOPT_KEYWORDS (char shortName,
130 const char *name,
131 const char *argumentHelp,
132 const char *description,
133 struct GNUNET_FS_Uri **topKeywords)
134{
135 struct GNUNET_GETOPT_CommandLineOption clo = {
136 .shortName = shortName,
137 .name = name,
138 .argumentHelp = argumentHelp,
139 .description = description,
140 .require_argument = 1,
141 .processor = &getopt_set_keywords,
142 .scls = (void *) topKeywords
143 };
144
145 return clo;
146}
147
148
149/**
150 * Command-line option parser function that allows the user to specify
151 * one or more '-m' options with metadata. Each specified entry of
152 * the form "type=value" will be added to the metadata. A pointer to
153 * the metadata must be passed as the "scls" argument.
154 *
155 * @param ctx command line processor context
156 * @param scls must be of type "struct GNUNET_MetaData **"
157 * @param option name of the option (typically 'k')
158 * @param value command line argument given
159 * @return #GNUNET_OK on success
160 */
161static int
162getopt_set_metadata (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
163 void *scls,
164 const char *option,
165 const char *value)
166{
167 struct GNUNET_CONTAINER_MetaData **mm = scls;
168
169#if HAVE_EXTRACTOR_H && HAVE_LIBEXTRACTOR
170 enum EXTRACTOR_MetaType type;
171 const char *typename;
172 const char *typename_i18n;
173#endif
174 struct GNUNET_CONTAINER_MetaData *meta;
175 char *tmp;
176
177 meta = *mm;
178 if (meta == NULL)
179 {
180 meta = GNUNET_CONTAINER_meta_data_create ();
181 *mm = meta;
182 }
183
184 /* Use GNUNET_STRINGS_get_utf8_args() in main() to acquire utf-8-encoded
185 * commandline arguments, so that the following line is not needed.
186 */
187 /*tmp = GNUNET_STRINGS_to_utf8 (value, strlen (value), locale_charset ());*/
188 tmp = GNUNET_strdup (value);
189#if HAVE_EXTRACTOR_H && HAVE_LIBEXTRACTOR
190 type = EXTRACTOR_metatype_get_max ();
191 while (type > 0)
192 {
193 type--;
194 typename = EXTRACTOR_metatype_to_string (type);
195 typename_i18n = dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN, typename);
196 if ((strlen (tmp) >= strlen (typename) + 1) &&
197 (tmp[strlen (typename)] == ':') &&
198 (0 == strncmp (typename, tmp, strlen (typename))))
199 {
200 GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>", type,
201 EXTRACTOR_METAFORMAT_UTF8,
202 "text/plain",
203 &tmp[strlen (typename) + 1],
204 strlen (&tmp[strlen (typename) + 1])
205 + 1);
206 GNUNET_free (tmp);
207 tmp = NULL;
208 break;
209 }
210 if ((strlen (tmp) >= strlen (typename_i18n) + 1) &&
211 (tmp[strlen (typename_i18n)] == ':') &&
212 (0 == strncmp (typename_i18n, tmp, strlen (typename_i18n))))
213 {
214 GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>", type,
215 EXTRACTOR_METAFORMAT_UTF8,
216 "text/plain",
217 &tmp[strlen (typename_i18n) + 1],
218 strlen (&tmp
219 [strlen (typename_i18n) + 1])
220 + 1);
221 GNUNET_free (tmp);
222 tmp = NULL;
223 break;
224 }
225 }
226#endif
227
228 if (NULL != tmp)
229 {
230 GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>",
231 EXTRACTOR_METATYPE_UNKNOWN,
232 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
233 tmp, strlen (tmp) + 1);
234 GNUNET_free (tmp);
235 printf (_
236 (
237 "Unknown metadata type in metadata option `%s'. Using metadata type `unknown' instead.\n"),
238 value);
239 }
240 return GNUNET_OK;
241}
242
243
244/**
245 * Allow user to specify metadata.
246 *
247 * @param shortName short name of the option
248 * @param name long name of the option
249 * @param argumentHelp help text for the option argument
250 * @param description long help text for the option
251 * @param[out] metadata set to the desired value
252 */
253struct GNUNET_GETOPT_CommandLineOption
254GNUNET_FS_GETOPT_METADATA (char shortName,
255 const char *name,
256 const char *argumentHelp,
257 const char *description,
258 struct GNUNET_CONTAINER_MetaData **meta)
259{
260 struct GNUNET_GETOPT_CommandLineOption clo = {
261 .shortName = shortName,
262 .name = name,
263 .argumentHelp = argumentHelp,
264 .description = description,
265 .require_argument = 1,
266 .processor = &getopt_set_metadata,
267 .scls = (void *) meta
268 };
269
270 return clo;
271}
272
273
274/* end of fs_getopt.c */
diff --git a/src/fs/fs_list_indexed.c b/src/fs/fs_list_indexed.c
deleted file mode 100644
index 0e16fb01b..000000000
--- a/src/fs/fs_list_indexed.c
+++ /dev/null
@@ -1,223 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003, 2004, 2006, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_list_indexed.c
23 * @author Christian Grothoff
24 * @brief provide a list of all indexed files
25 */
26
27#include "platform.h"
28#include "gnunet_constants.h"
29#include "gnunet_fs_service.h"
30#include "gnunet_protocols.h"
31#include "fs_api.h"
32
33
34/**
35 * Context for #GNUNET_FS_get_indexed_files().
36 */
37struct GNUNET_FS_GetIndexedContext
38{
39 /**
40 * Connection to the FS service.
41 */
42 struct GNUNET_MQ_Handle *mq;
43
44 /**
45 * Function to call for each indexed file.
46 */
47 GNUNET_FS_IndexedFileProcessor iterator;
48
49 /**
50 * Closure for @e iterator.
51 */
52 void *iterator_cls;
53
54 /**
55 * Continuation to trigger at the end.
56 */
57 GNUNET_SCHEDULER_TaskCallback cont;
58
59 /**
60 * Closure for @e cont.
61 */
62 void *cont_cls;
63};
64
65
66/**
67 * Function called on each response from the FS
68 * service with information about indexed files.
69 *
70 * @param cls closure (of type `struct GNUNET_FS_GetIndexedContext *`)
71 * @param msg message with indexing information
72 */
73static void
74handle_index_info_end (void *cls,
75 const struct GNUNET_MessageHeader *msg)
76{
77 struct GNUNET_FS_GetIndexedContext *gic = cls;
78
79 (void) gic->iterator (gic->iterator_cls,
80 NULL,
81 NULL);
82 GNUNET_FS_get_indexed_files_cancel (gic);
83}
84
85
86/**
87 * Check validity of response from the FS
88 * service with information about indexed files.
89 *
90 * @param cls closure (of type `struct GNUNET_FS_GetIndexedContext *`)
91 * @param iim message with indexing information
92 */
93static int
94check_index_info (void *cls,
95 const struct IndexInfoMessage *iim)
96{
97 uint16_t msize = ntohs (iim->header.size) - sizeof(*iim);
98 const char *filename;
99
100 filename = (const char *) &iim[1];
101 if (filename[msize - 1] != '\0')
102 {
103 GNUNET_break (0);
104 return GNUNET_SYSERR;
105 }
106 return GNUNET_OK;
107}
108
109
110/**
111 * Function called on each response from the FS
112 * service with information about indexed files.
113 *
114 * @param cls closure (of type `struct GNUNET_FS_GetIndexedContext *`)
115 * @param iim message with indexing information
116 */
117static void
118handle_index_info (void *cls,
119 const struct IndexInfoMessage *iim)
120{
121 struct GNUNET_FS_GetIndexedContext *gic = cls;
122 const char *filename;
123
124 filename = (const char *) &iim[1];
125 if (GNUNET_OK !=
126 gic->iterator (gic->iterator_cls,
127 filename,
128 &iim->file_id))
129 {
130 GNUNET_FS_get_indexed_files_cancel (gic);
131 return;
132 }
133}
134
135
136/**
137 * Generic error handler, called with the appropriate error code and
138 * the same closure specified at the creation of the message queue.
139 * Not every message queue implementation supports an error handler.
140 *
141 * @param cls closure with the `struct GNUNET_FS_GetIndexedContent *`
142 * @param error error code
143 */
144static void
145mq_error_handler (void *cls,
146 enum GNUNET_MQ_Error error)
147{
148 struct GNUNET_FS_GetIndexedContext *gic = cls;
149
150 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
151 _ ("Failed to receive response from `%s' service.\n"),
152 "fs");
153 (void) gic->iterator (gic->iterator_cls, NULL, NULL);
154 GNUNET_FS_get_indexed_files_cancel (gic);
155}
156
157
158/**
159 * Iterate over all indexed files.
160 *
161 * @param h handle to the file sharing subsystem
162 * @param iterator function to call on each indexed file
163 * @param iterator_cls closure for iterator
164 * @return NULL on error ('iter' is not called)
165 */
166struct GNUNET_FS_GetIndexedContext *
167GNUNET_FS_get_indexed_files (struct GNUNET_FS_Handle *h,
168 GNUNET_FS_IndexedFileProcessor iterator,
169 void *iterator_cls)
170{
171 struct GNUNET_FS_GetIndexedContext *gic
172 = GNUNET_new (struct GNUNET_FS_GetIndexedContext);
173 struct GNUNET_MQ_MessageHandler handlers[] = {
174 GNUNET_MQ_hd_fixed_size (index_info_end,
175 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END,
176 struct GNUNET_MessageHeader,
177 gic),
178 GNUNET_MQ_hd_var_size (index_info,
179 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY,
180 struct IndexInfoMessage,
181 gic),
182 GNUNET_MQ_handler_end ()
183 };
184 struct GNUNET_MQ_Envelope *env;
185 struct GNUNET_MessageHeader *msg;
186
187 gic->mq = GNUNET_CLIENT_connect (h->cfg,
188 "fs",
189 handlers,
190 &mq_error_handler,
191 h);
192 if (NULL == gic->mq)
193 {
194 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
195 _ ("Failed to not connect to `%s' service.\n"),
196 "fs");
197 GNUNET_free (gic);
198 return NULL;
199 }
200 gic->iterator = iterator;
201 gic->iterator_cls = iterator_cls;
202 env = GNUNET_MQ_msg (msg,
203 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET);
204 GNUNET_MQ_send (gic->mq,
205 env);
206 return gic;
207}
208
209
210/**
211 * Cancel iteration over all indexed files.
212 *
213 * @param gic operation to cancel
214 */
215void
216GNUNET_FS_get_indexed_files_cancel (struct GNUNET_FS_GetIndexedContext *gic)
217{
218 GNUNET_MQ_destroy (gic->mq);
219 GNUNET_free (gic);
220}
221
222
223/* end of fs_list_indexed.c */
diff --git a/src/fs/fs_misc.c b/src/fs/fs_misc.c
deleted file mode 100644
index 2e7816d65..000000000
--- a/src/fs/fs_misc.c
+++ /dev/null
@@ -1,164 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2011, 2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/fs_misc.c
22 * @brief misc. functions related to file-sharing in general
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_constants.h"
27#include "gnunet_fs_service.h"
28#include "fs_api.h"
29
30
31/**
32 * Suggest a filename based on given metadata.
33 *
34 * @param md given meta data
35 * @return NULL if meta data is useless for suggesting a filename
36 */
37char *
38GNUNET_FS_meta_data_suggest_filename (const struct GNUNET_CONTAINER_MetaData
39 *md)
40{
41 static const char *mimeMap[][2] = {
42 { "application/bz2", ".bz2" },
43 { "application/gnunet-directory", ".gnd" },
44 { "application/java", ".class" },
45 { "application/msword", ".doc" },
46 { "application/nar", ".nar" },
47 { "application/narinfo", ".narinfo" },
48 { "application/ogg", ".ogg" },
49 { "application/pdf", ".pdf" },
50 { "application/pgp-keys", ".key" },
51 { "application/pgp-signature", ".pgp" },
52 { "application/postscript", ".ps" },
53 { "application/rar", ".rar" },
54 { "application/rtf", ".rtf" },
55 { "application/xml", ".xml" },
56 { "application/x-debian-package", ".deb" },
57 { "application/x-dvi", ".dvi" },
58 { "application/x-flac", ".flac" },
59 { "application/x-gzip", ".gz" },
60 { "application/x-java-archive", ".jar" },
61 { "application/x-java-vm", ".class" },
62 { "application/x-python-code", ".pyc" },
63 { "application/x-redhat-package-manager", ".rpm" },
64 { "application/x-rpm", ".rpm" },
65 { "application/x-tar", ".tar" },
66 { "application/x-tex-pk", ".pk" },
67 { "application/x-texinfo", ".texinfo" },
68 { "application/x-xcf", ".xcf" },
69 { "application/x-xfig", ".xfig" },
70 { "application/zip", ".zip" },
71
72 { "audio/midi", ".midi" },
73 { "audio/mpeg", ".mp3" },
74 { "audio/real", ".rm" },
75 { "audio/x-wav", ".wav" },
76
77 { "image/gif", ".gif" },
78 { "image/jpeg", ".jpg" },
79 { "image/pcx", ".pcx" },
80 { "image/png", ".png" },
81 { "image/tiff", ".tiff" },
82 { "image/x-ms-bmp", ".bmp" },
83 { "image/x-xpixmap", ".xpm" },
84
85 { "text/css", ".css" },
86 { "text/html", ".html" },
87 { "text/plain", ".txt" },
88 { "text/rtf", ".rtf" },
89 { "text/x-c++hdr", ".h++" },
90 { "text/x-c++src", ".c++" },
91 { "text/x-chdr", ".h" },
92 { "text/x-csrc", ".c" },
93 { "text/x-java", ".java" },
94 { "text/x-moc", ".moc" },
95 { "text/x-pascal", ".pas" },
96 { "text/x-perl", ".pl" },
97 { "text/x-python", ".py" },
98 { "text/x-tex", ".tex" },
99
100 { "video/avi", ".avi" },
101 { "video/mpeg", ".mpeg" },
102 { "video/quicktime", ".qt" },
103 { "video/real", ".rm" },
104 { "video/x-msvideo", ".avi" },
105 { NULL, NULL },
106 };
107 char *ret;
108 unsigned int i;
109 char *mime;
110 char *base;
111 const char *ext;
112
113 ret =
114 GNUNET_CONTAINER_meta_data_get_by_type (md,
115 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
116 if (ret != NULL)
117 return ret;
118 ext = NULL;
119 mime =
120 GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_METATYPE_MIMETYPE);
121 if (mime != NULL)
122 {
123 i = 0;
124 while ((mimeMap[i][0] != NULL) && (0 != strcmp (mime, mimeMap[i][0])))
125 i++;
126 if (mimeMap[i][1] == NULL)
127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
128 _ ("Did not find mime type `%s' in extension list.\n"), mime);
129 else
130 ext = mimeMap[i][1];
131 GNUNET_free (mime);
132 }
133 base =
134 GNUNET_CONTAINER_meta_data_get_first_by_types (md,
135 EXTRACTOR_METATYPE_TITLE,
136 EXTRACTOR_METATYPE_BOOK_TITLE,
137 EXTRACTOR_METATYPE_ORIGINAL_TITLE,
138 EXTRACTOR_METATYPE_PACKAGE_NAME,
139 EXTRACTOR_METATYPE_URL,
140 EXTRACTOR_METATYPE_URI,
141 EXTRACTOR_METATYPE_DESCRIPTION,
142 EXTRACTOR_METATYPE_ISRC,
143 EXTRACTOR_METATYPE_JOURNAL_NAME,
144 EXTRACTOR_METATYPE_AUTHOR_NAME,
145 EXTRACTOR_METATYPE_SUBJECT,
146 EXTRACTOR_METATYPE_ALBUM,
147 EXTRACTOR_METATYPE_ARTIST,
148 EXTRACTOR_METATYPE_KEYWORDS,
149 EXTRACTOR_METATYPE_COMMENT,
150 EXTRACTOR_METATYPE_UNKNOWN,
151 -1);
152 if ((base == NULL) && (ext == NULL))
153 return NULL;
154 if (base == NULL)
155 return GNUNET_strdup (ext);
156 if (ext == NULL)
157 return base;
158 GNUNET_asprintf (&ret, "%s%s", base, ext);
159 GNUNET_free (base);
160 return ret;
161}
162
163
164/* end of fs_misc.c */
diff --git a/src/fs/fs_namespace.c b/src/fs/fs_namespace.c
deleted file mode 100644
index 155486be5..000000000
--- a/src/fs/fs_namespace.c
+++ /dev/null
@@ -1,818 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2003-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_namespace.c
23 * @brief publishing to namespaces, and tracking updateable entries
24 * for our namespaces
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_constants.h"
29#include "gnunet_signatures.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_fs_service.h"
32#include "fs_api.h"
33#include "fs_publish_ublock.h"
34
35
36/**
37 * Information about an (updateable) node in the
38 * namespace.
39 */
40struct NamespaceUpdateNode
41{
42 /**
43 * Identifier for this node.
44 */
45 char *id;
46
47 /**
48 * Identifier of children of this node.
49 */
50 char *update;
51
52 /**
53 * Metadata for this entry.
54 */
55 struct GNUNET_CONTAINER_MetaData *md;
56
57 /**
58 * URI of this entry in the namespace.
59 */
60 struct GNUNET_FS_Uri *uri;
61
62 /**
63 * Namespace update generation ID. Used to ensure
64 * freshness of the tree_id.
65 */
66 unsigned int nug;
67
68 /**
69 * TREE this entry belongs to (if nug is current).
70 */
71 unsigned int tree_id;
72};
73
74
75/**
76 * Handle to update information for a namespace.
77 */
78struct GNUNET_FS_UpdateInformationGraph
79{
80 /**
81 * Handle to the FS service context.
82 */
83 struct GNUNET_FS_Handle *h;
84
85 /**
86 * Array with information about nodes in the namespace.
87 */
88 struct NamespaceUpdateNode **update_nodes;
89
90 /**
91 * Private key for the namespace.
92 */
93 struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
94
95 /**
96 * Hash map mapping identifiers of update nodes
97 * to the update nodes (initialized on-demand).
98 */
99 struct GNUNET_CONTAINER_MultiHashMap *update_map;
100
101 /**
102 * Size of the update nodes array.
103 */
104 unsigned int update_node_count;
105
106 /**
107 * Reference counter.
108 */
109 unsigned int rc;
110
111 /**
112 * Generator for unique nug numbers.
113 */
114 unsigned int nug_gen;
115};
116
117
118/**
119 * Return the name of the directory in which we store
120 * the update information graph for the given local namespace.
121 *
122 * @param h file-sharing handle
123 * @param ns namespace handle
124 * @return NULL on error, otherwise the name of the directory
125 */
126static char *
127get_update_information_directory (
128 struct GNUNET_FS_Handle *h,
129 const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns)
130{
131 char *dn;
132 char *ret;
133 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
134 struct GNUNET_HashCode hc;
135 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
136
137 if (GNUNET_OK !=
138 GNUNET_CONFIGURATION_get_value_filename (h->cfg, "FS", "UPDATE_DIR", &dn))
139 {
140 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "fs", "UPDATE_DIR");
141 return NULL;
142 }
143 GNUNET_CRYPTO_ecdsa_key_get_public (ns, &pub);
144 GNUNET_CRYPTO_hash (&pub, sizeof(pub), &hc);
145 GNUNET_CRYPTO_hash_to_enc (&hc, &enc);
146 GNUNET_asprintf (&ret,
147 "%s%s%s",
148 dn,
149 DIR_SEPARATOR_STR,
150 (const char *) enc.encoding);
151 GNUNET_free (dn);
152 return ret;
153}
154
155
156/**
157 * Release memory occupied by UIG datastructure.
158 *
159 * @param uig data structure to free
160 */
161static void
162free_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig)
163{
164 unsigned int i;
165 struct NamespaceUpdateNode *nsn;
166
167 for (i = 0; i < uig->update_node_count; i++)
168 {
169 nsn = uig->update_nodes[i];
170 GNUNET_CONTAINER_meta_data_destroy (nsn->md);
171 GNUNET_FS_uri_destroy (nsn->uri);
172 GNUNET_free (nsn->id);
173 GNUNET_free (nsn->update);
174 GNUNET_free (nsn);
175 }
176 GNUNET_array_grow (uig->update_nodes, uig->update_node_count, 0);
177 if (NULL != uig->update_map)
178 GNUNET_CONTAINER_multihashmap_destroy (uig->update_map);
179 GNUNET_free (uig);
180}
181
182
183/**
184 * Write a namespace's update node graph to a file.
185 *
186 * @param uig update information graph to dump
187 */
188static void
189write_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig)
190{
191 char *fn;
192 struct GNUNET_BIO_WriteHandle *wh;
193 unsigned int i;
194 struct NamespaceUpdateNode *n;
195 char *uris;
196
197 fn = get_update_information_directory (uig->h, &uig->ns);
198 wh = GNUNET_BIO_write_open_file (fn);
199 if (NULL == wh)
200 {
201 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
202 _ ("Failed to open `%s' for writing: %s\n"),
203 fn,
204 strerror (errno));
205 GNUNET_free (fn);
206 return;
207 }
208 if (GNUNET_OK != GNUNET_BIO_write_int32 (wh,
209 "fs-namespace-node-count",
210 uig->update_node_count))
211 goto END;
212 for (i = 0; i < uig->update_node_count; i++)
213 {
214 n = uig->update_nodes[i];
215 uris = GNUNET_FS_uri_to_string (n->uri);
216 struct GNUNET_BIO_WriteSpec ws[] = {
217 GNUNET_BIO_write_spec_string ("fs-namespace-node-id", n->id),
218 GNUNET_BIO_write_spec_meta_data ("fs-namespace-node-meta", n->md),
219 GNUNET_BIO_write_spec_string ("fs-namespace-node-update", n->update),
220 GNUNET_BIO_write_spec_string ("fs-namespace-uris", uris),
221 GNUNET_BIO_write_spec_end (),
222 };
223 if (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))
224 {
225 GNUNET_free (uris);
226 break;
227 }
228 GNUNET_free (uris);
229 }
230END:
231 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
232 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
233 _ ("Failed to write `%s': %s\n"),
234 fn,
235 strerror (errno));
236 GNUNET_free (fn);
237}
238
239
240/**
241 * Read the namespace update node graph from a file.
242 *
243 * @param h FS handle to use
244 * @param ns namespace to read
245 * @return update graph, never NULL
246 */
247static struct GNUNET_FS_UpdateInformationGraph *
248read_update_information_graph (struct GNUNET_FS_Handle *h,
249 const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns)
250{
251 struct GNUNET_FS_UpdateInformationGraph *uig;
252 char *fn;
253 struct GNUNET_BIO_ReadHandle *rh;
254 unsigned int i;
255 struct NamespaceUpdateNode *n;
256 char *uris;
257 uint32_t count;
258 char *emsg;
259
260 uig = GNUNET_new (struct GNUNET_FS_UpdateInformationGraph);
261 uig->h = h;
262 uig->ns = *ns;
263 fn = get_update_information_directory (h, ns);
264 if (GNUNET_YES != GNUNET_DISK_file_test (fn))
265 {
266 GNUNET_free (fn);
267 return uig;
268 }
269 rh = GNUNET_BIO_read_open_file (fn);
270 if (NULL == rh)
271 {
272 GNUNET_free (fn);
273 return uig;
274 }
275 if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "fs-namespace-count",
276 (int32_t *) &count))
277 {
278 GNUNET_break (0);
279 goto END;
280 }
281 if (count > 1024 * 1024)
282 {
283 GNUNET_break (0);
284 goto END;
285 }
286 if (0 == count)
287 goto END;
288 uig->update_nodes =
289 GNUNET_malloc (count * sizeof(struct NamespaceUpdateNode *));
290
291 for (i = 0; i < count; i++)
292 {
293 n = GNUNET_new (struct NamespaceUpdateNode);
294 struct GNUNET_BIO_ReadSpec rs[] = {
295 GNUNET_BIO_read_spec_string ("identifier", &n->id, 1024),
296 GNUNET_BIO_read_spec_meta_data ("meta", &n->md),
297 GNUNET_BIO_read_spec_string ("update-id", &n->update, 1024),
298 GNUNET_BIO_read_spec_string ("uri", &uris, 1024 * 2),
299 GNUNET_BIO_read_spec_end (),
300 };
301 if (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs))
302 {
303 GNUNET_break (0);
304 GNUNET_free (n->id);
305 GNUNET_free (n->update);
306 if (n->md != NULL)
307 GNUNET_CONTAINER_meta_data_destroy (n->md);
308 GNUNET_free (n);
309 break;
310 }
311 n->uri = GNUNET_FS_uri_parse (uris, &emsg);
312 GNUNET_free (uris);
313 if (n->uri == NULL)
314 {
315 GNUNET_break (0);
316 GNUNET_free (emsg);
317 GNUNET_free (n->id);
318 GNUNET_free (n->update);
319 GNUNET_CONTAINER_meta_data_destroy (n->md);
320 GNUNET_free (n);
321 break;
322 }
323 uig->update_nodes[i] = n;
324 }
325 uig->update_node_count = i;
326END:
327 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
328 {
329 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
330 _ ("Failed to read `%s': %s\n"),
331 fn,
332 emsg);
333 GNUNET_free (emsg);
334 }
335 GNUNET_free (fn);
336 return uig;
337}
338
339
340/**
341 * Context for the SKS publication.
342 */
343struct GNUNET_FS_PublishSksContext
344{
345 /**
346 * URI of the new entry in the namespace.
347 */
348 struct GNUNET_FS_Uri *uri;
349
350 /**
351 * Namespace update node to add to namespace on success (or to be
352 * deleted if publishing failed).
353 */
354 struct NamespaceUpdateNode *nsn;
355
356 /**
357 * Namespace we're publishing to.
358 */
359 struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
360
361 /**
362 * Handle to the datastore.
363 */
364 struct GNUNET_DATASTORE_Handle *dsh;
365
366 /**
367 * Handle to FS.
368 */
369 struct GNUNET_FS_Handle *h;
370
371 /**
372 * Function to call once we're done.
373 */
374 GNUNET_FS_PublishContinuation cont;
375
376 /**
377 * Closure for cont.
378 */
379 void *cont_cls;
380
381 /**
382 * Handle for our UBlock operation request.
383 */
384 struct GNUNET_FS_PublishUblockContext *uc;
385};
386
387
388/**
389 * Function called by the UBlock construction with
390 * the result from the PUT (UBlock) request.
391 *
392 * @param cls closure of type "struct GNUNET_FS_PublishSksContext*"
393 * @param msg error message (or NULL)
394 */
395static void
396sks_publish_cont (void *cls, const char *msg)
397{
398 struct GNUNET_FS_PublishSksContext *psc = cls;
399 struct GNUNET_FS_UpdateInformationGraph *uig;
400
401 psc->uc = NULL;
402 if (NULL != msg)
403 {
404 if (NULL != psc->cont)
405 psc->cont (psc->cont_cls, NULL, msg);
406 GNUNET_FS_publish_sks_cancel (psc);
407 return;
408 }
409 if (NULL != psc->nsn)
410 {
411 /* FIXME: this can be done much more
412 * efficiently by simply appending to the
413 * file and overwriting the 4-byte header */
414 uig = read_update_information_graph (psc->h, &psc->ns);
415 GNUNET_array_append (uig->update_nodes, uig->update_node_count, psc->nsn);
416 psc->nsn = NULL;
417 write_update_information_graph (uig);
418 free_update_information_graph (uig);
419 }
420 if (NULL != psc->cont)
421 psc->cont (psc->cont_cls, psc->uri, NULL);
422 GNUNET_FS_publish_sks_cancel (psc);
423}
424
425
426/**
427 * Publish an SBlock on GNUnet.
428 *
429 * @param h handle to the file sharing subsystem
430 * @param ns namespace to publish in
431 * @param identifier identifier to use
432 * @param update update identifier to use
433 * @param meta metadata to use
434 * @param uri URI to refer to in the SBlock
435 * @param bo block options
436 * @param options publication options
437 * @param cont continuation
438 * @param cont_cls closure for cont
439 * @return NULL on error ('cont' will still be called)
440 */
441struct GNUNET_FS_PublishSksContext *
442GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
443 const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
444 const char *identifier,
445 const char *update,
446 const struct GNUNET_CONTAINER_MetaData *meta,
447 const struct GNUNET_FS_Uri *uri,
448 const struct GNUNET_FS_BlockOptions *bo,
449 enum GNUNET_FS_PublishOptions options,
450 GNUNET_FS_PublishContinuation cont,
451 void *cont_cls)
452{
453 struct GNUNET_FS_PublishSksContext *psc;
454 struct GNUNET_FS_Uri *sks_uri;
455
456 sks_uri = GNUNET_new (struct GNUNET_FS_Uri);
457 sks_uri->type = GNUNET_FS_URI_SKS;
458 sks_uri->data.sks.identifier = GNUNET_strdup (identifier);
459 GNUNET_CRYPTO_ecdsa_key_get_public (ns, &sks_uri->data.sks.ns);
460
461 psc = GNUNET_new (struct GNUNET_FS_PublishSksContext);
462 psc->h = h;
463 psc->uri = sks_uri;
464 psc->cont = cont;
465 psc->cont_cls = cont_cls;
466 psc->ns = *ns;
467 if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
468 {
469 psc->dsh = GNUNET_DATASTORE_connect (h->cfg);
470 if (NULL == psc->dsh)
471 {
472 sks_publish_cont (psc, _ ("Failed to connect to datastore."));
473 return NULL;
474 }
475 }
476 if (NULL != update)
477 {
478 psc->nsn = GNUNET_new (struct NamespaceUpdateNode);
479 psc->nsn->id = GNUNET_strdup (identifier);
480 psc->nsn->update = GNUNET_strdup (update);
481 psc->nsn->md = GNUNET_CONTAINER_meta_data_duplicate (meta);
482 psc->nsn->uri = GNUNET_FS_uri_dup (uri);
483 }
484 psc->uc = GNUNET_FS_publish_ublock_ (h,
485 psc->dsh,
486 identifier,
487 update,
488 ns,
489 meta,
490 uri,
491 bo,
492 options,
493 &sks_publish_cont,
494 psc);
495 return psc;
496}
497
498
499/**
500 * Abort the SKS publishing operation.
501 *
502 * @param psc context of the operation to abort.
503 */
504void
505GNUNET_FS_publish_sks_cancel (struct GNUNET_FS_PublishSksContext *psc)
506{
507 if (NULL != psc->uc)
508 {
509 GNUNET_FS_publish_ublock_cancel_ (psc->uc);
510 psc->uc = NULL;
511 }
512 if (NULL != psc->dsh)
513 {
514 GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO);
515 psc->dsh = NULL;
516 }
517 GNUNET_FS_uri_destroy (psc->uri);
518 if (NULL != psc->nsn)
519 {
520 GNUNET_CONTAINER_meta_data_destroy (psc->nsn->md);
521 GNUNET_FS_uri_destroy (psc->nsn->uri);
522 GNUNET_free (psc->nsn->id);
523 GNUNET_free (psc->nsn->update);
524 GNUNET_free (psc->nsn);
525 }
526 GNUNET_free (psc);
527}
528
529
530/**
531 * Closure for 'process_update_node'.
532 */
533struct ProcessUpdateClosure
534{
535 /**
536 * Function to call for each node.
537 */
538 GNUNET_FS_IdentifierProcessor ip;
539
540 /**
541 * Closure for 'ip'.
542 */
543 void *ip_cls;
544};
545
546
547/**
548 * Call the iterator in the closure for each node.
549 *
550 * @param cls closure (of type 'struct ProcessUpdateClosure *')
551 * @param key current key code
552 * @param value value in the hash map (of type 'struct NamespaceUpdateNode *')
553 * @return GNUNET_YES if we should continue to
554 * iterate,
555 * GNUNET_NO if not.
556 */
557static int
558process_update_node (void *cls, const struct GNUNET_HashCode *key, void *value)
559{
560 struct ProcessUpdateClosure *pc = cls;
561 struct NamespaceUpdateNode *nsn = value;
562
563 pc->ip (pc->ip_cls, nsn->id, nsn->uri, nsn->md, nsn->update);
564 return GNUNET_YES;
565}
566
567
568/**
569 * Closure for 'find_trees'.
570 */
571struct FindTreeClosure
572{
573 /**
574 * UIG we are operating on.
575 */
576 struct GNUNET_FS_UpdateInformationGraph *uig;
577
578 /**
579 * Array with 'head's of TREEs.
580 */
581 struct NamespaceUpdateNode **tree_array;
582
583 /**
584 * Size of 'tree_array'
585 */
586 unsigned int tree_array_size;
587
588 /**
589 * Current generational ID used.
590 */
591 unsigned int nug;
592
593 /**
594 * Identifier for the current TREE, or UINT_MAX for none yet.
595 */
596 unsigned int id;
597};
598
599
600/**
601 * Find all nodes reachable from the current node (including the
602 * current node itself). If they are in no tree, add them to the
603 * current one. If they are the head of another tree, merge the
604 * trees. If they are in the middle of another tree, let them be.
605 * We can tell that a node is already in an tree by checking if
606 * its 'nug' field is set to the current 'nug' value. It is the
607 * head of an tree if it is in the 'tree_array' under its respective
608 * 'tree_id'.
609 *
610 * In short, we're trying to find the smallest number of tree to
611 * cover a directed graph.
612 *
613 * @param cls closure (of type 'struct FindTreeClosure')
614 * @param key current key code
615 * @param value value in the hash map
616 * @return GNUNET_YES if we should continue to
617 * iterate,
618 * GNUNET_NO if not.
619 */
620static int
621find_trees (void *cls, const struct GNUNET_HashCode *key, void *value)
622{
623 struct FindTreeClosure *fc = cls;
624 struct NamespaceUpdateNode *nsn = value;
625 struct GNUNET_HashCode hc;
626
627 if (nsn->nug == fc->nug)
628 {
629 if (UINT_MAX == nsn->tree_id)
630 return GNUNET_YES; /* circular */
631 GNUNET_assert (nsn->tree_id < fc->tree_array_size);
632 if (fc->tree_array[nsn->tree_id] != nsn)
633 return GNUNET_YES; /* part of "another" (directed) TREE,
634 * and not root of it, end trace */
635 if (nsn->tree_id == fc->id)
636 return GNUNET_YES; /* that's our own root (can this be?) */
637 /* merge existing TREE, we have a root for both */
638 fc->tree_array[nsn->tree_id] = NULL;
639 if (UINT_MAX == fc->id)
640 fc->id = nsn->tree_id; /* take over ID */
641 }
642 else
643 {
644 nsn->nug = fc->nug;
645 nsn->tree_id = UINT_MAX; /* mark as undef */
646 /* trace */
647 GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc);
648 GNUNET_CONTAINER_multihashmap_get_multiple (fc->uig->update_map,
649 &hc,
650 &find_trees,
651 fc);
652 }
653 return GNUNET_YES;
654}
655
656
657/**
658 * List all of the identifiers in the namespace for which we could
659 * produce an update. Namespace updates form a graph where each node
660 * has a name. Each node can have any number of URI/meta-data entries
661 * which can each be linked to other nodes. Cycles are possible.
662 *
663 * Calling this function with "next_id" NULL will cause the library to
664 * call "ip" with a root for each strongly connected component of the
665 * graph (a root being a node from which all other nodes in the Tree
666 * are reachable).
667 *
668 * Calling this function with "next_id" being the name of a node will
669 * cause the library to call "ip" with all children of the node. Note
670 * that cycles within the final tree are possible (including self-loops).
671 * I know, odd definition of a tree, but the GUI will display an actual
672 * tree (GtkTreeView), so that's what counts for the term here.
673 *
674 * @param h fs handle to use
675 * @param ns namespace to inspect for updateable content
676 * @param next_id ID to look for; use NULL to look for tree roots
677 * @param ip function to call on each updateable identifier
678 * @param ip_cls closure for ip
679 */
680void
681GNUNET_FS_namespace_list_updateable (
682 struct GNUNET_FS_Handle *h,
683 const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
684 const char *next_id,
685 GNUNET_FS_IdentifierProcessor ip,
686 void *ip_cls)
687{
688 unsigned int i;
689 unsigned int nug;
690 struct GNUNET_HashCode hc;
691 struct NamespaceUpdateNode *nsn;
692 struct ProcessUpdateClosure pc;
693 struct FindTreeClosure fc;
694 struct GNUNET_FS_UpdateInformationGraph *uig;
695
696 uig = read_update_information_graph (h, ns);
697 if (NULL == uig->update_nodes)
698 {
699 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700 "No updateable nodes found for ID `%s'\n",
701 next_id);
702 free_update_information_graph (uig);
703 return; /* no nodes */
704 }
705 uig->update_map =
706 GNUNET_CONTAINER_multihashmap_create (2 + 3 * uig->update_node_count / 4,
707 GNUNET_NO);
708 for (i = 0; i < uig->update_node_count; i++)
709 {
710 nsn = uig->update_nodes[i];
711 GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc);
712 GNUNET_CONTAINER_multihashmap_put (
713 uig->update_map,
714 &hc,
715 nsn,
716 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
717 }
718 if (NULL != next_id)
719 {
720 GNUNET_CRYPTO_hash (next_id, strlen (next_id), &hc);
721 pc.ip = ip;
722 pc.ip_cls = ip_cls;
723 GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map,
724 &hc,
725 &process_update_node,
726 &pc);
727 free_update_information_graph (uig);
728 return;
729 }
730 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
731 "Calculating TREEs to find roots of update trees\n");
732 /* Find heads of TREEs in update graph */
733 nug = ++uig->nug_gen;
734 fc.tree_array = NULL;
735 fc.tree_array_size = 0;
736
737 for (i = 0; i < uig->update_node_count; i++)
738 {
739 nsn = uig->update_nodes[i];
740 if (nsn->nug == nug)
741 {
742 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
743 "TREE of node `%s' is %u\n",
744 nsn->id,
745 nsn->nug);
746 continue; /* already placed in TREE */
747 }
748 GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc);
749 nsn->nug = nug;
750 nsn->tree_id = UINT_MAX;
751 fc.id = UINT_MAX;
752 fc.nug = nug;
753 fc.uig = uig;
754 GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map,
755 &hc,
756 &find_trees,
757 &fc);
758 if (UINT_MAX == fc.id)
759 {
760 /* start new TREE */
761 for (fc.id = 0; fc.id < fc.tree_array_size; fc.id++)
762 {
763 if (NULL == fc.tree_array[fc.id])
764 {
765 fc.tree_array[fc.id] = nsn;
766 nsn->tree_id = fc.id;
767 break;
768 }
769 }
770 if (fc.id == fc.tree_array_size)
771 {
772 GNUNET_array_append (fc.tree_array, fc.tree_array_size, nsn);
773 nsn->tree_id = fc.id;
774 }
775 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
776 "Starting new TREE %u with node `%s'\n",
777 nsn->tree_id,
778 nsn->id);
779 /* put all nodes with same identifier into this TREE */
780 GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc);
781 fc.id = nsn->tree_id;
782 fc.nug = nug;
783 fc.uig = uig;
784 GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map,
785 &hc,
786 &find_trees,
787 &fc);
788 }
789 else
790 {
791 /* make head of TREE "id" */
792 fc.tree_array[fc.id] = nsn;
793 nsn->tree_id = fc.id;
794 }
795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796 "TREE of node `%s' is %u\n",
797 nsn->id,
798 fc.id);
799 }
800 for (i = 0; i < fc.tree_array_size; i++)
801 {
802 nsn = fc.tree_array[i];
803 if (NULL != nsn)
804 {
805 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
806 "Root of TREE %u is node `%s'\n",
807 i,
808 nsn->id);
809 ip (ip_cls, nsn->id, nsn->uri, nsn->md, nsn->update);
810 }
811 }
812 GNUNET_array_grow (fc.tree_array, fc.tree_array_size, 0);
813 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Done processing TREEs\n");
814 free_update_information_graph (uig);
815}
816
817
818/* end of fs_namespace.c */
diff --git a/src/fs/fs_publish.c b/src/fs/fs_publish.c
deleted file mode 100644
index f891f37a1..000000000
--- a/src/fs/fs_publish.c
+++ /dev/null
@@ -1,1624 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/fs_publish.c
22 * @brief publish a file or directory in GNUnet
23 * @see https://gnunet.org/encoding
24 * @author Krista Bennett
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_constants.h"
29#include "gnunet_signatures.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_fs_service.h"
32#include "fs_api.h"
33#include "fs_tree.h"
34
35
36/**
37 * Fill in all of the generic fields for
38 * a publish event and call the callback.
39 *
40 * @param pi structure to fill in
41 * @param pc overall publishing context
42 * @param p file information for the file being published
43 * @param offset where in the file are we so far
44 * @return value returned from callback
45 */
46void *
47GNUNET_FS_publish_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
48 struct GNUNET_FS_PublishContext *pc,
49 const struct GNUNET_FS_FileInformation *p,
50 uint64_t offset)
51{
52 pi->value.publish.pc = pc;
53 pi->value.publish.fi = p;
54 pi->value.publish.cctx = p->client_info;
55 pi->value.publish.pctx = (NULL == p->dir) ? NULL : p->dir->client_info;
56 pi->value.publish.filename = p->filename;
57 pi->value.publish.size =
58 (GNUNET_YES == p->is_directory) ? p->data.dir.dir_size :
59 p->data.file.file_size;
60 pi->value.publish.eta =
61 GNUNET_TIME_calculate_eta (p->start_time, offset,
62 pi->value.publish.size);
63 pi->value.publish.completed = offset;
64 pi->value.publish.duration =
65 GNUNET_TIME_absolute_get_duration (p->start_time);
66 pi->value.publish.anonymity = p->bo.anonymity_level;
67 pi->fsh = pc->h;
68 return pc->h->upcb (pc->h->upcb_cls, pi);
69}
70
71
72/**
73 * Cleanup the publish context, we're done with it.
74 *
75 * @param pc struct to clean up
76 */
77static void
78publish_cleanup (struct GNUNET_FS_PublishContext *pc)
79{
80 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
81 "Cleaning up publish context (done!)\n");
82 if (NULL != pc->fhc)
83 {
84 GNUNET_CRYPTO_hash_file_cancel (pc->fhc);
85 pc->fhc = NULL;
86 }
87 GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL);
88 GNUNET_free (pc->nid);
89 GNUNET_free (pc->nuid);
90 GNUNET_free (pc->serialization);
91 if (NULL != pc->dsh)
92 {
93 GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
94 pc->dsh = NULL;
95 }
96 if (NULL != pc->mq)
97 {
98 GNUNET_MQ_destroy (pc->mq);
99 pc->mq = NULL;
100 }
101 GNUNET_assert (NULL == pc->upload_task);
102 GNUNET_free (pc);
103}
104
105
106/**
107 * Function called by the datastore API with
108 * the result from the PUT request.
109 *
110 * @param cls the `struct GNUNET_FS_PublishContext *`
111 * @param success #GNUNET_OK on success
112 * @param min_expiration minimum expiration time required for content to be stored
113 * @param msg error message (or NULL)
114 */
115static void
116ds_put_cont (void *cls,
117 int success,
118 struct GNUNET_TIME_Absolute min_expiration,
119 const char *msg)
120{
121 struct GNUNET_FS_PublishContext *pc = cls;
122 struct GNUNET_FS_ProgressInfo pi;
123
124 pc->qre = NULL;
125 if (GNUNET_SYSERR == success)
126 {
127 GNUNET_asprintf (&pc->fi_pos->emsg,
128 _ ("Publishing failed: %s"),
129 msg);
130 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
131 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
132 pi.value.publish.specifics.error.message = pc->fi_pos->emsg;
133 pc->fi_pos->client_info =
134 GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi_pos, 0);
135 if ((GNUNET_YES != pc->fi_pos->is_directory) &&
136 (NULL != pc->fi_pos->filename) &&
137 (GNUNET_YES == pc->any_done) &&
138 (GNUNET_YES == pc->fi_pos->data.file.do_index))
139 {
140 /* run unindex to clean up */
141 GNUNET_FS_unindex_start (pc->h,
142 pc->fi_pos->filename,
143 NULL);
144 }
145 return;
146 }
147 pc->any_done = GNUNET_YES;
148 GNUNET_assert (NULL == pc->upload_task);
149 pc->upload_task =
150 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
151 &GNUNET_FS_publish_main_, pc);
152}
153
154
155/**
156 * Generate the callback that signals clients
157 * that a file (or directory) has been completely
158 * published.
159 *
160 * @param p the completed upload
161 * @param pc context of the publication
162 */
163static void
164signal_publish_completion (struct GNUNET_FS_FileInformation *p,
165 struct GNUNET_FS_PublishContext *pc)
166{
167 struct GNUNET_FS_ProgressInfo pi;
168
169 pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED;
170 pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO;
171 pi.value.publish.specifics.completed.chk_uri = p->chk_uri;
172 pi.value.publish.specifics.completed.sks_uri = p->sks_uri;
173 p->client_info =
174 GNUNET_FS_publish_make_status_ (&pi, pc, p,
175 p->data.file.file_size);
176}
177
178
179/**
180 * Generate the callback that signals clients
181 * that a file (or directory) has encountered
182 * a problem during publication.
183 *
184 * @param p the upload that had trouble
185 * @param pc context of the publication
186 * @param emsg error message
187 */
188static void
189signal_publish_error (struct GNUNET_FS_FileInformation *p,
190 struct GNUNET_FS_PublishContext *pc,
191 const char *emsg)
192{
193 struct GNUNET_FS_ProgressInfo pi;
194
195 p->emsg = GNUNET_strdup (emsg);
196 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
197 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
198 pi.value.publish.specifics.error.message = emsg;
199 p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
200 if ((p->is_directory != GNUNET_YES) &&
201 (NULL != p->filename) &&
202 (GNUNET_YES == pc->any_done) &&
203 (p->data.file.do_index == GNUNET_YES))
204 {
205 /* run unindex to clean up */
206 GNUNET_FS_unindex_start (pc->h,
207 p->filename,
208 NULL);
209 }
210}
211
212
213/**
214 * Datastore returns from reservation cancel request.
215 *
216 * @param cls the `struct GNUNET_FS_PublishContext *`
217 * @param success success code (not used)
218 * @param min_expiration minimum expiration time required for content to be stored
219 * @param msg error message (typically NULL, not used)
220 */
221static void
222finish_release_reserve (void *cls, int success,
223 struct GNUNET_TIME_Absolute min_expiration,
224 const char *msg)
225{
226 struct GNUNET_FS_PublishContext *pc = cls;
227
228 pc->qre = NULL;
229 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
230 "Releasing reserve done!\n");
231 signal_publish_completion (pc->fi, pc);
232 pc->all_done = GNUNET_YES;
233 GNUNET_FS_publish_sync_ (pc);
234}
235
236
237/**
238 * We've finished publishing the SBlock as part of a larger upload.
239 * Check the result and complete the larger upload.
240 *
241 * @param cls the `struct GNUNET_FS_PublishContext *` of the larger upload
242 * @param uri URI of the published SBlock
243 * @param emsg NULL on success, otherwise error message
244 */
245static void
246publish_sblocks_cont (void *cls,
247 const struct GNUNET_FS_Uri *uri,
248 const char *emsg)
249{
250 struct GNUNET_FS_PublishContext *pc = cls;
251
252 pc->sks_pc = NULL;
253 if (NULL != emsg)
254 {
255 signal_publish_error (pc->fi, pc, emsg);
256 GNUNET_FS_publish_sync_ (pc);
257 return;
258 }
259 if (NULL != uri)
260 {
261 /* sks publication, remember namespace URI */
262 pc->fi->sks_uri = GNUNET_FS_uri_dup (uri);
263 }
264 GNUNET_assert (pc->qre == NULL);
265 if ((pc->dsh != NULL) && (pc->rid != 0))
266 {
267 pc->qre =
268 GNUNET_DATASTORE_release_reserve (pc->dsh, pc->rid, UINT_MAX, UINT_MAX,
269 &finish_release_reserve, pc);
270 }
271 else
272 {
273 finish_release_reserve (pc, GNUNET_OK, GNUNET_TIME_UNIT_ZERO_ABS, NULL);
274 }
275}
276
277
278/**
279 * We are almost done publishing the structure,
280 * add SBlocks (if needed).
281 *
282 * @param pc overall upload data
283 */
284static void
285publish_sblock (struct GNUNET_FS_PublishContext *pc)
286{
287 if (NULL != pc->ns)
288 pc->sks_pc = GNUNET_FS_publish_sks (pc->h,
289 pc->ns,
290 pc->nid,
291 pc->nuid,
292 pc->fi->meta,
293 pc->fi->chk_uri,
294 &pc->fi->bo,
295 pc->options,
296 &publish_sblocks_cont, pc);
297 else
298 publish_sblocks_cont (pc, NULL, NULL);
299}
300
301
302/**
303 * We've finished publishing a KBlock as part of a larger upload.
304 * Check the result and continue the larger upload.
305 *
306 * @param cls the `struct GNUNET_FS_PublishContext *`
307 * of the larger upload
308 * @param uri URI of the published blocks
309 * @param emsg NULL on success, otherwise error message
310 */
311static void
312publish_kblocks_cont (void *cls,
313 const struct GNUNET_FS_Uri *uri,
314 const char *emsg)
315{
316 struct GNUNET_FS_PublishContext *pc = cls;
317 struct GNUNET_FS_FileInformation *p = pc->fi_pos;
318
319 pc->ksk_pc = NULL;
320 if (NULL != emsg)
321 {
322 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
323 "Error uploading KSK blocks: %s\n",
324 emsg);
325 signal_publish_error (p, pc, emsg);
326 GNUNET_FS_file_information_sync_ (p);
327 GNUNET_FS_publish_sync_ (pc);
328 GNUNET_assert (NULL == pc->upload_task);
329 pc->upload_task =
330 GNUNET_SCHEDULER_add_with_priority
331 (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
332 &GNUNET_FS_publish_main_,
333 pc);
334 return;
335 }
336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337 "KSK blocks published, moving on to next file\n");
338 if (NULL != p->dir)
339 signal_publish_completion (p, pc);
340 /* move on to next file */
341 if (NULL != p->next)
342 pc->fi_pos = p->next;
343 else
344 pc->fi_pos = p->dir;
345 GNUNET_FS_publish_sync_ (pc);
346 GNUNET_assert (NULL == pc->upload_task);
347 pc->upload_task =
348 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
349 &GNUNET_FS_publish_main_, pc);
350}
351
352
353/**
354 * Function called by the tree encoder to obtain
355 * a block of plaintext data (for the lowest level
356 * of the tree).
357 *
358 * @param cls our publishing context
359 * @param offset identifies which block to get
360 * @param max (maximum) number of bytes to get; returning
361 * fewer will also cause errors
362 * @param buf where to copy the plaintext buffer
363 * @param emsg location to store an error message (on error)
364 * @return number of bytes copied to buf, 0 on error
365 */
366static size_t
367block_reader (void *cls,
368 uint64_t offset,
369 size_t max,
370 void *buf,
371 char **emsg)
372{
373 struct GNUNET_FS_PublishContext *pc = cls;
374 struct GNUNET_FS_FileInformation *p;
375 const char *dd;
376 size_t pt_size;
377
378 p = pc->fi_pos;
379 if (GNUNET_YES == p->is_directory)
380 {
381 pt_size = GNUNET_MIN (max, p->data.dir.dir_size - offset);
382 dd = p->data.dir.dir_data;
383 GNUNET_memcpy (buf, &dd[offset], pt_size);
384 }
385 else
386 {
387 if (UINT64_MAX == offset)
388 {
389 if (&GNUNET_FS_data_reader_file_ == p->data.file.reader)
390 {
391 /* force closing the file to avoid keeping too many files open */
392 p->data.file.reader (p->data.file.reader_cls, offset, 0, NULL, NULL);
393 }
394 return 0;
395 }
396 pt_size = GNUNET_MIN (max, p->data.file.file_size - offset);
397 if (0 == pt_size)
398 return 0; /* calling reader with pt_size==0
399 * might free buf, so don't! */
400 if (pt_size !=
401 p->data.file.reader (p->data.file.reader_cls, offset, pt_size, buf,
402 emsg))
403 return 0;
404 }
405 return pt_size;
406}
407
408
409/**
410 * The tree encoder has finished processing a
411 * file. Call it's finish method and deal with
412 * the final result.
413 *
414 * @param cls our publishing context
415 */
416static void
417encode_cont (void *cls)
418{
419 struct GNUNET_FS_PublishContext *pc = cls;
420 struct GNUNET_FS_FileInformation *p;
421 struct GNUNET_FS_ProgressInfo pi;
422 char *emsg;
423 uint64_t flen;
424
425 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
426 "Finished with tree encoder\n");
427 p = pc->fi_pos;
428 p->chk_uri = GNUNET_FS_tree_encoder_get_uri (p->te);
429 GNUNET_FS_file_information_sync_ (p);
430 GNUNET_FS_tree_encoder_finish (p->te, &emsg);
431 p->te = NULL;
432 if (NULL != emsg)
433 {
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
435 "Error during tree walk: %s\n",
436 emsg);
437 GNUNET_asprintf (&p->emsg,
438 _ ("Publishing failed: %s"),
439 emsg);
440 GNUNET_free (emsg);
441 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
442 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
443 pi.value.publish.specifics.error.message = p->emsg;
444 p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
445 }
446 else
447 {
448 /* final progress event */
449 GNUNET_assert (NULL != p->chk_uri);
450 flen = GNUNET_FS_uri_chk_get_file_size (p->chk_uri);
451 pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
452 pi.value.publish.specifics.progress.data = NULL;
453 pi.value.publish.specifics.progress.offset = flen;
454 pi.value.publish.specifics.progress.data_len = 0;
455 pi.value.publish.specifics.progress.depth = GNUNET_FS_compute_depth (flen);
456 p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, flen);
457 }
458 /* continue with main */ /* continue with main */
459 GNUNET_assert (NULL == pc->upload_task);
460 pc->upload_task =
461 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
462 &GNUNET_FS_publish_main_, pc);
463}
464
465
466/**
467 * Function called asking for the current (encoded)
468 * block to be processed. After processing the
469 * client should either call #GNUNET_FS_tree_encoder_next
470 * or (on error) #GNUNET_FS_tree_encoder_finish.
471 *
472 * @param cls closure
473 * @param chk content hash key for the block
474 * @param offset offset of the block in the file
475 * @param depth depth of the block in the file, 0 for DBLOCK
476 * @param type type of the block (IBLOCK or DBLOCK)
477 * @param block the (encrypted) block
478 * @param block_size size of @a block (in bytes)
479 */
480static void
481block_proc (void *cls,
482 const struct ContentHashKey *chk,
483 uint64_t offset,
484 unsigned int depth,
485 enum GNUNET_BLOCK_Type type,
486 const void *block,
487 uint16_t block_size)
488{
489 struct GNUNET_FS_PublishContext *pc = cls;
490 struct GNUNET_FS_FileInformation *p;
491 struct OnDemandBlock odb;
492
493 p = pc->fi_pos;
494 if (NULL == pc->dsh)
495 {
496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497 "Waiting for datastore connection\n");
498 GNUNET_assert (NULL == pc->upload_task);
499 pc->upload_task =
500 GNUNET_SCHEDULER_add_with_priority
501 (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, pc);
502 return;
503 }
504
505 if ((GNUNET_YES != p->is_directory) &&
506 (GNUNET_YES == p->data.file.do_index) &&
507 (GNUNET_BLOCK_TYPE_FS_DBLOCK == type))
508 {
509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
510 "Indexing block `%s' for offset %llu with index size %u\n",
511 GNUNET_h2s (&chk->query),
512 (unsigned long long) offset,
513 (unsigned int) sizeof(struct OnDemandBlock));
514 odb.offset = GNUNET_htonll (offset);
515 odb.file_id = p->data.file.file_id;
516 GNUNET_assert (pc->qre == NULL);
517 pc->qre =
518 GNUNET_DATASTORE_put (pc->dsh,
519 (p->is_directory == GNUNET_YES) ? 0 : pc->rid,
520 &chk->query,
521 sizeof(struct OnDemandBlock),
522 &odb,
523 GNUNET_BLOCK_TYPE_FS_ONDEMAND,
524 p->bo.content_priority,
525 p->bo.anonymity_level,
526 p->bo.replication_level,
527 p->bo.expiration_time,
528 -2, 1,
529 &ds_put_cont, pc);
530 return;
531 }
532 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
533 "Publishing block `%s' for offset %llu with size %u\n",
534 GNUNET_h2s (&chk->query),
535 (unsigned long long) offset,
536 (unsigned int) block_size);
537 GNUNET_assert (pc->qre == NULL);
538 pc->qre =
539 GNUNET_DATASTORE_put (pc->dsh, (p->is_directory == GNUNET_YES) ? 0 :
540 pc->rid,
541 &chk->query,
542 block_size,
543 block,
544 type,
545 p->bo.content_priority,
546 p->bo.anonymity_level,
547 p->bo.replication_level,
548 p->bo.expiration_time,
549 -2, 1,
550 &ds_put_cont,
551 pc);
552}
553
554
555/**
556 * Function called with information about our
557 * progress in computing the tree encoding.
558 *
559 * @param cls closure
560 * @param offset where are we in the file
561 * @param pt_block plaintext of the currently processed block
562 * @param pt_size size of @a pt_block
563 * @param depth depth of the block in the tree, 0 for DBLOCK
564 */
565static void
566progress_proc (void *cls, uint64_t offset,
567 const void *pt_block,
568 size_t pt_size,
569 unsigned int depth)
570{
571 struct GNUNET_FS_PublishContext *pc = cls;
572 struct GNUNET_FS_FileInformation *p;
573 struct GNUNET_FS_FileInformation *par;
574 struct GNUNET_FS_ProgressInfo pi;
575
576 p = pc->fi_pos;
577 pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
578 pi.value.publish.specifics.progress.data = pt_block;
579 pi.value.publish.specifics.progress.offset = offset;
580 pi.value.publish.specifics.progress.data_len = pt_size;
581 pi.value.publish.specifics.progress.depth = depth;
582 p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, offset);
583 if ((0 != depth) ||
584 (GNUNET_YES == p->is_directory))
585 return;
586 while (NULL != (par = p->dir))
587 {
588 p = par;
589 GNUNET_assert (GNUNET_YES == par->is_directory);
590 p->data.dir.contents_completed += pt_size;
591 pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY;
592 pi.value.publish.specifics.progress_directory.completed =
593 p->data.dir.contents_completed;
594 pi.value.publish.specifics.progress_directory.total =
595 p->data.dir.contents_size;
596 pi.value.publish.specifics.progress_directory.eta =
597 GNUNET_TIME_calculate_eta (p->start_time,
598 p
599 ->data.dir.contents_completed,
600 p
601 ->data.dir.contents_size);
602 p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
603 }
604}
605
606
607/**
608 * We are uploading a file or directory; load (if necessary) the next
609 * block into memory, encrypt it and send it to the FS service. Then
610 * continue with the main task.
611 *
612 * @param pc overall upload data
613 */
614static void
615publish_content (struct GNUNET_FS_PublishContext *pc)
616{
617 struct GNUNET_FS_FileInformation *p;
618 char *emsg;
619 struct GNUNET_FS_DirectoryBuilder *db;
620 struct GNUNET_FS_FileInformation *dirpos;
621 void *raw_data;
622 uint64_t size;
623
624 p = pc->fi_pos;
625 GNUNET_assert (NULL != p);
626 if (NULL == p->te)
627 {
628 if (GNUNET_YES == p->is_directory)
629 {
630 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating directory\n");
631 db = GNUNET_FS_directory_builder_create (p->meta);
632 dirpos = p->data.dir.entries;
633 while (NULL != dirpos)
634 {
635 if (GNUNET_YES == dirpos->is_directory)
636 {
637 raw_data = dirpos->data.dir.dir_data;
638 dirpos->data.dir.dir_data = NULL;
639 }
640 else
641 {
642 raw_data = NULL;
643 if ((dirpos->data.file.file_size < MAX_INLINE_SIZE) &&
644 (dirpos->data.file.file_size > 0))
645 {
646 raw_data = GNUNET_malloc (dirpos->data.file.file_size);
647 emsg = NULL;
648 if (dirpos->data.file.file_size !=
649 dirpos->data.file.reader (dirpos->data.file.reader_cls, 0,
650 dirpos->data.file.file_size, raw_data,
651 &emsg))
652 {
653 GNUNET_free (emsg);
654 GNUNET_free (raw_data);
655 raw_data = NULL;
656 }
657 dirpos->data.file.reader (dirpos->data.file.reader_cls, UINT64_MAX,
658 0, 0, NULL);
659 }
660 }
661 GNUNET_FS_directory_builder_add (db, dirpos->chk_uri, dirpos->meta,
662 raw_data);
663 GNUNET_free (raw_data);
664 dirpos = dirpos->next;
665 }
666 GNUNET_free (p->data.dir.dir_data);
667 p->data.dir.dir_data = NULL;
668 p->data.dir.dir_size = 0;
669 GNUNET_FS_directory_builder_finish (db, &p->data.dir.dir_size,
670 &p->data.dir.dir_data);
671 GNUNET_FS_file_information_sync_ (p);
672 }
673 size = (GNUNET_YES == p->is_directory) ? p->data.dir.dir_size :
674 p->data.file.file_size;
675 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
676 "Creating tree encoder\n");
677 p->te =
678 GNUNET_FS_tree_encoder_create (pc->h, size, pc, &block_reader,
679 &block_proc, &progress_proc,
680 &encode_cont);
681 }
682 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
683 "Processing next block from tree\n");
684 GNUNET_FS_tree_encoder_next (p->te);
685}
686
687
688/**
689 * Check the response from the "fs" service to our 'start index'
690 * request.
691 *
692 * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
693 * @param msg the response we got
694 */
695static int
696check_index_start_failed (void *cls,
697 const struct GNUNET_MessageHeader *msg)
698{
699 size_t msize = ntohs (msg->size) - sizeof(*msg);
700 const char *emsg = (const char *) &msg[1];
701
702 if (emsg[msize - sizeof(struct GNUNET_MessageHeader) - 1] != '\0')
703 {
704 GNUNET_break (0);
705 return GNUNET_SYSERR;
706 }
707 return GNUNET_OK;
708}
709
710
711/**
712 * Process the response from the "fs" service to our 'start index'
713 * request.
714 *
715 * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
716 * @param msg the response we got
717 */
718static void
719handle_index_start_failed (void *cls,
720 const struct GNUNET_MessageHeader *msg)
721{
722 struct GNUNET_FS_PublishContext *pc = cls;
723 struct GNUNET_FS_FileInformation *p;
724 const char *emsg = (const char *) &msg[1];
725 char *msgtxt;
726
727 GNUNET_MQ_destroy (pc->mq);
728 pc->mq = NULL;
729 p = pc->fi_pos;
730 GNUNET_asprintf (&msgtxt,
731 _ ("Can not index file `%s': %s.\n"),
732 p->filename,
733 gettext (emsg));
734 signal_publish_error (p,
735 pc,
736 msgtxt);
737 GNUNET_free (msgtxt);
738 GNUNET_FS_file_information_sync_ (p);
739 GNUNET_FS_publish_sync_ (pc);
740}
741
742
743/**
744 * Process the response from the "fs" service to our 'start index'
745 * request.
746 *
747 * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
748 * @param msg the response we got
749 */
750static void
751handle_index_start_ok (void *cls,
752 const struct GNUNET_MessageHeader *msg)
753{
754 struct GNUNET_FS_PublishContext *pc = cls;
755 struct GNUNET_FS_FileInformation *p;
756
757 GNUNET_MQ_destroy (pc->mq);
758 pc->mq = NULL;
759 p = pc->fi_pos;
760 p->data.file.index_start_confirmed = GNUNET_YES;
761 GNUNET_FS_file_information_sync_ (p);
762 publish_content (pc);
763}
764
765
766/**
767 * Generic error handler, called with the appropriate error code and
768 * the same closure specified at the creation of the message queue.
769 * Not every message queue implementation supports an error handler.
770 *
771 * @param cls closure with the `struct GNUNET_FS_PublishContext *`
772 * @param error error code
773 */
774static void
775index_mq_error_handler (void *cls,
776 enum GNUNET_MQ_Error error)
777{
778 struct GNUNET_FS_PublishContext *pc = cls;
779 struct GNUNET_FS_FileInformation *p;
780
781 if (NULL != pc->mq)
782 {
783 GNUNET_MQ_destroy (pc->mq);
784 pc->mq = NULL;
785 }
786 p = pc->fi_pos;
787 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
788 _ ("Can not index file `%s': %s. Will try to insert instead.\n"),
789 p->filename,
790 _ ("error on index-start request to `fs' service"));
791 p->data.file.do_index = GNUNET_NO;
792 GNUNET_FS_file_information_sync_ (p);
793 publish_content (pc);
794}
795
796
797/**
798 * Function called once the hash computation over an
799 * indexed file has completed.
800 *
801 * @param cls closure, our publishing context
802 * @param res resulting hash, NULL on error
803 */
804static void
805hash_for_index_cb (void *cls,
806 const struct GNUNET_HashCode *res)
807{
808 struct GNUNET_FS_PublishContext *pc = cls;
809 struct GNUNET_MQ_MessageHandler handlers[] = {
810 GNUNET_MQ_hd_fixed_size (index_start_ok,
811 GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK,
812 struct GNUNET_MessageHeader,
813 pc),
814 GNUNET_MQ_hd_var_size (index_start_failed,
815 GNUNET_MESSAGE_TYPE_FS_INDEX_START_FAILED,
816 struct GNUNET_MessageHeader,
817 pc),
818 GNUNET_MQ_handler_end ()
819 };
820 struct GNUNET_FS_FileInformation *p;
821 struct GNUNET_MQ_Envelope *env;
822 struct IndexStartMessage *ism;
823 size_t slen;
824 uint64_t dev;
825 uint64_t ino;
826 char *fn;
827
828 pc->fhc = NULL;
829 p = pc->fi_pos;
830 if (NULL == res)
831 {
832 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
833 _ (
834 "Can not index file `%s': %s. Will try to insert instead.\n"),
835 p->filename,
836 _ ("failed to compute hash"));
837 p->data.file.do_index = GNUNET_NO;
838 GNUNET_FS_file_information_sync_ (p);
839 publish_content (pc);
840 return;
841 }
842 if (GNUNET_YES == p->data.file.index_start_confirmed)
843 {
844 publish_content (pc);
845 return;
846 }
847 fn = GNUNET_STRINGS_filename_expand (p->filename);
848 GNUNET_assert (fn != NULL);
849 slen = strlen (fn) + 1;
850 if (slen >=
851 GNUNET_MAX_MESSAGE_SIZE - sizeof(struct IndexStartMessage))
852 {
853 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
854 _
855 ("Can not index file `%s': %s. Will try to insert instead.\n"),
856 fn, _ ("filename too long"));
857 GNUNET_free (fn);
858 p->data.file.do_index = GNUNET_NO;
859 GNUNET_FS_file_information_sync_ (p);
860 publish_content (pc);
861 return;
862 }
863 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864 "Hash of indexed file `%s' is `%s'\n",
865 p->filename,
866 GNUNET_h2s (res));
867 if (0 != (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
868 {
869 p->data.file.file_id = *res;
870 p->data.file.have_hash = GNUNET_YES;
871 p->data.file.index_start_confirmed = GNUNET_YES;
872 GNUNET_FS_file_information_sync_ (p);
873 publish_content (pc);
874 GNUNET_free (fn);
875 return;
876 }
877 pc->mq = GNUNET_CLIENT_connect (pc->h->cfg,
878 "fs",
879 handlers,
880 &index_mq_error_handler,
881 pc);
882 if (NULL == pc->mq)
883 {
884 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
885 _ (
886 "Can not index file `%s': %s. Will try to insert instead.\n"),
887 p->filename,
888 _ ("could not connect to `fs' service"));
889 p->data.file.do_index = GNUNET_NO;
890 publish_content (pc);
891 GNUNET_free (fn);
892 return;
893 }
894 if (p->data.file.have_hash != GNUNET_YES)
895 {
896 p->data.file.file_id = *res;
897 p->data.file.have_hash = GNUNET_YES;
898 GNUNET_FS_file_information_sync_ (p);
899 }
900 env = GNUNET_MQ_msg_extra (ism,
901 slen,
902 GNUNET_MESSAGE_TYPE_FS_INDEX_START);
903 if (GNUNET_OK ==
904 GNUNET_DISK_file_get_identifiers (p->filename,
905 &dev,
906 &ino))
907 {
908 ism->device = GNUNET_htonll (dev);
909 ism->inode = GNUNET_htonll (ino);
910 }
911 else
912 {
913 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
914 _ ("Failed to get file identifiers for `%s'\n"),
915 p->filename);
916 }
917 ism->file_id = *res;
918 GNUNET_memcpy (&ism[1],
919 fn,
920 slen);
921 GNUNET_free (fn);
922 GNUNET_MQ_send (pc->mq,
923 env);
924}
925
926
927/**
928 * We've computed the CHK/LOC URI, now publish the KSKs (if applicable).
929 *
930 * @param pc publishing context to do this for
931 */
932static void
933publish_kblocks (struct GNUNET_FS_PublishContext *pc)
934{
935 struct GNUNET_FS_FileInformation *p;
936
937 p = pc->fi_pos;
938 /* upload of "p" complete, publish KBlocks! */
939 if (NULL != p->keywords)
940 {
941 pc->ksk_pc = GNUNET_FS_publish_ksk (pc->h,
942 p->keywords,
943 p->meta,
944 p->chk_uri,
945 &p->bo,
946 pc->options,
947 &publish_kblocks_cont,
948 pc);
949 }
950 else
951 {
952 publish_kblocks_cont (pc, p->chk_uri, NULL);
953 }
954}
955
956
957/**
958 * Process the response from the "fs" service to our LOC sign request.
959 *
960 * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
961 * @param sig the response we got
962 */
963static void
964handle_signature_response (void *cls,
965 const struct ResponseLocSignatureMessage *sig)
966{
967 struct GNUNET_FS_PublishContext *pc = cls;
968 struct GNUNET_FS_FileInformation *p;
969
970 p = pc->fi_pos;
971 p->chk_uri->type = GNUNET_FS_URI_LOC;
972 /* p->data.loc.fi kept from CHK before */
973 p->chk_uri->data.loc.peer = sig->peer;
974 p->chk_uri->data.loc.expirationTime
975 = GNUNET_TIME_absolute_ntoh (sig->expiration_time);
976 p->chk_uri->data.loc.contentSignature = sig->signature;
977 GNUNET_FS_file_information_sync_ (p);
978 GNUNET_FS_publish_sync_ (pc);
979 publish_kblocks (pc);
980}
981
982
983/**
984 * Generic error handler, called with the appropriate error code and
985 * the same closure specified at the creation of the message queue.
986 * Not every message queue implementation supports an error handler.
987 *
988 * @param cls closure with the `struct GNUNET_FS_PublishContext *`
989 * @param error error code
990 */
991static void
992loc_mq_error_handler (void *cls,
993 enum GNUNET_MQ_Error error)
994{
995 struct GNUNET_FS_PublishContext *pc = cls;
996
997 if (NULL != pc->mq)
998 {
999 GNUNET_MQ_destroy (pc->mq);
1000 pc->mq = NULL;
1001 }
1002 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1003 _ ("Can not create LOC URI. Will continue with CHK instead.\n"));
1004 publish_kblocks (pc);
1005}
1006
1007
1008/**
1009 * We're publishing without anonymity. Contact the FS service
1010 * to create a signed LOC URI for further processing, then
1011 * continue with KSKs.
1012 *
1013 * @param pc the publishing context do to this for
1014 */
1015static void
1016create_loc_uri (struct GNUNET_FS_PublishContext *pc)
1017{
1018 struct GNUNET_MQ_MessageHandler handlers[] = {
1019 GNUNET_MQ_hd_fixed_size (signature_response,
1020 GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGNATURE,
1021 struct ResponseLocSignatureMessage,
1022 pc),
1023 GNUNET_MQ_handler_end ()
1024 };
1025 struct GNUNET_MQ_Envelope *env;
1026 struct RequestLocSignatureMessage *req;
1027 struct GNUNET_FS_FileInformation *p;
1028
1029 if (NULL != pc->mq)
1030 GNUNET_MQ_destroy (pc->mq);
1031 pc->mq = GNUNET_CLIENT_connect (pc->h->cfg,
1032 "fs",
1033 handlers,
1034 &loc_mq_error_handler,
1035 pc);
1036 if (NULL == pc->mq)
1037 {
1038 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1039 _ (
1040 "Can not create LOC URI. Will continue with CHK instead.\n"));
1041 publish_kblocks (pc);
1042 return;
1043 }
1044 p = pc->fi_pos;
1045 env = GNUNET_MQ_msg (req,
1046 GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGN);
1047 req->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
1048 req->expiration_time = GNUNET_TIME_absolute_hton (p->bo.expiration_time);
1049 req->chk = p->chk_uri->data.chk.chk;
1050 req->file_length = GNUNET_htonll (p->chk_uri->data.chk.file_length);
1051 GNUNET_MQ_send (pc->mq,
1052 env);
1053}
1054
1055
1056/**
1057 * Main function that performs the upload.
1058 *
1059 * @param cls `struct GNUNET_FS_PublishContext *` identifies the upload
1060 */
1061void
1062GNUNET_FS_publish_main_ (void *cls)
1063{
1064 struct GNUNET_FS_PublishContext *pc = cls;
1065 struct GNUNET_FS_ProgressInfo pi;
1066 struct GNUNET_FS_FileInformation *p;
1067 char *fn;
1068
1069 pc->upload_task = NULL;
1070 p = pc->fi_pos;
1071 if (NULL == p)
1072 {
1073 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1074 "Publishing complete, now publishing SKS and KSK blocks.\n");
1075 /* upload of entire hierarchy complete,
1076 * publish namespace entries */
1077 GNUNET_FS_publish_sync_ (pc);
1078 publish_sblock (pc);
1079 return;
1080 }
1081 /* find starting position */
1082 while ((GNUNET_YES == p->is_directory) &&
1083 (NULL != p->data.dir.entries) &&
1084 (NULL == p->emsg) &&
1085 (NULL == p->data.dir.entries->chk_uri))
1086 {
1087 p = p->data.dir.entries;
1088 pc->fi_pos = p;
1089 GNUNET_FS_publish_sync_ (pc);
1090 }
1091 /* abort on error */
1092 if (NULL != p->emsg)
1093 {
1094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1095 "Error uploading: %s\n",
1096 p->emsg);
1097 /* error with current file, abort all
1098 * related files as well! */
1099 while (NULL != p->dir)
1100 {
1101 fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
1102 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
1103 p = p->dir;
1104 if (fn != NULL)
1105 {
1106 GNUNET_asprintf (&p->emsg,
1107 _ ("Recursive upload failed at `%s': %s"),
1108 fn,
1109 p->emsg);
1110 GNUNET_free (fn);
1111 }
1112 else
1113 {
1114 GNUNET_asprintf (&p->emsg,
1115 _ ("Recursive upload failed: %s"),
1116 p->emsg);
1117 }
1118 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
1119 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
1120 pi.value.publish.specifics.error.message = p->emsg;
1121 p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
1122 }
1123 pc->all_done = GNUNET_YES;
1124 GNUNET_FS_publish_sync_ (pc);
1125 return;
1126 }
1127 /* handle completion */
1128 if (NULL != p->chk_uri)
1129 {
1130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1131 "File upload complete, now publishing KSK blocks.\n");
1132 GNUNET_FS_publish_sync_ (pc);
1133
1134 if ((0 == p->bo.anonymity_level) &&
1135 (GNUNET_YES !=
1136 GNUNET_FS_uri_test_loc (p->chk_uri)))
1137 {
1138 /* zero anonymity, box CHK URI in LOC URI */
1139 create_loc_uri (pc);
1140 }
1141 else
1142 {
1143 publish_kblocks (pc);
1144 }
1145 return;
1146 }
1147 if ((GNUNET_YES != p->is_directory) && (p->data.file.do_index))
1148 {
1149 if (NULL == p->filename)
1150 {
1151 p->data.file.do_index = GNUNET_NO;
1152 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1153 _ (
1154 "Can not index file `%s': %s. Will try to insert instead.\n"),
1155 "<no-name>",
1156 _ ("needs to be an actual file"));
1157 GNUNET_FS_file_information_sync_ (p);
1158 publish_content (pc);
1159 return;
1160 }
1161 if (p->data.file.have_hash)
1162 {
1163 hash_for_index_cb (pc, &p->data.file.file_id);
1164 }
1165 else
1166 {
1167 p->start_time = GNUNET_TIME_absolute_get ();
1168 pc->fhc =
1169 GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, p->filename,
1170 HASHING_BLOCKSIZE, &hash_for_index_cb, pc);
1171 }
1172 return;
1173 }
1174 publish_content (pc);
1175}
1176
1177
1178/**
1179 * Signal the FS's progress function that we are starting
1180 * an upload.
1181 *
1182 * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1183 * @param fi the entry in the publish-structure
1184 * @param length length of the file or directory
1185 * @param meta metadata for the file or directory (can be modified)
1186 * @param uri pointer to the keywords that will be used for this entry (can be modified)
1187 * @param bo block options
1188 * @param do_index should we index?
1189 * @param client_info pointer to client context set upon creation (can be modified)
1190 * @return #GNUNET_OK to continue (always)
1191 */
1192static int
1193fip_signal_start (void *cls,
1194 struct GNUNET_FS_FileInformation *fi,
1195 uint64_t length,
1196 struct GNUNET_CONTAINER_MetaData *meta,
1197 struct GNUNET_FS_Uri **uri,
1198 struct GNUNET_FS_BlockOptions *bo,
1199 int *do_index,
1200 void **client_info)
1201{
1202 struct GNUNET_FS_PublishContext *pc = cls;
1203 struct GNUNET_FS_ProgressInfo pi;
1204 unsigned int kc;
1205 uint64_t left;
1206
1207 if (GNUNET_YES == pc->skip_next_fi_callback)
1208 {
1209 pc->skip_next_fi_callback = GNUNET_NO;
1210 return GNUNET_OK;
1211 }
1212 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1213 "Starting publish operation\n");
1214 if (*do_index)
1215 {
1216 /* space for on-demand blocks */
1217 pc->reserve_space +=
1218 ((length + DBLOCK_SIZE
1219 - 1) / DBLOCK_SIZE) * sizeof(struct OnDemandBlock);
1220 }
1221 else
1222 {
1223 /* space for DBlocks */
1224 pc->reserve_space += length;
1225 }
1226 /* entries for IBlocks and DBlocks, space for IBlocks */
1227 left = length;
1228 while (1)
1229 {
1230 left = (left + DBLOCK_SIZE - 1) / DBLOCK_SIZE;
1231 pc->reserve_entries += left;
1232 if (left <= 1)
1233 break;
1234 left = left * sizeof(struct ContentHashKey);
1235 pc->reserve_space += left;
1236 }
1237 pc->reserve_entries++;
1238 /* entries and space for keywords */
1239 if (NULL != *uri)
1240 {
1241 kc = GNUNET_FS_uri_ksk_get_keyword_count (*uri);
1242 pc->reserve_entries += kc;
1243 pc->reserve_space += GNUNET_MAX_MESSAGE_SIZE * kc;
1244 }
1245 pi.status = GNUNET_FS_STATUS_PUBLISH_START;
1246 *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0);
1247 GNUNET_FS_file_information_sync_ (fi);
1248 if ((fi->is_directory) && (fi->dir != NULL))
1249 {
1250 /* We are a directory, and we are not top-level; process entries in directory */
1251 pc->skip_next_fi_callback = GNUNET_YES;
1252 GNUNET_FS_file_information_inspect (fi, &fip_signal_start, pc);
1253 }
1254 return GNUNET_OK;
1255}
1256
1257
1258/**
1259 * Actually signal the FS's progress function that we are suspending
1260 * an upload.
1261 *
1262 * @param fi the entry in the publish-structure
1263 * @param pc the publish context of which a file is being suspended
1264 */
1265static void
1266suspend_operation (struct GNUNET_FS_FileInformation *fi,
1267 struct GNUNET_FS_PublishContext *pc)
1268{
1269 struct GNUNET_FS_ProgressInfo pi;
1270 uint64_t off;
1271
1272 if (NULL != pc->ksk_pc)
1273 {
1274 GNUNET_FS_publish_ksk_cancel (pc->ksk_pc);
1275 pc->ksk_pc = NULL;
1276 }
1277 if (NULL != pc->sks_pc)
1278 {
1279 GNUNET_FS_publish_sks_cancel (pc->sks_pc);
1280 pc->sks_pc = NULL;
1281 }
1282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1283 "Suspending publish operation\n");
1284 GNUNET_free (fi->serialization);
1285 fi->serialization = NULL;
1286 off = (NULL == fi->chk_uri) ? 0 : (GNUNET_YES == fi->is_directory) ?
1287 fi->data.dir.dir_size : fi->data.file.file_size;
1288 pi.status = GNUNET_FS_STATUS_PUBLISH_SUSPEND;
1289 GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1290 if (NULL != pc->qre)
1291 {
1292 GNUNET_DATASTORE_cancel (pc->qre);
1293 pc->qre = NULL;
1294 }
1295 if (NULL != pc->dsh)
1296 {
1297 GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1298 pc->dsh = NULL;
1299 }
1300 pc->rid = 0;
1301}
1302
1303
1304/**
1305 * Signal the FS's progress function that we are suspending
1306 * an upload. Performs the recursion.
1307 *
1308 * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1309 * @param fi the entry in the publish-structure
1310 * @param length length of the file or directory
1311 * @param meta metadata for the file or directory (can be modified)
1312 * @param uri pointer to the keywords that will be used for this entry (can be modified)
1313 * @param bo block options
1314 * @param do_index should we index?
1315 * @param client_info pointer to client context set upon creation (can be modified)
1316 * @return #GNUNET_OK to continue (always)
1317 */
1318static int
1319fip_signal_suspend (void *cls,
1320 struct GNUNET_FS_FileInformation *fi,
1321 uint64_t length,
1322 struct GNUNET_CONTAINER_MetaData *meta,
1323 struct GNUNET_FS_Uri **uri,
1324 struct GNUNET_FS_BlockOptions *bo,
1325 int *do_index,
1326 void **client_info)
1327{
1328 struct GNUNET_FS_PublishContext *pc = cls;
1329
1330 if (GNUNET_YES == pc->skip_next_fi_callback)
1331 {
1332 pc->skip_next_fi_callback = GNUNET_NO;
1333 return GNUNET_OK;
1334 }
1335 if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1336 {
1337 /* process entries in directory */
1338 pc->skip_next_fi_callback = GNUNET_YES;
1339 GNUNET_FS_file_information_inspect (fi, &fip_signal_suspend, pc);
1340 }
1341 suspend_operation (fi, pc);
1342 *client_info = NULL;
1343 return GNUNET_OK;
1344}
1345
1346
1347/**
1348 * Create SUSPEND event for the given publish operation
1349 * and then clean up our state (without stop signal).
1350 *
1351 * @param cls the `struct GNUNET_FS_PublishContext` to signal for
1352 */
1353void
1354GNUNET_FS_publish_signal_suspend_ (void *cls)
1355{
1356 struct GNUNET_FS_PublishContext *pc = cls;
1357
1358 if (NULL != pc->upload_task)
1359 {
1360 GNUNET_SCHEDULER_cancel (pc->upload_task);
1361 pc->upload_task = NULL;
1362 }
1363 pc->skip_next_fi_callback = GNUNET_YES;
1364 GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_suspend, pc);
1365 suspend_operation (pc->fi, pc);
1366 GNUNET_FS_end_top (pc->h, pc->top);
1367 pc->top = NULL;
1368 publish_cleanup (pc);
1369}
1370
1371
1372/**
1373 * We have gotten a reply for our space reservation request.
1374 * Either fail (insufficient space) or start publishing for good.
1375 *
1376 * @param cls the `struct GNUNET_FS_PublishContext *`
1377 * @param success positive reservation ID on success
1378 * @param min_expiration minimum expiration time required for content to be stored
1379 * @param msg error message on error, otherwise NULL
1380 */
1381static void
1382finish_reserve (void *cls,
1383 int success,
1384 struct GNUNET_TIME_Absolute min_expiration,
1385 const char *msg)
1386{
1387 struct GNUNET_FS_PublishContext *pc = cls;
1388
1389 pc->qre = NULL;
1390 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1391 "Reservation complete (%d)!\n",
1392 success);
1393 if ((msg != NULL) || (success <= 0))
1394 {
1395 GNUNET_asprintf (&pc->fi->emsg,
1396 _ ("Datastore failure: %s"),
1397 msg);
1398 signal_publish_error (pc->fi, pc, pc->fi->emsg);
1399 return;
1400 }
1401 pc->rid = success;
1402 GNUNET_assert (NULL == pc->upload_task);
1403 pc->upload_task =
1404 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1405 &GNUNET_FS_publish_main_, pc);
1406}
1407
1408
1409/**
1410 * Calculate the total size of all of the files in the directory structure.
1411 *
1412 * @param fi file structure to traverse
1413 */
1414static uint64_t
1415compute_contents_size (struct GNUNET_FS_FileInformation *fi)
1416{
1417 struct GNUNET_FS_FileInformation *ent;
1418
1419 if (GNUNET_YES != fi->is_directory)
1420 return fi->data.file.file_size;
1421 fi->data.dir.contents_size = 0;
1422 for (ent = fi->data.dir.entries; NULL != ent; ent = ent->next)
1423 fi->data.dir.contents_size += compute_contents_size (ent);
1424 return fi->data.dir.contents_size;
1425}
1426
1427
1428/**
1429 * Publish a file or directory.
1430 *
1431 * @param h handle to the file sharing subsystem
1432 * @param fi information about the file or directory structure to publish
1433 * @param ns namespace to publish the file in, NULL for no namespace
1434 * @param nid identifier to use for the published content in the namespace
1435 * (can be NULL, must be NULL if namespace is NULL)
1436 * @param nuid update-identifier that will be used for future updates
1437 * (can be NULL, must be NULL if namespace or nid is NULL)
1438 * @param options options for the publication
1439 * @return context that can be used to control the publish operation
1440 */
1441struct GNUNET_FS_PublishContext *
1442GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
1443 struct GNUNET_FS_FileInformation *fi,
1444 const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
1445 const char *nid,
1446 const char *nuid,
1447 enum GNUNET_FS_PublishOptions options)
1448{
1449 struct GNUNET_FS_PublishContext *ret;
1450 struct GNUNET_DATASTORE_Handle *dsh;
1451
1452 GNUNET_assert (NULL != h);
1453 compute_contents_size (fi);
1454 if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1455 {
1456 dsh = GNUNET_DATASTORE_connect (h->cfg);
1457 if (NULL == dsh)
1458 return NULL;
1459 }
1460 else
1461 {
1462 dsh = NULL;
1463 }
1464 ret = GNUNET_new (struct GNUNET_FS_PublishContext);
1465 ret->dsh = dsh;
1466 ret->h = h;
1467 ret->fi = fi;
1468 if (NULL != ns)
1469 {
1470 ret->ns = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
1471 *ret->ns = *ns;
1472 GNUNET_assert (NULL != nid);
1473 ret->nid = GNUNET_strdup (nid);
1474 if (NULL != nuid)
1475 ret->nuid = GNUNET_strdup (nuid);
1476 }
1477 ret->options = options;
1478 /* signal start */
1479 GNUNET_FS_file_information_inspect (ret->fi, &fip_signal_start, ret);
1480 ret->fi_pos = ret->fi;
1481 ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, ret);
1482 GNUNET_FS_publish_sync_ (ret);
1483 if (NULL != ret->dsh)
1484 {
1485 GNUNET_assert (NULL == ret->qre);
1486 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1487 _ (
1488 "Reserving space for %u entries and %llu bytes for publication\n"),
1489 (unsigned int) ret->reserve_entries,
1490 (unsigned long long) ret->reserve_space);
1491 ret->qre =
1492 GNUNET_DATASTORE_reserve (ret->dsh, ret->reserve_space,
1493 ret->reserve_entries,
1494 &finish_reserve,
1495 ret);
1496 }
1497 else
1498 {
1499 GNUNET_assert (NULL == ret->upload_task);
1500 ret->upload_task =
1501 GNUNET_SCHEDULER_add_with_priority
1502 (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, ret);
1503 }
1504 return ret;
1505}
1506
1507
1508/**
1509 * Signal the FS's progress function that we are stopping
1510 * an upload.
1511 *
1512 * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1513 * @param fi the entry in the publish-structure
1514 * @param length length of the file or directory
1515 * @param meta metadata for the file or directory (can be modified)
1516 * @param uri pointer to the keywords that will be used for this entry (can be modified)
1517 * @param bo block options (can be modified)
1518 * @param do_index should we index?
1519 * @param client_info pointer to client context set upon creation (can be modified)
1520 * @return #GNUNET_OK to continue (always)
1521 */
1522static int
1523fip_signal_stop (void *cls,
1524 struct GNUNET_FS_FileInformation *fi,
1525 uint64_t length,
1526 struct GNUNET_CONTAINER_MetaData *meta,
1527 struct GNUNET_FS_Uri **uri,
1528 struct GNUNET_FS_BlockOptions *bo,
1529 int *do_index, void **client_info)
1530{
1531 struct GNUNET_FS_PublishContext *pc = cls;
1532 struct GNUNET_FS_ProgressInfo pi;
1533 uint64_t off;
1534
1535 if (GNUNET_YES == pc->skip_next_fi_callback)
1536 {
1537 pc->skip_next_fi_callback = GNUNET_NO;
1538 return GNUNET_OK;
1539 }
1540 if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1541 {
1542 /* process entries in directory first */
1543 pc->skip_next_fi_callback = GNUNET_YES;
1544 GNUNET_FS_file_information_inspect (fi, &fip_signal_stop, pc);
1545 }
1546 if (NULL != fi->serialization)
1547 {
1548 GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1549 fi->serialization);
1550 GNUNET_free (fi->serialization);
1551 fi->serialization = NULL;
1552 }
1553 off = (fi->chk_uri == NULL) ? 0 : length;
1554 pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1555 GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1556 *client_info = NULL;
1557 return GNUNET_OK;
1558}
1559
1560
1561/**
1562 * Stop an upload. Will abort incomplete uploads (but
1563 * not remove blocks that have already been published) or
1564 * simply clean up the state for completed uploads.
1565 * Must NOT be called from within the event callback!
1566 *
1567 * @param pc context for the upload to stop
1568 */
1569void
1570GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *pc)
1571{
1572 struct GNUNET_FS_ProgressInfo pi;
1573 uint64_t off;
1574
1575 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1576 "Publish stop called\n");
1577 GNUNET_FS_end_top (pc->h, pc->top);
1578 if (NULL != pc->ksk_pc)
1579 {
1580 GNUNET_FS_publish_ksk_cancel (pc->ksk_pc);
1581 pc->ksk_pc = NULL;
1582 }
1583 if (NULL != pc->sks_pc)
1584 {
1585 GNUNET_FS_publish_sks_cancel (pc->sks_pc);
1586 pc->sks_pc = NULL;
1587 }
1588 if (NULL != pc->upload_task)
1589 {
1590 GNUNET_SCHEDULER_cancel (pc->upload_task);
1591 pc->upload_task = NULL;
1592 }
1593 pc->skip_next_fi_callback = GNUNET_YES;
1594 GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_stop, pc);
1595
1596 if (NULL != pc->fi->serialization)
1597 {
1598 GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1599 pc->fi->serialization);
1600 GNUNET_free (pc->fi->serialization);
1601 pc->fi->serialization = NULL;
1602 }
1603 off = (NULL == pc->fi->chk_uri) ? 0 : GNUNET_ntohll (
1604 pc->fi->chk_uri->data.chk.file_length);
1605
1606 if (NULL != pc->serialization)
1607 {
1608 GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1609 pc->serialization);
1610 GNUNET_free (pc->serialization);
1611 pc->serialization = NULL;
1612 }
1613 if (NULL != pc->qre)
1614 {
1615 GNUNET_DATASTORE_cancel (pc->qre);
1616 pc->qre = NULL;
1617 }
1618 pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1619 GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi, off));
1620 publish_cleanup (pc);
1621}
1622
1623
1624/* end of fs_publish.c */
diff --git a/src/fs/fs_publish_ksk.c b/src/fs/fs_publish_ksk.c
deleted file mode 100644
index 372ac705d..000000000
--- a/src/fs/fs_publish_ksk.c
+++ /dev/null
@@ -1,260 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_publish_ksk.c
23 * @brief publish a URI under a keyword in GNUnet
24 * @see https://gnunet.org/encoding and #2564
25 * @author Krista Bennett
26 * @author Christian Grothoff
27 */
28
29#include "platform.h"
30#include "gnunet_constants.h"
31#include "gnunet_signatures.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_fs_service.h"
34#include "fs_api.h"
35#include "fs_tree.h"
36#include "fs_publish_ublock.h"
37
38/**
39 * Context for the KSK publication.
40 */
41struct GNUNET_FS_PublishKskContext
42{
43 /**
44 * Keywords to use.
45 */
46 struct GNUNET_FS_Uri *ksk_uri;
47
48 /**
49 * URI to publish.
50 */
51 struct GNUNET_FS_Uri *uri;
52
53 /**
54 * Metadata to use.
55 */
56 struct GNUNET_CONTAINER_MetaData *meta;
57
58 /**
59 * Global FS context.
60 */
61 struct GNUNET_FS_Handle *h;
62
63 /**
64 * UBlock publishing operation that is active.
65 */
66 struct GNUNET_FS_PublishUblockContext *uc;
67
68 /**
69 * Handle to the datastore, NULL if we are just simulating.
70 */
71 struct GNUNET_DATASTORE_Handle *dsh;
72
73 /**
74 * Current task.
75 */
76 struct GNUNET_SCHEDULER_Task *ksk_task;
77
78 /**
79 * Function to call once we're done.
80 */
81 GNUNET_FS_PublishContinuation cont;
82
83 /**
84 * Closure for cont.
85 */
86 void *cont_cls;
87
88 /**
89 * When should the KBlocks expire?
90 */
91 struct GNUNET_FS_BlockOptions bo;
92
93 /**
94 * Options to use.
95 */
96 enum GNUNET_FS_PublishOptions options;
97
98 /**
99 * Keyword that we are currently processing.
100 */
101 unsigned int i;
102};
103
104
105/**
106 * Continuation of #GNUNET_FS_publish_ksk() that performs
107 * the actual publishing operation (iterating over all
108 * of the keywords).
109 *
110 * @param cls closure of type `struct PublishKskContext *`
111 */
112static void
113publish_ksk_cont (void *cls);
114
115
116/**
117 * Function called by the datastore API with
118 * the result from the PUT request.
119 *
120 * @param cls closure of type `struct GNUNET_FS_PublishKskContext *`
121 * @param msg error message (or NULL)
122 */
123static void
124kb_put_cont (void *cls,
125 const char *msg)
126{
127 struct GNUNET_FS_PublishKskContext *pkc = cls;
128
129 pkc->uc = NULL;
130 if (NULL != msg)
131 {
132 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
133 "KBlock PUT operation failed: %s\n", msg);
134 pkc->cont (pkc->cont_cls, NULL, msg);
135 GNUNET_FS_publish_ksk_cancel (pkc);
136 return;
137 }
138 pkc->ksk_task = GNUNET_SCHEDULER_add_now (&publish_ksk_cont, pkc);
139}
140
141
142/**
143 * Continuation of #GNUNET_FS_publish_ksk() that performs the actual
144 * publishing operation (iterating over all of the keywords).
145 *
146 * @param cls closure of type `struct GNUNET_FS_PublishKskContext *`
147 */
148static void
149publish_ksk_cont (void *cls)
150{
151 struct GNUNET_FS_PublishKskContext *pkc = cls;
152 const char *keyword;
153
154 pkc->ksk_task = NULL;
155 if ((pkc->i == pkc->ksk_uri->data.ksk.keywordCount) ||
156 (NULL == pkc->dsh))
157 {
158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
159 "KSK PUT operation complete\n");
160 pkc->cont (pkc->cont_cls, pkc->ksk_uri,
161 NULL);
162 GNUNET_FS_publish_ksk_cancel (pkc);
163 return;
164 }
165 keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
166 pkc->uc = GNUNET_FS_publish_ublock_ (pkc->h,
167 pkc->dsh,
168 keyword + 1 /* skip '+' */,
169 NULL,
170 GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
171 pkc->meta,
172 pkc->uri,
173 &pkc->bo,
174 pkc->options,
175 &kb_put_cont, pkc);
176}
177
178
179/**
180 * Publish a CHK under various keywords on GNUnet.
181 *
182 * @param h handle to the file sharing subsystem
183 * @param ksk_uri keywords to use
184 * @param meta metadata to use
185 * @param uri URI to refer to in the KBlock
186 * @param bo per-block options
187 * @param options publication options
188 * @param cont continuation
189 * @param cont_cls closure for cont
190 * @return NULL on error ('cont' will still be called)
191 */
192struct GNUNET_FS_PublishKskContext *
193GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
194 const struct GNUNET_FS_Uri *ksk_uri,
195 const struct GNUNET_CONTAINER_MetaData *meta,
196 const struct GNUNET_FS_Uri *uri,
197 const struct GNUNET_FS_BlockOptions *bo,
198 enum GNUNET_FS_PublishOptions options,
199 GNUNET_FS_PublishContinuation cont, void *cont_cls)
200{
201 struct GNUNET_FS_PublishKskContext *pkc;
202
203 GNUNET_assert (NULL != uri);
204 pkc = GNUNET_new (struct GNUNET_FS_PublishKskContext);
205 pkc->h = h;
206 pkc->bo = *bo;
207 pkc->options = options;
208 pkc->cont = cont;
209 pkc->cont_cls = cont_cls;
210 pkc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
211 if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
212 {
213 pkc->dsh = GNUNET_DATASTORE_connect (h->cfg);
214 if (NULL == pkc->dsh)
215 {
216 cont (cont_cls,
217 NULL,
218 _ ("Could not connect to datastore."));
219 GNUNET_free (pkc);
220 return NULL;
221 }
222 }
223 pkc->uri = GNUNET_FS_uri_dup (uri);
224 pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
225 pkc->ksk_task = GNUNET_SCHEDULER_add_now (&publish_ksk_cont, pkc);
226 return pkc;
227}
228
229
230/**
231 * Abort the KSK publishing operation.
232 *
233 * @param pkc context of the operation to abort.
234 */
235void
236GNUNET_FS_publish_ksk_cancel (struct GNUNET_FS_PublishKskContext *pkc)
237{
238 if (NULL != pkc->ksk_task)
239 {
240 GNUNET_SCHEDULER_cancel (pkc->ksk_task);
241 pkc->ksk_task = NULL;
242 }
243 if (NULL != pkc->uc)
244 {
245 GNUNET_FS_publish_ublock_cancel_ (pkc->uc);
246 pkc->uc = NULL;
247 }
248 if (NULL != pkc->dsh)
249 {
250 GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
251 pkc->dsh = NULL;
252 }
253 GNUNET_CONTAINER_meta_data_destroy (pkc->meta);
254 GNUNET_FS_uri_destroy (pkc->ksk_uri);
255 GNUNET_FS_uri_destroy (pkc->uri);
256 GNUNET_free (pkc);
257}
258
259
260/* end of fs_publish_ksk.c */
diff --git a/src/fs/fs_publish_ublock.c b/src/fs/fs_publish_ublock.c
deleted file mode 100644
index fd6f2bd72..000000000
--- a/src/fs/fs_publish_ublock.c
+++ /dev/null
@@ -1,324 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_publish_ublock.c
23 * @brief publish a UBLOCK in GNUnet
24 * @see https://gnunet.org/encoding and #2564
25 * @author Krista Bennett
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_constants.h"
30#include "gnunet_signatures.h"
31#include "fs_publish_ublock.h"
32#include "fs_api.h"
33#include "fs_tree.h"
34
35
36/**
37 * Derive the key for symmetric encryption/decryption from
38 * the public key and the label.
39 *
40 * @param skey where to store symmetric key
41 * @param iv where to store the IV
42 * @param label label to use for key derivation
43 * @param pub public key to use for key derivation
44 */
45static void
46derive_ublock_encryption_key (struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
47 struct GNUNET_CRYPTO_SymmetricInitializationVector
48 *iv,
49 const char *label,
50 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
51{
52 struct GNUNET_HashCode key;
53
54 /* derive key from 'label' and public key of the namespace */
55 GNUNET_assert (GNUNET_YES ==
56 GNUNET_CRYPTO_kdf (&key, sizeof(key),
57 "UBLOCK-ENC", strlen ("UBLOCK-ENC"),
58 label, strlen (label),
59 pub, sizeof(*pub),
60 NULL, 0));
61 GNUNET_CRYPTO_hash_to_aes_key (&key, skey, iv);
62}
63
64
65/**
66 * Decrypt the given UBlock, storing the result in output.
67 *
68 * @param input input data
69 * @param input_len number of bytes in @a input
70 * @param ns public key under which the UBlock was stored
71 * @param label label under which the UBlock was stored
72 * @param output where to write the result, has input_len bytes
73 */
74void
75GNUNET_FS_ublock_decrypt_ (const void *input,
76 size_t input_len,
77 const struct GNUNET_CRYPTO_EcdsaPublicKey *ns,
78 const char *label,
79 void *output)
80{
81 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
82 struct GNUNET_CRYPTO_SymmetricSessionKey skey;
83
84 derive_ublock_encryption_key (&skey, &iv,
85 label, ns);
86 GNUNET_CRYPTO_symmetric_decrypt (input, input_len,
87 &skey, &iv,
88 output);
89}
90
91
92/**
93 * Context for 'ublock_put_cont'.
94 */
95struct GNUNET_FS_PublishUblockContext
96{
97 /**
98 * Function to call when done.
99 */
100 GNUNET_FS_UBlockContinuation cont;
101
102 /**
103 * Closure of 'cont'.
104 */
105 void *cont_cls;
106
107 /**
108 * Handle for active datastore operation.
109 */
110 struct GNUNET_DATASTORE_QueueEntry *qre;
111
112 /**
113 * Task to run continuation asynchronously.
114 */
115 struct GNUNET_SCHEDULER_Task *task;
116};
117
118
119/**
120 * Continuation of #GNUNET_FS_publish_ublock_().
121 *
122 * @param cls closure of type "struct GNUNET_FS_PublishUblockContext*"
123 * @param success #GNUNET_SYSERR on failure (including timeout/queue drop)
124 * #GNUNET_NO if content was already there
125 * #GNUNET_YES (or other positive value) on success
126 * @param min_expiration minimum expiration time required for 0-priority content to be stored
127 * by the datacache at this time, zero for unknown, forever if we have no
128 * space for 0-priority content
129 * @param msg NULL on success, otherwise an error message
130 */
131static void
132ublock_put_cont (void *cls,
133 int32_t success,
134 struct GNUNET_TIME_Absolute min_expiration,
135 const char *msg)
136{
137 struct GNUNET_FS_PublishUblockContext *uc = cls;
138
139 uc->qre = NULL;
140 uc->cont (uc->cont_cls, msg);
141 GNUNET_free (uc);
142}
143
144
145/**
146 * Run the continuation.
147 *
148 * @param cls the `struct GNUNET_FS_PublishUblockContext *`
149 */
150static void
151run_cont (void *cls)
152{
153 struct GNUNET_FS_PublishUblockContext *uc = cls;
154
155 uc->task = NULL;
156 uc->cont (uc->cont_cls, NULL);
157 GNUNET_free (uc);
158}
159
160
161/**
162 * Publish a UBlock.
163 *
164 * @param h handle to the file sharing subsystem
165 * @param dsh datastore handle to use for storage operation
166 * @param label identifier to use
167 * @param ulabel update label to use, may be an empty string for none
168 * @param ns namespace to publish in
169 * @param meta metadata to use
170 * @param uri URI to refer to in the UBlock
171 * @param bo per-block options
172 * @param options publication options
173 * @param cont continuation
174 * @param cont_cls closure for @a cont
175 * @return NULL on error (@a cont will still be called)
176 */
177struct GNUNET_FS_PublishUblockContext *
178GNUNET_FS_publish_ublock_ (struct GNUNET_FS_Handle *h,
179 struct GNUNET_DATASTORE_Handle *dsh,
180 const char *label,
181 const char *ulabel,
182 const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
183 const struct GNUNET_CONTAINER_MetaData *meta,
184 const struct GNUNET_FS_Uri *uri,
185 const struct GNUNET_FS_BlockOptions *bo,
186 enum GNUNET_FS_PublishOptions options,
187 GNUNET_FS_UBlockContinuation cont, void *cont_cls)
188{
189 struct GNUNET_FS_PublishUblockContext *uc;
190 struct GNUNET_HashCode query;
191 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
192 struct GNUNET_CRYPTO_SymmetricSessionKey skey;
193 struct GNUNET_CRYPTO_EcdsaPrivateKey *nsd;
194 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
195 char *uris;
196 size_t size;
197 char *kbe;
198 char *sptr;
199 ssize_t mdsize;
200 size_t slen;
201 size_t ulen;
202 struct UBlock *ub_plain;
203 struct UBlock *ub_enc;
204
205 /* compute ublock to publish */
206 if (NULL == meta)
207 mdsize = 0;
208 else
209 mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
210 GNUNET_assert (mdsize >= 0);
211 uris = GNUNET_FS_uri_to_string (uri);
212 slen = strlen (uris) + 1;
213 if (NULL == ulabel)
214 ulen = 1;
215 else
216 ulen = strlen (ulabel) + 1;
217 size = mdsize + sizeof(struct UBlock) + slen + ulen;
218 if (size > MAX_UBLOCK_SIZE)
219 {
220 size = MAX_UBLOCK_SIZE;
221 mdsize = size - sizeof(struct UBlock) - (slen + ulen);
222 }
223 ub_plain = GNUNET_malloc (size);
224 kbe = (char *) &ub_plain[1];
225 if (NULL != ulabel)
226 GNUNET_memcpy (kbe, ulabel, ulen);
227 kbe += ulen;
228 GNUNET_memcpy (kbe, uris, slen);
229 kbe += slen;
230 GNUNET_free (uris);
231 sptr = kbe;
232 if (NULL != meta)
233 mdsize =
234 GNUNET_CONTAINER_meta_data_serialize (meta, &sptr, mdsize,
235 GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
236 if (-1 == mdsize)
237 {
238 GNUNET_break (0);
239 GNUNET_free (ub_plain);
240 cont (cont_cls, _ ("Internal error."));
241 return NULL;
242 }
243 size = sizeof(struct UBlock) + slen + mdsize + ulen;
244
245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
246 "Publishing under identifier `%s'\n",
247 label);
248 /* get public key of the namespace */
249 GNUNET_CRYPTO_ecdsa_key_get_public (ns,
250 &pub);
251 derive_ublock_encryption_key (&skey, &iv,
252 label, &pub);
253
254 /* encrypt ublock */
255 ub_enc = GNUNET_malloc (size);
256 GNUNET_CRYPTO_symmetric_encrypt (&ub_plain[1],
257 ulen + slen + mdsize,
258 &skey, &iv,
259 &ub_enc[1]);
260 GNUNET_free (ub_plain);
261 ub_enc->purpose.size = htonl (ulen + slen + mdsize
262 + sizeof(struct UBlock)
263 - sizeof(struct GNUNET_CRYPTO_EcdsaSignature));
264 ub_enc->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_UBLOCK);
265
266 /* derive signing-key from 'label' and public key of the namespace */
267 nsd = GNUNET_CRYPTO_ecdsa_private_key_derive (ns, label, "fs-ublock");
268 GNUNET_CRYPTO_ecdsa_key_get_public (nsd,
269 &ub_enc->verification_key);
270 GNUNET_assert (GNUNET_OK ==
271 GNUNET_CRYPTO_ecdsa_sign_ (nsd,
272 &ub_enc->purpose,
273 &ub_enc->signature));
274 GNUNET_CRYPTO_hash (&ub_enc->verification_key,
275 sizeof(ub_enc->verification_key),
276 &query);
277 GNUNET_free (nsd);
278
279 uc = GNUNET_new (struct GNUNET_FS_PublishUblockContext);
280 uc->cont = cont;
281 uc->cont_cls = cont_cls;
282 if (NULL != dsh)
283 {
284 uc->qre =
285 GNUNET_DATASTORE_put (dsh,
286 0,
287 &query,
288 ulen + slen + mdsize + sizeof(struct UBlock),
289 ub_enc,
290 GNUNET_BLOCK_TYPE_FS_UBLOCK,
291 bo->content_priority,
292 bo->anonymity_level,
293 bo->replication_level,
294 bo->expiration_time,
295 -2, 1,
296 &ublock_put_cont, uc);
297 }
298 else
299 {
300 uc->task = GNUNET_SCHEDULER_add_now (&run_cont,
301 uc);
302 }
303 GNUNET_free (ub_enc);
304 return uc;
305}
306
307
308/**
309 * Abort UBlock publishing operation.
310 *
311 * @param uc operation to abort.
312 */
313void
314GNUNET_FS_publish_ublock_cancel_ (struct GNUNET_FS_PublishUblockContext *uc)
315{
316 if (NULL != uc->qre)
317 GNUNET_DATASTORE_cancel (uc->qre);
318 if (NULL != uc->task)
319 GNUNET_SCHEDULER_cancel (uc->task);
320 GNUNET_free (uc);
321}
322
323
324/* end of fs_publish_ublock.c */
diff --git a/src/fs/fs_publish_ublock.h b/src/fs/fs_publish_ublock.h
deleted file mode 100644
index 83c6a50aa..000000000
--- a/src/fs/fs_publish_ublock.h
+++ /dev/null
@@ -1,108 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_publish_ublock.h
23 * @brief publish a UBLOCK in GNUnet
24 * @see https://gnunet.org/encoding and #2564
25 * @author Krista Bennett
26 * @author Christian Grothoff
27 */
28#ifndef FS_PUBLISH_UBLOCK_H
29#define FS_PUBLISH_UBLOCK_H
30
31#include "gnunet_util_lib.h"
32#include "gnunet_datastore_service.h"
33#include "gnunet_fs_service.h"
34#include "gnunet_identity_service.h"
35
36
37/**
38 * Decrypt the given UBlock, storing the result in output.
39 *
40 * @param input input data
41 * @param input_len number of bytes in input
42 * @param ns public key under which the UBlock was stored
43 * @param label label under which the UBlock was stored
44 * @param output where to write the result, has input_len bytes
45 */
46void
47GNUNET_FS_ublock_decrypt_ (const void *input,
48 size_t input_len,
49 const struct GNUNET_CRYPTO_EcdsaPublicKey *ns,
50 const char *label,
51 void *output);
52
53
54/**
55 * Context for 'ublock_put_cont'.
56 */
57struct GNUNET_FS_PublishUblockContext;
58
59
60/**
61 * Signature of a function called as the continuation of a UBlock
62 * publication.
63 *
64 * @param cls closure
65 * @param emsg error message, NULL on success
66 */
67typedef void (*GNUNET_FS_UBlockContinuation) (void *cls,
68 const char *emsg);
69
70
71/**
72 * Publish a UBlock.
73 *
74 * @param h handle to the file sharing subsystem
75 * @param dsh datastore handle to use for storage operation
76 * @param label identifier to use
77 * @param ulabel update label to use, may be an empty string for none
78 * @param ns namespace to publish in
79 * @param meta metadata to use
80 * @param uri URI to refer to in the UBlock
81 * @param bo per-block options
82 * @param options publication options
83 * @param cont continuation
84 * @param cont_cls closure for cont
85 * @return NULL on error ('cont' will still be called)
86 */
87struct GNUNET_FS_PublishUblockContext *
88GNUNET_FS_publish_ublock_ (struct GNUNET_FS_Handle *h,
89 struct GNUNET_DATASTORE_Handle *dsh,
90 const char *label,
91 const char *ulabel,
92 const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
93 const struct GNUNET_CONTAINER_MetaData *meta,
94 const struct GNUNET_FS_Uri *uri,
95 const struct GNUNET_FS_BlockOptions *bo,
96 enum GNUNET_FS_PublishOptions options,
97 GNUNET_FS_UBlockContinuation cont, void *cont_cls);
98
99
100/**
101 * Abort UBlock publishing operation.
102 *
103 * @param uc operation to abort.
104 */
105void
106GNUNET_FS_publish_ublock_cancel_ (struct GNUNET_FS_PublishUblockContext *uc);
107
108#endif
diff --git a/src/fs/fs_search.c b/src/fs/fs_search.c
deleted file mode 100644
index 3dbee43ef..000000000
--- a/src/fs/fs_search.c
+++ /dev/null
@@ -1,1835 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/fs_search.c
22 * @brief Helper functions for searching.
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_constants.h"
27#include "gnunet_fs_service.h"
28#include "gnunet_protocols.h"
29#include "fs_api.h"
30#include "fs_publish_ublock.h"
31
32
33/**
34 * Number of availability trials we perform per search result.
35 */
36#define AVAILABILITY_TRIALS_MAX 8
37
38/**
39 * Fill in all of the generic fields for a search event and
40 * call the callback.
41 *
42 * @param pi structure to fill in
43 * @param h file-sharing handle
44 * @param sc overall search context
45 * @return value returned by the callback
46 */
47void *
48GNUNET_FS_search_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
49 struct GNUNET_FS_Handle *h,
50 struct GNUNET_FS_SearchContext *sc)
51{
52 void *ret;
53
54 pi->value.search.sc = sc;
55 pi->value.search.cctx = (NULL != sc) ? sc->client_info : NULL;
56 pi->value.search.pctx =
57 ((NULL == sc) || (NULL == sc->psearch_result))
58 ? NULL
59 : sc->psearch_result->client_info;
60 pi->value.search.query = (NULL != sc) ? sc->uri : NULL;
61 pi->value.search.duration = (NULL != sc)
62 ? GNUNET_TIME_absolute_get_duration (
63 sc->start_time)
64 : GNUNET_TIME_UNIT_ZERO;
65 pi->value.search.anonymity = (NULL != sc) ? sc->anonymity : 0;
66 pi->fsh = h;
67 ret = h->upcb (h->upcb_cls, pi);
68 return ret;
69}
70
71
72/**
73 * Check if the given result is identical to the given URI.
74 *
75 * @param cls points to the URI we check against
76 * @param key not used
77 * @param value a `struct GNUNET_FS_SearchResult` who's URI we
78 * should compare with
79 * @return #GNUNET_SYSERR if the result is present,
80 * #GNUNET_OK otherwise
81 */
82static int
83test_result_present (void *cls,
84 const struct GNUNET_HashCode *key,
85 void *value)
86{
87 const struct GNUNET_FS_Uri *uri = cls;
88 struct GNUNET_FS_SearchResult *sr = value;
89
90 if (GNUNET_FS_uri_test_equal (uri, sr->uri))
91 return GNUNET_SYSERR;
92 return GNUNET_OK;
93}
94
95
96/**
97 * We've found a new CHK result. Let the client
98 * know about it.
99 *
100 * @param sc the search context
101 * @param sr the specific result
102 */
103static void
104notify_client_chk_result (struct GNUNET_FS_SearchContext *sc,
105 struct GNUNET_FS_SearchResult *sr)
106{
107 struct GNUNET_FS_ProgressInfo pi;
108
109 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT;
110 pi.value.search.specifics.result.meta = sr->meta;
111 pi.value.search.specifics.result.uri = sr->uri;
112 pi.value.search.specifics.result.result = sr;
113 pi.value.search.specifics.result.applicability_rank = sr->optional_support;
114 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
115}
116
117
118/**
119 * We've found new information about an existing CHK result. Let the
120 * client know about it.
121 *
122 * @param sc the search context
123 * @param sr the specific result
124 */
125static void
126notify_client_chk_update (struct GNUNET_FS_SearchContext *sc,
127 struct GNUNET_FS_SearchResult *sr)
128{
129 struct GNUNET_FS_ProgressInfo pi;
130
131 pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
132 pi.value.search.specifics.update.cctx = sr->client_info;
133 pi.value.search.specifics.update.meta = sr->meta;
134 pi.value.search.specifics.update.uri = sr->uri;
135 pi.value.search.specifics.update.availability_rank =
136 2 * sr->availability_success - sr->availability_trials;
137 pi.value.search.specifics.update.availability_certainty =
138 sr->availability_trials;
139 pi.value.search.specifics.update.applicability_rank = sr->optional_support;
140 pi.value.search.specifics.update.current_probe_time
141 = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
142 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
143}
144
145
146/**
147 * Context for "get_result_present".
148 */
149struct GetResultContext
150{
151 /**
152 * The URI we're looking for.
153 */
154 const struct GNUNET_FS_Uri *uri;
155
156 /**
157 * Where to store a pointer to the search
158 * result struct if we found a match.
159 */
160 struct GNUNET_FS_SearchResult *sr;
161};
162
163
164/**
165 * Check if the given result is identical to the given URI and if so
166 * return it.
167 *
168 * @param cls a `struct GetResultContext`
169 * @param key not used
170 * @param value a `struct GNUNET_FS_SearchResult` who's URI we
171 * should compare with
172 * @return #GNUNET_OK
173 */
174static int
175get_result_present (void *cls,
176 const struct GNUNET_HashCode *key,
177 void *value)
178{
179 struct GetResultContext *grc = cls;
180 struct GNUNET_FS_SearchResult *sr = value;
181
182 if (GNUNET_FS_uri_test_equal (grc->uri, sr->uri))
183 grc->sr = sr;
184 return GNUNET_OK;
185}
186
187
188/**
189 * Signal result of last probe to client and then schedule next
190 * probe.
191 *
192 * @param sr search result to signal for
193 */
194static void
195signal_probe_result (struct GNUNET_FS_SearchResult *sr)
196{
197 struct GNUNET_FS_ProgressInfo pi;
198
199 pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
200 pi.value.search.specifics.update.cctx = sr->client_info;
201 pi.value.search.specifics.update.meta = sr->meta;
202 pi.value.search.specifics.update.uri = sr->uri;
203 pi.value.search.specifics.update.availability_rank
204 = 2 * sr->availability_success - sr->availability_trials;
205 pi.value.search.specifics.update.availability_certainty
206 = sr->availability_trials;
207 pi.value.search.specifics.update.applicability_rank = sr->optional_support;
208 pi.value.search.specifics.update.current_probe_time
209 = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
210 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->h, sr->sc);
211 GNUNET_FS_search_start_probe_ (sr);
212}
213
214
215/**
216 * Handle the case where we have failed to receive a response for our probe.
217 *
218 * @param cls our `struct GNUNET_FS_SearchResult *`
219 */
220static void
221probe_failure_handler (void *cls)
222{
223 struct GNUNET_FS_SearchResult *sr = cls;
224
225 sr->probe_cancel_task = NULL;
226 sr->availability_trials++;
227 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
228 sr->probe_ctx = NULL;
229 GNUNET_FS_stop_probe_ping_task_ (sr);
230 GNUNET_FS_search_result_sync_ (sr);
231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
232 "Probe #%u for search result %p failed\n",
233 sr->availability_trials,
234 sr);
235 signal_probe_result (sr);
236}
237
238
239/**
240 * Handle the case where we have gotten a response for our probe.
241 *
242 * @param cls our `struct GNUNET_FS_SearchResult *`
243 */
244static void
245probe_success_handler (void *cls)
246{
247 struct GNUNET_FS_SearchResult *sr = cls;
248
249 sr->probe_cancel_task = NULL;
250 sr->availability_trials++;
251 sr->availability_success++;
252 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
253 sr->probe_ctx = NULL;
254 GNUNET_FS_stop_probe_ping_task_ (sr);
255 GNUNET_FS_search_result_sync_ (sr);
256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
257 "Probe #%u for search result %p succeeded\n",
258 sr->availability_trials,
259 sr);
260 signal_probe_result (sr);
261}
262
263
264/**
265 * Notification of FS that a search probe has made progress.
266 * This function is used INSTEAD of the client's event handler
267 * for downloads where the #GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
268 *
269 * @param cls closure, always NULL (!), actual closure
270 * is in the client-context of the info struct
271 * @param info details about the event, specifying the event type
272 * and various bits about the event
273 * @return client-context (for the next progress call
274 * for this operation; should be set to NULL for
275 * SUSPEND and STOPPED events). The value returned
276 * will be passed to future callbacks in the respective
277 * field in the `struct GNUNET_FS_ProgressInfo`.
278 */
279void *
280GNUNET_FS_search_probe_progress_ (void *cls,
281 const struct GNUNET_FS_ProgressInfo *info)
282{
283 struct GNUNET_FS_SearchResult *sr = info->value.download.cctx;
284 struct GNUNET_TIME_Relative dur;
285
286 switch (info->status)
287 {
288 case GNUNET_FS_STATUS_DOWNLOAD_START:
289 /* ignore */
290 break;
291 case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
292 /* probes should never be resumed */
293 GNUNET_assert (0);
294 break;
295 case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
296 /* probes should never be suspended */
297 GNUNET_break (0);
298 break;
299 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
300 /* ignore */
301 break;
302 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
303 if (NULL != sr->probe_cancel_task)
304 {
305 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
306 sr->probe_cancel_task = NULL;
307 }
308 sr->probe_cancel_task =
309 GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
310 &probe_failure_handler, sr);
311 break;
312 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
313 if (NULL != sr->probe_cancel_task)
314 {
315 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
316 sr->probe_cancel_task = NULL;
317 }
318 sr->probe_cancel_task =
319 GNUNET_SCHEDULER_add_now (&probe_success_handler, sr);
320 break;
321 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
322 if (NULL != sr->probe_cancel_task)
323 {
324 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
325 sr->probe_cancel_task = NULL;
326 }
327 sr = NULL;
328 break;
329 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
330 if (NULL == sr->probe_cancel_task)
331 {
332 sr->probe_active_time = GNUNET_TIME_absolute_get ();
333 sr->probe_cancel_task =
334 GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
335 &probe_failure_handler, sr);
336 }
337 break;
338 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
339 if (NULL != sr->probe_cancel_task)
340 {
341 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
342 sr->probe_cancel_task = NULL;
343 }
344 dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
345 sr->remaining_probe_time =
346 GNUNET_TIME_relative_subtract (sr->remaining_probe_time, dur);
347 if (0 == sr->remaining_probe_time.rel_value_us)
348 sr->probe_cancel_task =
349 GNUNET_SCHEDULER_add_now (&probe_failure_handler, sr);
350 GNUNET_FS_search_result_sync_ (sr);
351 break;
352
353 default:
354 GNUNET_break (0);
355 return NULL;
356 }
357 return sr;
358}
359
360
361/**
362 * Task run periodically to remind clients that a probe is active.
363 *
364 * @param cls the `struct GNUNET_FS_SearchResult` that we are probing for
365 */
366static void
367probe_ping_task_cb (void *cls)
368{
369 struct GNUNET_FS_Handle *h = cls;
370
371 for (struct GNUNET_FS_SearchResult *sr = h->probes_head;
372 NULL != sr;
373 sr = sr->next)
374 if (NULL != sr->probe_ctx->mq)
375 signal_probe_result (sr);
376 h->probe_ping_task
377 = GNUNET_SCHEDULER_add_delayed (GNUNET_FS_PROBE_UPDATE_FREQUENCY,
378 &probe_ping_task_cb,
379 h);
380}
381
382
383/**
384 * Start the ping task for this search result.
385 *
386 * @param sr result to start pinging for.
387 */
388static void
389start_probe_ping_task (struct GNUNET_FS_SearchResult *sr)
390{
391 struct GNUNET_FS_Handle *h = sr->h;
392
393 GNUNET_CONTAINER_DLL_insert (h->probes_head,
394 h->probes_tail,
395 sr);
396 if (NULL == h->probe_ping_task)
397 h->probe_ping_task
398 = GNUNET_SCHEDULER_add_now (&probe_ping_task_cb,
399 h);
400}
401
402
403/**
404 * Stop the ping task for this search result.
405 *
406 * @param sr result to start pinging for.
407 */
408void
409GNUNET_FS_stop_probe_ping_task_ (struct GNUNET_FS_SearchResult *sr)
410{
411 struct GNUNET_FS_Handle *h = sr->h;
412
413 GNUNET_CONTAINER_DLL_remove (h->probes_head,
414 h->probes_tail,
415 sr);
416 if (NULL == h->probes_head)
417 {
418 GNUNET_SCHEDULER_cancel (h->probe_ping_task);
419 h->probe_ping_task = NULL;
420 }
421}
422
423
424/**
425 * Start download probes for the given search result.
426 *
427 * @param sr the search result
428 */
429void
430GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr)
431{
432 uint64_t off;
433 uint64_t len;
434
435 if (NULL != sr->probe_ctx)
436 return;
437 if (NULL != sr->download)
438 return;
439 if (0 == (sr->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
440 return;
441 if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
442 return;
443 if ( (GNUNET_FS_URI_CHK != sr->uri->type) &&
444 (GNUNET_FS_URI_LOC != sr->uri->type) )
445 return;
446 len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
447 if (0 == len)
448 return;
449 if ((len <= DBLOCK_SIZE) && (sr->availability_success > 0))
450 return;
451 off = len / DBLOCK_SIZE;
452 if (off > 0)
453 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
454 off *= DBLOCK_SIZE;
455 if (len - off < DBLOCK_SIZE)
456 len = len - off;
457 else
458 len = DBLOCK_SIZE;
459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
460 "Starting probe #%u (at offset %llu) for search result %p\n",
461 sr->availability_trials + 1,
462 (unsigned long long) off,
463 sr);
464 sr->remaining_probe_time =
465 GNUNET_TIME_relative_saturating_multiply (sr->h->avg_block_latency,
466 2 * (1
467 + sr->availability_trials));
468 sr->probe_ctx =
469 GNUNET_FS_download_start (sr->h, sr->uri, sr->meta, NULL, NULL, off,
470 len, sr->anonymity,
471 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES
472 | GNUNET_FS_DOWNLOAD_IS_PROBE, sr, NULL);
473 start_probe_ping_task (sr);
474}
475
476
477/**
478 * Start download probes for the given search result.
479 *
480 * @param h file-sharing handle to use for the operation
481 * @param uri URI to probe
482 * @param meta meta data associated with the URI
483 * @param client_info client info pointer to use for associated events
484 * @param anonymity anonymity level to use for the probes
485 * @return the search result handle to access the probe activity
486 */
487struct GNUNET_FS_SearchResult *
488GNUNET_FS_probe (struct GNUNET_FS_Handle *h,
489 const struct GNUNET_FS_Uri *uri,
490 const struct GNUNET_CONTAINER_MetaData *meta,
491 void *client_info,
492 uint32_t anonymity)
493{
494 struct GNUNET_FS_SearchResult *sr;
495
496 GNUNET_assert (NULL != h);
497 GNUNET_assert (NULL != uri);
498 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
499 sr->h = h;
500 sr->uri = GNUNET_FS_uri_dup (uri);
501 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
502 sr->client_info = client_info;
503 sr->anonymity = anonymity;
504 GNUNET_FS_search_start_probe_ (sr);
505 return sr;
506}
507
508
509/**
510 * Stop probing activity associated with a search result.
511 *
512 * @param sr search result
513 */
514static void
515GNUNET_FS_search_stop_probe_ (struct GNUNET_FS_SearchResult *sr)
516{
517 if (NULL != sr->probe_ctx)
518 {
519 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
520 sr->probe_ctx = NULL;
521 GNUNET_FS_stop_probe_ping_task_ (sr);
522 }
523 if (NULL != sr->probe_cancel_task)
524 {
525 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
526 sr->probe_cancel_task = NULL;
527 }
528}
529
530
531/**
532 * Stop probe activity. Must ONLY be used on values
533 * returned from #GNUNET_FS_probe.
534 *
535 * @param sr search result to stop probing for (freed)
536 * @return the value of the 'client_info' pointer
537 */
538void *
539GNUNET_FS_probe_stop (struct GNUNET_FS_SearchResult *sr)
540{
541 void *client_info;
542
543 GNUNET_assert (NULL == sr->sc);
544 GNUNET_FS_search_stop_probe_ (sr);
545 GNUNET_FS_uri_destroy (sr->uri);
546 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
547 client_info = sr->client_info;
548 GNUNET_free (sr);
549 return client_info;
550}
551
552
553/**
554 * We have received a KSK result. Check how it fits in with the
555 * overall query and notify the client accordingly.
556 *
557 * @param sc context for the overall query
558 * @param ent entry for the specific keyword
559 * @param uri the URI that was found
560 * @param meta metadata associated with the URI
561 * under the @a ent keyword
562 */
563static void
564process_ksk_result (struct GNUNET_FS_SearchContext *sc,
565 struct SearchRequestEntry *ent,
566 const struct GNUNET_FS_Uri *uri,
567 const struct GNUNET_CONTAINER_MetaData *meta)
568{
569 struct GNUNET_HashCode key;
570 struct GNUNET_FS_SearchResult *sr;
571 struct GetResultContext grc;
572 int is_new;
573 unsigned int koff;
574
575 /* check if new */
576 GNUNET_assert (NULL != sc);
577 if (GNUNET_OK !=
578 GNUNET_FS_uri_to_key (uri,
579 &key))
580 {
581 GNUNET_break_op (0);
582 return;
583 }
584 if (GNUNET_SYSERR ==
585 GNUNET_CONTAINER_multihashmap_get_multiple (ent->results,
586 &key,
587 &test_result_present,
588 (void *) uri))
589 return; /* duplicate result */
590 /* try to find search result in master map */
591 grc.sr = NULL;
592 grc.uri = uri;
593 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
594 &key,
595 &get_result_present, &grc);
596 sr = grc.sr;
597 is_new = (NULL == sr) || (sr->mandatory_missing > 0);
598 if (NULL == sr)
599 {
600 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
601 sr->h = sc->h;
602 sr->sc = sc;
603 sr->anonymity = sc->anonymity;
604 sr->uri = GNUNET_FS_uri_dup (uri);
605 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
606 sr->mandatory_missing = sc->mandatory_count;
607 sr->key = key;
608 sr->keyword_bitmap = GNUNET_malloc ((sc->uri->data.ksk.keywordCount + 7)
609 / 8); /* round up, count bits */
610 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map, &key, sr,
611 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
612 }
613 else
614 {
615 GNUNET_CONTAINER_meta_data_merge (sr->meta, meta);
616 }
617 GNUNET_break (GNUNET_OK ==
618 GNUNET_CONTAINER_multihashmap_put (ent->results,
619 &sr->key,
620 sr,
621 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
622
623 koff = ent - sc->requests;
624 GNUNET_assert ((ent >= sc->requests) &&
625 (koff < sc->uri->data.ksk.keywordCount));
626 sr->keyword_bitmap[koff / 8] |= (1 << (koff % 8));
627 /* check if mandatory satisfied */
628 if (1 <= GNUNET_CONTAINER_multihashmap_size (ent->results))
629 {
630 if (ent->mandatory)
631 {
632 GNUNET_break (sr->mandatory_missing > 0);
633 sr->mandatory_missing--;
634 }
635 else
636 {
637 sr->optional_support++;
638 }
639 }
640 if (0 != sr->mandatory_missing)
641 {
642 GNUNET_break (NULL == sr->client_info);
643 return;
644 }
645 if (is_new)
646 notify_client_chk_result (sc, sr);
647 else
648 notify_client_chk_update (sc, sr);
649 GNUNET_FS_search_result_sync_ (sr);
650 GNUNET_FS_search_start_probe_ (sr);
651}
652
653
654/**
655 * Start search for content, internal API.
656 *
657 * @param h handle to the file sharing subsystem
658 * @param uri specifies the search parameters; can be
659 * a KSK URI or an SKS URI.
660 * @param anonymity desired level of anonymity
661 * @param options options for the search
662 * @param cctx client context
663 * @param psearch parent search result (for namespace update searches)
664 * @return context that can be used to control the search
665 */
666static struct GNUNET_FS_SearchContext *
667search_start (struct GNUNET_FS_Handle *h,
668 const struct GNUNET_FS_Uri *uri,
669 uint32_t anonymity,
670 enum GNUNET_FS_SearchOptions options,
671 void *cctx,
672 struct GNUNET_FS_SearchResult *psearch);
673
674
675/**
676 * We have received an SKS result. Start searching for updates and
677 * notify the client if it is a new result.
678 *
679 * @param sc context for the overall query
680 * @param id_update identifier for updates, NULL for none
681 * @param uri the URI that was found
682 * @param meta metadata associated with the URI
683 */
684static void
685process_sks_result (struct GNUNET_FS_SearchContext *sc,
686 const char *id_update,
687 const struct GNUNET_FS_Uri *uri,
688 const struct GNUNET_CONTAINER_MetaData *meta)
689{
690 struct GNUNET_FS_Uri uu;
691 struct GNUNET_HashCode key;
692 struct GNUNET_FS_SearchResult *sr;
693
694 /* check if new */
695 GNUNET_assert (NULL != sc);
696 if (GNUNET_OK !=
697 GNUNET_FS_uri_to_key (uri,
698 &key))
699 {
700 GNUNET_break (0);
701 return;
702 }
703 GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
704 &uri->data.chk.chk.query,
705 &key);
706 if (GNUNET_SYSERR ==
707 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map, &key,
708 &test_result_present,
709 (void *) uri))
710 return; /* duplicate result */
711 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
712 sr->h = sc->h;
713 sr->sc = sc;
714 sr->anonymity = sc->anonymity;
715 sr->uri = GNUNET_FS_uri_dup (uri);
716 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
717 sr->key = key;
718 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map, &key, sr,
719 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
720 GNUNET_FS_search_result_sync_ (sr);
721 GNUNET_FS_search_start_probe_ (sr);
722 /* notify client */
723 if (0 == sr->mandatory_missing)
724 notify_client_chk_result (sc, sr);
725 else
726 GNUNET_break (NULL == sr->client_info);
727 /* search for updates */
728 if (0 == strlen (id_update))
729 return; /* no updates */
730 uu.type = GNUNET_FS_URI_SKS;
731 uu.data.sks.ns = sc->uri->data.sks.ns;
732 uu.data.sks.identifier = GNUNET_strdup (id_update);
733 (void) search_start (sc->h, &uu, sc->anonymity, sc->options, NULL, sr);
734 GNUNET_free (uu.data.sks.identifier);
735}
736
737
738/**
739 * Decrypt a ublock using a 'keyword' as the passphrase. Given the
740 * KSK public key derived from the keyword, this function looks up
741 * the original keyword in the search context and decrypts the
742 * given ciphertext block.
743 *
744 * @param sc search context with the keywords
745 * @param dpub derived public key used for the search
746 * @param edata encrypted data
747 * @param edata_size number of bytes in @a edata (and @a data)
748 * @param data where to store the plaintext
749 * @return keyword index on success, #GNUNET_SYSERR on error (no such
750 * keyword, internal error)
751 */
752static int
753decrypt_block_with_keyword (const struct GNUNET_FS_SearchContext *sc,
754 const struct GNUNET_CRYPTO_EcdsaPublicKey *dpub,
755 const void *edata,
756 size_t edata_size,
757 char *data)
758{
759 const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
760 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
761 unsigned int i;
762
763 /* find key */
764 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
765 if (0 == memcmp (dpub,
766 &sc->requests[i].dpub,
767 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
768 break;
769 if (i == sc->uri->data.ksk.keywordCount)
770 {
771 /* oops, does not match any of our keywords!? */
772 GNUNET_break (0);
773 return GNUNET_SYSERR;
774 }
775 /* decrypt */
776 anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
777 GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub);
778 GNUNET_FS_ublock_decrypt_ (edata, edata_size,
779 &anon_pub,
780 sc->requests[i].keyword,
781 data);
782 return i;
783}
784
785
786/**
787 * Process a keyword search result. The actual type of block is
788 * a UBlock; we know it is a keyword search result because that's
789 * what we were searching for.
790 *
791 * @param sc our search context
792 * @param ub the ublock with the keyword search result
793 * @param size size of @a ub
794 */
795static void
796process_kblock (struct GNUNET_FS_SearchContext *sc,
797 const struct UBlock *ub,
798 size_t size)
799{
800 size_t j;
801 char pt[size - sizeof(struct UBlock)];
802 const char *eos;
803 struct GNUNET_CONTAINER_MetaData *meta;
804 struct GNUNET_FS_Uri *uri;
805 char *emsg;
806 int i;
807
808 if (-1 == (i = decrypt_block_with_keyword (sc,
809 &ub->verification_key,
810 &ub[1],
811 size - sizeof(struct UBlock),
812 pt)))
813 return;
814 /* parse; pt[0] is just '\0', so we skip over that */
815 eos = memchr (&pt[1], '\0', sizeof(pt) - 1);
816 if (NULL == eos)
817 {
818 GNUNET_break_op (0);
819 return;
820 }
821 if (NULL == (uri = GNUNET_FS_uri_parse (&pt[1], &emsg)))
822 {
823 if (GNUNET_FS_VERSION > 0x00090400)
824 {
825 /* we broke this in 0x00090300, so don't bitch
826 too loudly just one version up... */
827 GNUNET_break_op (0); /* ublock malformed */
828 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
829 _ ("Failed to parse URI `%s': %s\n"),
830 &pt[1],
831 emsg);
832 }
833 GNUNET_free (emsg);
834 return;
835 }
836 j = eos - pt + 1;
837 if (sizeof(pt) == j)
838 meta = GNUNET_CONTAINER_meta_data_create ();
839 else
840 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j], sizeof(pt) - j);
841 if (NULL == meta)
842 {
843 GNUNET_break_op (0); /* ublock malformed */
844 GNUNET_FS_uri_destroy (uri);
845 return;
846 }
847 process_ksk_result (sc,
848 &sc->requests[i],
849 uri,
850 meta);
851
852 /* clean up */
853 GNUNET_CONTAINER_meta_data_destroy (meta);
854 GNUNET_FS_uri_destroy (uri);
855}
856
857
858/**
859 * Process a namespace-search result. The actual type of block is
860 * a UBlock; we know it is a namespace search result because that's
861 * what we were searching for.
862 *
863 * @param sc our search context
864 * @param ub the ublock with a namespace result
865 * @param size size of @a ub
866 */
867static void
868process_sblock (struct GNUNET_FS_SearchContext *sc,
869 const struct UBlock *ub,
870 size_t size)
871{
872 size_t len = size - sizeof(struct UBlock);
873 char pt[len];
874 struct GNUNET_FS_Uri *uri;
875 struct GNUNET_CONTAINER_MetaData *meta;
876 const char *id;
877 const char *uris;
878 size_t off;
879 char *emsg;
880
881 GNUNET_FS_ublock_decrypt_ (&ub[1], len,
882 &sc->uri->data.sks.ns,
883 sc->uri->data.sks.identifier,
884 pt);
885 /* parse */
886 if (0 == (off = GNUNET_STRINGS_buffer_tokenize (pt, len, 2, &id, &uris)))
887 {
888 GNUNET_break_op (0); /* ublock malformed */
889 return;
890 }
891 if (NULL == (meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[off], len
892 - off)))
893 {
894 GNUNET_break_op (0); /* ublock malformed */
895 return;
896 }
897 if (NULL == (uri = GNUNET_FS_uri_parse (uris, &emsg)))
898 {
899 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
900 _ ("Failed to parse URI `%s': %s\n"),
901 uris, emsg);
902 GNUNET_break_op (0); /* ublock malformed */
903 GNUNET_free (emsg);
904 GNUNET_CONTAINER_meta_data_destroy (meta);
905 return;
906 }
907 /* process */
908 process_sks_result (sc, id, uri, meta);
909 /* clean up */
910 GNUNET_FS_uri_destroy (uri);
911 GNUNET_CONTAINER_meta_data_destroy (meta);
912}
913
914
915/**
916 * Shutdown any existing connection to the FS
917 * service and try to establish a fresh one
918 * (and then re-transmit our search request).
919 *
920 * @param sc the search to reconnec
921 */
922static void
923try_reconnect (struct GNUNET_FS_SearchContext *sc);
924
925
926/**
927 * We check a result message from the service.
928 *
929 * @param cls closure
930 * @param msg result message received
931 */
932static int
933check_result (void *cls,
934 const struct ClientPutMessage *cm)
935{
936 /* payload of any variable size is OK */
937 return GNUNET_OK;
938}
939
940
941/**
942 * We process a search result from the service.
943 *
944 * @param cls closure
945 * @param msg result message received
946 */
947static void
948handle_result (void *cls,
949 const struct ClientPutMessage *cm)
950{
951 struct GNUNET_FS_SearchContext *sc = cls;
952 uint16_t msize = ntohs (cm->header.size) - sizeof(*cm);
953 enum GNUNET_BLOCK_Type type = ntohl (cm->type);
954
955 if (GNUNET_TIME_absolute_get_duration (GNUNET_TIME_absolute_ntoh (
956 cm->expiration)).rel_value_us > 0)
957 {
958 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
959 "Result received has already expired.\n");
960 return; /* result expired */
961 }
962 switch (type)
963 {
964 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
965 if (GNUNET_FS_URI_SKS == sc->uri->type)
966 process_sblock (sc,
967 (const struct UBlock *) &cm[1],
968 msize);
969 else
970 process_kblock (sc,
971 (const struct UBlock *) &cm[1],
972 msize);
973 break;
974
975 case GNUNET_BLOCK_TYPE_ANY:
976 GNUNET_break (0);
977 break;
978
979 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
980 GNUNET_break (0);
981 break;
982
983 case GNUNET_BLOCK_TYPE_FS_ONDEMAND:
984 GNUNET_break (0);
985 break;
986
987 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
988 GNUNET_break (0);
989 break;
990
991 default:
992 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
993 _ ("Got result with unknown block type `%d', ignoring"),
994 type);
995 break;
996 }
997}
998
999
1000/**
1001 * Schedule the transmission of the (next) search request
1002 * to the service.
1003 *
1004 * @param sc context for the search
1005 */
1006static void
1007schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc);
1008
1009
1010/**
1011 * Closure for #build_result_set().
1012 */
1013struct MessageBuilderContext
1014{
1015 /**
1016 * How many entries can we store to xoff.
1017 */
1018 unsigned int put_cnt;
1019
1020 /**
1021 * How many entries should we skip.
1022 */
1023 unsigned int skip_cnt;
1024
1025 /**
1026 * Where to store the keys.
1027 */
1028 struct GNUNET_HashCode *xoff;
1029
1030 /**
1031 * Search context we are iterating for.
1032 */
1033 struct GNUNET_FS_SearchContext *sc;
1034
1035 /**
1036 * Keyword offset the search result must match (0 for SKS)
1037 */
1038 unsigned int keyword_offset;
1039};
1040
1041
1042/**
1043 * Iterating over the known results, pick those matching the given
1044 * result range and store their keys at 'xoff'.
1045 *
1046 * @param cls the `struct MessageBuilderContext`
1047 * @param key key for a result
1048 * @param value the search result
1049 * @return #GNUNET_OK to continue iterating
1050 */
1051static int
1052build_result_set (void *cls,
1053 const struct GNUNET_HashCode *key,
1054 void *value)
1055{
1056 struct MessageBuilderContext *mbc = cls;
1057 struct GNUNET_FS_SearchResult *sr = value;
1058
1059 if ((NULL != sr->keyword_bitmap) &&
1060 (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1
1061 << (mbc->
1062 keyword_offset
1063 % 8)))))
1064 return GNUNET_OK; /* have no match for this keyword yet */
1065 if (mbc->skip_cnt > 0)
1066 {
1067 mbc->skip_cnt--;
1068 return GNUNET_OK;
1069 }
1070 if (0 == mbc->put_cnt)
1071 return GNUNET_SYSERR;
1072 mbc->xoff[--mbc->put_cnt] = *key;
1073
1074 return GNUNET_OK;
1075}
1076
1077
1078/**
1079 * Iterating over the known results, count those matching the given
1080 * result range and increment put count for each.
1081 *
1082 * @param cls the `struct MessageBuilderContext`
1083 * @param key key for a result
1084 * @param value the search result
1085 * @return #GNUNET_OK to continue iterating
1086 */
1087static int
1088find_result_set (void *cls,
1089 const struct GNUNET_HashCode *key,
1090 void *value)
1091{
1092 struct MessageBuilderContext *mbc = cls;
1093 struct GNUNET_FS_SearchResult *sr = value;
1094
1095 if ((NULL != sr->keyword_bitmap) &&
1096 (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1
1097 << (mbc->
1098 keyword_offset
1099 % 8)))))
1100 return GNUNET_OK; /* have no match for this keyword yet */
1101 mbc->put_cnt++;
1102 return GNUNET_OK;
1103}
1104
1105
1106/**
1107 * Schedule the transmission of the (next) search request
1108 * to the service.
1109 *
1110 * @param sc context for the search
1111 */
1112static void
1113schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc)
1114{
1115 struct MessageBuilderContext mbc;
1116 struct GNUNET_MQ_Envelope *env;
1117 struct SearchMessage *sm;
1118 struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
1119 unsigned int total_seen_results; /* total number of result hashes to send */
1120 uint32_t options;
1121 unsigned int left;
1122 unsigned int todo;
1123 unsigned int fit;
1124 unsigned int search_request_map_offset;
1125 unsigned int keyword_offset;
1126 int first_call;
1127
1128 memset (&mbc, 0, sizeof(mbc));
1129 mbc.sc = sc;
1130 if (GNUNET_FS_uri_test_ksk (sc->uri))
1131 {
1132 /* This will calculate the result set size ONLY for
1133 "keyword_offset == 0", so we will have to recalculate
1134 it for the other keywords later! */
1135 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1136 &find_result_set,
1137 &mbc);
1138 total_seen_results = mbc.put_cnt;
1139 }
1140 else
1141 {
1142 total_seen_results
1143 = GNUNET_CONTAINER_multihashmap_size (sc->master_result_map);
1144 }
1145 search_request_map_offset = 0;
1146 keyword_offset = 0;
1147 first_call = GNUNET_YES;
1148 while ((0 != (left =
1149 (total_seen_results - search_request_map_offset))) ||
1150 (GNUNET_YES == first_call))
1151 {
1152 first_call = GNUNET_NO;
1153 options = SEARCH_MESSAGE_OPTION_NONE;
1154 if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
1155 options |= SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY;
1156
1157 fit = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(*sm)) / sizeof(struct
1158 GNUNET_HashCode);
1159 todo = GNUNET_MIN (fit,
1160 left);
1161 env = GNUNET_MQ_msg_extra (sm,
1162 sizeof(struct GNUNET_HashCode) * todo,
1163 GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1164 mbc.skip_cnt = search_request_map_offset;
1165 mbc.xoff = (struct GNUNET_HashCode *) &sm[1];
1166 sm->type = htonl (GNUNET_BLOCK_TYPE_FS_UBLOCK);
1167 sm->anonymity_level = htonl (sc->anonymity);
1168 memset (&sm->target,
1169 0,
1170 sizeof(struct GNUNET_PeerIdentity));
1171
1172 if (GNUNET_FS_uri_test_ksk (sc->uri))
1173 {
1174 mbc.keyword_offset = keyword_offset;
1175 /* calculate how many results we can send in this message */
1176 mbc.put_cnt = todo;
1177 /* now build message */
1178 sm->query = sc->requests[keyword_offset].uquery;
1179 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1180 &build_result_set,
1181 &mbc);
1182 search_request_map_offset += todo;
1183 GNUNET_assert (0 == mbc.put_cnt);
1184 GNUNET_assert (total_seen_results >= search_request_map_offset);
1185 if (total_seen_results != search_request_map_offset)
1186 {
1187 /* more requesting to be done... */
1188 sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
1189 }
1190 else
1191 {
1192 sm->options = htonl (options);
1193 keyword_offset++;
1194 if (sc->uri->data.ksk.keywordCount != keyword_offset)
1195 {
1196 /* more keywords => more requesting to be done... */
1197 first_call = GNUNET_YES;
1198 search_request_map_offset = 0;
1199 mbc.put_cnt = 0;
1200 mbc.keyword_offset = keyword_offset;
1201 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1202 &find_result_set,
1203 &mbc);
1204 total_seen_results = mbc.put_cnt;
1205 }
1206 }
1207 }
1208 else
1209 {
1210 GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
1211
1212 GNUNET_CRYPTO_ecdsa_public_key_derive (&sc->uri->data.sks.ns,
1213 sc->uri->data.sks.identifier,
1214 "fs-ublock",
1215 &dpub);
1216 GNUNET_CRYPTO_hash (&dpub,
1217 sizeof(dpub),
1218 &sm->query);
1219 mbc.put_cnt = todo;
1220 mbc.keyword_offset = 0;
1221 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1222 &build_result_set,
1223 &mbc);
1224 GNUNET_assert (total_seen_results >= search_request_map_offset);
1225 if (total_seen_results != search_request_map_offset)
1226 {
1227 /* more requesting to be done... */
1228 sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
1229 }
1230 else
1231 {
1232 sm->options = htonl (options);
1233 }
1234 }
1235 GNUNET_MQ_send (sc->mq,
1236 env);
1237 }
1238}
1239
1240
1241/**
1242 * Generic error handler, called with the appropriate error code and
1243 * the same closure specified at the creation of the message queue.
1244 * Not every message queue implementation supports an error handler.
1245 *
1246 * @param cls closure with the `struct GNUNET_FS_SearchContext *`
1247 * @param error error code
1248 */
1249static void
1250search_mq_error_handler (void *cls,
1251 enum GNUNET_MQ_Error error)
1252{
1253 struct GNUNET_FS_SearchContext *sc = cls;
1254
1255 if (NULL != sc->mq)
1256 {
1257 GNUNET_MQ_destroy (sc->mq);
1258 sc->mq = NULL;
1259 }
1260 try_reconnect (sc);
1261}
1262
1263
1264/**
1265 * Reconnect to the FS service and transmit
1266 * our queries NOW.
1267 *
1268 * @param cls our search context
1269 */
1270static void
1271do_reconnect (void *cls)
1272{
1273 struct GNUNET_FS_SearchContext *sc = cls;
1274 struct GNUNET_MQ_MessageHandler handlers[] = {
1275 GNUNET_MQ_hd_var_size (result,
1276 GNUNET_MESSAGE_TYPE_FS_PUT,
1277 struct ClientPutMessage,
1278 sc),
1279 GNUNET_MQ_handler_end ()
1280 };
1281
1282 sc->task = NULL;
1283 sc->mq = GNUNET_CLIENT_connect (sc->h->cfg,
1284 "fs",
1285 handlers,
1286 &search_mq_error_handler,
1287 sc);
1288 if (NULL == sc->mq)
1289 {
1290 try_reconnect (sc);
1291 return;
1292 }
1293 schedule_transmit_search_request (sc);
1294}
1295
1296
1297/**
1298 * Shutdown any existing connection to the FS
1299 * service and try to establish a fresh one
1300 * (and then re-transmit our search request).
1301 *
1302 * @param sc the search to reconnec
1303 */
1304static void
1305try_reconnect (struct GNUNET_FS_SearchContext *sc)
1306{
1307 if (NULL != sc->mq)
1308 {
1309 GNUNET_MQ_destroy (sc->mq);
1310 sc->mq = NULL;
1311 }
1312 sc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (sc->reconnect_backoff);
1313 sc->task =
1314 GNUNET_SCHEDULER_add_delayed (sc->reconnect_backoff,
1315 &do_reconnect,
1316 sc);
1317}
1318
1319
1320/**
1321 * Start search for content, internal API.
1322 *
1323 * @param h handle to the file sharing subsystem
1324 * @param uri specifies the search parameters; can be
1325 * a KSK URI or an SKS URI.
1326 * @param anonymity desired level of anonymity
1327 * @param options options for the search
1328 * @param cctx initial value for the client context
1329 * @param psearch parent search result (for namespace update searches)
1330 * @return context that can be used to control the search
1331 */
1332static struct GNUNET_FS_SearchContext *
1333search_start (struct GNUNET_FS_Handle *h,
1334 const struct GNUNET_FS_Uri *uri,
1335 uint32_t anonymity,
1336 enum GNUNET_FS_SearchOptions options,
1337 void *cctx,
1338 struct GNUNET_FS_SearchResult *psearch)
1339{
1340 struct GNUNET_FS_SearchContext *sc;
1341 struct GNUNET_FS_ProgressInfo pi;
1342
1343 sc = GNUNET_new (struct GNUNET_FS_SearchContext);
1344 sc->h = h;
1345 sc->options = options;
1346 sc->uri = GNUNET_FS_uri_dup (uri);
1347 sc->anonymity = anonymity;
1348 sc->start_time = GNUNET_TIME_absolute_get ();
1349 if (NULL != psearch)
1350 {
1351 sc->psearch_result = psearch;
1352 psearch->update_search = sc;
1353 }
1354 sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
1355 sc->client_info = cctx;
1356 if (GNUNET_OK != GNUNET_FS_search_start_searching_ (sc))
1357 {
1358 GNUNET_FS_uri_destroy (sc->uri);
1359 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1360 GNUNET_free (sc);
1361 return NULL;
1362 }
1363 GNUNET_FS_search_sync_ (sc);
1364 pi.status = GNUNET_FS_STATUS_SEARCH_START;
1365 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1366 return sc;
1367}
1368
1369
1370/**
1371 * Update the 'results' map for the individual keywords with the
1372 * results from the 'global' result set.
1373 *
1374 * @param cls closure, the `struct GNUNET_FS_SearchContext *`
1375 * @param key current key code
1376 * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *`
1377 * @return #GNUNET_YES (we should continue to iterate)
1378 */
1379static int
1380update_sre_result_maps (void *cls,
1381 const struct GNUNET_HashCode *key,
1382 void *value)
1383{
1384 struct GNUNET_FS_SearchContext *sc = cls;
1385 struct GNUNET_FS_SearchResult *sr = value;
1386
1387 for (unsigned int i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1388 {
1389 if (0 != (sr->keyword_bitmap[i / 8] & (1 << (i % 8))))
1390 GNUNET_break (GNUNET_OK ==
1391 GNUNET_CONTAINER_multihashmap_put (sc->requests[i].results,
1392 &sr->key,
1393 sr,
1394 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1395 }
1396 return GNUNET_YES;
1397}
1398
1399
1400/**
1401 * Build the request and actually initiate the search using the
1402 * GNUnet FS service.
1403 *
1404 * @param sc search context
1405 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1406 */
1407int
1408GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc)
1409{
1410 unsigned int i;
1411 const char *keyword;
1412 const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
1413 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
1414 struct SearchRequestEntry *sre;
1415
1416 GNUNET_assert (NULL == sc->mq);
1417 if (GNUNET_FS_uri_test_ksk (sc->uri))
1418 {
1419 GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1420 anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
1421 GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub);
1422 sc->requests
1423 = GNUNET_new_array (sc->uri->data.ksk.keywordCount,
1424 struct SearchRequestEntry);
1425
1426 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1427 {
1428 keyword = &sc->uri->data.ksk.keywords[i][1];
1429 sre = &sc->requests[i];
1430 sre->keyword = GNUNET_strdup (keyword);
1431 GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub,
1432 keyword,
1433 "fs-ublock",
1434 &sre->dpub);
1435 GNUNET_CRYPTO_hash (&sre->dpub,
1436 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
1437 &sre->uquery);
1438 sre->mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1439 if (sre->mandatory)
1440 sc->mandatory_count++;
1441 sre->results = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO);
1442 }
1443 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1444 &update_sre_result_maps,
1445 sc);
1446 }
1447 GNUNET_assert (NULL == sc->task);
1448 do_reconnect (sc);
1449 if (NULL == sc->mq)
1450 {
1451 GNUNET_SCHEDULER_cancel (sc->task);
1452 sc->task = NULL;
1453 return GNUNET_SYSERR;
1454 }
1455 return GNUNET_OK;
1456}
1457
1458
1459/**
1460 * Freeze probes for the given search result.
1461 *
1462 * @param cls the global FS handle
1463 * @param key the key for the search result (unused)
1464 * @param value the search result to free
1465 * @return #GNUNET_OK
1466 */
1467static int
1468search_result_freeze_probes (void *cls,
1469 const struct GNUNET_HashCode *key,
1470 void *value)
1471{
1472 struct GNUNET_FS_SearchResult *sr = value;
1473
1474 if (NULL != sr->probe_ctx)
1475 {
1476 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1477 sr->probe_ctx = NULL;
1478 GNUNET_FS_stop_probe_ping_task_ (sr);
1479 }
1480 if (NULL != sr->probe_cancel_task)
1481 {
1482 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1483 sr->probe_cancel_task = NULL;
1484 }
1485 if (NULL != sr->update_search)
1486 GNUNET_FS_search_pause (sr->update_search);
1487 return GNUNET_OK;
1488}
1489
1490
1491/**
1492 * Resume probes for the given search result.
1493 *
1494 * @param cls the global FS handle
1495 * @param key the key for the search result (unused)
1496 * @param value the search result to free
1497 * @return #GNUNET_OK
1498 */
1499static int
1500search_result_resume_probes (void *cls,
1501 const struct GNUNET_HashCode *key,
1502 void *value)
1503{
1504 struct GNUNET_FS_SearchResult *sr = value;
1505
1506 GNUNET_FS_search_start_probe_ (sr);
1507 if (NULL != sr->update_search)
1508 GNUNET_FS_search_continue (sr->update_search);
1509 return GNUNET_OK;
1510}
1511
1512
1513/**
1514 * Signal suspend and free the given search result.
1515 *
1516 * @param cls the global FS handle
1517 * @param key the key for the search result (unused)
1518 * @param value the search result to free
1519 * @return #GNUNET_OK
1520 */
1521static int
1522search_result_suspend (void *cls,
1523 const struct GNUNET_HashCode *key,
1524 void *value)
1525{
1526 struct GNUNET_FS_SearchContext *sc = cls;
1527 struct GNUNET_FS_SearchResult *sr = value;
1528 struct GNUNET_FS_ProgressInfo pi;
1529
1530 if (NULL != sr->download)
1531 {
1532 GNUNET_FS_download_signal_suspend_ (sr->download);
1533 sr->download = NULL;
1534 }
1535 if (NULL != sr->update_search)
1536 {
1537 GNUNET_FS_search_signal_suspend_ (sr->update_search);
1538 sr->update_search = NULL;
1539 }
1540 GNUNET_FS_search_stop_probe_ (sr);
1541 if (0 == sr->mandatory_missing)
1542 {
1543 /* client is aware of search result, notify about suspension event */
1544 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND;
1545 pi.value.search.specifics.result_suspend.cctx = sr->client_info;
1546 pi.value.search.specifics.result_suspend.meta = sr->meta;
1547 pi.value.search.specifics.result_suspend.uri = sr->uri;
1548 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1549 }
1550 GNUNET_break (NULL == sr->client_info);
1551 GNUNET_free (sr->serialization);
1552 GNUNET_FS_uri_destroy (sr->uri);
1553 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1554 GNUNET_free (sr->keyword_bitmap);
1555 GNUNET_free (sr);
1556 return GNUNET_OK;
1557}
1558
1559
1560/**
1561 * Create SUSPEND event for the given search operation
1562 * and then clean up our state (without stop signal).
1563 *
1564 * @param cls the `struct GNUNET_FS_SearchContext` to signal for
1565 */
1566void
1567GNUNET_FS_search_signal_suspend_ (void *cls)
1568{
1569 struct GNUNET_FS_SearchContext *sc = cls;
1570 struct GNUNET_FS_ProgressInfo pi;
1571 unsigned int i;
1572
1573 GNUNET_FS_end_top (sc->h, sc->top);
1574 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1575 &search_result_suspend, sc);
1576 pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
1577 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1578 GNUNET_break (NULL == sc->client_info);
1579 if (sc->task != NULL)
1580 {
1581 GNUNET_SCHEDULER_cancel (sc->task);
1582 sc->task = NULL;
1583 }
1584 if (NULL != sc->mq)
1585 {
1586 GNUNET_MQ_destroy (sc->mq);
1587 sc->mq = NULL;
1588 }
1589 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1590 if (NULL != sc->requests)
1591 {
1592 GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1593 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1594 {
1595 GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1596 GNUNET_free (sc->requests[i].keyword);
1597 }
1598 }
1599 GNUNET_free (sc->requests);
1600 GNUNET_free (sc->emsg);
1601 GNUNET_FS_uri_destroy (sc->uri);
1602 GNUNET_free (sc->serialization);
1603 GNUNET_free (sc);
1604}
1605
1606
1607/**
1608 * Start search for content.
1609 *
1610 * @param h handle to the file sharing subsystem
1611 * @param uri specifies the search parameters; can be
1612 * a KSK URI or an SKS URI.
1613 * @param anonymity desired level of anonymity
1614 * @param options options for the search
1615 * @param cctx initial value for the client context
1616 * @return context that can be used to control the search
1617 */
1618struct GNUNET_FS_SearchContext *
1619GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1620 const struct GNUNET_FS_Uri *uri, uint32_t anonymity,
1621 enum GNUNET_FS_SearchOptions options, void *cctx)
1622{
1623 struct GNUNET_FS_SearchContext *ret;
1624
1625 ret = search_start (h, uri, anonymity, options, cctx, NULL);
1626 if (NULL == ret)
1627 return NULL;
1628 ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, ret);
1629 return ret;
1630}
1631
1632
1633/**
1634 * Pause search.
1635 *
1636 * @param sc context for the search that should be paused
1637 */
1638void
1639GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
1640{
1641 struct GNUNET_FS_ProgressInfo pi;
1642
1643 if (NULL != sc->task)
1644 {
1645 GNUNET_SCHEDULER_cancel (sc->task);
1646 sc->task = NULL;
1647 }
1648 if (NULL != sc->mq)
1649 {
1650 GNUNET_MQ_destroy (sc->mq);
1651 sc->mq = NULL;
1652 }
1653 GNUNET_FS_search_sync_ (sc);
1654 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1655 &search_result_freeze_probes,
1656 sc);
1657 pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1658 sc->client_info = GNUNET_FS_search_make_status_ (&pi,
1659 sc->h,
1660 sc);
1661}
1662
1663
1664/**
1665 * Continue paused search.
1666 *
1667 * @param sc context for the search that should be resumed
1668 */
1669void
1670GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1671{
1672 struct GNUNET_FS_ProgressInfo pi;
1673
1674 GNUNET_assert (NULL == sc->mq);
1675 GNUNET_assert (NULL == sc->task);
1676 do_reconnect (sc);
1677 GNUNET_FS_search_sync_ (sc);
1678 pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1679 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1680 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1681 &search_result_resume_probes, sc);
1682}
1683
1684
1685/**
1686 * Signal stop for the given search result.
1687 *
1688 * @param cls the global FS handle
1689 * @param key the key for the search result (unused)
1690 * @param value the search result to free
1691 * @return #GNUNET_OK
1692 */
1693static int
1694search_result_stop (void *cls,
1695 const struct GNUNET_HashCode *key,
1696 void *value)
1697{
1698 struct GNUNET_FS_SearchContext *sc = cls;
1699 struct GNUNET_FS_SearchResult *sr = value;
1700 struct GNUNET_FS_ProgressInfo pi;
1701
1702 GNUNET_FS_search_stop_probe_ (sr);
1703 if (NULL != sr->download)
1704 {
1705 sr->download->search = NULL;
1706 sr->download->top
1707 = GNUNET_FS_make_top (sr->download->h,
1708 &GNUNET_FS_download_signal_suspend_,
1709 sr->download);
1710 if (NULL != sr->download->serialization)
1711 {
1712 GNUNET_FS_remove_sync_file_ (sc->h,
1713 GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
1714 sr->download->serialization);
1715 GNUNET_free (sr->download->serialization);
1716 sr->download->serialization = NULL;
1717 }
1718 pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT;
1719 GNUNET_FS_download_make_status_ (&pi,
1720 sr->download);
1721 GNUNET_FS_download_sync_ (sr->download);
1722 sr->download = NULL;
1723 }
1724 if (0 != sr->mandatory_missing)
1725 {
1726 /* client is unaware of search result as
1727 it does not match required keywords */
1728 GNUNET_break (NULL == sr->client_info);
1729 return GNUNET_OK;
1730 }
1731 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1732 pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1733 pi.value.search.specifics.result_stopped.meta = sr->meta;
1734 pi.value.search.specifics.result_stopped.uri = sr->uri;
1735 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->h, sc);
1736 return GNUNET_OK;
1737}
1738
1739
1740/**
1741 * Free the given search result.
1742 *
1743 * @param cls the global FS handle
1744 * @param key the key for the search result (unused)
1745 * @param value the search result to free
1746 * @return #GNUNET_OK
1747 */
1748static int
1749search_result_free (void *cls,
1750 const struct GNUNET_HashCode *key,
1751 void *value)
1752{
1753 struct GNUNET_FS_SearchResult *sr = value;
1754
1755 if (NULL != sr->update_search)
1756 {
1757 GNUNET_FS_search_stop (sr->update_search);
1758 GNUNET_assert (NULL == sr->update_search);
1759 }
1760 GNUNET_break (NULL == sr->probe_ctx);
1761 GNUNET_break (NULL == sr->probe_cancel_task);
1762 GNUNET_break (NULL == sr->client_info);
1763 GNUNET_free (sr->serialization);
1764 GNUNET_FS_uri_destroy (sr->uri);
1765 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1766 GNUNET_free (sr->keyword_bitmap);
1767 GNUNET_free (sr);
1768 return GNUNET_OK;
1769}
1770
1771
1772/**
1773 * Stop search for content.
1774 *
1775 * @param sc context for the search that should be stopped
1776 */
1777void
1778GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1779{
1780 struct GNUNET_FS_ProgressInfo pi;
1781 unsigned int i;
1782
1783 if (NULL != sc->top)
1784 GNUNET_FS_end_top (sc->h, sc->top);
1785 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1786 &search_result_stop,
1787 sc);
1788 if (NULL != sc->psearch_result)
1789 sc->psearch_result->update_search = NULL;
1790 if (NULL != sc->serialization)
1791 {
1792 GNUNET_FS_remove_sync_file_ (sc->h,
1793 (NULL != sc->psearch_result)
1794 ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH
1795 : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1796 sc->serialization);
1797 GNUNET_FS_remove_sync_dir_ (sc->h,
1798 (NULL != sc->psearch_result)
1799 ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH
1800 : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1801 sc->serialization);
1802 GNUNET_free (sc->serialization);
1803 }
1804 pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1805 sc->client_info = GNUNET_FS_search_make_status_ (&pi,
1806 sc->h,
1807 sc);
1808 GNUNET_break (NULL == sc->client_info);
1809 if (NULL != sc->task)
1810 {
1811 GNUNET_SCHEDULER_cancel (sc->task);
1812 sc->task = NULL;
1813 }
1814 if (NULL != sc->mq)
1815 {
1816 GNUNET_MQ_destroy (sc->mq);
1817 sc->mq = NULL;
1818 }
1819 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1820 &search_result_free, sc);
1821 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1822 if (NULL != sc->requests)
1823 {
1824 GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1825 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1826 GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1827 }
1828 GNUNET_free (sc->requests);
1829 GNUNET_free (sc->emsg);
1830 GNUNET_FS_uri_destroy (sc->uri);
1831 GNUNET_free (sc);
1832}
1833
1834
1835/* end of fs_search.c */
diff --git a/src/fs/fs_sharetree.c b/src/fs/fs_sharetree.c
deleted file mode 100644
index 3610b202e..000000000
--- a/src/fs/fs_sharetree.c
+++ /dev/null
@@ -1,456 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2005-2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_sharetree.c
23 * @brief code to manipulate the 'struct GNUNET_FS_ShareTreeItem' tree
24 * @author LRN
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_fs_service.h"
29#include "gnunet_scheduler_lib.h"
30#include <pthread.h>
31
32
33/**
34 * Entry for each unique keyword to track how often
35 * it occurred. Contains the keyword and the counter.
36 */
37struct KeywordCounter
38{
39 /**
40 * This is a doubly-linked list
41 */
42 struct KeywordCounter *prev;
43
44 /**
45 * This is a doubly-linked list
46 */
47 struct KeywordCounter *next;
48
49 /**
50 * Keyword that was found.
51 */
52 const char *value;
53
54 /**
55 * How many files have this keyword?
56 */
57 unsigned int count;
58};
59
60
61/**
62 * Aggregate information we keep for meta data in each directory.
63 */
64struct MetaCounter
65{
66 /**
67 * This is a doubly-linked list
68 */
69 struct MetaCounter *prev;
70
71 /**
72 * This is a doubly-linked list
73 */
74 struct MetaCounter *next;
75
76 /**
77 * Name of the plugin that provided that piece of metadata
78 */
79 const char *plugin_name;
80
81 /**
82 * MIME-type of the metadata itself
83 */
84 const char *data_mime_type;
85
86 /**
87 * The actual meta data.
88 */
89 const char *data;
90
91 /**
92 * Number of bytes in 'data'.
93 */
94 size_t data_size;
95
96 /**
97 * Type of the data
98 */
99 enum EXTRACTOR_MetaType type;
100
101 /**
102 * Format of the data
103 */
104 enum EXTRACTOR_MetaFormat format;
105
106 /**
107 * How many files have meta entries matching this value?
108 * (type and format do not have to match).
109 */
110 unsigned int count;
111};
112
113
114/**
115 * A structure that forms a singly-linked list that serves as a stack
116 * for metadata-processing function.
117 */
118struct TrimContext
119{
120 /**
121 * Map from the hash over the keyword to an 'struct KeywordCounter *'
122 * counter that says how often this keyword was
123 * encountered in the current directory.
124 */
125 struct GNUNET_CONTAINER_MultiHashMap *keywordcounter;
126
127 /**
128 * Map from the hash over the metadata to an 'struct MetaCounter *'
129 * counter that says how often this metadata was
130 * encountered in the current directory.
131 */
132 struct GNUNET_CONTAINER_MultiHashMap *metacounter;
133
134 /**
135 * Position we are currently manipulating.
136 */
137 struct GNUNET_FS_ShareTreeItem *pos;
138
139 /**
140 * Number of times an item has to be found to be moved to the parent.
141 */
142 unsigned int move_threshold;
143};
144
145
146/**
147 * Add the given keyword to the keyword statistics tracker.
148 *
149 * @param cls the multihashmap we store the keyword counters in
150 * @param keyword the keyword to count
151 * @param is_mandatory ignored
152 * @return always GNUNET_OK
153 */
154static int
155add_to_keyword_counter (void *cls, const char *keyword, int is_mandatory)
156{
157 struct GNUNET_CONTAINER_MultiHashMap *mcm = cls;
158 struct KeywordCounter *cnt;
159 struct GNUNET_HashCode hc;
160 size_t klen;
161
162 klen = strlen (keyword) + 1;
163 GNUNET_CRYPTO_hash (keyword, klen - 1, &hc);
164 cnt = GNUNET_CONTAINER_multihashmap_get (mcm, &hc);
165 if (cnt == NULL)
166 {
167 cnt = GNUNET_malloc (sizeof(struct KeywordCounter) + klen);
168 cnt->value = (const char *) &cnt[1];
169 GNUNET_memcpy (&cnt[1], keyword, klen);
170 GNUNET_assert (GNUNET_OK ==
171 GNUNET_CONTAINER_multihashmap_put (mcm,
172 &hc, cnt,
173 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
174 }
175 cnt->count++;
176 return GNUNET_OK;
177}
178
179
180/**
181 * Function called on each meta data item. Increments the
182 * respective counter.
183 *
184 * @param cls the container multihashmap to update
185 * @param plugin_name name of the plugin that produced this value;
186 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
187 * used in the main libextractor library and yielding
188 * meta data).
189 * @param type libextractor-type describing the meta data
190 * @param format basic format information about data
191 * @param data_mime_type mime-type of data (not of the original file);
192 * can be NULL (if mime-type is not known)
193 * @param data actual meta-data found
194 * @param data_len number of bytes in data
195 * @return 0 to continue extracting / iterating
196 */
197static int
198add_to_meta_counter (void *cls, const char *plugin_name,
199 enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat
200 format,
201 const char *data_mime_type, const char *data, size_t
202 data_len)
203{
204 struct GNUNET_CONTAINER_MultiHashMap *map = cls;
205 struct GNUNET_HashCode key;
206 struct MetaCounter *cnt;
207
208 GNUNET_CRYPTO_hash (data, data_len, &key);
209 cnt = GNUNET_CONTAINER_multihashmap_get (map, &key);
210 if (NULL == cnt)
211 {
212 cnt = GNUNET_new (struct MetaCounter);
213 cnt->data = data;
214 cnt->data_size = data_len;
215 cnt->plugin_name = plugin_name;
216 cnt->type = type;
217 cnt->format = format;
218 cnt->data_mime_type = data_mime_type;
219 GNUNET_assert (GNUNET_OK ==
220 GNUNET_CONTAINER_multihashmap_put (map,
221 &key, cnt,
222 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
223 }
224 cnt->count++;
225 return 0;
226}
227
228
229/**
230 * Remove keywords above the threshold.
231 *
232 * @param cls the 'struct TrimContext' with the pos to remove the keywords from
233 * @param keyword the keyword to check
234 * @param is_mandatory ignored
235 * @return always GNUNET_OK
236 */
237static int
238remove_high_frequency_keywords (void *cls, const char *keyword, int
239 is_mandatory)
240{
241 struct TrimContext *tc = cls;
242 struct KeywordCounter *counter;
243 struct GNUNET_HashCode hc;
244 size_t klen;
245
246 klen = strlen (keyword) + 1;
247 GNUNET_CRYPTO_hash (keyword, klen - 1, &hc);
248 counter = GNUNET_CONTAINER_multihashmap_get (tc->keywordcounter, &hc);
249 GNUNET_assert (NULL != counter);
250 if (counter->count < tc->move_threshold)
251 return GNUNET_OK;
252 GNUNET_FS_uri_ksk_remove_keyword (tc->pos->ksk_uri,
253 counter->value);
254 return GNUNET_OK;
255}
256
257
258/**
259 * Move "frequent" keywords over to the target ksk uri, free the
260 * counters.
261 *
262 * @param cls the 'struct TrimContext'
263 * @param key key of the entry
264 * @param value the 'struct KeywordCounter'
265 * @return GNUNET_YES (always)
266 */
267static int
268migrate_and_drop_keywords (void *cls, const struct GNUNET_HashCode *key,
269 void *value)
270{
271 struct TrimContext *tc = cls;
272 struct KeywordCounter *counter = value;
273
274 if (counter->count >= tc->move_threshold)
275 {
276 if (NULL == tc->pos->ksk_uri)
277 tc->pos->ksk_uri = GNUNET_FS_uri_ksk_create_from_args (1,
278 &counter->value);
279 else
280 GNUNET_FS_uri_ksk_add_keyword (tc->pos->ksk_uri, counter->value,
281 GNUNET_NO);
282 }
283 GNUNET_assert (GNUNET_YES ==
284 GNUNET_CONTAINER_multihashmap_remove (tc->keywordcounter,
285 key,
286 counter));
287 GNUNET_free (counter);
288 return GNUNET_YES;
289}
290
291
292/**
293 * Copy "frequent" metadata items over to the
294 * target metadata container, free the counters.
295 *
296 * @param cls the 'struct TrimContext'
297 * @param key key of the entry
298 * @param value the 'struct KeywordCounter'
299 * @return GNUNET_YES (always)
300 */
301static int
302migrate_and_drop_metadata (void *cls, const struct GNUNET_HashCode *key,
303 void *value)
304{
305 struct TrimContext *tc = cls;
306 struct MetaCounter *counter = value;
307
308 if (counter->count >= tc->move_threshold)
309 {
310 if (NULL == tc->pos->meta)
311 tc->pos->meta = GNUNET_CONTAINER_meta_data_create ();
312 GNUNET_CONTAINER_meta_data_insert (tc->pos->meta,
313 counter->plugin_name,
314 counter->type,
315 counter->format,
316 counter->data_mime_type, counter->data,
317 counter->data_size);
318 }
319 GNUNET_assert (GNUNET_YES ==
320 GNUNET_CONTAINER_multihashmap_remove (tc->metacounter,
321 key,
322 counter));
323 GNUNET_free (counter);
324 return GNUNET_YES;
325}
326
327
328/**
329 * Process a share item tree, moving frequent keywords up and
330 * copying frequent metadata up.
331 *
332 * @param tc trim context with hash maps to use
333 * @param tree tree to trim
334 */
335static void
336share_tree_trim (struct TrimContext *tc,
337 struct GNUNET_FS_ShareTreeItem *tree)
338{
339 struct GNUNET_FS_ShareTreeItem *pos;
340 unsigned int num_children;
341
342 /* first, trim all children */
343 num_children = 0;
344 for (pos = tree->children_head; NULL != pos; pos = pos->next)
345 {
346 share_tree_trim (tc, pos);
347 num_children++;
348 }
349
350 /* consider adding filename to directory meta data */
351 if (tree->is_directory == GNUNET_YES)
352 {
353 const char *user = getenv ("USER");
354 if ((user == NULL) ||
355 (0 != strncasecmp (user, tree->short_filename, strlen (user))))
356 {
357 /* only use filename if it doesn't match $USER */
358 if (NULL == tree->meta)
359 tree->meta = GNUNET_CONTAINER_meta_data_create ();
360 GNUNET_CONTAINER_meta_data_insert (tree->meta, "<libgnunetfs>",
361 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
362 EXTRACTOR_METAFORMAT_UTF8,
363 "text/plain", tree->short_filename,
364 strlen (tree->short_filename) + 1);
365 }
366 }
367
368 if (1 >= num_children)
369 return; /* nothing to trim */
370
371 /* now, count keywords and meta data in children */
372 for (pos = tree->children_head; NULL != pos; pos = pos->next)
373 {
374 if (NULL != pos->meta)
375 GNUNET_CONTAINER_meta_data_iterate (pos->meta, &add_to_meta_counter,
376 tc->metacounter);
377 if (NULL != pos->ksk_uri)
378 GNUNET_FS_uri_ksk_get_keywords (pos->ksk_uri, &add_to_keyword_counter,
379 tc->keywordcounter);
380 }
381
382 /* calculate threshold for moving keywords / meta data */
383 tc->move_threshold = 1 + (num_children / 2);
384
385 /* remove high-frequency keywords from children */
386 for (pos = tree->children_head; NULL != pos; pos = pos->next)
387 {
388 tc->pos = pos;
389 if (NULL != pos->ksk_uri)
390 {
391 struct GNUNET_FS_Uri *ksk_uri_copy = GNUNET_FS_uri_dup (pos->ksk_uri);
392 GNUNET_FS_uri_ksk_get_keywords (ksk_uri_copy,
393 &remove_high_frequency_keywords, tc);
394 GNUNET_FS_uri_destroy (ksk_uri_copy);
395 }
396 }
397
398 /* add high-frequency meta data and keywords to parent */
399 tc->pos = tree;
400 GNUNET_CONTAINER_multihashmap_iterate (tc->keywordcounter,
401 &migrate_and_drop_keywords,
402 tc);
403 GNUNET_CONTAINER_multihashmap_iterate (tc->metacounter,
404 &migrate_and_drop_metadata,
405 tc);
406}
407
408
409/**
410 * Process a share item tree, moving frequent keywords up and
411 * copying frequent metadata up.
412 *
413 * @param toplevel toplevel directory in the tree, returned by the scanner
414 */
415void
416GNUNET_FS_share_tree_trim (struct GNUNET_FS_ShareTreeItem *toplevel)
417{
418 struct TrimContext tc;
419
420 if (toplevel == NULL)
421 return;
422 tc.keywordcounter = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO);
423 tc.metacounter = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO);
424 share_tree_trim (&tc, toplevel);
425 GNUNET_CONTAINER_multihashmap_destroy (tc.keywordcounter);
426 GNUNET_CONTAINER_multihashmap_destroy (tc.metacounter);
427}
428
429
430/**
431 * Release memory of a share item tree.
432 *
433 * @param toplevel toplevel of the tree to be freed
434 */
435void
436GNUNET_FS_share_tree_free (struct GNUNET_FS_ShareTreeItem *toplevel)
437{
438 struct GNUNET_FS_ShareTreeItem *pos;
439
440 while (NULL != (pos = toplevel->children_head))
441 GNUNET_FS_share_tree_free (pos);
442 if (NULL != toplevel->parent)
443 GNUNET_CONTAINER_DLL_remove (toplevel->parent->children_head,
444 toplevel->parent->children_tail,
445 toplevel);
446 if (NULL != toplevel->meta)
447 GNUNET_CONTAINER_meta_data_destroy (toplevel->meta);
448 if (NULL != toplevel->ksk_uri)
449 GNUNET_FS_uri_destroy (toplevel->ksk_uri);
450 GNUNET_free (toplevel->filename);
451 GNUNET_free (toplevel->short_filename);
452 GNUNET_free (toplevel);
453}
454
455
456/* end fs_sharetree.c */
diff --git a/src/fs/fs_test_lib.c b/src/fs/fs_test_lib.c
deleted file mode 100644
index dc4b214d9..000000000
--- a/src/fs/fs_test_lib.c
+++ /dev/null
@@ -1,659 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2011, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_test_lib.c
23 * @brief library routines for testing FS publishing and downloading;
24 * this code is limited to flat files
25 * and no keywords (those functions can be tested with
26 * single-peer setups; this is for testing routing).
27 * @author Christian Grothoff
28 */
29#include "platform.h"
30#include "fs_api.h"
31#include "fs_test_lib.h"
32
33
34#define CONTENT_LIFETIME GNUNET_TIME_UNIT_HOURS
35
36
37/**
38 * Handle for a publishing operation started for testing FS.
39 */
40struct TestPublishOperation
41{
42 /**
43 * Handle for the operation to connect to the peer's 'fs' service.
44 */
45 struct GNUNET_TESTBED_Operation *fs_op;
46
47 /**
48 * Handle to the file sharing context using this daemon.
49 */
50 struct GNUNET_FS_Handle *fs;
51
52 /**
53 * Function to call when upload is done.
54 */
55 GNUNET_FS_TEST_UriContinuation publish_cont;
56
57 /**
58 * Closure for publish_cont.
59 */
60 void *publish_cont_cls;
61
62 /**
63 * Task to abort publishing (timeout).
64 */
65 struct GNUNET_SCHEDULER_Task *publish_timeout_task;
66
67 /**
68 * Seed for file generation.
69 */
70 uint32_t publish_seed;
71
72 /**
73 * Context for current publishing operation.
74 */
75 struct GNUNET_FS_PublishContext *publish_context;
76
77 /**
78 * Result URI.
79 */
80 struct GNUNET_FS_Uri *publish_uri;
81
82 /**
83 * Name of the temporary file used, or NULL for none.
84 */
85 char *publish_tmp_file;
86
87 /**
88 * Size of the file.
89 */
90 uint64_t size;
91
92 /**
93 * Anonymity level used.
94 */
95 uint32_t anonymity;
96
97 /**
98 * Verbosity level of the current operation.
99 */
100 unsigned int verbose;
101
102 /**
103 * Are we testing indexing? (YES: index, NO: insert, SYSERR: simulate)
104 */
105 int do_index;
106};
107
108
109/**
110 * Handle for a download operation started for testing FS.
111 */
112struct TestDownloadOperation
113{
114 /**
115 * Handle for the operation to connect to the peer's 'fs' service.
116 */
117 struct GNUNET_TESTBED_Operation *fs_op;
118
119 /**
120 * Handle to the file sharing context using this daemon.
121 */
122 struct GNUNET_FS_Handle *fs;
123
124 /**
125 * Handle to the daemon via testing.
126 */
127 struct GNUNET_TESTING_Daemon *daemon;
128
129 /**
130 * Function to call when download is done.
131 */
132 GNUNET_SCHEDULER_TaskCallback download_cont;
133
134 /**
135 * Closure for download_cont.
136 */
137 void *download_cont_cls;
138
139 /**
140 * URI to download.
141 */
142 struct GNUNET_FS_Uri *uri;
143
144 /**
145 * Task to abort downloading (timeout).
146 */
147 struct GNUNET_SCHEDULER_Task *download_timeout_task;
148
149 /**
150 * Context for current download operation.
151 */
152 struct GNUNET_FS_DownloadContext *download_context;
153
154 /**
155 * Size of the file.
156 */
157 uint64_t size;
158
159 /**
160 * Anonymity level used.
161 */
162 uint32_t anonymity;
163
164 /**
165 * Seed for download verification.
166 */
167 uint32_t download_seed;
168
169 /**
170 * Verbosity level of the current operation.
171 */
172 unsigned int verbose;
173};
174
175
176/**
177 * Task scheduled to report on the completion of our publish operation.
178 *
179 * @param cls the publish operation context
180 * @param tc scheduler context (unused)
181 */
182static void
183report_uri (void *cls)
184{
185 struct TestPublishOperation *po = cls;
186
187 GNUNET_FS_publish_stop (po->publish_context);
188 GNUNET_TESTBED_operation_done (po->fs_op);
189 po->publish_cont (po->publish_cont_cls,
190 po->publish_uri,
191 (GNUNET_YES == po->do_index)
192 ? po->publish_tmp_file
193 : NULL);
194 GNUNET_FS_uri_destroy (po->publish_uri);
195 if ((GNUNET_YES != po->do_index) &&
196 (NULL != po->publish_tmp_file))
197 (void) GNUNET_DISK_directory_remove (po->publish_tmp_file);
198 GNUNET_free (po->publish_tmp_file);
199 GNUNET_free (po);
200}
201
202
203/**
204 * Task scheduled to run when publish operation times out.
205 *
206 * @param cls the publish operation context
207 */
208static void
209publish_timeout (void *cls)
210{
211 struct TestPublishOperation *po = cls;
212
213 po->publish_timeout_task = NULL;
214 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
215 "Timeout while trying to publish data\n");
216 GNUNET_TESTBED_operation_done (po->fs_op);
217 GNUNET_FS_publish_stop (po->publish_context);
218 po->publish_cont (po->publish_cont_cls, NULL, NULL);
219 (void) GNUNET_DISK_directory_remove (po->publish_tmp_file);
220 GNUNET_free (po->publish_tmp_file);
221 GNUNET_free (po);
222}
223
224
225/**
226 * Progress callback for file-sharing events while publishing.
227 *
228 * @param cls the publish operation context
229 * @param info information about the event
230 */
231static void *
232publish_progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
233{
234 struct TestPublishOperation *po = cls;
235
236 switch (info->status)
237 {
238 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
239 GNUNET_SCHEDULER_cancel (po->publish_timeout_task);
240 po->publish_timeout_task = NULL;
241 po->publish_uri =
242 GNUNET_FS_uri_dup (info->value.publish.specifics.completed.chk_uri);
243 GNUNET_SCHEDULER_add_now (&report_uri,
244 po);
245 break;
246
247 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
248 if (po->verbose)
249 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Publishing at %llu/%llu bytes\n",
250 (unsigned long long) info->value.publish.completed,
251 (unsigned long long) info->value.publish.size);
252 break;
253
254 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
255 break;
256
257 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
258 if (po->verbose)
259 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Download at %llu/%llu bytes\n",
260 (unsigned long long) info->value.download.completed,
261 (unsigned long long) info->value.download.size);
262 break;
263
264 default:
265 break;
266 }
267 return NULL;
268}
269
270
271/**
272 * Generate test data for publishing test.
273 *
274 * @param cls pointer to uint32_t with publishing seed
275 * @param offset offset to generate data for
276 * @param max maximum number of bytes to generate
277 * @param buf where to write generated data
278 * @param emsg where to store error message (unused)
279 * @return number of bytes written to buf
280 */
281static size_t
282file_generator (void *cls,
283 uint64_t offset,
284 size_t max,
285 void *buf,
286 char **emsg)
287{
288 uint32_t *publish_seed = cls;
289 uint64_t pos;
290 uint8_t *cbuf = buf;
291 int mod;
292
293 if (emsg != NULL)
294 *emsg = NULL;
295 if (buf == NULL)
296 return 0;
297 for (pos = 0; pos < 8; pos++)
298 cbuf[pos] = (uint8_t) (offset >> pos * 8);
299 for (pos = 8; pos < max; pos++)
300 {
301 mod = (255 - (offset / 1024 / 32));
302 if (mod == 0)
303 mod = 1;
304 cbuf[pos] = (uint8_t) ((offset * (*publish_seed)) % mod);
305 }
306 return max;
307}
308
309
310/**
311 * Connect adapter for publishing operation.
312 *
313 * @param cls the 'struct TestPublishOperation'
314 * @param cfg configuration of the peer to connect to; will be available until
315 * GNUNET_TESTBED_operation_done() is called on the operation returned
316 * from GNUNET_TESTBED_service_connect()
317 * @return service handle to return in 'op_result', NULL on error
318 */
319static void *
320publish_connect_adapter (void *cls,
321 const struct GNUNET_CONFIGURATION_Handle *cfg)
322{
323 struct TestPublishOperation *po = cls;
324
325 return GNUNET_FS_start (cfg,
326 "fs-test-publish",
327 &publish_progress_cb, po,
328 GNUNET_FS_FLAGS_NONE,
329 GNUNET_FS_OPTIONS_END);
330}
331
332
333/**
334 * Adapter function called to destroy connection to file-sharing service.
335 *
336 * @param cls the 'struct GNUNET_FS_Handle'
337 * @param op_result unused (different for publish/download!)
338 */
339static void
340fs_disconnect_adapter (void *cls,
341 void *op_result)
342{
343 struct GNUNET_FS_Handle *fs = op_result;
344
345 GNUNET_FS_stop (fs);
346}
347
348
349/**
350 * Callback to be called when testbed has connected to the fs service
351 *
352 * @param cls the 'struct TestPublishOperation'
353 * @param op the operation that has been finished
354 * @param ca_result the 'struct GNUNET_FS_Handle ' (NULL on error)
355 * @param emsg error message in case the operation has failed; will be NULL if
356 * operation has executed successfully.
357 */
358static void
359publish_fs_connect_complete_cb (void *cls,
360 struct GNUNET_TESTBED_Operation *op,
361 void *ca_result,
362 const char *emsg)
363{
364 struct TestPublishOperation *po = cls;
365 struct GNUNET_FS_FileInformation *fi;
366 struct GNUNET_DISK_FileHandle *fh;
367 char *em;
368 uint64_t off;
369 char buf[DBLOCK_SIZE];
370 size_t bsize;
371 struct GNUNET_FS_BlockOptions bo;
372
373 if (NULL == ca_result)
374 {
375 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
376 "Failed to connect to FS for publishing: %s\n", emsg);
377 po->publish_cont (po->publish_cont_cls,
378 NULL, NULL);
379 GNUNET_TESTBED_operation_done (po->fs_op);
380 GNUNET_free (po);
381 return;
382 }
383 po->fs = ca_result;
384
385 bo.expiration_time = GNUNET_TIME_relative_to_absolute (CONTENT_LIFETIME);
386 bo.anonymity_level = po->anonymity;
387 bo.content_priority = 42;
388 bo.replication_level = 1;
389 if (GNUNET_YES == po->do_index)
390 {
391 po->publish_tmp_file = GNUNET_DISK_mktemp ("fs-test-publish-index");
392 GNUNET_assert (po->publish_tmp_file != NULL);
393 fh = GNUNET_DISK_file_open (po->publish_tmp_file,
394 GNUNET_DISK_OPEN_WRITE
395 | GNUNET_DISK_OPEN_CREATE,
396 GNUNET_DISK_PERM_USER_READ
397 | GNUNET_DISK_PERM_USER_WRITE);
398 GNUNET_assert (NULL != fh);
399 off = 0;
400 while (off < po->size)
401 {
402 bsize = GNUNET_MIN (sizeof(buf), po->size - off);
403 emsg = NULL;
404 GNUNET_assert (bsize == file_generator (&po->publish_seed, off, bsize,
405 buf, &em));
406 GNUNET_assert (em == NULL);
407 GNUNET_assert (bsize == GNUNET_DISK_file_write (fh, buf, bsize));
408 off += bsize;
409 }
410 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
411 fi = GNUNET_FS_file_information_create_from_file (po->fs, po,
412 po->publish_tmp_file,
413 NULL, NULL, po->do_index,
414 &bo);
415 GNUNET_assert (NULL != fi);
416 }
417 else
418 {
419 fi = GNUNET_FS_file_information_create_from_reader (po->fs, po,
420 po->size,
421 &file_generator,
422 &po->publish_seed,
423 NULL, NULL,
424 po->do_index, &bo);
425 GNUNET_assert (NULL != fi);
426 }
427 po->publish_context =
428 GNUNET_FS_publish_start (po->fs, fi, NULL, NULL, NULL,
429 GNUNET_FS_PUBLISH_OPTION_NONE);
430}
431
432
433/**
434 * Publish a file at the given peer.
435 *
436 * @param peer where to publish
437 * @param timeout if this operation cannot be completed within the
438 * given period, call the continuation with an error code
439 * @param anonymity option for publication
440 * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
441 * GNUNET_SYSERR for simulation
442 * @param size size of the file to publish
443 * @param seed seed to use for file generation
444 * @param verbose how verbose to be in reporting
445 * @param cont function to call when done
446 * @param cont_cls closure for cont
447 */
448void
449GNUNET_FS_TEST_publish (struct GNUNET_TESTBED_Peer *peer,
450 struct GNUNET_TIME_Relative timeout, uint32_t anonymity,
451 int do_index, uint64_t size, uint32_t seed,
452 unsigned int verbose,
453 GNUNET_FS_TEST_UriContinuation cont, void *cont_cls)
454{
455 struct TestPublishOperation *po;
456
457 po = GNUNET_new (struct TestPublishOperation);
458 po->publish_cont = cont;
459 po->publish_cont_cls = cont_cls;
460 po->publish_seed = seed;
461 po->anonymity = anonymity;
462 po->size = size;
463 po->verbose = verbose;
464 po->do_index = do_index;
465 po->fs_op = GNUNET_TESTBED_service_connect (po,
466 peer,
467 "fs",
468 &publish_fs_connect_complete_cb,
469 po,
470 &publish_connect_adapter,
471 &fs_disconnect_adapter,
472 po);
473 po->publish_timeout_task =
474 GNUNET_SCHEDULER_add_delayed (timeout, &publish_timeout, po);
475}
476
477
478/* ************************** download ************************ */
479
480
481/**
482 * Task scheduled to run when download operation times out.
483 *
484 * @param cls the download operation context
485 */
486static void
487download_timeout (void *cls)
488{
489 struct TestDownloadOperation *dop = cls;
490
491 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
492 "Timeout while trying to download file\n");
493 dop->download_timeout_task = NULL;
494 GNUNET_FS_download_stop (dop->download_context,
495 GNUNET_YES);
496 GNUNET_SCHEDULER_add_now (dop->download_cont,
497 dop->download_cont_cls);
498 GNUNET_TESTBED_operation_done (dop->fs_op);
499 GNUNET_FS_uri_destroy (dop->uri);
500 GNUNET_free (dop);
501}
502
503
504/**
505 * Task scheduled to report on the completion of our download operation.
506 *
507 * @param cls the download operation context
508 */
509static void
510report_success (void *cls)
511{
512 struct TestDownloadOperation *dop = cls;
513
514 GNUNET_FS_download_stop (dop->download_context,
515 GNUNET_YES);
516 GNUNET_SCHEDULER_add_now (dop->download_cont,
517 dop->download_cont_cls);
518 GNUNET_TESTBED_operation_done (dop->fs_op);
519 GNUNET_FS_uri_destroy (dop->uri);
520 GNUNET_free (dop);
521}
522
523
524/**
525 * Progress callback for file-sharing events while downloading.
526 *
527 * @param cls the download operation context
528 * @param info information about the event
529 */
530static void *
531download_progress_cb (void *cls,
532 const struct GNUNET_FS_ProgressInfo *info)
533{
534 struct TestDownloadOperation *dop = cls;
535
536 switch (info->status)
537 {
538 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
539 if (dop->verbose)
540 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
541 "Download at %llu/%llu bytes\n",
542 (unsigned long long) info->value.download.completed,
543 (unsigned long long) info->value.download.size);
544 break;
545
546 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
547 GNUNET_SCHEDULER_cancel (dop->download_timeout_task);
548 dop->download_timeout_task = NULL;
549 GNUNET_SCHEDULER_add_now (&report_success, dop);
550 break;
551
552 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
553 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
554 break;
555
556 /* FIXME: monitor data correctness during download progress */
557 /* FIXME: do performance reports given sufficient verbosity */
558 /* FIXME: advance timeout task to "immediate" on error */
559 default:
560 break;
561 }
562 return NULL;
563}
564
565
566/**
567 * Connect adapter for download operation.
568 *
569 * @param cls the 'struct TestDownloadOperation'
570 * @param cfg configuration of the peer to connect to; will be available until
571 * GNUNET_TESTBED_operation_done() is called on the operation returned
572 * from GNUNET_TESTBED_service_connect()
573 * @return service handle to return in 'op_result', NULL on error
574 */
575static void *
576download_connect_adapter (void *cls,
577 const struct GNUNET_CONFIGURATION_Handle *cfg)
578{
579 struct TestPublishOperation *po = cls;
580
581 return GNUNET_FS_start (cfg,
582 "fs-test-download",
583 &download_progress_cb, po,
584 GNUNET_FS_FLAGS_NONE,
585 GNUNET_FS_OPTIONS_END);
586}
587
588
589/**
590 * Callback to be called when testbed has connected to the fs service
591 *
592 * @param cls the 'struct TestPublishOperation'
593 * @param op the operation that has been finished
594 * @param ca_result the 'struct GNUNET_FS_Handle ' (NULL on error)
595 * @param emsg error message in case the operation has failed; will be NULL if
596 * operation has executed successfully.
597 */
598static void
599download_fs_connect_complete_cb (void *cls,
600 struct GNUNET_TESTBED_Operation *op,
601 void *ca_result,
602 const char *emsg)
603{
604 struct TestDownloadOperation *dop = cls;
605
606 dop->fs = ca_result;
607 GNUNET_assert (NULL != dop->fs);
608 dop->download_context =
609 GNUNET_FS_download_start (dop->fs, dop->uri, NULL, NULL, NULL, 0, dop->size,
610 dop->anonymity, GNUNET_FS_DOWNLOAD_OPTION_NONE,
611 NULL, NULL);
612}
613
614
615/**
616 * Perform test download.
617 *
618 * @param peer which peer to download from
619 * @param timeout if this operation cannot be completed within the
620 * given period, call the continuation with an error code
621 * @param anonymity option for download
622 * @param seed used for file validation
623 * @param uri URI of file to download (CHK/LOC only)
624 * @param verbose how verbose to be in reporting
625 * @param cont function to call when done
626 * @param cont_cls closure for cont
627 */
628void
629GNUNET_FS_TEST_download (struct GNUNET_TESTBED_Peer *peer,
630 struct GNUNET_TIME_Relative timeout,
631 uint32_t anonymity, uint32_t seed,
632 const struct GNUNET_FS_Uri *uri, unsigned int verbose,
633 GNUNET_SCHEDULER_TaskCallback cont, void *cont_cls)
634{
635 struct TestDownloadOperation *dop;
636
637 dop = GNUNET_new (struct TestDownloadOperation);
638 dop->uri = GNUNET_FS_uri_dup (uri);
639 dop->size = GNUNET_FS_uri_chk_get_file_size (uri);
640 dop->verbose = verbose;
641 dop->anonymity = anonymity;
642 dop->download_cont = cont;
643 dop->download_cont_cls = cont_cls;
644 dop->download_seed = seed;
645
646 dop->fs_op = GNUNET_TESTBED_service_connect (dop,
647 peer,
648 "fs",
649 &download_fs_connect_complete_cb,
650 dop,
651 &download_connect_adapter,
652 &fs_disconnect_adapter,
653 dop);
654 dop->download_timeout_task =
655 GNUNET_SCHEDULER_add_delayed (timeout, &download_timeout, dop);
656}
657
658
659/* end of fs_test_lib.c */
diff --git a/src/fs/fs_test_lib.h b/src/fs/fs_test_lib.h
deleted file mode 100644
index 36244eb19..000000000
--- a/src/fs/fs_test_lib.h
+++ /dev/null
@@ -1,102 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_test_lib.h
23 * @brief library routines for testing FS publishing and downloading;
24 * this code is limited to flat files
25 * and no keywords (those functions can be tested with
26 * single-peer setups; this is for testing routing).
27 * @author Christian Grothoff
28 */
29#ifndef FS_TEST_LIB_H
30#define FS_TEST_LIB_H
31
32#include "gnunet_util_lib.h"
33#include "gnunet_fs_service.h"
34#include "gnunet_testbed_service.h"
35
36
37/**
38 * Function signature.
39 *
40 * @param cls closure (user defined)
41 * @param uri a URI, NULL for errors
42 * @param fn name of the file on disk to be removed upon
43 * completion, or NULL for inserted files (also NULL on error)
44 */
45typedef void
46(*GNUNET_FS_TEST_UriContinuation) (void *cls,
47 const struct GNUNET_FS_Uri *uri,
48 const char *fn);
49
50
51/**
52 * Publish a file at the given daemon.
53 *
54 * @param peer where to publish
55 * @param timeout if this operation cannot be completed within the
56 * given period, call the continuation with an error code
57 * @param anonymity option for publication
58 * @param do_index #GNUNET_YES for index, #GNUNET_NO for insertion,
59 * #GNUNET_SYSERR for simulation
60 * @param size size of the file to publish
61 * @param seed seed to use for file generation
62 * @param verbose how verbose to be in reporting
63 * @param cont function to call when done
64 * @param cont_cls closure for @a cont
65 */
66void
67GNUNET_FS_TEST_publish (struct GNUNET_TESTBED_Peer *peer,
68 struct GNUNET_TIME_Relative timeout,
69 uint32_t anonymity,
70 int do_index,
71 uint64_t size,
72 uint32_t seed,
73 unsigned int verbose,
74 GNUNET_FS_TEST_UriContinuation cont,
75 void *cont_cls);
76
77
78/**
79 * Perform test download.
80 *
81 * @param peer which peer to download from
82 * @param timeout if this operation cannot be completed within the
83 * given period, call the continuation with an error code
84 * @param anonymity option for download
85 * @param seed used for file validation
86 * @param uri URI of file to download (CHK/LOC only)
87 * @param verbose how verbose to be in reporting
88 * @param cont function to call when done
89 * @param cont_cls closure for @a cont
90 */
91void
92GNUNET_FS_TEST_download (struct GNUNET_TESTBED_Peer *peer,
93 struct GNUNET_TIME_Relative timeout,
94 uint32_t anonymity,
95 uint32_t seed,
96 const struct GNUNET_FS_Uri *uri,
97 unsigned int verbose,
98 GNUNET_SCHEDULER_TaskCallback cont,
99 void *cont_cls);
100
101
102#endif
diff --git a/src/fs/fs_test_lib_data.conf b/src/fs/fs_test_lib_data.conf
deleted file mode 100644
index c99674798..000000000
--- a/src/fs/fs_test_lib_data.conf
+++ /dev/null
@@ -1,17 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-fs-test-lib/
4
5[ats]
6WAN_QUOTA_IN = 3932160
7WAN_QUOTA_OUT = 3932160
8
9[datastore]
10QUOTA = 2 GB
11#PLUGIN = heap
12#
13[fs]
14IMMEDIATE_START = YES
15
16[testbed]
17OVERLAY_TOPOLOGY = CLIQUE
diff --git a/src/fs/fs_tree.c b/src/fs/fs_tree.c
deleted file mode 100644
index 4d49809f8..000000000
--- a/src/fs/fs_tree.c
+++ /dev/null
@@ -1,463 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/fs_tree.c
22 * @brief Merkle-tree-ish-CHK file encoding for GNUnet
23 * @see http://gnunet.org/encoding.php3
24 * @author Krista Bennett
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "fs_tree.h"
29
30
31/**
32 * Context for an ECRS-based file encoder that computes
33 * the Merkle-ish-CHK tree.
34 */
35struct GNUNET_FS_TreeEncoder
36{
37 /**
38 * Global FS context.
39 */
40 struct GNUNET_FS_Handle *h;
41
42 /**
43 * Closure for all callbacks.
44 */
45 void *cls;
46
47 /**
48 * Function to call on encrypted blocks.
49 */
50 GNUNET_FS_TreeBlockProcessor proc;
51
52 /**
53 * Function to call with progress information.
54 */
55 GNUNET_FS_TreeProgressCallback progress;
56
57 /**
58 * Function to call to receive input data.
59 */
60 GNUNET_FS_DataReader reader;
61
62 /**
63 * Function to call once we're done with processing.
64 */
65 GNUNET_SCHEDULER_TaskCallback cont;
66
67 /**
68 * Set to an error message (if we had an error).
69 */
70 char *emsg;
71
72 /**
73 * Set to the URI (upon successful completion)
74 */
75 struct GNUNET_FS_Uri *uri;
76
77 /**
78 * Overall file size.
79 */
80 uint64_t size;
81
82 /**
83 * How far are we?
84 */
85 uint64_t publish_offset;
86
87 /**
88 * How deep are we? Depth 0 is for the DBLOCKs.
89 */
90 unsigned int current_depth;
91
92 /**
93 * How deep is the tree? Always > 0.
94 */
95 unsigned int chk_tree_depth;
96
97 /**
98 * In-memory cache of the current CHK tree.
99 * This struct will contain the CHK values
100 * from the root to the currently processed
101 * node in the tree as identified by
102 * "current_depth" and "publish_offset".
103 * The "chktree" will be initially NULL,
104 * then allocated to a sufficient number of
105 * entries for the size of the file and
106 * finally freed once the upload is complete.
107 */
108 struct ContentHashKey *chk_tree;
109
110 /**
111 * Are we currently in 'GNUNET_FS_tree_encoder_next'?
112 * Flag used to prevent recursion.
113 */
114 int in_next;
115};
116
117
118/**
119 * Compute the depth of the CHK tree.
120 *
121 * @param flen file length for which to compute the depth
122 * @return depth of the tree, always > 0. A depth of 1 means only a DBLOCK.
123 */
124unsigned int
125GNUNET_FS_compute_depth (uint64_t flen)
126{
127 unsigned int treeDepth;
128 uint64_t fl;
129
130 treeDepth = 1;
131 fl = DBLOCK_SIZE;
132 while (fl < flen)
133 {
134 treeDepth++;
135 if (fl * CHK_PER_INODE < fl)
136 {
137 /* integer overflow, this is a HUGE file... */
138 return treeDepth;
139 }
140 fl = fl * CHK_PER_INODE;
141 }
142 return treeDepth;
143}
144
145
146/**
147 * Calculate how many bytes of payload a block tree of the given
148 * depth MAY correspond to at most (this function ignores the fact that
149 * some blocks will only be present partially due to the total file
150 * size cutting some blocks off at the end).
151 *
152 * @param depth depth of the block. depth==0 is a DBLOCK.
153 * @return number of bytes of payload a subtree of this depth may correspond to
154 */
155uint64_t
156GNUNET_FS_tree_compute_tree_size (unsigned int depth)
157{
158 uint64_t rsize;
159 unsigned int i;
160
161 rsize = DBLOCK_SIZE;
162 for (i = 0; i < depth; i++)
163 rsize *= CHK_PER_INODE;
164 return rsize;
165}
166
167
168/**
169 * Compute the size of the current IBLOCK. The encoder is
170 * triggering the calculation of the size of an IBLOCK at the
171 * *end* (hence end_offset) of its construction. The IBLOCK
172 * maybe a full or a partial IBLOCK, and this function is to
173 * calculate how long it should be.
174 *
175 * @param depth depth of the IBlock in the tree, 0 would be a DBLOCK,
176 * must be > 0 (this function is for IBLOCKs only!)
177 * @param end_offset current offset in the payload (!) of the overall file,
178 * must be > 0 (since this function is called at the
179 * end of a block).
180 * @return size of the corresponding IBlock
181 */
182static uint16_t
183GNUNET_FS_tree_compute_iblock_size (unsigned int depth, uint64_t end_offset)
184{
185 unsigned int ret;
186 uint64_t mod;
187 uint64_t bds;
188
189 GNUNET_assert (depth > 0);
190 GNUNET_assert (end_offset > 0);
191 bds = GNUNET_FS_tree_compute_tree_size (depth);
192 mod = end_offset % bds;
193 if (0 == mod)
194 {
195 /* we were triggered at the end of a full block */
196 ret = CHK_PER_INODE;
197 }
198 else
199 {
200 /* we were triggered at the end of the file */
201 bds /= CHK_PER_INODE;
202 ret = mod / bds;
203 if (0 != mod % bds)
204 ret++;
205 }
206 return (uint16_t) (ret * sizeof(struct ContentHashKey));
207}
208
209
210/**
211 * Compute how many bytes of data should be stored in
212 * the specified block.
213 *
214 * @param fsize overall file size, must be > 0.
215 * @param offset offset in the original data corresponding
216 * to the beginning of the tree induced by the block;
217 * must be <= fsize
218 * @param depth depth of the node in the tree, 0 for DBLOCK
219 * @return number of bytes stored in this node
220 */
221size_t
222GNUNET_FS_tree_calculate_block_size (uint64_t fsize, uint64_t offset,
223 unsigned int depth)
224{
225 size_t ret;
226 uint64_t rsize;
227 uint64_t epos;
228 unsigned int chks;
229
230 GNUNET_assert (fsize > 0);
231 GNUNET_assert (offset <= fsize);
232 if (depth == 0)
233 {
234 ret = DBLOCK_SIZE;
235 if ((offset + ret > fsize) || (offset + ret < offset))
236 ret = (size_t) (fsize - offset);
237 return ret;
238 }
239
240 rsize = GNUNET_FS_tree_compute_tree_size (depth - 1);
241 epos = offset + rsize * CHK_PER_INODE;
242 if ((epos < offset) || (epos > fsize))
243 epos = fsize;
244 /* round up when computing #CHKs in our IBlock */
245 chks = (epos - offset + rsize - 1) / rsize;
246 GNUNET_assert (chks <= CHK_PER_INODE);
247 return chks * sizeof(struct ContentHashKey);
248}
249
250
251/**
252 * Initialize a tree encoder. This function will call @a proc and
253 * "progress" on each block in the tree. Once all blocks have been
254 * processed, "cont" will be scheduled. The @a reader will be called
255 * to obtain the (plaintext) blocks for the file. Note that this
256 * function will not actually call @a proc. The client must
257 * call #GNUNET_FS_tree_encoder_next to trigger encryption (and
258 * calling of @a proc) for the each block.
259 *
260 * @param h the global FS context
261 * @param size overall size of the file to encode
262 * @param cls closure for reader, proc, progress and cont
263 * @param reader function to call to read plaintext data
264 * @param proc function to call on each encrypted block
265 * @param progress function to call with progress information
266 * @param cont function to call when done
267 */
268struct GNUNET_FS_TreeEncoder *
269GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h, uint64_t size,
270 void *cls,
271 GNUNET_FS_DataReader reader,
272 GNUNET_FS_TreeBlockProcessor proc,
273 GNUNET_FS_TreeProgressCallback progress,
274 GNUNET_SCHEDULER_TaskCallback cont)
275{
276 struct GNUNET_FS_TreeEncoder *te;
277
278 te = GNUNET_new (struct GNUNET_FS_TreeEncoder);
279 te->h = h;
280 te->size = size;
281 te->cls = cls;
282 te->reader = reader;
283 te->proc = proc;
284 te->progress = progress;
285 te->cont = cont;
286 te->chk_tree_depth = GNUNET_FS_compute_depth (size);
287 te->chk_tree
288 = GNUNET_new_array (te->chk_tree_depth * CHK_PER_INODE,
289 struct ContentHashKey);
290 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
291 "Created tree encoder for file with %llu bytes and depth %u\n",
292 (unsigned long long) size,
293 te->chk_tree_depth);
294 return te;
295}
296
297
298/**
299 * Compute the offset of the CHK for the
300 * current block in the IBlock above.
301 *
302 * @param depth depth of the IBlock in the tree (aka overall
303 * number of tree levels minus depth); 0 == DBlock
304 * @param end_offset current offset in the overall file,
305 * at the *beginning* of the block for DBLOCKs (depth==0),
306 * otherwise at the *end* of the block (exclusive)
307 * @return (array of CHKs') offset in the above IBlock
308 */
309static unsigned int
310compute_chk_offset (unsigned int depth, uint64_t end_offset)
311{
312 uint64_t bds;
313 unsigned int ret;
314
315 bds = GNUNET_FS_tree_compute_tree_size (depth);
316 if (depth > 0)
317 end_offset--; /* round down since for depth > 0 offset is at the END of the block */
318 ret = end_offset / bds;
319 return ret % CHK_PER_INODE;
320}
321
322
323/**
324 * Encrypt the next block of the file (and call proc and progress
325 * accordingly; or of course "cont" if we have already completed
326 * encoding of the entire file).
327 *
328 * @param te tree encoder to use
329 */
330void
331GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder *te)
332{
333 struct ContentHashKey *mychk;
334 const void *pt_block;
335 uint16_t pt_size;
336 char iob[DBLOCK_SIZE];
337 char enc[DBLOCK_SIZE];
338 struct GNUNET_CRYPTO_SymmetricSessionKey sk;
339 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
340 unsigned int off;
341
342 GNUNET_assert (GNUNET_NO == te->in_next);
343 te->in_next = GNUNET_YES;
344 if (te->chk_tree_depth == te->current_depth)
345 {
346 off = CHK_PER_INODE * (te->chk_tree_depth - 1);
347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TE done, reading CHK `%s' from %u\n",
348 GNUNET_h2s (&te->chk_tree[off].query), off);
349 te->uri = GNUNET_new (struct GNUNET_FS_Uri);
350 te->uri->type = GNUNET_FS_URI_CHK;
351 te->uri->data.chk.chk = te->chk_tree[off];
352 te->uri->data.chk.file_length = GNUNET_htonll (te->size);
353 te->in_next = GNUNET_NO;
354 te->cont (te->cls);
355 return;
356 }
357 if (0 == te->current_depth)
358 {
359 /* read DBLOCK */
360 pt_size = GNUNET_MIN (DBLOCK_SIZE, te->size - te->publish_offset);
361 if (pt_size !=
362 te->reader (te->cls, te->publish_offset, pt_size, iob, &te->emsg))
363 {
364 te->in_next = GNUNET_NO;
365 te->cont (te->cls);
366 return;
367 }
368 pt_block = iob;
369 }
370 else
371 {
372 pt_size =
373 GNUNET_FS_tree_compute_iblock_size (te->current_depth,
374 te->publish_offset);
375 pt_block = &te->chk_tree[(te->current_depth - 1) * CHK_PER_INODE];
376 }
377 off = compute_chk_offset (te->current_depth, te->publish_offset);
378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379 "TE is at offset %llu and depth %u with block size %u and target-CHK-offset %u\n",
380 (unsigned long long) te->publish_offset, te->current_depth,
381 (unsigned int) pt_size, (unsigned int) off);
382 mychk = &te->chk_tree[te->current_depth * CHK_PER_INODE + off];
383 GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
384 GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
385 GNUNET_CRYPTO_symmetric_encrypt (pt_block, pt_size, &sk, &iv, enc);
386 GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
388 "TE calculates query to be `%s', stored at %u\n",
389 GNUNET_h2s (&mychk->query),
390 te->current_depth * CHK_PER_INODE + off);
391 if (NULL != te->proc)
392 te->proc (te->cls, mychk, te->publish_offset, te->current_depth,
393 (0 ==
394 te->current_depth) ? GNUNET_BLOCK_TYPE_FS_DBLOCK :
395 GNUNET_BLOCK_TYPE_FS_IBLOCK, enc, pt_size);
396 if (NULL != te->progress)
397 te->progress (te->cls, te->publish_offset, pt_block, pt_size,
398 te->current_depth);
399 if (0 == te->current_depth)
400 {
401 te->publish_offset += pt_size;
402 if ((te->publish_offset == te->size) ||
403 (0 == te->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE)))
404 te->current_depth++;
405 }
406 else
407 {
408 if ((off == CHK_PER_INODE) || (te->publish_offset == te->size))
409 te->current_depth++;
410 else
411 te->current_depth = 0;
412 }
413 te->in_next = GNUNET_NO;
414}
415
416
417/**
418 * Get the resulting URI from the encoding.
419 *
420 * @param te the tree encoder to clean up
421 * @return uri set to the resulting URI (if encoding finished), NULL otherwise
422 */
423struct GNUNET_FS_Uri *
424GNUNET_FS_tree_encoder_get_uri (struct GNUNET_FS_TreeEncoder *te)
425{
426 if (NULL != te->uri)
427 return GNUNET_FS_uri_dup (te->uri);
428 return NULL;
429}
430
431
432/**
433 * Clean up a tree encoder and return information
434 * about possible errors.
435 *
436 * @param te the tree encoder to clean up
437 * @param emsg set to an error message (if an error occurred
438 * within the tree encoder; if this function is called
439 * prior to completion and prior to an internal error,
440 * both "*emsg" will be set to NULL).
441 */
442void
443GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder *te,
444 char **emsg)
445{
446 if (NULL != te->reader)
447 {
448 (void) te->reader (te->cls, UINT64_MAX, 0, 0, NULL);
449 te->reader = NULL;
450 }
451 GNUNET_assert (GNUNET_NO == te->in_next);
452 if (NULL != te->uri)
453 GNUNET_FS_uri_destroy (te->uri);
454 if (emsg != NULL)
455 *emsg = te->emsg;
456 else
457 GNUNET_free (te->emsg);
458 GNUNET_free (te->chk_tree);
459 GNUNET_free (te);
460}
461
462
463/* end of fs_tree.c */
diff --git a/src/fs/fs_tree.h b/src/fs/fs_tree.h
deleted file mode 100644
index 1fb681d27..000000000
--- a/src/fs/fs_tree.h
+++ /dev/null
@@ -1,217 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_tree.h
23 * @brief Merkle-tree-ish-CHK file encoding for GNUnet
24 * @see https://gnunet.org/encoding
25 * @author Krista Bennett
26 * @author Christian Grothoff
27 *
28 * TODO:
29 * - decide if this API should be made public (gnunet_fs_service.h)
30 * or remain "internal" (but with exported symbols?)
31 */
32#ifndef GNUNET_FS_TREE_H
33#define GNUNET_FS_TREE_H
34
35#include "fs_api.h"
36
37/**
38 * Compute the depth of the CHK tree.
39 *
40 * @param flen file length for which to compute the depth
41 * @return depth of the tree, always > 0. A depth of 1 means only a DBLOCK.
42 */
43unsigned int
44GNUNET_FS_compute_depth (uint64_t flen);
45
46
47/**
48 * Calculate how many bytes of payload a block tree of the given
49 * depth MAY correspond to at most (this function ignores the fact that
50 * some blocks will only be present partially due to the total file
51 * size cutting some blocks off at the end).
52 *
53 * @param depth depth of the block. depth==0 is a DBLOCK.
54 * @return number of bytes of payload a subtree of this depth may correspond to
55 */
56uint64_t
57GNUNET_FS_tree_compute_tree_size (unsigned int depth);
58
59
60/**
61 * Compute how many bytes of data should be stored in
62 * the specified block.
63 *
64 * @param fsize overall file size, must be > 0.
65 * @param offset offset in the original data corresponding
66 * to the beginning of the tree induced by the block;
67 * must be < fsize
68 * @param depth depth of the node in the tree, 0 for DBLOCK
69 * @return number of bytes stored in this node
70 */
71size_t
72GNUNET_FS_tree_calculate_block_size (uint64_t fsize, uint64_t offset,
73 unsigned int depth);
74
75
76/**
77 * Context for an ECRS-based file encoder that computes
78 * the Merkle-ish-CHK tree.
79 */
80struct GNUNET_FS_TreeEncoder;
81
82
83/**
84 * Function called asking for the current (encoded)
85 * block to be processed. After processing the
86 * client should either call "GNUNET_FS_tree_encode_next"
87 * or (on error) "GNUNET_FS_tree_encode_finish".
88 *
89 * @param cls closure
90 * @param chk content hash key for the block
91 * @param offset offset of the block
92 * @param depth depth of the block, 0 for DBLOCKs
93 * @param type type of the block (IBLOCK or DBLOCK)
94 * @param block the (encrypted) block
95 * @param block_size size of block (in bytes)
96 */
97typedef void (*GNUNET_FS_TreeBlockProcessor) (void *cls,
98 const struct ContentHashKey *chk,
99 uint64_t offset,
100 unsigned int depth,
101 enum GNUNET_BLOCK_Type type,
102 const void *block,
103 uint16_t block_size);
104
105
106/**
107 * Function called with information about our
108 * progress in computing the tree encoding.
109 *
110 * @param cls closure
111 * @param offset where are we in the file
112 * @param pt_block plaintext of the currently processed block
113 * @param pt_size size of pt_block
114 * @param depth depth of the block in the tree, 0 for DBLOCKS
115 */
116typedef void (*GNUNET_FS_TreeProgressCallback) (void *cls, uint64_t offset,
117 const void *pt_block,
118 size_t pt_size,
119 unsigned int depth);
120
121
122/**
123 * Initialize a tree encoder. This function will call "proc" and
124 * "progress" on each block in the tree. Once all blocks have been
125 * processed, "cont" will be scheduled. The "reader" will be called
126 * to obtain the (plaintext) blocks for the file. Note that this
127 * function will actually never call "proc"; the "proc" function must
128 * be triggered by calling "GNUNET_FS_tree_encoder_next" to trigger
129 * encryption (and calling of "proc") for each block.
130 *
131 * @param h the global FS context
132 * @param size overall size of the file to encode
133 * @param cls closure for reader, proc, progress and cont
134 * @param reader function to call to read plaintext data
135 * @param proc function to call on each encrypted block
136 * @param progress function to call with progress information
137 * @param cont function to call when done
138 * @return tree encoder context
139 */
140struct GNUNET_FS_TreeEncoder *
141GNUNET_FS_tree_encoder_create (struct GNUNET_FS_Handle *h, uint64_t size,
142 void *cls, GNUNET_FS_DataReader reader,
143 GNUNET_FS_TreeBlockProcessor proc,
144 GNUNET_FS_TreeProgressCallback progress,
145 GNUNET_SCHEDULER_TaskCallback cont);
146
147
148/**
149 * Encrypt the next block of the file (and
150 * call proc and progress accordingly; or
151 * of course "cont" if we have already completed
152 * encoding of the entire file).
153 *
154 * @param te tree encoder to use
155 */
156void
157GNUNET_FS_tree_encoder_next (struct GNUNET_FS_TreeEncoder *te);
158
159
160/**
161 * Get the resulting URI from the encoding.
162 *
163 * @param te the tree encoder to clean up
164 * @return uri set to the resulting URI (if encoding finished), NULL otherwise
165 */
166struct GNUNET_FS_Uri *
167GNUNET_FS_tree_encoder_get_uri (struct GNUNET_FS_TreeEncoder *te);
168
169
170/**
171 * Clean up a tree encoder and return information
172 * about possible errors.
173 *
174 * @param te the tree encoder to clean up
175 * @param emsg set to an error message (if an error occurred
176 * within the tree encoder; if this function is called
177 * prior to completion and prior to an internal error,
178 * both "*emsg" will be set to NULL).
179 */
180void
181GNUNET_FS_tree_encoder_finish (struct GNUNET_FS_TreeEncoder *te,
182 char **emsg);
183
184
185#if 0
186/* the functions below will be needed for persistence
187 but are not yet implemented -- FIXME... */
188/**
189 * Get data that would be needed to resume
190 * the encoding later.
191 *
192 * @param te encoding to resume
193 * @param data set to the resume data
194 * @param size set to the size of the resume data
195 */
196void
197GNUNET_FS_tree_encoder_resume_get_data (const struct GNUNET_FS_TreeEncoder *te,
198 void **data, size_t *size);
199
200
201/**
202 * Reset tree encoder to point previously
203 * obtained for resuming.
204 *
205 * @param te encoding to resume
206 * @param data the resume data
207 * @param size the size of the resume data
208 */
209void
210GNUNET_FS_tree_encoder_resume (struct GNUNET_FS_TreeEncoder *te,
211 const void *data, size_t size);
212
213#endif
214
215#endif
216
217/* end of fs_tree.h */
diff --git a/src/fs/fs_unindex.c b/src/fs/fs_unindex.c
deleted file mode 100644
index 8c27af9de..000000000
--- a/src/fs/fs_unindex.c
+++ /dev/null
@@ -1,901 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003--2013, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_unindex.c
23 * @author Krista Grothoff
24 * @author Christian Grothoff
25 * @brief Unindex file.
26 */
27#include "platform.h"
28#include "gnunet_constants.h"
29#include "gnunet_fs_service.h"
30#include "gnunet_protocols.h"
31#include "fs_api.h"
32#include "fs_tree.h"
33#include "block_fs.h"
34#include "fs_publish_ublock.h"
35
36
37/**
38 * Function called by the tree encoder to obtain
39 * a block of plaintext data (for the lowest level
40 * of the tree).
41 *
42 * @param cls our publishing context
43 * @param offset identifies which block to get
44 * @param max (maximum) number of bytes to get; returning
45 * fewer will also cause errors
46 * @param buf where to copy the plaintext buffer
47 * @param emsg location to store an error message (on error)
48 * @return number of bytes copied to buf, 0 on error
49 */
50static size_t
51unindex_reader (void *cls,
52 uint64_t offset,
53 size_t max,
54 void *buf,
55 char **emsg)
56{
57 struct GNUNET_FS_UnindexContext *uc = cls;
58 size_t pt_size;
59
60 pt_size = GNUNET_MIN (max, uc->file_size - offset);
61 if (offset != GNUNET_DISK_file_seek (uc->fh, offset, GNUNET_DISK_SEEK_SET))
62 {
63 *emsg = GNUNET_strdup (_ ("Failed to find given position in file"));
64 return 0;
65 }
66 if (pt_size != GNUNET_DISK_file_read (uc->fh, buf, pt_size))
67 {
68 *emsg = GNUNET_strdup (_ ("Failed to read file"));
69 return 0;
70 }
71 return pt_size;
72}
73
74
75/**
76 * Fill in all of the generic fields for
77 * an unindex event and call the callback.
78 *
79 * @param pi structure to fill in
80 * @param uc overall unindex context
81 * @param offset where we are in the file (for progress)
82 */
83void
84GNUNET_FS_unindex_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
85 struct GNUNET_FS_UnindexContext *uc,
86 uint64_t offset)
87{
88 pi->value.unindex.uc = uc;
89 pi->value.unindex.cctx = uc->client_info;
90 pi->value.unindex.filename = uc->filename;
91 pi->value.unindex.size = uc->file_size;
92 pi->value.unindex.eta =
93 GNUNET_TIME_calculate_eta (uc->start_time, offset, uc->file_size);
94 pi->value.unindex.duration =
95 GNUNET_TIME_absolute_get_duration (uc->start_time);
96 pi->value.unindex.completed = offset;
97 pi->fsh = uc->h;
98 uc->client_info = uc->h->upcb (uc->h->upcb_cls, pi);
99}
100
101
102/**
103 * Function called with information about our
104 * progress in computing the tree encoding.
105 *
106 * @param cls closure
107 * @param offset where are we in the file
108 * @param pt_block plaintext of the currently processed block
109 * @param pt_size size of pt_block
110 * @param depth depth of the block in the tree, 0 for DBLOCK
111 */
112static void
113unindex_progress (void *cls,
114 uint64_t offset,
115 const void *pt_block,
116 size_t pt_size,
117 unsigned int depth)
118{
119 struct GNUNET_FS_UnindexContext *uc = cls;
120 struct GNUNET_FS_ProgressInfo pi;
121
122 pi.status = GNUNET_FS_STATUS_UNINDEX_PROGRESS;
123 pi.value.unindex.specifics.progress.data = pt_block;
124 pi.value.unindex.specifics.progress.offset = offset;
125 pi.value.unindex.specifics.progress.data_len = pt_size;
126 pi.value.unindex.specifics.progress.depth = depth;
127 GNUNET_FS_unindex_make_status_ (&pi, uc, offset);
128}
129
130
131/**
132 * We've encountered an error during
133 * unindexing. Signal the client.
134 *
135 * @param uc context for the failed unindexing operation
136 */
137static void
138signal_unindex_error (struct GNUNET_FS_UnindexContext *uc)
139{
140 struct GNUNET_FS_ProgressInfo pi;
141
142 pi.status = GNUNET_FS_STATUS_UNINDEX_ERROR;
143 pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
144 pi.value.unindex.specifics.error.message = uc->emsg;
145 GNUNET_FS_unindex_make_status_ (&pi, uc, 0);
146}
147
148
149/**
150 * Continuation called to notify client about result of the
151 * datastore removal operation.
152 *
153 * @param cls closure
154 * @param success #GNUNET_SYSERR on failure
155 * @param min_expiration minimum expiration time required for content to be stored
156 * @param msg NULL on success, otherwise an error message
157 */
158static void
159process_cont (void *cls,
160 int success,
161 struct GNUNET_TIME_Absolute min_expiration,
162 const char *msg)
163{
164 struct GNUNET_FS_UnindexContext *uc = cls;
165
166 if (success == GNUNET_SYSERR)
167 {
168 uc->emsg = GNUNET_strdup (msg);
169 signal_unindex_error (uc);
170 return;
171 }
172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173 "Datastore REMOVE operation succeeded\n");
174 GNUNET_FS_tree_encoder_next (uc->tc);
175}
176
177
178/**
179 * Function called asking for the current (encoded)
180 * block to be processed. After processing the
181 * client should either call "GNUNET_FS_tree_encode_next"
182 * or (on error) "GNUNET_FS_tree_encode_finish".
183 *
184 * @param cls closure
185 * @param chk content hash key for the block (key for lookup in the datastore)
186 * @param offset offset of the block
187 * @param depth depth of the block, 0 for DBLOCK
188 * @param type type of the block (IBLOCK or DBLOCK)
189 * @param block the (encrypted) block
190 * @param block_size size of block (in bytes)
191 */
192static void
193unindex_process (void *cls,
194 const struct ContentHashKey *chk,
195 uint64_t offset,
196 unsigned int depth,
197 enum GNUNET_BLOCK_Type type,
198 const void *block,
199 uint16_t block_size)
200{
201 struct GNUNET_FS_UnindexContext *uc = cls;
202 uint32_t size;
203 const void *data;
204 struct OnDemandBlock odb;
205
206 if (type != GNUNET_BLOCK_TYPE_FS_DBLOCK)
207 {
208 size = block_size;
209 data = block;
210 }
211 else /* on-demand encoded DBLOCK */
212 {
213 size = sizeof(struct OnDemandBlock);
214 odb.offset = GNUNET_htonll (offset);
215 odb.file_id = uc->file_id;
216 data = &odb;
217 }
218 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
219 "Sending REMOVE request to DATASTORE service\n");
220 GNUNET_DATASTORE_remove (uc->dsh, &chk->query, size, data, -2, 1,
221 &process_cont, uc);
222 uc->chk = *chk;
223}
224
225
226/**
227 * Function called with the response from the FS service to our
228 * unindexing request.
229 *
230 * @param cls closure, unindex context
231 * @param msg the response
232 */
233static void
234handle_unindex_response (void *cls,
235 const struct GNUNET_MessageHeader *msg)
236{
237 struct GNUNET_FS_UnindexContext *uc = cls;
238 struct GNUNET_FS_ProgressInfo pi;
239
240 if (NULL != uc->mq)
241 {
242 GNUNET_MQ_destroy (uc->mq);
243 uc->mq = NULL;
244 }
245 uc->state = UNINDEX_STATE_COMPLETE;
246 pi.status = GNUNET_FS_STATUS_UNINDEX_COMPLETED;
247 pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
248 GNUNET_FS_unindex_sync_ (uc);
249 GNUNET_FS_unindex_make_status_ (&pi,
250 uc,
251 uc->file_size);
252}
253
254
255/**
256 * Generic error handler, called with the appropriate error code and
257 * the same closure specified at the creation of the message queue.
258 * Not every message queue implementation supports an error handler.
259 *
260 * @param cls closure with the `struct GNUNET_FS_UnindexContext *`
261 * @param error error code
262 */
263static void
264unindex_mq_error_handler (void *cls,
265 enum GNUNET_MQ_Error error)
266{
267 struct GNUNET_FS_UnindexContext *uc = cls;
268
269 if (NULL != uc->mq)
270 {
271 GNUNET_MQ_destroy (uc->mq);
272 uc->mq = NULL;
273 }
274 uc->state = UNINDEX_STATE_ERROR;
275 uc->emsg = GNUNET_strdup (_ ("Error communicating with `fs' service."));
276 GNUNET_FS_unindex_sync_ (uc);
277 signal_unindex_error (uc);
278}
279
280
281/**
282 * Function called when we are done with removing UBlocks.
283 * Disconnect from datastore and notify FS service about
284 * the unindex event.
285 *
286 * @param uc our unindexing context
287 */
288static void
289unindex_finish (struct GNUNET_FS_UnindexContext *uc)
290{
291 struct GNUNET_MQ_MessageHandler handlers[] = {
292 GNUNET_MQ_hd_fixed_size (unindex_response,
293 GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK,
294 struct GNUNET_MessageHeader,
295 uc),
296 GNUNET_MQ_handler_end ()
297 };
298 char *emsg;
299 struct GNUNET_MQ_Envelope *env;
300 struct UnindexMessage *req;
301
302 /* generate final progress message */
303 unindex_progress (uc,
304 uc->file_size,
305 NULL,
306 0,
307 0);
308 GNUNET_FS_tree_encoder_finish (uc->tc,
309 &emsg);
310 uc->tc = NULL;
311 GNUNET_DISK_file_close (uc->fh);
312 uc->fh = NULL;
313 GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
314 uc->dsh = NULL;
315 uc->state = UNINDEX_STATE_FS_NOTIFY;
316 GNUNET_FS_unindex_sync_ (uc);
317 uc->mq = GNUNET_CLIENT_connect (uc->h->cfg,
318 "fs",
319 handlers,
320 &unindex_mq_error_handler,
321 uc);
322 if (NULL == uc->mq)
323 {
324 uc->state = UNINDEX_STATE_ERROR;
325 uc->emsg =
326 GNUNET_strdup (_ ("Failed to connect to FS service for unindexing."));
327 GNUNET_FS_unindex_sync_ (uc);
328 signal_unindex_error (uc);
329 return;
330 }
331 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
332 "Sending UNINDEX message to FS service\n");
333 env = GNUNET_MQ_msg (req,
334 GNUNET_MESSAGE_TYPE_FS_UNINDEX);
335 req->reserved = 0;
336 req->file_id = uc->file_id;
337 GNUNET_MQ_send (uc->mq,
338 env);
339}
340
341
342/**
343 * Function called by the directory scanner as we extract keywords
344 * that we will need to remove UBlocks.
345 *
346 * @param cls the 'struct GNUNET_FS_UnindexContext *'
347 * @param filename which file we are making progress on
348 * @param is_directory #GNUNET_YES if this is a directory,
349 * #GNUNET_NO if this is a file
350 * #GNUNET_SYSERR if it is neither (or unknown)
351 * @param reason kind of progress we are making
352 */
353static void
354unindex_directory_scan_cb (void *cls,
355 const char *filename,
356 int is_directory,
357 enum GNUNET_FS_DirScannerProgressUpdateReason reason)
358{
359 struct GNUNET_FS_UnindexContext *uc = cls;
360 static struct GNUNET_FS_ShareTreeItem *directory_scan_result;
361
362 switch (reason)
363 {
364 case GNUNET_FS_DIRSCANNER_FINISHED:
365 directory_scan_result = GNUNET_FS_directory_scan_get_result (uc->dscan);
366 uc->dscan = NULL;
367 if (NULL != directory_scan_result->ksk_uri)
368 {
369 uc->ksk_uri = GNUNET_FS_uri_dup (directory_scan_result->ksk_uri);
370 uc->state = UNINDEX_STATE_DS_REMOVE_KBLOCKS;
371 GNUNET_FS_unindex_sync_ (uc);
372 GNUNET_FS_unindex_do_remove_kblocks_ (uc);
373 }
374 else
375 {
376 uc->emsg = GNUNET_strdup (_ ("Failed to get KSKs from directory scan."));
377 GNUNET_FS_unindex_sync_ (uc);
378 unindex_finish (uc);
379 }
380 GNUNET_FS_share_tree_free (directory_scan_result);
381 break;
382
383 case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
384 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
385 _ ("Internal error scanning `%s'.\n"),
386 uc->filename);
387 GNUNET_FS_directory_scan_abort (uc->dscan);
388 uc->dscan = NULL;
389 uc->emsg = GNUNET_strdup (_ ("Failed to get KSKs from directory scan."));
390 GNUNET_FS_unindex_sync_ (uc);
391 unindex_finish (uc);
392 break;
393
394 default:
395 break;
396 }
397}
398
399
400/**
401 * If necessary, connect to the datastore and remove the UBlocks.
402 *
403 * @param uc context for the unindex operation.
404 */
405void
406GNUNET_FS_unindex_do_extract_keywords_ (struct GNUNET_FS_UnindexContext *uc)
407{
408 char *ex;
409
410 if (GNUNET_OK !=
411 GNUNET_CONFIGURATION_get_value_string (uc->h->cfg, "FS", "EXTRACTORS",
412 &ex))
413 ex = NULL;
414 uc->dscan = GNUNET_FS_directory_scan_start (uc->filename,
415 GNUNET_NO, ex,
416 &unindex_directory_scan_cb,
417 uc);
418 GNUNET_free (ex);
419}
420
421
422/**
423 * Continuation called to notify client about result of the remove
424 * operation for the UBlock.
425 *
426 * @param cls the 'struct GNUNET_FS_UnindexContext *'
427 * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
428 * GNUNET_NO if content was already there
429 * GNUNET_YES (or other positive value) on success
430 * @param min_expiration minimum expiration time required for 0-priority content to be stored
431 * by the datacache at this time, zero for unknown, forever if we have no
432 * space for 0-priority content
433 * @param msg NULL on success, otherwise an error message
434 */
435static void
436continue_after_remove (void *cls,
437 int32_t success,
438 struct GNUNET_TIME_Absolute min_expiration,
439 const char *msg)
440{
441 struct GNUNET_FS_UnindexContext *uc = cls;
442
443 uc->dqe = NULL;
444 if (success != GNUNET_YES)
445 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
446 _ ("Failed to remove UBlock: %s\n"),
447 msg);
448 uc->ksk_offset++;
449 GNUNET_FS_unindex_do_remove_kblocks_ (uc);
450}
451
452
453/**
454 * Function called from datastore with result from us looking for
455 * a UBlock. There are four cases:
456 * 1) no result, means we move on to the next keyword
457 * 2) data hash is the same as an already seen data hash, means we move on to
458 * next keyword
459 * 3) UBlock for a different CHK, means we keep looking for more
460 * 4) UBlock is for our CHK, means we remove the block and then move
461 * on to the next keyword
462 *
463 * @param cls the 'struct GNUNET_FS_UnindexContext *'
464 * @param key key for the content
465 * @param size number of bytes in data
466 * @param data content stored
467 * @param type type of the content
468 * @param priority priority of the content
469 * @param anonymity anonymity-level for the content
470 * @param replication replication-level for the content
471 * @param expiration expiration time for the content
472 * @param uid unique identifier for the datum;
473 * maybe 0 if no unique identifier is available
474 */
475static void
476process_kblock_for_unindex (void *cls,
477 const struct GNUNET_HashCode *key,
478 size_t size,
479 const void *data,
480 enum GNUNET_BLOCK_Type type,
481 uint32_t priority,
482 uint32_t anonymity,
483 uint32_t replication,
484 struct GNUNET_TIME_Absolute expiration,
485 uint64_t uid)
486{
487 struct GNUNET_FS_UnindexContext *uc = cls;
488 const struct UBlock *ub;
489 struct GNUNET_FS_Uri *chk_uri;
490 struct GNUNET_HashCode query;
491
492 uc->dqe = NULL;
493 if (NULL == data)
494 {
495 /* no result */
496 uc->ksk_offset++;
497 GNUNET_FS_unindex_do_remove_kblocks_ (uc);
498 return;
499 }
500 GNUNET_assert (GNUNET_BLOCK_TYPE_FS_UBLOCK == type);
501 if (size < sizeof(struct UBlock))
502 {
503 GNUNET_break (0);
504 goto get_next;
505 }
506 ub = data;
507 GNUNET_CRYPTO_hash (&ub->verification_key,
508 sizeof(ub->verification_key),
509 &query);
510 if (0 != memcmp (&query,
511 key,
512 sizeof(struct GNUNET_HashCode)))
513 {
514 /* result does not match our keyword, skip */
515 goto get_next;
516 }
517 {
518 char pt[size - sizeof(struct UBlock)];
519 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
520 const char *keyword;
521
522 GNUNET_CRYPTO_ecdsa_key_get_public (
523 GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
524 &anon_pub);
525 keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
526 GNUNET_FS_ublock_decrypt_ (&ub[1], size - sizeof(struct UBlock),
527 &anon_pub,
528 keyword,
529 pt);
530 if (NULL == memchr (&pt[1], 0, sizeof(pt) - 1))
531 {
532 GNUNET_break_op (0); /* malformed UBlock */
533 goto get_next;
534 }
535 chk_uri = GNUNET_FS_uri_parse (&pt[1], NULL);
536 if (NULL == chk_uri)
537 {
538 GNUNET_break_op (0); /* malformed UBlock */
539 goto get_next;
540 }
541 }
542 if (0 != memcmp (&uc->chk,
543 &chk_uri->data.chk.chk,
544 sizeof(struct ContentHashKey)))
545 {
546 /* different CHK, ignore */
547 GNUNET_FS_uri_destroy (chk_uri);
548 goto get_next;
549 }
550 GNUNET_FS_uri_destroy (chk_uri);
551 /* matches! */
552 uc->dqe = GNUNET_DATASTORE_remove (uc->dsh,
553 key,
554 size,
555 data,
556 0 /* priority */,
557 1 /* queue size */,
558 &continue_after_remove,
559 uc);
560 return;
561get_next:
562 uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
563 uid + 1 /* next_uid */,
564 false /* random */,
565 &uc->uquery,
566 GNUNET_BLOCK_TYPE_FS_UBLOCK,
567 0 /* priority */,
568 1 /* queue size */,
569 &process_kblock_for_unindex,
570 uc);
571}
572
573
574/**
575 * If necessary, connect to the datastore and remove the KBlocks.
576 *
577 * @param uc context for the unindex operation.
578 */
579void
580GNUNET_FS_unindex_do_remove_kblocks_ (struct GNUNET_FS_UnindexContext *uc)
581{
582 const char *keyword;
583 const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
584 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
585 struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
586
587 if (NULL == uc->dsh)
588 uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
589 if (NULL == uc->dsh)
590 {
591 uc->state = UNINDEX_STATE_ERROR;
592 uc->emsg = GNUNET_strdup (_ ("Failed to connect to `datastore' service."));
593 GNUNET_FS_unindex_sync_ (uc);
594 signal_unindex_error (uc);
595 return;
596 }
597 if ((NULL == uc->ksk_uri) ||
598 (uc->ksk_offset >= uc->ksk_uri->data.ksk.keywordCount))
599 {
600 unindex_finish (uc);
601 return;
602 }
603 anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
604 GNUNET_CRYPTO_ecdsa_key_get_public (anon,
605 &anon_pub);
606 keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
607 GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub,
608 keyword,
609 "fs-ublock",
610 &dpub);
611 GNUNET_CRYPTO_hash (&dpub,
612 sizeof(dpub),
613 &uc->uquery);
614 uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
615 0 /* next_uid */,
616 false /* random */,
617 &uc->uquery,
618 GNUNET_BLOCK_TYPE_FS_UBLOCK,
619 0 /* priority */,
620 1 /* queue size */,
621 &process_kblock_for_unindex,
622 uc);
623}
624
625
626/**
627 * Function called when the tree encoder has
628 * processed all blocks. Clean up.
629 *
630 * @param cls our unindexing context
631 */
632static void
633unindex_extract_keywords (void *cls)
634{
635 struct GNUNET_FS_UnindexContext *uc = cls;
636
637 uc->state = UNINDEX_STATE_EXTRACT_KEYWORDS;
638 GNUNET_FS_unindex_sync_ (uc);
639 GNUNET_FS_unindex_do_extract_keywords_ (uc);
640}
641
642
643/**
644 * Connect to the datastore and remove the blocks.
645 *
646 * @param uc context for the unindex operation.
647 */
648void
649GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc)
650{
651 if (NULL == uc->dsh)
652 uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
653 if (NULL == uc->dsh)
654 {
655 uc->state = UNINDEX_STATE_ERROR;
656 uc->emsg = GNUNET_strdup (_ ("Failed to connect to `datastore' service."));
657 GNUNET_FS_unindex_sync_ (uc);
658 signal_unindex_error (uc);
659 return;
660 }
661 uc->fh =
662 GNUNET_DISK_file_open (uc->filename, GNUNET_DISK_OPEN_READ,
663 GNUNET_DISK_PERM_NONE);
664 if (NULL == uc->fh)
665 {
666 GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
667 uc->dsh = NULL;
668 uc->state = UNINDEX_STATE_ERROR;
669 uc->emsg = GNUNET_strdup (_ ("Failed to open file for unindexing."));
670 GNUNET_FS_unindex_sync_ (uc);
671 signal_unindex_error (uc);
672 return;
673 }
674 uc->tc =
675 GNUNET_FS_tree_encoder_create (uc->h,
676 uc->file_size,
677 uc,
678 &unindex_reader,
679 &unindex_process,
680 &unindex_progress,
681 &unindex_extract_keywords);
682 GNUNET_FS_tree_encoder_next (uc->tc);
683}
684
685
686/**
687 * Function called once the hash of the file
688 * that is being unindexed has been computed.
689 *
690 * @param cls closure, unindex context
691 * @param file_id computed hash, NULL on error
692 */
693void
694GNUNET_FS_unindex_process_hash_ (void *cls,
695 const struct GNUNET_HashCode *file_id)
696{
697 struct GNUNET_FS_UnindexContext *uc = cls;
698
699 uc->fhc = NULL;
700 if (uc->state != UNINDEX_STATE_HASHING)
701 {
702 GNUNET_FS_unindex_stop (uc);
703 return;
704 }
705 if (file_id == NULL)
706 {
707 uc->state = UNINDEX_STATE_ERROR;
708 uc->emsg = GNUNET_strdup (_ ("Failed to compute hash of file."));
709 GNUNET_FS_unindex_sync_ (uc);
710 signal_unindex_error (uc);
711 return;
712 }
713 uc->file_id = *file_id;
714 uc->state = UNINDEX_STATE_DS_REMOVE;
715 GNUNET_FS_unindex_sync_ (uc);
716 GNUNET_FS_unindex_do_remove_ (uc);
717}
718
719
720/**
721 * Create SUSPEND event for the given unindex operation
722 * and then clean up our state (without stop signal).
723 *
724 * @param cls the `struct GNUNET_FS_UnindexContext` to signal for
725 */
726void
727GNUNET_FS_unindex_signal_suspend_ (void *cls)
728{
729 struct GNUNET_FS_UnindexContext *uc = cls;
730 struct GNUNET_FS_ProgressInfo pi;
731
732 /* FIXME: lots of duplication with unindex_stop here! */
733 if (uc->dscan != NULL)
734 {
735 GNUNET_FS_directory_scan_abort (uc->dscan);
736 uc->dscan = NULL;
737 }
738 if (NULL != uc->dqe)
739 {
740 GNUNET_DATASTORE_cancel (uc->dqe);
741 uc->dqe = NULL;
742 }
743 if (uc->fhc != NULL)
744 {
745 GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
746 uc->fhc = NULL;
747 }
748 if (NULL != uc->ksk_uri)
749 {
750 GNUNET_FS_uri_destroy (uc->ksk_uri);
751 uc->ksk_uri = NULL;
752 }
753 if (NULL != uc->mq)
754 {
755 GNUNET_MQ_destroy (uc->mq);
756 uc->mq = NULL;
757 }
758 if (NULL != uc->dsh)
759 {
760 GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
761 uc->dsh = NULL;
762 }
763 if (NULL != uc->tc)
764 {
765 GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
766 uc->tc = NULL;
767 }
768 if (uc->fh != NULL)
769 {
770 GNUNET_DISK_file_close (uc->fh);
771 uc->fh = NULL;
772 }
773 GNUNET_FS_end_top (uc->h, uc->top);
774 pi.status = GNUNET_FS_STATUS_UNINDEX_SUSPEND;
775 GNUNET_FS_unindex_make_status_ (&pi, uc,
776 (uc->state ==
777 UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
778 GNUNET_break (NULL == uc->client_info);
779 GNUNET_free (uc->filename);
780 GNUNET_free (uc->serialization);
781 GNUNET_free (uc->emsg);
782 GNUNET_free (uc);
783}
784
785
786/**
787 * Unindex a file.
788 *
789 * @param h handle to the file sharing subsystem
790 * @param filename file to unindex
791 * @param cctx initial value for the client context
792 * @return NULL on error, otherwise handle
793 */
794struct GNUNET_FS_UnindexContext *
795GNUNET_FS_unindex_start (struct GNUNET_FS_Handle *h,
796 const char *filename,
797 void *cctx)
798{
799 struct GNUNET_FS_UnindexContext *uc;
800 struct GNUNET_FS_ProgressInfo pi;
801 uint64_t size;
802
803 if (GNUNET_OK !=
804 GNUNET_DISK_file_size (filename,
805 &size,
806 GNUNET_YES,
807 GNUNET_YES))
808 return NULL;
809 uc = GNUNET_new (struct GNUNET_FS_UnindexContext);
810 uc->h = h;
811 uc->filename = GNUNET_strdup (filename);
812 uc->start_time = GNUNET_TIME_absolute_get ();
813 uc->file_size = size;
814 uc->client_info = cctx;
815 GNUNET_FS_unindex_sync_ (uc);
816 pi.status = GNUNET_FS_STATUS_UNINDEX_START;
817 pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
818 GNUNET_FS_unindex_make_status_ (&pi, uc, 0);
819 uc->fhc =
820 GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE,
821 filename,
822 HASHING_BLOCKSIZE,
823 &GNUNET_FS_unindex_process_hash_, uc);
824 uc->top = GNUNET_FS_make_top (h,
825 &GNUNET_FS_unindex_signal_suspend_,
826 uc);
827 return uc;
828}
829
830
831/**
832 * Clean up after completion of an unindex operation.
833 *
834 * @param uc handle
835 */
836void
837GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc)
838{
839 struct GNUNET_FS_ProgressInfo pi;
840
841 if (NULL != uc->dscan)
842 {
843 GNUNET_FS_directory_scan_abort (uc->dscan);
844 uc->dscan = NULL;
845 }
846 if (NULL != uc->dqe)
847 {
848 GNUNET_DATASTORE_cancel (uc->dqe);
849 uc->dqe = NULL;
850 }
851 if (NULL != uc->fhc)
852 {
853 GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
854 uc->fhc = NULL;
855 }
856 if (NULL != uc->mq)
857 {
858 GNUNET_MQ_destroy (uc->mq);
859 uc->mq = NULL;
860 }
861 if (NULL != uc->dsh)
862 {
863 GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
864 uc->dsh = NULL;
865 }
866 if (NULL != uc->ksk_uri)
867 {
868 GNUNET_FS_uri_destroy (uc->ksk_uri);
869 uc->ksk_uri = NULL;
870 }
871 if (NULL != uc->tc)
872 {
873 GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
874 uc->tc = NULL;
875 }
876 if (uc->fh != NULL)
877 {
878 GNUNET_DISK_file_close (uc->fh);
879 uc->fh = NULL;
880 }
881 GNUNET_FS_end_top (uc->h, uc->top);
882 if (uc->serialization != NULL)
883 {
884 GNUNET_FS_remove_sync_file_ (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
885 uc->serialization);
886 GNUNET_free (uc->serialization);
887 uc->serialization = NULL;
888 }
889 pi.status = GNUNET_FS_STATUS_UNINDEX_STOPPED;
890 pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
891 GNUNET_FS_unindex_make_status_ (&pi, uc,
892 (uc->state ==
893 UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
894 GNUNET_break (NULL == uc->client_info);
895 GNUNET_free (uc->emsg);
896 GNUNET_free (uc->filename);
897 GNUNET_free (uc);
898}
899
900
901/* end of fs_unindex.c */
diff --git a/src/fs/fs_uri.c b/src/fs/fs_uri.c
deleted file mode 100644
index 2d5566b54..000000000
--- a/src/fs/fs_uri.c
+++ /dev/null
@@ -1,2061 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003--2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/fs_uri.c
23 * @brief Parses and produces uri strings.
24 * @author Igor Wronsky, Christian Grothoff
25 *
26 * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER".
27 * The specific structure of "IDENTIFIER" depends on the module and
28 * maybe differentiated into additional subcategories if applicable.
29 * This module only deals with fs identifiers (MODULE = "fs").
30 * <p>
31 *
32 * This module only parses URIs for the AFS module. The FS URIs fall
33 * into four categories, "chk", "sks", "ksk" and "loc". The first three
34 * categories were named in analogy (!) to Freenet, but they do NOT
35 * work in exactly the same way. They are very similar from the user's
36 * point of view (unique file identifier, subspace, keyword), but the
37 * implementation is rather different in pretty much every detail.
38 * The concrete URI formats are:
39 *
40 * </p>
41 *
42 * <ul><li>
43 *
44 * First, there are URIs that identify a file. They have the format
45 * "gnunet://fs/chk/HEX1.HEX2.SIZE". These URIs can be used to
46 * download the file. The description, filename, mime-type and other
47 * meta-data is NOT part of the file-URI since a URI uniquely
48 * identifies a resource (and the contents of the file would be the
49 * same even if it had a different description).
50 *
51 * </li><li>
52 *
53 * The second category identifies entries in a namespace. The format
54 * is "gnunet://fs/sks/NAMESPACE/IDENTIFIER" where the namespace
55 * should be given in HEX. Applications may allow using a nickname
56 * for the namespace if the nickname is not ambiguous. The identifier
57 * can be either an ASCII sequence or a HEX-encoding. If the
58 * identifier is in ASCII but the format is ambiguous and could denote
59 * a HEX-string a "/" is appended to indicate ASCII encoding.
60 *
61 * </li> <li>
62 *
63 * The third category identifies ordinary searches. The format is
64 * "gnunet://fs/ksk/KEYWORD[+KEYWORD]*". Using the "+" syntax
65 * it is possible to encode searches with the boolean "AND" operator.
66 * "+" is used since it indicates a commutative 'and' operation and
67 * is unlikely to be used in a keyword by itself.
68 *
69 * </li><li>
70 *
71 * The last category identifies a datum on a specific machine. The
72 * format is "gnunet://fs/loc/HEX1.HEX2.SIZE.PEER.SIG.EXPTIME". PEER is
73 * the BinName of the public key of the peer storing the datum. The
74 * signature (SIG) certifies that this peer has this content.
75 * HEX1, HEX2 and SIZE correspond to a 'chk' URI.
76 *
77 * </li></ul>
78 *
79 * The encoding for hexadecimal values is defined in the hashing.c
80 * module in the gnunetutil library and discussed there.
81 *
82 */
83#include "platform.h"
84#include "gnunet_fs_service.h"
85#include "gnunet_signatures.h"
86#include "fs_api.h"
87#include <unitypes.h>
88#include <unicase.h>
89#include <uniconv.h>
90#include <unistr.h>
91#include <unistdio.h>
92
93
94/**
95 * Get a unique key from a URI. This is for putting URIs
96 * into HashMaps. The key may change between FS implementations.
97 *
98 * @param uri uri to convert to a unique key
99 * @param key where to store the unique key
100 * @return #GNUNET_OK on success
101 */
102int
103GNUNET_FS_uri_to_key (const struct GNUNET_FS_Uri *uri,
104 struct GNUNET_HashCode *key)
105{
106 switch (uri->type)
107 {
108 case GNUNET_FS_URI_CHK:
109 *key = uri->data.chk.chk.query;
110 return GNUNET_OK;
111
112 case GNUNET_FS_URI_SKS:
113 GNUNET_CRYPTO_hash (uri->data.sks.identifier,
114 strlen (uri->data.sks.identifier),
115 key);
116 return GNUNET_OK;
117
118 case GNUNET_FS_URI_KSK:
119 if (uri->data.ksk.keywordCount > 0)
120 {
121 GNUNET_CRYPTO_hash (uri->data.ksk.keywords[0],
122 strlen (uri->data.ksk.keywords[0]),
123 key);
124 return GNUNET_OK;
125 }
126 else
127 {
128 memset (key, 0, sizeof(struct GNUNET_HashCode));
129 return GNUNET_SYSERR;
130 }
131 break;
132
133 case GNUNET_FS_URI_LOC:
134 GNUNET_CRYPTO_hash (&uri->data.loc.fi,
135 sizeof(struct FileIdentifier)
136 + sizeof(struct GNUNET_PeerIdentity),
137 key);
138 return GNUNET_OK;
139
140 default:
141 memset (key, 0, sizeof(struct GNUNET_HashCode));
142 return GNUNET_SYSERR;
143 }
144}
145
146
147/**
148 * Convert keyword URI to a human readable format
149 * (i.e. the search query that was used in the first place)
150 *
151 * @param uri ksk uri to convert to a string
152 * @return string with the keywords
153 */
154char *
155GNUNET_FS_uri_ksk_to_string_fancy (const struct GNUNET_FS_Uri *uri)
156{
157 size_t n;
158 char *ret;
159 unsigned int i;
160 const char *keyword;
161 char **keywords;
162 unsigned int keywordCount;
163
164 if ((NULL == uri) || (GNUNET_FS_URI_KSK != uri->type))
165 {
166 GNUNET_break (0);
167 return NULL;
168 }
169 keywords = uri->data.ksk.keywords;
170 keywordCount = uri->data.ksk.keywordCount;
171 n = keywordCount + 1;
172 for (i = 0; i < keywordCount; i++)
173 {
174 keyword = keywords[i];
175 n += strlen (keyword) - 1;
176 if (NULL != strstr (&keyword[1], " "))
177 n += 2;
178 if (keyword[0] == '+')
179 n++;
180 }
181 ret = GNUNET_malloc (n);
182 strcpy (ret, "");
183 for (i = 0; i < keywordCount; i++)
184 {
185 keyword = keywords[i];
186 if (NULL != strstr (&keyword[1], " "))
187 {
188 strcat (ret, "\"");
189 if (keyword[0] == '+')
190 strcat (ret, keyword);
191 else
192 strcat (ret, &keyword[1]);
193 strcat (ret, "\"");
194 }
195 else
196 {
197 if (keyword[0] == '+')
198 strcat (ret, keyword);
199 else
200 strcat (ret, &keyword[1]);
201 }
202 strcat (ret, " ");
203 }
204 return ret;
205}
206
207
208/**
209 * Given a keyword with %-encoding (and possibly quotes to protect
210 * spaces), return a copy of the keyword without %-encoding and
211 * without double-quotes (%22). Also, add a space at the beginning
212 * if there is not a '+'.
213 *
214 * @param in string with %-encoding
215 * @param emsg where to store the parser error message (if any)
216 * @return decoded string with leading space (or preserved plus)
217 */
218static char *
219percent_decode_keyword (const char *in, char **emsg)
220{
221 char *out;
222 char *ret;
223 unsigned int rpos;
224 unsigned int wpos;
225 unsigned int hx;
226
227 out = GNUNET_strdup (in);
228 rpos = 0;
229 wpos = 0;
230 while (out[rpos] != '\0')
231 {
232 if (out[rpos] == '%')
233 {
234 if (1 != sscanf (&out[rpos + 1], "%2X", &hx))
235 {
236 GNUNET_free (out);
237 *emsg = GNUNET_strdup (
238 _ ( /* xgettext:no-c-format */
239 "Malformed KSK URI (`%' must be followed by HEX number)"));
240 return NULL;
241 }
242 rpos += 3;
243 if (hx == '"')
244 continue; /* skip double quote */
245 out[wpos++] = (char) hx;
246 }
247 else
248 {
249 out[wpos++] = out[rpos++];
250 }
251 }
252 out[wpos] = '\0';
253 if (out[0] == '+')
254 {
255 ret = GNUNET_strdup (out);
256 }
257 else
258 {
259 /* need to prefix with space */
260 ret = GNUNET_malloc (strlen (out) + 2);
261 strcpy (ret, " ");
262 strcat (ret, out);
263 }
264 GNUNET_free (out);
265 return ret;
266}
267
268
269#define GNUNET_FS_URI_KSK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_KSK_INFIX
270
271/**
272 * Parse a KSK URI.
273 *
274 * @param s an uri string
275 * @param emsg where to store the parser error message (if any)
276 * @return NULL on error, otherwise the KSK URI
277 */
278static struct GNUNET_FS_Uri *
279uri_ksk_parse (const char *s, char **emsg)
280{
281 struct GNUNET_FS_Uri *ret;
282 char **keywords;
283 unsigned int pos;
284 int max;
285 int iret;
286 int i;
287 size_t slen;
288 char *dup;
289 int saw_quote;
290
291 slen = strlen (s);
292 pos = strlen (GNUNET_FS_URI_KSK_PREFIX);
293 if ((slen <= pos) || (0 != strncmp (s, GNUNET_FS_URI_KSK_PREFIX, pos)))
294 return NULL; /* not KSK URI */
295 if ((s[slen - 1] == '+') || (s[pos] == '+'))
296 {
297 *emsg =
298 GNUNET_strdup (_ ("Malformed KSK URI (must not begin or end with `+')"));
299 return NULL;
300 }
301 max = 1;
302 saw_quote = 0;
303 for (i = pos; i < slen; i++)
304 {
305 if ((s[i] == '%') && (&s[i] == strstr (&s[i], "%22")))
306 {
307 saw_quote = (saw_quote + 1) % 2;
308 i += 3;
309 continue;
310 }
311 if ((s[i] == '+') && (saw_quote == 0))
312 {
313 max++;
314 if (s[i - 1] == '+')
315 {
316 *emsg = GNUNET_strdup (_ ("Malformed KSK URI (`++' not allowed)"));
317 return NULL;
318 }
319 }
320 }
321 if (saw_quote == 1)
322 {
323 *emsg = GNUNET_strdup (_ ("Malformed KSK URI (quotes not balanced)"));
324 return NULL;
325 }
326 iret = max;
327 dup = GNUNET_strdup (s);
328 keywords = GNUNET_new_array (max, char *);
329 for (i = slen - 1; i >= (int) pos; i--)
330 {
331 if ((s[i] == '%') && (&s[i] == strstr (&s[i], "%22")))
332 {
333 saw_quote = (saw_quote + 1) % 2;
334 continue;
335 }
336 if ((dup[i] == '+') && (saw_quote == 0))
337 {
338 keywords[--max] = percent_decode_keyword (&dup[i + 1], emsg);
339 if (NULL == keywords[max])
340 goto CLEANUP;
341 dup[i] = '\0';
342 }
343 }
344 keywords[--max] = percent_decode_keyword (&dup[pos], emsg);
345 if (NULL == keywords[max])
346 goto CLEANUP;
347 GNUNET_assert (0 == max);
348 GNUNET_free (dup);
349 ret = GNUNET_new (struct GNUNET_FS_Uri);
350 ret->type = GNUNET_FS_URI_KSK;
351 ret->data.ksk.keywordCount = iret;
352 ret->data.ksk.keywords = keywords;
353 return ret;
354CLEANUP:
355 for (i = 0; i < max; i++)
356 GNUNET_free (keywords[i]);
357 GNUNET_free (keywords);
358 GNUNET_free (dup);
359 return NULL;
360}
361
362
363#define GNUNET_FS_URI_SKS_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_SKS_INFIX
364
365/**
366 * Parse an SKS URI.
367 *
368 * @param s an uri string
369 * @param emsg where to store the parser error message (if any)
370 * @return NULL on error, SKS URI otherwise
371 */
372static struct GNUNET_FS_Uri *
373uri_sks_parse (const char *s, char **emsg)
374{
375 struct GNUNET_FS_Uri *ret;
376 struct GNUNET_CRYPTO_EcdsaPublicKey ns;
377 size_t pos;
378 char *end;
379
380 pos = strlen (GNUNET_FS_URI_SKS_PREFIX);
381 if ((strlen (s) <= pos) || (0 != strncmp (s, GNUNET_FS_URI_SKS_PREFIX, pos)))
382 return NULL; /* not an SKS URI */
383 end = strchr (&s[pos], '/');
384 if ((NULL == end) ||
385 (GNUNET_OK != GNUNET_STRINGS_string_to_data (&s[pos],
386 end - &s[pos],
387 &ns,
388 sizeof(ns))))
389 {
390 *emsg = GNUNET_strdup (_ ("Malformed SKS URI (wrong syntax)"));
391 return NULL; /* malformed */
392 }
393 end++; /* skip over '/' */
394 ret = GNUNET_new (struct GNUNET_FS_Uri);
395 ret->type = GNUNET_FS_URI_SKS;
396 ret->data.sks.ns = ns;
397 ret->data.sks.identifier = GNUNET_strdup (end);
398 return ret;
399}
400
401
402#define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX
403
404
405/**
406 * Parse a CHK URI.
407 *
408 * @param s an uri string
409 * @param emsg where to store the parser error message (if any)
410 * @return NULL on error, CHK URI otherwise
411 */
412static struct GNUNET_FS_Uri *
413uri_chk_parse (const char *s, char **emsg)
414{
415 struct GNUNET_FS_Uri *ret;
416 struct FileIdentifier fi;
417 unsigned int pos;
418 unsigned long long flen;
419 size_t slen;
420 char h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)];
421 char h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)];
422
423 slen = strlen (s);
424 pos = strlen (GNUNET_FS_URI_CHK_PREFIX);
425 if ((slen < pos + 2 * sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) + 1) ||
426 (0 != strncmp (s, GNUNET_FS_URI_CHK_PREFIX, pos)))
427 return NULL; /* not a CHK URI */
428 if ((s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] != '.') ||
429 (s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2 - 1] != '.'))
430 {
431 *emsg = GNUNET_strdup (_ ("Malformed CHK URI (wrong syntax)"));
432 return NULL;
433 }
434 GNUNET_memcpy (h1, &s[pos], sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded));
435 h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
436 GNUNET_memcpy (h2,
437 &s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)],
438 sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded));
439 h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
440
441 if ((GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h1, &fi.chk.key)) ||
442 (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h2, &fi.chk.query)) ||
443 (1 !=
444 sscanf (&s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2],
445 "%llu",
446 &flen)))
447 {
448 *emsg = GNUNET_strdup (_ ("Malformed CHK URI (failed to decode CHK)"));
449 return NULL;
450 }
451 fi.file_length = GNUNET_htonll (flen);
452 ret = GNUNET_new (struct GNUNET_FS_Uri);
453 ret->type = GNUNET_FS_URI_CHK;
454 ret->data.chk = fi;
455 return ret;
456}
457
458
459GNUNET_NETWORK_STRUCT_BEGIN
460/**
461 * Structure that defines how the contents of a location URI must be
462 * assembled in memory to create or verify the signature of a location
463 * URI.
464 */
465struct LocUriAssembly
466{
467 /**
468 * What is being signed (rest of this struct).
469 */
470 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
471
472 /**
473 * Expiration time of the offer.
474 */
475 struct GNUNET_TIME_AbsoluteNBO exptime;
476
477 /**
478 * File being offered.
479 */
480 struct FileIdentifier fi;
481
482 /**
483 * Peer offering the file.
484 */
485 struct GNUNET_PeerIdentity peer;
486};
487GNUNET_NETWORK_STRUCT_END
488
489
490#define GNUNET_FS_URI_LOC_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_LOC_INFIX
491
492#define SIGNATURE_ASCII_LENGTH 103
493
494/**
495 * Parse a LOC URI.
496 * Also verifies validity of the location URI.
497 *
498 * @param s an uri string
499 * @param emsg where to store the parser error message (if any)
500 * @return NULL on error, valid LOC URI otherwise
501 */
502static struct GNUNET_FS_Uri *
503uri_loc_parse (const char *s, char **emsg)
504{
505 struct GNUNET_FS_Uri *uri;
506 char h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)];
507 char h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)];
508 unsigned int pos;
509 unsigned int npos;
510 unsigned long long exptime;
511 unsigned long long flen;
512 struct GNUNET_TIME_Absolute et;
513 struct GNUNET_CRYPTO_EddsaSignature sig;
514 struct LocUriAssembly ass;
515 size_t slen;
516
517 slen = strlen (s);
518 pos = strlen (GNUNET_FS_URI_LOC_PREFIX);
519 if ((slen < pos + 2 * sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) + 1) ||
520 (0 != strncmp (s, GNUNET_FS_URI_LOC_PREFIX, pos)))
521 return NULL; /* not a LOC URI */
522 if ((s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] != '.') ||
523 (s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2 - 1] != '.'))
524 {
525 *emsg = GNUNET_strdup (_ ("LOC URI malformed (wrong syntax)"));
526 return NULL;
527 }
528 GNUNET_memcpy (h1, &s[pos], sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded));
529 h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
530 GNUNET_memcpy (h2,
531 &s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)],
532 sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded));
533 h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
534
535 if ((GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h1, &ass.fi.chk.key)) ||
536 (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h2, &ass.fi.chk.query)) ||
537 (1 !=
538 sscanf (&s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2],
539 "%llu",
540 &flen)))
541 {
542 *emsg = GNUNET_strdup (_ ("LOC URI malformed (no CHK)"));
543 return NULL;
544 }
545 ass.fi.file_length = GNUNET_htonll (flen);
546
547 npos = pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2;
548 while ((s[npos] != '\0') && (s[npos] != '.'))
549 npos++;
550 if (s[npos] == '\0')
551 {
552 *emsg = GNUNET_strdup (_ ("LOC URI malformed (missing LOC)"));
553 goto ERR;
554 }
555 npos++;
556 if ((strlen (&s[npos]) <= GNUNET_CRYPTO_PKEY_ASCII_LENGTH + 1) ||
557 ('.' != s[npos + GNUNET_CRYPTO_PKEY_ASCII_LENGTH]))
558 {
559 *emsg =
560 GNUNET_strdup (_ ("LOC URI malformed (wrong syntax for public key)"));
561 }
562 if (
563 GNUNET_OK !=
564 GNUNET_CRYPTO_eddsa_public_key_from_string (&s[npos],
565 GNUNET_CRYPTO_PKEY_ASCII_LENGTH,
566 &ass.peer.public_key))
567 {
568 *emsg =
569 GNUNET_strdup (_ ("LOC URI malformed (could not decode public key)"));
570 goto ERR;
571 }
572 npos += GNUNET_CRYPTO_PKEY_ASCII_LENGTH;
573 if (s[npos++] != '.')
574 {
575 *emsg = GNUNET_strdup (_ ("LOC URI malformed (could not find signature)"));
576 goto ERR;
577 }
578 if ((strlen (&s[npos]) <= SIGNATURE_ASCII_LENGTH + 1) ||
579 ('.' != s[npos + SIGNATURE_ASCII_LENGTH]))
580 {
581 *emsg =
582 GNUNET_strdup (_ ("LOC URI malformed (wrong syntax for signature)"));
583 goto ERR;
584 }
585 if (GNUNET_OK !=
586 GNUNET_STRINGS_string_to_data (&s[npos],
587 SIGNATURE_ASCII_LENGTH,
588 &sig,
589 sizeof(
590 struct GNUNET_CRYPTO_EddsaSignature)))
591 {
592 *emsg =
593 GNUNET_strdup (_ ("LOC URI malformed (could not decode signature)"));
594 goto ERR;
595 }
596 npos += SIGNATURE_ASCII_LENGTH;
597 if (s[npos++] != '.')
598 {
599 *emsg = GNUNET_strdup (
600 _ ("LOC URI malformed (wrong syntax for expiration time)"));
601 goto ERR;
602 }
603 if (1 != sscanf (&s[npos], "%llu", &exptime))
604 {
605 *emsg =
606 GNUNET_strdup (_ ("LOC URI malformed (could not parse expiration time)"));
607 goto ERR;
608 }
609 ass.purpose.size = htonl (sizeof(struct LocUriAssembly));
610 ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
611 et.abs_value_us = exptime * 1000LL * 1000LL;
612 ass.exptime = GNUNET_TIME_absolute_hton (et);
613 if (GNUNET_OK !=
614 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT,
615 &ass,
616 &sig,
617 &ass.peer.public_key))
618 {
619 *emsg =
620 GNUNET_strdup (_ ("LOC URI malformed (signature failed validation)"));
621 goto ERR;
622 }
623 uri = GNUNET_new (struct GNUNET_FS_Uri);
624 uri->type = GNUNET_FS_URI_LOC;
625 uri->data.loc.fi = ass.fi;
626 uri->data.loc.peer = ass.peer;
627 uri->data.loc.expirationTime = et;
628 uri->data.loc.contentSignature = sig;
629
630 return uri;
631ERR:
632 return NULL;
633}
634
635
636/**
637 * Convert a UTF-8 String to a URI.
638 *
639 * @param uri string to parse
640 * @param emsg where to store the parser error message (if any)
641 * @return NULL on error
642 */
643struct GNUNET_FS_Uri *
644GNUNET_FS_uri_parse (const char *uri, char **emsg)
645{
646 struct GNUNET_FS_Uri *ret;
647 char *msg;
648
649 if (NULL == uri)
650 {
651 GNUNET_break (0);
652 if (NULL != emsg)
653 *emsg = GNUNET_strdup (_ ("invalid argument"));
654 return NULL;
655 }
656 /**
657 * FIXME: Do we want to log this?
658 */
659 msg = NULL;
660 if (NULL != (ret = uri_chk_parse (uri, &msg)))
661 return ret;
662 GNUNET_free (msg);
663 if (NULL != (ret = uri_ksk_parse (uri, &msg)))
664 return ret;
665 GNUNET_free (msg);
666 if (NULL != (ret = uri_sks_parse (uri, &msg)))
667 return ret;
668 GNUNET_free (msg);
669 if (NULL != (ret = uri_loc_parse (uri, &msg)))
670 return ret;
671 GNUNET_free (msg);
672 if (NULL != emsg)
673 *emsg = GNUNET_strdup (_ ("Unrecognized URI type"));
674 return NULL;
675}
676
677
678/**
679 * Free URI.
680 *
681 * @param uri uri to free
682 */
683void
684GNUNET_FS_uri_destroy (struct GNUNET_FS_Uri *uri)
685{
686 unsigned int i;
687
688 switch (uri->type)
689 {
690 case GNUNET_FS_URI_KSK:
691 for (i = 0; i < uri->data.ksk.keywordCount; i++)
692 GNUNET_free (uri->data.ksk.keywords[i]);
693 GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount, 0);
694 break;
695
696 case GNUNET_FS_URI_SKS:
697 GNUNET_free (uri->data.sks.identifier);
698 break;
699
700 case GNUNET_FS_URI_LOC:
701 break;
702
703 default:
704 /* do nothing */
705 break;
706 }
707 GNUNET_free (uri);
708}
709
710
711/**
712 * How many keywords are ANDed in this keyword URI?
713 *
714 * @param uri ksk uri to get the number of keywords from
715 * @return 0 if this is not a keyword URI
716 */
717unsigned int
718GNUNET_FS_uri_ksk_get_keyword_count (const struct GNUNET_FS_Uri *uri)
719{
720 if (uri->type != GNUNET_FS_URI_KSK)
721 return 0;
722 return uri->data.ksk.keywordCount;
723}
724
725
726/**
727 * Iterate over all keywords in this keyword URI.
728 *
729 * @param uri ksk uri to get the keywords from
730 * @param iterator function to call on each keyword
731 * @param iterator_cls closure for iterator
732 * @return -1 if this is not a keyword URI, otherwise number of
733 * keywords iterated over until iterator aborted
734 */
735int
736GNUNET_FS_uri_ksk_get_keywords (const struct GNUNET_FS_Uri *uri,
737 GNUNET_FS_KeywordIterator iterator,
738 void *iterator_cls)
739{
740 unsigned int i;
741 char *keyword;
742
743 if (uri->type != GNUNET_FS_URI_KSK)
744 return -1;
745 if (NULL == iterator)
746 return uri->data.ksk.keywordCount;
747 for (i = 0; i < uri->data.ksk.keywordCount; i++)
748 {
749 keyword = uri->data.ksk.keywords[i];
750 /* first character of keyword indicates
751 * if it is mandatory or not */
752 if (GNUNET_OK != iterator (iterator_cls, &keyword[1],(keyword[0] == '+') ))
753 return i;
754 }
755 return i;
756}
757
758
759/**
760 * Add the given keyword to the set of keywords represented by the URI.
761 * Does nothing if the keyword is already present.
762 *
763 * @param uri ksk uri to modify
764 * @param keyword keyword to add
765 * @param is_mandatory is this keyword mandatory?
766 */
767void
768GNUNET_FS_uri_ksk_add_keyword (struct GNUNET_FS_Uri *uri,
769 const char *keyword,
770 int is_mandatory)
771{
772 unsigned int i;
773 const char *old;
774 char *n;
775
776 GNUNET_assert (uri->type == GNUNET_FS_URI_KSK);
777 for (i = 0; i < uri->data.ksk.keywordCount; i++)
778 {
779 old = uri->data.ksk.keywords[i];
780 if (0 == strcmp (&old[1], keyword))
781 return;
782 }
783 GNUNET_asprintf (&n, is_mandatory ? "+%s" : " %s", keyword);
784 GNUNET_array_append (uri->data.ksk.keywords, uri->data.ksk.keywordCount, n);
785}
786
787
788/**
789 * Remove the given keyword from the set of keywords represented by the URI.
790 * Does nothing if the keyword is not present.
791 *
792 * @param uri ksk uri to modify
793 * @param keyword keyword to add
794 */
795void
796GNUNET_FS_uri_ksk_remove_keyword (struct GNUNET_FS_Uri *uri,
797 const char *keyword)
798{
799 unsigned int i;
800 char *old;
801
802 GNUNET_assert (uri->type == GNUNET_FS_URI_KSK);
803 for (i = 0; i < uri->data.ksk.keywordCount; i++)
804 {
805 old = uri->data.ksk.keywords[i];
806 if (0 == strcmp (&old[1], keyword))
807 {
808 uri->data.ksk.keywords[i] =
809 uri->data.ksk.keywords[uri->data.ksk.keywordCount - 1];
810 GNUNET_array_grow (uri->data.ksk.keywords,
811 uri->data.ksk.keywordCount,
812 uri->data.ksk.keywordCount - 1);
813 GNUNET_free (old);
814 return;
815 }
816 }
817}
818
819
820/**
821 * Obtain the identity of the peer offering the data
822 *
823 * @param uri the location URI to inspect
824 * @param peer where to store the identify of the peer (presumably) offering the content
825 * @return #GNUNET_SYSERR if this is not a location URI, otherwise #GNUNET_OK
826 */
827int
828GNUNET_FS_uri_loc_get_peer_identity (const struct GNUNET_FS_Uri *uri,
829 struct GNUNET_PeerIdentity *peer)
830{
831 if (uri->type != GNUNET_FS_URI_LOC)
832 return GNUNET_SYSERR;
833 *peer = uri->data.loc.peer;
834 return GNUNET_OK;
835}
836
837
838/**
839 * Obtain the expiration of the LOC URI.
840 *
841 * @param uri location URI to get the expiration from
842 * @return expiration time of the URI
843 */
844struct GNUNET_TIME_Absolute
845GNUNET_FS_uri_loc_get_expiration (const struct GNUNET_FS_Uri *uri)
846{
847 GNUNET_assert (uri->type == GNUNET_FS_URI_LOC);
848 return uri->data.loc.expirationTime;
849}
850
851
852/**
853 * Obtain the URI of the content itself.
854 *
855 * @param uri location URI to get the content URI from
856 * @return NULL if argument is not a location URI
857 */
858struct GNUNET_FS_Uri *
859GNUNET_FS_uri_loc_get_uri (const struct GNUNET_FS_Uri *uri)
860{
861 struct GNUNET_FS_Uri *ret;
862
863 if (uri->type != GNUNET_FS_URI_LOC)
864 return NULL;
865 ret = GNUNET_new (struct GNUNET_FS_Uri);
866 ret->type = GNUNET_FS_URI_CHK;
867 ret->data.chk = uri->data.loc.fi;
868 return ret;
869}
870
871
872/**
873 * Construct a location URI (this peer will be used for the location).
874 * This function should only be called from within gnunet-service-fs,
875 * as it requires the peer's private key which is generally unavailable
876 * to processes directly under the user's control. However, for
877 * testing and as it logically fits under URIs, it is in this API.
878 *
879 * @param base_uri content offered by the sender
880 * @param sign_key private key of the peer
881 * @param expiration_time how long will the content be offered?
882 * @return the location URI, NULL on error
883 */
884struct GNUNET_FS_Uri *
885GNUNET_FS_uri_loc_create (const struct GNUNET_FS_Uri *base_uri,
886 const struct GNUNET_CRYPTO_EddsaPrivateKey *sign_key,
887 struct GNUNET_TIME_Absolute expiration_time)
888{
889 struct GNUNET_FS_Uri *uri;
890 struct GNUNET_CRYPTO_EddsaPublicKey my_public_key;
891 struct LocUriAssembly ass;
892 struct GNUNET_TIME_Absolute et;
893
894 if (GNUNET_FS_URI_CHK != base_uri->type)
895 return NULL;
896 /* we round expiration time to full seconds for SKS URIs */
897 et.abs_value_us = (expiration_time.abs_value_us / 1000000LL) * 1000000LL;
898 GNUNET_CRYPTO_eddsa_key_get_public (sign_key, &my_public_key);
899 ass.purpose.size = htonl (sizeof(struct LocUriAssembly));
900 ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
901 ass.exptime = GNUNET_TIME_absolute_hton (et);
902 ass.fi = base_uri->data.chk;
903 ass.peer.public_key = my_public_key;
904 uri = GNUNET_new (struct GNUNET_FS_Uri);
905 uri->type = GNUNET_FS_URI_LOC;
906 uri->data.loc.fi = base_uri->data.chk;
907 uri->data.loc.expirationTime = et;
908 uri->data.loc.peer.public_key = my_public_key;
909 GNUNET_CRYPTO_eddsa_sign (sign_key,
910 &ass,
911 &uri->data.loc.contentSignature);
912 return uri;
913}
914
915
916/**
917 * Create an SKS URI from a namespace ID and an identifier.
918 *
919 * @param ns namespace ID
920 * @param id identifier
921 * @return an FS URI for the given namespace and identifier
922 */
923struct GNUNET_FS_Uri *
924GNUNET_FS_uri_sks_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *ns,
925 const char *id)
926{
927 struct GNUNET_FS_Uri *ns_uri;
928
929 ns_uri = GNUNET_new (struct GNUNET_FS_Uri);
930 ns_uri->type = GNUNET_FS_URI_SKS;
931 ns_uri->data.sks.ns = *ns;
932 ns_uri->data.sks.identifier = GNUNET_strdup (id);
933 return ns_uri;
934}
935
936
937/**
938 * Merge the sets of keywords from two KSK URIs.
939 * (useful for merging the canonicalized keywords with
940 * the original keywords for sharing).
941 *
942 * @param u1 first uri
943 * @param u2 second uri
944 * @return merged URI, NULL on error
945 */
946struct GNUNET_FS_Uri *
947GNUNET_FS_uri_ksk_merge (const struct GNUNET_FS_Uri *u1,
948 const struct GNUNET_FS_Uri *u2)
949{
950 struct GNUNET_FS_Uri *ret;
951 unsigned int kc;
952 unsigned int i;
953 unsigned int j;
954 int found;
955 const char *kp;
956 char **kl;
957
958 if ((u1 == NULL) && (u2 == NULL))
959 return NULL;
960 if (u1 == NULL)
961 return GNUNET_FS_uri_dup (u2);
962 if (u2 == NULL)
963 return GNUNET_FS_uri_dup (u1);
964 if ((u1->type != GNUNET_FS_URI_KSK) || (u2->type != GNUNET_FS_URI_KSK))
965 {
966 GNUNET_break (0);
967 return NULL;
968 }
969 kc = u1->data.ksk.keywordCount;
970 kl = GNUNET_new_array (kc + u2->data.ksk.keywordCount, char *);
971 for (i = 0; i < u1->data.ksk.keywordCount; i++)
972 kl[i] = GNUNET_strdup (u1->data.ksk.keywords[i]);
973 for (i = 0; i < u2->data.ksk.keywordCount; i++)
974 {
975 kp = u2->data.ksk.keywords[i];
976 found = 0;
977 for (j = 0; j < u1->data.ksk.keywordCount; j++)
978 if (0 == strcmp (kp + 1, kl[j] + 1))
979 {
980 found = 1;
981 if (kp[0] == '+')
982 kl[j][0] = '+';
983 break;
984 }
985 if (0 == found)
986 kl[kc++] = GNUNET_strdup (kp);
987 }
988 ret = GNUNET_new (struct GNUNET_FS_Uri);
989 ret->type = GNUNET_FS_URI_KSK;
990 ret->data.ksk.keywordCount = kc;
991 ret->data.ksk.keywords = kl;
992 return ret;
993}
994
995
996/**
997 * Duplicate URI.
998 *
999 * @param uri the URI to duplicate
1000 * @return copy of the URI
1001 */
1002struct GNUNET_FS_Uri *
1003GNUNET_FS_uri_dup (const struct GNUNET_FS_Uri *uri)
1004{
1005 struct GNUNET_FS_Uri *ret;
1006 unsigned int i;
1007
1008 if (uri == NULL)
1009 return NULL;
1010 ret = GNUNET_new (struct GNUNET_FS_Uri);
1011 GNUNET_memcpy (ret, uri, sizeof(struct GNUNET_FS_Uri));
1012 switch (ret->type)
1013 {
1014 case GNUNET_FS_URI_KSK:
1015 if (ret->data.ksk.keywordCount >=
1016 GNUNET_MAX_MALLOC_CHECKED / sizeof(char *))
1017 {
1018 GNUNET_break (0);
1019 GNUNET_free (ret);
1020 return NULL;
1021 }
1022 if (ret->data.ksk.keywordCount > 0)
1023 {
1024 ret->data.ksk.keywords =
1025 GNUNET_new_array (ret->data.ksk.keywordCount, char *);
1026 for (i = 0; i < ret->data.ksk.keywordCount; i++)
1027 ret->data.ksk.keywords[i] = GNUNET_strdup (uri->data.ksk.keywords[i]);
1028 }
1029 else
1030 ret->data.ksk.keywords = NULL; /* just to be sure */
1031 break;
1032
1033 case GNUNET_FS_URI_SKS:
1034 ret->data.sks.identifier = GNUNET_strdup (uri->data.sks.identifier);
1035 break;
1036
1037 case GNUNET_FS_URI_LOC:
1038 break;
1039
1040 default:
1041 break;
1042 }
1043 return ret;
1044}
1045
1046
1047/**
1048 * Create an FS URI from a single user-supplied string of keywords.
1049 * The string is broken up at spaces into individual keywords.
1050 * Keywords that start with "+" are mandatory. Double-quotes can
1051 * be used to prevent breaking up strings at spaces (and also
1052 * to specify non-mandatory keywords starting with "+").
1053 *
1054 * Keywords must contain a balanced number of double quotes and
1055 * double quotes can not be used in the actual keywords (for
1056 * example, the string '""foo bar""' will be turned into two
1057 * "OR"ed keywords 'foo' and 'bar', not into '"foo bar"'.
1058 *
1059 * @param keywords the keyword string
1060 * @param emsg where to store an error message
1061 * @return an FS URI for the given keywords, NULL
1062 * if keywords is not legal (i.e. empty).
1063 */
1064struct GNUNET_FS_Uri *
1065GNUNET_FS_uri_ksk_create (const char *keywords, char **emsg)
1066{
1067 char **keywordarr;
1068 unsigned int num_Words;
1069 int inWord;
1070 char *pos;
1071 struct GNUNET_FS_Uri *uri;
1072 char *searchString;
1073 int saw_quote;
1074
1075 if (keywords == NULL)
1076 {
1077 *emsg = GNUNET_strdup (_ ("No keywords specified!\n"));
1078 GNUNET_break (0);
1079 return NULL;
1080 }
1081 searchString = GNUNET_strdup (keywords);
1082 num_Words = 0;
1083 inWord = 0;
1084 saw_quote = 0;
1085 pos = searchString;
1086 while ('\0' != *pos)
1087 {
1088 if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1089 {
1090 inWord = 0;
1091 }
1092 else if (0 == inWord)
1093 {
1094 inWord = 1;
1095 ++num_Words;
1096 }
1097 if ('"' == *pos)
1098 saw_quote = (saw_quote + 1) % 2;
1099 pos++;
1100 }
1101 if (num_Words == 0)
1102 {
1103 GNUNET_free (searchString);
1104 *emsg = GNUNET_strdup (_ ("No keywords specified!\n"));
1105 return NULL;
1106 }
1107 if (saw_quote != 0)
1108 {
1109 GNUNET_free (searchString);
1110 *emsg = GNUNET_strdup (_ ("Number of double-quotes not balanced!\n"));
1111 return NULL;
1112 }
1113 keywordarr = GNUNET_new_array (num_Words, char *);
1114 num_Words = 0;
1115 inWord = 0;
1116 pos = searchString;
1117 while ('\0' != *pos)
1118 {
1119 if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1120 {
1121 inWord = 0;
1122 *pos = '\0';
1123 }
1124 else if (0 == inWord)
1125 {
1126 keywordarr[num_Words] = pos;
1127 inWord = 1;
1128 ++num_Words;
1129 }
1130 if ('"' == *pos)
1131 saw_quote = (saw_quote + 1) % 2;
1132 pos++;
1133 }
1134 uri =
1135 GNUNET_FS_uri_ksk_create_from_args (num_Words, (const char **) keywordarr);
1136 GNUNET_free (keywordarr);
1137 GNUNET_free (searchString);
1138 return uri;
1139}
1140
1141
1142/**
1143 * Create an FS URI from a user-supplied command line of keywords.
1144 * Arguments should start with "+" to indicate mandatory
1145 * keywords.
1146 *
1147 * @param argc number of keywords
1148 * @param argv keywords (double quotes are not required for
1149 * keywords containing spaces; however, double
1150 * quotes are required for keywords starting with
1151 * "+"); there is no mechanism for having double
1152 * quotes in the actual keywords (if the user
1153 * did specifically specify double quotes, the
1154 * caller should convert each double quote
1155 * into two single quotes).
1156 * @return an FS URI for the given keywords, NULL
1157 * if keywords is not legal (i.e. empty).
1158 */
1159struct GNUNET_FS_Uri *
1160GNUNET_FS_uri_ksk_create_from_args (unsigned int argc, const char **argv)
1161{
1162 unsigned int i;
1163 struct GNUNET_FS_Uri *uri;
1164 const char *keyword;
1165 char *val;
1166 const char *r;
1167 char *w;
1168 char *emsg;
1169
1170 if (argc == 0)
1171 return NULL;
1172 /* allow URI to be given as one and only keyword and
1173 * handle accordingly */
1174 emsg = NULL;
1175 if ((argc == 1) && (strlen (argv[0]) > strlen (GNUNET_FS_URI_PREFIX)) &&
1176 (0 == strncmp (argv[0],
1177 GNUNET_FS_URI_PREFIX,
1178 strlen (GNUNET_FS_URI_PREFIX))) &&
1179 (NULL != (uri = GNUNET_FS_uri_parse (argv[0], &emsg))))
1180 return uri;
1181 GNUNET_free (emsg);
1182 uri = GNUNET_new (struct GNUNET_FS_Uri);
1183 uri->type = GNUNET_FS_URI_KSK;
1184 uri->data.ksk.keywordCount = argc;
1185 uri->data.ksk.keywords = GNUNET_new_array (argc, char *);
1186 for (i = 0; i < argc; i++)
1187 {
1188 keyword = argv[i];
1189 if (keyword[0] == '+')
1190 val = GNUNET_strdup (keyword);
1191 else
1192 GNUNET_asprintf (&val, " %s", keyword);
1193 r = val;
1194 w = val;
1195 while ('\0' != *r)
1196 {
1197 if ('"' == *r)
1198 r++;
1199 else
1200 *(w++) = *(r++);
1201 }
1202 *w = '\0';
1203 uri->data.ksk.keywords[i] = val;
1204 }
1205 return uri;
1206}
1207
1208
1209/**
1210 * Test if two URIs are equal.
1211 *
1212 * @param u1 one of the URIs
1213 * @param u2 the other URI
1214 * @return #GNUNET_YES if the URIs are equal
1215 */
1216int
1217GNUNET_FS_uri_test_equal (const struct GNUNET_FS_Uri *u1,
1218 const struct GNUNET_FS_Uri *u2)
1219{
1220 int ret;
1221 unsigned int i;
1222 unsigned int j;
1223
1224 GNUNET_assert (u1 != NULL);
1225 GNUNET_assert (u2 != NULL);
1226 if (u1->type != u2->type)
1227 return GNUNET_NO;
1228 switch (u1->type)
1229 {
1230 case GNUNET_FS_URI_CHK:
1231 if (0 ==
1232 memcmp (&u1->data.chk, &u2->data.chk, sizeof(struct FileIdentifier)))
1233 return GNUNET_YES;
1234 return GNUNET_NO;
1235
1236 case GNUNET_FS_URI_SKS:
1237 if ((0 == memcmp (&u1->data.sks.ns,
1238 &u2->data.sks.ns,
1239 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey))) &&
1240 (0 == strcmp (u1->data.sks.identifier, u2->data.sks.identifier)))
1241
1242 return GNUNET_YES;
1243 return GNUNET_NO;
1244
1245 case GNUNET_FS_URI_KSK:
1246 if (u1->data.ksk.keywordCount != u2->data.ksk.keywordCount)
1247 return GNUNET_NO;
1248 for (i = 0; i < u1->data.ksk.keywordCount; i++)
1249 {
1250 ret = GNUNET_NO;
1251 for (j = 0; j < u2->data.ksk.keywordCount; j++)
1252 {
1253 if (0 == strcmp (u1->data.ksk.keywords[i], u2->data.ksk.keywords[j]))
1254 {
1255 ret = GNUNET_YES;
1256 break;
1257 }
1258 }
1259 if (ret == GNUNET_NO)
1260 return GNUNET_NO;
1261 }
1262 return GNUNET_YES;
1263
1264 case GNUNET_FS_URI_LOC:
1265 if (memcmp (&u1->data.loc,
1266 &u2->data.loc,
1267 sizeof(struct FileIdentifier)
1268 + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)
1269 + sizeof(struct GNUNET_TIME_Absolute)
1270 + sizeof(unsigned short) + sizeof(unsigned short)) != 0)
1271 return GNUNET_NO;
1272 return GNUNET_YES;
1273
1274 default:
1275 return GNUNET_NO;
1276 }
1277}
1278
1279
1280/**
1281 * Is this a namespace URI?
1282 *
1283 * @param uri the uri to check
1284 * @return #GNUNET_YES if this is an SKS uri
1285 */
1286int
1287GNUNET_FS_uri_test_sks (const struct GNUNET_FS_Uri *uri)
1288{
1289 return uri->type == GNUNET_FS_URI_SKS;
1290}
1291
1292
1293/**
1294 * Get the ID of a namespace from the given
1295 * namespace URI.
1296 *
1297 * @param uri the uri to get the namespace ID from
1298 * @param pseudonym where to store the ID of the namespace
1299 * @return #GNUNET_OK on success
1300 */
1301int
1302GNUNET_FS_uri_sks_get_namespace (const struct GNUNET_FS_Uri *uri,
1303 struct GNUNET_CRYPTO_EcdsaPublicKey *pseudonym)
1304{
1305 if (! GNUNET_FS_uri_test_sks (uri))
1306 {
1307 GNUNET_break (0);
1308 return GNUNET_SYSERR;
1309 }
1310 *pseudonym = uri->data.sks.ns;
1311 return GNUNET_OK;
1312}
1313
1314
1315/**
1316 * Get the content identifier of an SKS URI.
1317 *
1318 * @param uri the sks uri
1319 * @return NULL on error (not a valid SKS URI)
1320 */
1321char *
1322GNUNET_FS_uri_sks_get_content_id (const struct GNUNET_FS_Uri *uri)
1323{
1324 if (! GNUNET_FS_uri_test_sks (uri))
1325 {
1326 GNUNET_break (0);
1327 return NULL;
1328 }
1329 return GNUNET_strdup (uri->data.sks.identifier);
1330}
1331
1332
1333/**
1334 * Is this a keyword URI?
1335 *
1336 * @param uri the uri
1337 * @return #GNUNET_YES if this is a KSK uri
1338 */
1339int
1340GNUNET_FS_uri_test_ksk (const struct GNUNET_FS_Uri *uri)
1341{
1342#if EXTRA_CHECKS
1343 unsigned int i;
1344
1345 if (uri->type == GNUNET_FS_URI_KSK)
1346 {
1347 for (i = 0; i < uri->data.ksk.keywordCount; i++)
1348 GNUNET_assert (uri->data.ksk.keywords[i] != NULL);
1349 }
1350#endif
1351 return uri->type == GNUNET_FS_URI_KSK;
1352}
1353
1354
1355/**
1356 * Is this a file (or directory) URI?
1357 *
1358 * @param uri the uri to check
1359 * @return #GNUNET_YES if this is a CHK uri
1360 */
1361int
1362GNUNET_FS_uri_test_chk (const struct GNUNET_FS_Uri *uri)
1363{
1364 return uri->type == GNUNET_FS_URI_CHK;
1365}
1366
1367
1368/**
1369 * What is the size of the file that this URI
1370 * refers to?
1371 *
1372 * @param uri the CHK URI to inspect
1373 * @return size of the file as specified in the CHK URI
1374 */
1375uint64_t
1376GNUNET_FS_uri_chk_get_file_size (const struct GNUNET_FS_Uri *uri)
1377{
1378 switch (uri->type)
1379 {
1380 case GNUNET_FS_URI_CHK:
1381 return GNUNET_ntohll (uri->data.chk.file_length);
1382
1383 case GNUNET_FS_URI_LOC:
1384 return GNUNET_ntohll (uri->data.loc.fi.file_length);
1385
1386 default:
1387 GNUNET_assert (0);
1388 }
1389 return 0; /* unreachable */
1390}
1391
1392
1393/**
1394 * Is this a location URI?
1395 *
1396 * @param uri the uri to check
1397 * @return #GNUNET_YES if this is a LOC uri
1398 */
1399int
1400GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri)
1401{
1402 return uri->type == GNUNET_FS_URI_LOC;
1403}
1404
1405
1406/**
1407 * Add a keyword as non-mandatory (with ' '-prefix) to the
1408 * given keyword list at offset 'index'. The array is
1409 * guaranteed to be long enough.
1410 *
1411 * @param s keyword to add
1412 * @param array array to add the keyword to
1413 * @param index offset where to add the keyword
1414 */
1415static void
1416insert_non_mandatory_keyword (const char *s, char **array, int index)
1417{
1418 char *nkword;
1419
1420 GNUNET_asprintf (&nkword,
1421 " %s", /* space to mark as 'non mandatory' */
1422 s);
1423 array[index] = nkword;
1424}
1425
1426
1427/**
1428 * Test if the given keyword @a s is already present in the
1429 * given array, ignoring the '+'-mandatory prefix in the array.
1430 *
1431 * @param s keyword to test
1432 * @param array keywords to test against, with ' ' or '+' prefix to ignore
1433 * @param array_length length of the @a array
1434 * @return #GNUNET_YES if the keyword exists, #GNUNET_NO if not
1435 */
1436static int
1437find_duplicate (const char *s, const char **array, int array_length)
1438{
1439 int j;
1440
1441 for (j = array_length - 1; j >= 0; j--)
1442 if (0 == strcmp (&array[j][1], s))
1443 return GNUNET_YES;
1444 return GNUNET_NO;
1445}
1446
1447
1448/**
1449 * FIXME: comment
1450 */
1451static char *
1452normalize_metadata (enum EXTRACTOR_MetaFormat format,
1453 const char *data,
1454 size_t data_len)
1455{
1456 uint8_t *free_str = NULL;
1457 uint8_t *str_to_normalize = (uint8_t *) data;
1458 uint8_t *normalized;
1459 size_t r_len;
1460
1461 if (str_to_normalize == NULL)
1462 return NULL;
1463 /* Don't trust libextractor */
1464 if (format == EXTRACTOR_METAFORMAT_UTF8)
1465 {
1466 free_str = (uint8_t *) u8_check ((const uint8_t *) data, data_len);
1467 if (free_str == NULL)
1468 free_str = NULL;
1469 else
1470 format = EXTRACTOR_METAFORMAT_C_STRING;
1471 }
1472 if (format == EXTRACTOR_METAFORMAT_C_STRING)
1473 {
1474 free_str = u8_strconv_from_encoding (data,
1475 locale_charset (),
1476 iconveh_escape_sequence);
1477 if (free_str == NULL)
1478 return NULL;
1479 }
1480
1481 normalized = u8_tolower (str_to_normalize,
1482 strlen ((char *) str_to_normalize),
1483 NULL,
1484 UNINORM_NFD,
1485 NULL,
1486 &r_len);
1487 /* free_str is allocated by libunistring internally, use free() */
1488 if (free_str != NULL)
1489 free (free_str);
1490 if (normalized != NULL)
1491 {
1492 /* u8_tolower allocates a non-NULL-terminated string! */
1493 free_str = GNUNET_malloc (r_len + 1);
1494 GNUNET_memcpy (free_str, normalized, r_len);
1495 free_str[r_len] = '\0';
1496 free (normalized);
1497 normalized = free_str;
1498 }
1499 return (char *) normalized;
1500}
1501
1502
1503/**
1504 * Counts the number of UTF-8 characters (not bytes) in the string,
1505 * returns that count.
1506 */
1507static size_t
1508u8_strcount (const uint8_t *s)
1509{
1510 size_t count;
1511 ucs4_t c;
1512
1513 GNUNET_assert (s != NULL);
1514 if (s[0] == 0)
1515 return 0;
1516 for (count = 0; s != NULL; count++)
1517 s = u8_next (&c, s);
1518 return count - 1;
1519}
1520
1521
1522/**
1523 * Break the filename up by matching [], () and {} pairs to make
1524 * keywords. In case of nesting parentheses only the inner pair counts.
1525 * You can't escape parentheses to scan something like "[blah\{foo]" to
1526 * make a "blah{foo" keyword, this function is only a heuristic!
1527 *
1528 * @param s string to break down.
1529 * @param array array to fill with enclosed tokens. If NULL, then tokens
1530 * are only counted.
1531 * @param index index at which to start filling the array (entries prior
1532 * to it are used to check for duplicates). ignored if @a array == NULL.
1533 * @return number of tokens counted (including duplicates), or number of
1534 * tokens extracted (excluding duplicates). 0 if there are no
1535 * matching parens in the string (when counting), or when all tokens
1536 * were duplicates (when extracting).
1537 */
1538static int
1539get_keywords_from_parens (const char *s, char **array, int index)
1540{
1541 int count = 0;
1542 char *open_paren;
1543 char *close_paren;
1544 char *ss;
1545 char tmp;
1546
1547 if (NULL == s)
1548 return 0;
1549 ss = GNUNET_strdup (s);
1550 open_paren = ss - 1;
1551 while (NULL != (open_paren = strpbrk (open_paren + 1, "[{(")))
1552 {
1553 int match = 0;
1554
1555 close_paren = strpbrk (open_paren + 1, "]})");
1556 if (NULL == close_paren)
1557 continue;
1558 switch (open_paren[0])
1559 {
1560 case '[':
1561 if (']' == close_paren[0])
1562 match = 1;
1563 break;
1564
1565 case '{':
1566 if ('}' == close_paren[0])
1567 match = 1;
1568 break;
1569
1570 case '(':
1571 if (')' == close_paren[0])
1572 match = 1;
1573 break;
1574
1575 default:
1576 break;
1577 }
1578 if (match && (close_paren - open_paren > 1))
1579 {
1580 tmp = close_paren[0];
1581 close_paren[0] = '\0';
1582 /* Keywords must be at least 3 characters long */
1583 if (u8_strcount ((const uint8_t *) &open_paren[1]) <= 2)
1584 {
1585 close_paren[0] = tmp;
1586 continue;
1587 }
1588 if (NULL != array)
1589 {
1590 char *normalized;
1591 if (GNUNET_NO == find_duplicate ((const char *) &open_paren[1],
1592 (const char **) array,
1593 index + count))
1594 {
1595 insert_non_mandatory_keyword ((const char *) &open_paren[1],
1596 array,
1597 index + count);
1598 count++;
1599 }
1600 normalized = normalize_metadata (EXTRACTOR_METAFORMAT_UTF8,
1601 &open_paren[1],
1602 close_paren - &open_paren[1]);
1603 if (normalized != NULL)
1604 {
1605 if (GNUNET_NO == find_duplicate ((const char *) normalized,
1606 (const char **) array,
1607 index + count))
1608 {
1609 insert_non_mandatory_keyword ((const char *) normalized,
1610 array,
1611 index + count);
1612 count++;
1613 }
1614 GNUNET_free (normalized);
1615 }
1616 }
1617 else
1618 count++;
1619 close_paren[0] = tmp;
1620 }
1621 }
1622 GNUNET_free (ss);
1623 return count;
1624}
1625
1626
1627/**
1628 * Where to break up keywords
1629 */
1630#define TOKENS "_. /-!?#&+@\"\'\\;:,()[]{}$<>|"
1631
1632/**
1633 * Break the filename up by TOKENS to make
1634 * keywords.
1635 *
1636 * @param s string to break down.
1637 * @param array array to fill with tokens. If NULL, then tokens are only
1638 * counted.
1639 * @param index index at which to start filling the array (entries prior
1640 * to it are used to check for duplicates). ignored if @a array == NULL.
1641 * @return number of tokens (>1) counted (including duplicates), or number of
1642 * tokens extracted (excluding duplicates). 0 if there are no
1643 * separators in the string (when counting), or when all tokens were
1644 * duplicates (when extracting).
1645 */
1646static int
1647get_keywords_from_tokens (const char *s, char **array, int index)
1648{
1649 char *p;
1650 char *ss;
1651 int seps = 0;
1652
1653 ss = GNUNET_strdup (s);
1654 for (p = strtok (ss, TOKENS); p != NULL; p = strtok (NULL, TOKENS))
1655 {
1656 /* Keywords must be at least 3 characters long */
1657 if (u8_strcount ((const uint8_t *) p) <= 2)
1658 continue;
1659 if (NULL != array)
1660 {
1661 char *normalized;
1662 if (GNUNET_NO == find_duplicate (p, (const char **) array, index + seps))
1663 {
1664 insert_non_mandatory_keyword (p, array, index + seps);
1665 seps++;
1666 }
1667 normalized =
1668 normalize_metadata (EXTRACTOR_METAFORMAT_UTF8, p, strlen (p));
1669 if (normalized != NULL)
1670 {
1671 if (GNUNET_NO == find_duplicate ((const char *) normalized,
1672 (const char **) array,
1673 index + seps))
1674 {
1675 insert_non_mandatory_keyword ((const char *) normalized,
1676 array,
1677 index + seps);
1678 seps++;
1679 }
1680 GNUNET_free (normalized);
1681 }
1682 }
1683 else
1684 seps++;
1685 }
1686 GNUNET_free (ss);
1687 return seps;
1688}
1689
1690
1691#undef TOKENS
1692
1693
1694/**
1695 * Function called on each value in the meta data.
1696 * Adds it to the URI.
1697 *
1698 * @param cls URI to update
1699 * @param plugin_name name of the plugin that produced this value;
1700 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
1701 * used in the main libextractor library and yielding
1702 * meta data).
1703 * @param type libextractor-type describing the meta data
1704 * @param format basic format information about data
1705 * @param data_mime_type mime-type of data (not of the original file);
1706 * can be NULL (if mime-type is not known)
1707 * @param data actual meta-data found
1708 * @param data_len number of bytes in @a data
1709 * @return 0 (always)
1710 */
1711static int
1712gather_uri_data (void *cls,
1713 const char *plugin_name,
1714 enum EXTRACTOR_MetaType type,
1715 enum EXTRACTOR_MetaFormat format,
1716 const char *data_mime_type,
1717 const char *data,
1718 size_t data_len)
1719{
1720 struct GNUNET_FS_Uri *uri = cls;
1721 char *normalized_data;
1722 const char *sep;
1723
1724 if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
1725 (format != EXTRACTOR_METAFORMAT_C_STRING))
1726 return 0;
1727 /* Keywords must be at least 3 characters long
1728 * If given non-utf8 string it will, most likely, find it to be invalid,
1729 * and will return the length of its valid part, skipping the keyword.
1730 * If it does - fix the extractor, not this check!
1731 */if (u8_strcount ((const uint8_t *) data) <= 2)
1732 return 0;
1733 if ((EXTRACTOR_METATYPE_MIMETYPE == type) &&
1734 (NULL != (sep = memchr (data, '/', data_len))) && (sep != data))
1735 {
1736 char *xtra;
1737
1738 GNUNET_asprintf (&xtra, "mimetype:%.*s", (int) (sep - data), data);
1739 if (! find_duplicate (xtra,
1740 (const char **) uri->data.ksk.keywords,
1741 uri->data.ksk.keywordCount))
1742 {
1743 insert_non_mandatory_keyword (xtra,
1744 uri->data.ksk.keywords,
1745 uri->data.ksk.keywordCount);
1746 uri->data.ksk.keywordCount++;
1747 }
1748 GNUNET_free (xtra);
1749 }
1750
1751 normalized_data = normalize_metadata (format, data, data_len);
1752 if (! find_duplicate (data,
1753 (const char **) uri->data.ksk.keywords,
1754 uri->data.ksk.keywordCount))
1755 {
1756 insert_non_mandatory_keyword (data,
1757 uri->data.ksk.keywords,
1758 uri->data.ksk.keywordCount);
1759 uri->data.ksk.keywordCount++;
1760 }
1761 if (NULL != normalized_data)
1762 {
1763 if (! find_duplicate (normalized_data,
1764 (const char **) uri->data.ksk.keywords,
1765 uri->data.ksk.keywordCount))
1766 {
1767 insert_non_mandatory_keyword (normalized_data,
1768 uri->data.ksk.keywords,
1769 uri->data.ksk.keywordCount);
1770 uri->data.ksk.keywordCount++;
1771 }
1772 GNUNET_free (normalized_data);
1773 }
1774 return 0;
1775}
1776
1777
1778/**
1779 * Construct a keyword-URI from meta-data (take all entries
1780 * in the meta-data and construct one large keyword URI
1781 * that lists all keywords that can be found in the meta-data).
1782 *
1783 * @param md metadata to use
1784 * @return NULL on error, otherwise a KSK URI
1785 */
1786struct GNUNET_FS_Uri *
1787GNUNET_FS_uri_ksk_create_from_meta_data (
1788 const struct GNUNET_CONTAINER_MetaData *md)
1789{
1790 struct GNUNET_FS_Uri *ret;
1791 char *filename;
1792 char *full_name = NULL;
1793 char *ss;
1794 int ent;
1795 int tok_keywords = 0;
1796 int paren_keywords = 0;
1797
1798 if (NULL == md)
1799 return NULL;
1800 ret = GNUNET_new (struct GNUNET_FS_Uri);
1801 ret->type = GNUNET_FS_URI_KSK;
1802 ent = GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL);
1803 if (ent > 0)
1804 {
1805 full_name = GNUNET_CONTAINER_meta_data_get_first_by_types (
1806 md,
1807 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
1808 -1);
1809 if (NULL != full_name)
1810 {
1811 filename = full_name;
1812 while (NULL != (ss = strstr (filename, DIR_SEPARATOR_STR)))
1813 filename = ss + 1;
1814 tok_keywords = get_keywords_from_tokens (filename, NULL, 0);
1815 paren_keywords = get_keywords_from_parens (filename, NULL, 0);
1816 }
1817 /* x3 because there might be a normalized variant of every keyword,
1818 plus theoretically one more for mime... */
1819 ret->data.ksk.keywords =
1820 GNUNET_new_array ((ent + tok_keywords + paren_keywords) * 3, char *);
1821 GNUNET_CONTAINER_meta_data_iterate (md, &gather_uri_data, ret);
1822 }
1823 if (tok_keywords > 0)
1824 ret->data.ksk.keywordCount +=
1825 get_keywords_from_tokens (filename,
1826 ret->data.ksk.keywords,
1827 ret->data.ksk.keywordCount);
1828 if (paren_keywords > 0)
1829 ret->data.ksk.keywordCount +=
1830 get_keywords_from_parens (filename,
1831 ret->data.ksk.keywords,
1832 ret->data.ksk.keywordCount);
1833 if (ent > 0)
1834 GNUNET_free (full_name);
1835 return ret;
1836}
1837
1838
1839/**
1840 * In URI-encoding, does the given character
1841 * need to be encoded using %-encoding?
1842 */
1843static int
1844needs_percent (char c)
1845{
1846 return(! ((isalnum ((unsigned char) c)) || (c == '-') || (c == '_') ||
1847 (c == '.') || (c == '~')));
1848}
1849
1850
1851/**
1852 * Convert a KSK URI to a string.
1853 *
1854 * @param uri the URI to convert
1855 * @return NULL on error (i.e. keywordCount == 0)
1856 */
1857static char *
1858uri_ksk_to_string (const struct GNUNET_FS_Uri *uri)
1859{
1860 char **keywords;
1861 unsigned int keywordCount;
1862 size_t n;
1863 char *ret;
1864 unsigned int i;
1865 unsigned int j;
1866 unsigned int wpos;
1867 size_t slen;
1868 const char *keyword;
1869
1870 if (uri->type != GNUNET_FS_URI_KSK)
1871 return NULL;
1872 keywords = uri->data.ksk.keywords;
1873 keywordCount = uri->data.ksk.keywordCount;
1874 n = keywordCount + strlen (GNUNET_FS_URI_PREFIX)
1875 + strlen (GNUNET_FS_URI_KSK_INFIX) + 1;
1876 for (i = 0; i < keywordCount; i++)
1877 {
1878 keyword = keywords[i];
1879 slen = strlen (keyword);
1880 n += slen;
1881 for (j = 0; j < slen; j++)
1882 {
1883 if ((j == 0) && (keyword[j] == ' '))
1884 {
1885 n--;
1886 continue; /* skip leading space */
1887 }
1888 if (needs_percent (keyword[j]))
1889 n += 2; /* will use %-encoding */
1890 }
1891 }
1892 ret = GNUNET_malloc (n);
1893 strcpy (ret, GNUNET_FS_URI_PREFIX);
1894 strcat (ret, GNUNET_FS_URI_KSK_INFIX);
1895 wpos = strlen (ret);
1896 for (i = 0; i < keywordCount; i++)
1897 {
1898 keyword = keywords[i];
1899 slen = strlen (keyword);
1900 for (j = 0; j < slen; j++)
1901 {
1902 if ((j == 0) && (keyword[j] == ' '))
1903 continue; /* skip leading space */
1904 if (needs_percent (keyword[j]))
1905 {
1906 sprintf (&ret[wpos], "%%%02X", (unsigned char) keyword[j]);
1907 wpos += 3;
1908 }
1909 else
1910 {
1911 ret[wpos++] = keyword[j];
1912 }
1913 }
1914 if (i != keywordCount - 1)
1915 ret[wpos++] = '+';
1916 }
1917 return ret;
1918}
1919
1920
1921/**
1922 * Convert SKS URI to a string.
1923 *
1924 * @param uri sks uri to convert
1925 * @return NULL on error
1926 */
1927static char *
1928uri_sks_to_string (const struct GNUNET_FS_Uri *uri)
1929{
1930 char *ret;
1931 char buf[1024];
1932
1933 if (GNUNET_FS_URI_SKS != uri->type)
1934 return NULL;
1935 ret =
1936 GNUNET_STRINGS_data_to_string (&uri->data.sks.ns,
1937 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
1938 buf,
1939 sizeof(buf));
1940 GNUNET_assert (NULL != ret);
1941 ret[0] = '\0';
1942 GNUNET_asprintf (&ret,
1943 "%s%s%s/%s",
1944 GNUNET_FS_URI_PREFIX,
1945 GNUNET_FS_URI_SKS_INFIX,
1946 buf,
1947 uri->data.sks.identifier);
1948 return ret;
1949}
1950
1951
1952/**
1953 * Convert a CHK URI to a string.
1954 *
1955 * @param uri chk uri to convert
1956 * @return NULL on error
1957 */
1958static char *
1959uri_chk_to_string (const struct GNUNET_FS_Uri *uri)
1960{
1961 const struct FileIdentifier *fi;
1962 char *ret;
1963 struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1964 struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1965
1966 if (uri->type != GNUNET_FS_URI_CHK)
1967 return NULL;
1968 fi = &uri->data.chk;
1969 GNUNET_CRYPTO_hash_to_enc (&fi->chk.key, &keyhash);
1970 GNUNET_CRYPTO_hash_to_enc (&fi->chk.query, &queryhash);
1971
1972 GNUNET_asprintf (&ret,
1973 "%s%s%s.%s.%llu",
1974 GNUNET_FS_URI_PREFIX,
1975 GNUNET_FS_URI_CHK_INFIX,
1976 (const char *) &keyhash,
1977 (const char *) &queryhash,
1978 (unsigned long long) GNUNET_ntohll (fi->file_length));
1979 return ret;
1980}
1981
1982
1983/**
1984 * Convert a LOC URI to a string.
1985 *
1986 * @param uri loc uri to convert
1987 * @return NULL on error
1988 */
1989static char *
1990uri_loc_to_string (const struct GNUNET_FS_Uri *uri)
1991{
1992 char *ret;
1993 struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1994 struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1995 char *peer_id;
1996 char peer_sig[SIGNATURE_ASCII_LENGTH + 1];
1997
1998 GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.key, &keyhash);
1999 GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.query, &queryhash);
2000 peer_id =
2001 GNUNET_CRYPTO_eddsa_public_key_to_string (&uri->data.loc.peer.public_key);
2002 GNUNET_assert (
2003 NULL !=
2004 GNUNET_STRINGS_data_to_string (&uri->data.loc.contentSignature,
2005 sizeof(struct GNUNET_CRYPTO_EddsaSignature),
2006 peer_sig,
2007 sizeof(peer_sig)));
2008 GNUNET_asprintf (&ret,
2009 "%s%s%s.%s.%llu.%s.%s.%llu",
2010 GNUNET_FS_URI_PREFIX,
2011 GNUNET_FS_URI_LOC_INFIX,
2012 (const char *) &keyhash,
2013 (const char *) &queryhash,
2014 (unsigned long long) GNUNET_ntohll (
2015 uri->data.loc.fi.file_length),
2016 peer_id,
2017 peer_sig,
2018 (unsigned long long)
2019 uri->data.loc.expirationTime.abs_value_us
2020 / 1000000LL);
2021 GNUNET_free (peer_id);
2022 return ret;
2023}
2024
2025
2026/**
2027 * Convert a URI to a UTF-8 String.
2028 *
2029 * @param uri uri to convert to a string
2030 * @return the UTF-8 string
2031 */
2032char *
2033GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri)
2034{
2035 if (uri == NULL)
2036 {
2037 GNUNET_break (0);
2038 return NULL;
2039 }
2040 switch (uri->type)
2041 {
2042 case GNUNET_FS_URI_KSK:
2043 return uri_ksk_to_string (uri);
2044
2045 case GNUNET_FS_URI_SKS:
2046 return uri_sks_to_string (uri);
2047
2048 case GNUNET_FS_URI_CHK:
2049 return uri_chk_to_string (uri);
2050
2051 case GNUNET_FS_URI_LOC:
2052 return uri_loc_to_string (uri);
2053
2054 default:
2055 GNUNET_break (0);
2056 return NULL;
2057 }
2058}
2059
2060
2061/* end of fs_uri.c */
diff --git a/src/fs/gnunet-auto-share.c b/src/fs/gnunet-auto-share.c
deleted file mode 100644
index f91e9d00d..000000000
--- a/src/fs/gnunet-auto-share.c
+++ /dev/null
@@ -1,791 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001--2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-auto-share.c
22 * @brief automatically publish files on GNUnet
23 * @author Christian Grothoff
24 *
25 * TODO:
26 * - support loading meta data / keywords from resource file
27 * - add stability timer (a la buildbot)
28 */
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32#define MAX_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
33
34#define MIN_DELAY GNUNET_TIME_UNIT_MINUTES
35
36
37/**
38 * Item in our work queue (or in the set of files/directories
39 * we have successfully published).
40 */
41struct WorkItem
42{
43 /**
44 * PENDING Work is kept in a linked list.
45 */
46 struct WorkItem *prev;
47
48 /**
49 * PENDING Work is kept in a linked list.
50 */
51 struct WorkItem *next;
52
53 /**
54 * Filename of the work item.
55 */
56 char *filename;
57
58 /**
59 * Unique identity for this work item (used to detect
60 * if we need to do the work again).
61 */
62 struct GNUNET_HashCode id;
63};
64
65
66/**
67 * Global return value from 'main'.
68 */
69static int ret;
70
71/**
72 * Are we running 'verbosely'?
73 */
74static unsigned int verbose;
75
76/**
77 * Configuration to use.
78 */
79static const struct GNUNET_CONFIGURATION_Handle *cfg;
80
81/**
82 * Name of the configuration file.
83 */
84static char *cfg_filename;
85
86/**
87 * Disable extractor option to use for publishing.
88 */
89static int disable_extractor;
90
91/**
92 * Disable creation time option to use for publishing.
93 */
94static int do_disable_creation_time;
95
96/**
97 * Handle for the main task that does scanning and working.
98 */
99static struct GNUNET_SCHEDULER_Task *run_task;
100
101/**
102 * Anonymity level option to use for publishing.
103 */
104static unsigned int anonymity_level = 1;
105
106/**
107 * Content priority option to use for publishing.
108 */
109static unsigned int content_priority = 365;
110
111/**
112 * Replication level option to use for publishing.
113 */
114static unsigned int replication_level = 1;
115
116/**
117 * Top-level directory we monitor to auto-publish.
118 */
119static const char *dir_name;
120
121/**
122 * Head of linked list of files still to publish.
123 */
124static struct WorkItem *work_head;
125
126/**
127 * Tail of linked list of files still to publish.
128 */
129static struct WorkItem *work_tail;
130
131/**
132 * Map from the hash of the filename (!) to a `struct WorkItem`
133 * that was finished.
134 */
135static struct GNUNET_CONTAINER_MultiHashMap *work_finished;
136
137/**
138 * Set to #GNUNET_YES if we are shutting down.
139 */
140static int do_shutdown;
141
142/**
143 * Start time of the current round; used to determine how long
144 * one iteration takes (which influences how fast we schedule
145 * the next one).
146 */
147static struct GNUNET_TIME_Absolute start_time;
148
149/**
150 * Pipe used to communicate 'gnunet-publish' completion (SIGCHLD) via signal.
151 */
152static struct GNUNET_DISK_PipeHandle *sigpipe;
153
154/**
155 * Handle to the 'gnunet-publish' process that we executed.
156 */
157static struct GNUNET_OS_Process *publish_proc;
158
159
160/**
161 * Compute the name of the state database file we will use.
162 */
163static char *
164get_state_file ()
165{
166 char *ret;
167
168 GNUNET_asprintf (&ret,
169 "%s%s.auto-share",
170 dir_name,
171 (DIR_SEPARATOR == dir_name[strlen (dir_name) - 1])
172 ? ""
173 : DIR_SEPARATOR_STR);
174 return ret;
175}
176
177
178/**
179 * Load the set of #work_finished items from disk.
180 */
181static void
182load_state ()
183{
184 char *fn;
185 struct GNUNET_BIO_ReadHandle *rh;
186 uint32_t n;
187 struct GNUNET_HashCode id;
188 struct WorkItem *wi;
189 char *emsg;
190
191 emsg = NULL;
192 fn = get_state_file ();
193 rh = GNUNET_BIO_read_open_file (fn);
194 GNUNET_free (fn);
195 if (NULL == rh)
196 return;
197 fn = NULL;
198 if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "number of files",
199 (int32_t *) &n))
200 goto error;
201 while (n-- > 0)
202 {
203 struct GNUNET_BIO_ReadSpec rs[] = {
204 GNUNET_BIO_read_spec_string ("filename", &fn, 1024),
205 GNUNET_BIO_read_spec_object ("id", &id, sizeof(struct GNUNET_HashCode)),
206 GNUNET_BIO_read_spec_end (),
207 };
208 if (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs))
209 goto error;
210 wi = GNUNET_new (struct WorkItem);
211 wi->id = id;
212 wi->filename = fn;
213 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
214 "Loaded serialization ID for `%s' is `%s'\n",
215 wi->filename,
216 GNUNET_h2s (&id));
217 fn = NULL;
218 GNUNET_CRYPTO_hash (wi->filename, strlen (wi->filename), &id);
219 GNUNET_break (GNUNET_OK ==
220 GNUNET_CONTAINER_multihashmap_put (
221 work_finished,
222 &id,
223 wi,
224 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
225 }
226 if (GNUNET_OK == GNUNET_BIO_read_close (rh, &emsg))
227 return;
228 rh = NULL;
229error:
230 GNUNET_free (fn);
231 if (NULL != rh)
232 (void) GNUNET_BIO_read_close (rh, &emsg);
233 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
234 _ ("Failed to load state: %s\n"),
235 emsg);
236 GNUNET_free (emsg);
237}
238
239
240/**
241 * Write work item from the #work_finished map to the given write handle.
242 *
243 * @param cls the `struct GNUNET_BIO_WriteHandle *`
244 * @param key key of the item in the map (unused)
245 * @param value the `struct WorkItem` to write
246 * @return #GNUNET_OK to continue to iterate (if write worked)
247 */
248static int
249write_item (void *cls, const struct GNUNET_HashCode *key, void *value)
250{
251 struct GNUNET_BIO_WriteHandle *wh = cls;
252 struct WorkItem *wi = value;
253
254 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
255 "Saving serialization ID of file `%s' with value `%s'\n",
256 wi->filename,
257 GNUNET_h2s (&wi->id));
258 struct GNUNET_BIO_WriteSpec ws[] = {
259 GNUNET_BIO_write_spec_string ("auto-share-write-item-filename",
260 wi->filename),
261 GNUNET_BIO_write_spec_object ("id", &wi->id, sizeof(struct
262 GNUNET_HashCode)),
263 GNUNET_BIO_write_spec_end (),
264 };
265 if (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))
266 return GNUNET_SYSERR; /* write error, abort iteration */
267 return GNUNET_OK;
268}
269
270
271/**
272 * Save the set of #work_finished items on disk.
273 */
274static void
275save_state ()
276{
277 uint32_t n;
278 struct GNUNET_BIO_WriteHandle *wh;
279 char *fn;
280
281 n = GNUNET_CONTAINER_multihashmap_size (work_finished);
282 fn = get_state_file ();
283 wh = GNUNET_BIO_write_open_file (fn);
284 if (NULL == wh)
285 {
286 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
287 _ ("Failed to save state to file %s\n"),
288 fn);
289 GNUNET_free (fn);
290 return;
291 }
292 if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "size of state", n))
293 {
294 (void) GNUNET_BIO_write_close (wh, NULL);
295 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
296 _ ("Failed to save state to file %s\n"),
297 fn);
298 GNUNET_free (fn);
299 return;
300 }
301 (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished, &write_item, wh);
302 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
303 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
304 _ ("Failed to save state to file %s\n"),
305 fn);
306 GNUNET_free (fn);
307}
308
309
310/**
311 * Task run on shutdown. Serializes our current state to disk.
312 *
313 * @param cls closure, unused
314 */
315static void
316do_stop_task (void *cls)
317{
318 do_shutdown = GNUNET_YES;
319 if (NULL != publish_proc)
320 {
321 GNUNET_OS_process_kill (publish_proc, SIGKILL);
322 return;
323 }
324 if (NULL != run_task)
325 {
326 GNUNET_SCHEDULER_cancel (run_task);
327 run_task = NULL;
328 }
329}
330
331
332/**
333 * Decide what the next task is (working or scanning) and schedule it.
334 */
335static void
336schedule_next_task (void);
337
338
339/**
340 * Task triggered whenever we receive a SIGCHLD (child
341 * process died).
342 *
343 * @param cls the `struct WorkItem` we were working on
344 */
345static void
346maint_child_death (void *cls)
347{
348 struct WorkItem *wi = cls;
349 struct GNUNET_HashCode key;
350 enum GNUNET_OS_ProcessStatusType type;
351 unsigned long code;
352 int ret;
353 char c;
354 const struct GNUNET_DISK_FileHandle *pr;
355 const struct GNUNET_SCHEDULER_TaskContext *tc;
356
357 run_task = NULL;
358 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
359 tc = GNUNET_SCHEDULER_get_task_context ();
360 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
361 {
362 /* shutdown scheduled us, someone else will kill child,
363 we should just try again */
364 run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
365 pr,
366 &maint_child_death,
367 wi);
368 return;
369 }
370 /* consume the signal */
371 GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof(c)));
372
373 ret = GNUNET_OS_process_status (publish_proc, &type, &code);
374 GNUNET_assert (GNUNET_SYSERR != ret);
375 if (GNUNET_NO == ret)
376 {
377 /* process still running? Then where did the SIGCHLD come from?
378 Well, let's declare it spurious (kernel bug?) and keep rolling.
379 */
380 GNUNET_break (0);
381 run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
382 pr,
383 &maint_child_death,
384 wi);
385 return;
386 }
387 GNUNET_assert (GNUNET_OK == ret);
388
389 GNUNET_OS_process_destroy (publish_proc);
390 publish_proc = NULL;
391
392 if (GNUNET_YES == do_shutdown)
393 {
394 GNUNET_free (wi->filename);
395 GNUNET_free (wi);
396 return;
397 }
398 if ((GNUNET_OS_PROCESS_EXITED == type) && (0 == code))
399 {
400 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
401 _ ("Publication of `%s' done\n"),
402 wi->filename);
403 GNUNET_CRYPTO_hash (wi->filename, strlen (wi->filename), &key);
404 GNUNET_break (GNUNET_OK ==
405 GNUNET_CONTAINER_multihashmap_put (
406 work_finished,
407 &key,
408 wi,
409 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
410 }
411 else
412 {
413 GNUNET_CONTAINER_DLL_insert_tail (work_head, work_tail, wi);
414 }
415 save_state ();
416 schedule_next_task ();
417}
418
419
420/**
421 * Signal handler called for SIGCHLD. Triggers the
422 * respective handler by writing to the trigger pipe.
423 */
424static void
425sighandler_child_death ()
426{
427 static char c;
428 int old_errno = errno; /* back-up errno */
429
430 GNUNET_break (
431 1 ==
432 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe,
433 GNUNET_DISK_PIPE_END_WRITE),
434 &c,
435 sizeof(c)));
436 errno = old_errno; /* restore errno */
437}
438
439
440/**
441 * Function called to process work items.
442 *
443 * @param cls closure, NULL
444 */
445static void
446work (void *cls)
447{
448 static char *argv[14];
449 static char anon_level[20];
450 static char content_prio[20];
451 static char repl_level[20];
452 struct WorkItem *wi;
453 const struct GNUNET_DISK_FileHandle *pr;
454 int argc;
455
456 run_task = NULL;
457 wi = work_head;
458 GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi);
459 argc = 0;
460 argv[argc++] = "gnunet-publish";
461 if (verbose)
462 argv[argc++] = "-V";
463 if (disable_extractor)
464 argv[argc++] = "-D";
465 if (do_disable_creation_time)
466 argv[argc++] = "-d";
467 argv[argc++] = "-c";
468 argv[argc++] = cfg_filename;
469 GNUNET_snprintf (anon_level, sizeof(anon_level), "%u", anonymity_level);
470 argv[argc++] = "-a";
471 argv[argc++] = anon_level;
472 GNUNET_snprintf (content_prio, sizeof(content_prio), "%u", content_priority);
473 argv[argc++] = "-p";
474 argv[argc++] = content_prio;
475 GNUNET_snprintf (repl_level, sizeof(repl_level), "%u", replication_level);
476 argv[argc++] = "-r";
477 argv[argc++] = repl_level;
478 argv[argc++] = wi->filename;
479 argv[argc] = NULL;
480 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Publishing `%s'\n"), wi->filename);
481 GNUNET_assert (NULL == publish_proc);
482 publish_proc = GNUNET_OS_start_process_vap (GNUNET_OS_USE_PIPE_CONTROL,
483 NULL,
484 NULL,
485 NULL,
486 "gnunet-publish",
487 argv);
488 if (NULL == publish_proc)
489 {
490 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
491 _ ("Failed to run `%s'\n"),
492 "gnunet-publish");
493 GNUNET_CONTAINER_DLL_insert (work_head, work_tail, wi);
494 run_task =
495 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, &work, NULL);
496 return;
497 }
498 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
499 run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
500 pr,
501 &maint_child_death,
502 wi);
503}
504
505
506/**
507 * Recursively scan the given file/directory structure to determine
508 * a unique ID that represents the current state of the hierarchy.
509 *
510 * @param cls where to store the unique ID we are computing
511 * @param filename file to scan
512 * @return #GNUNET_OK (always)
513 */
514static int
515determine_id (void *cls, const char *filename)
516{
517 struct GNUNET_HashCode *id = cls;
518 struct stat sbuf;
519 struct GNUNET_HashCode fx[2];
520 struct GNUNET_HashCode ft;
521
522 if (0 != stat (filename, &sbuf))
523 {
524 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
525 return GNUNET_OK;
526 }
527 GNUNET_CRYPTO_hash (filename, strlen (filename), &fx[0]);
528 if (! S_ISDIR (sbuf.st_mode))
529 {
530 uint64_t fattr[2];
531
532 fattr[0] = GNUNET_htonll (sbuf.st_size);
533 fattr[0] = GNUNET_htonll (sbuf.st_mtime);
534
535 GNUNET_CRYPTO_hash (fattr, sizeof(fattr), &fx[1]);
536 }
537 else
538 {
539 memset (&fx[1], 1, sizeof(struct GNUNET_HashCode));
540 GNUNET_DISK_directory_scan (filename, &determine_id, &fx[1]);
541 }
542 /* use hash here to make hierarchical structure distinct from
543 all files on the same level */
544 GNUNET_CRYPTO_hash (fx, sizeof(fx), &ft);
545 /* use XOR here so that order of the files in the directory
546 does not matter! */
547 GNUNET_CRYPTO_hash_xor (&ft, id, id);
548 return GNUNET_OK;
549}
550
551
552/**
553 * Function called with a filename (or directory name) to publish
554 * (if it has changed since the last time we published it). This function
555 * is called for the top-level files only.
556 *
557 * @param cls closure, NULL
558 * @param filename complete filename (absolute path)
559 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR during shutdown
560 */
561static int
562add_file (void *cls, const char *filename)
563{
564 struct WorkItem *wi;
565 struct GNUNET_HashCode key;
566 struct GNUNET_HashCode id;
567
568 if (GNUNET_YES == do_shutdown)
569 return GNUNET_SYSERR;
570 if ((NULL != strstr (filename, "/.auto-share")) ||
571 (NULL != strstr (filename, "\\.auto-share")))
572 return GNUNET_OK; /* skip internal file */
573 GNUNET_CRYPTO_hash (filename, strlen (filename), &key);
574 wi = GNUNET_CONTAINER_multihashmap_get (work_finished, &key);
575 memset (&id, 0, sizeof(struct GNUNET_HashCode));
576 determine_id (&id, filename);
577 if (NULL != wi)
578 {
579 if (0 == memcmp (&id, &wi->id, sizeof(struct GNUNET_HashCode)))
580 return GNUNET_OK; /* skip: we did this one already */
581 /* contents changed, need to re-do the directory... */
582 GNUNET_assert (
583 GNUNET_YES ==
584 GNUNET_CONTAINER_multihashmap_remove (work_finished, &key, wi));
585 }
586 else
587 {
588 wi = GNUNET_new (struct WorkItem);
589 wi->filename = GNUNET_strdup (filename);
590 }
591 wi->id = id;
592 GNUNET_CONTAINER_DLL_insert (work_head, work_tail, wi);
593 if (GNUNET_YES == do_shutdown)
594 return GNUNET_SYSERR;
595 return GNUNET_OK;
596}
597
598
599/**
600 * Periodically run task to update our view of the directory to share.
601 *
602 * @param cls NULL
603 */
604static void
605scan (void *cls)
606{
607 run_task = NULL;
608 start_time = GNUNET_TIME_absolute_get ();
609 (void) GNUNET_DISK_directory_scan (dir_name, &add_file, NULL);
610 schedule_next_task ();
611}
612
613
614/**
615 * Decide what the next task is (working or scanning) and schedule it.
616 */
617static void
618schedule_next_task ()
619{
620 struct GNUNET_TIME_Relative delay;
621
622 if (GNUNET_YES == do_shutdown)
623 return;
624 GNUNET_assert (NULL == run_task);
625 if (NULL == work_head)
626 {
627 /* delay by at most 4h, at least 1s, and otherwise in between depending
628 on how long it took to scan */
629 delay = GNUNET_TIME_absolute_get_duration (start_time);
630 delay = GNUNET_TIME_relative_saturating_multiply (delay, 100);
631 delay = GNUNET_TIME_relative_min (delay, MAX_DELAY);
632 delay = GNUNET_TIME_relative_max (delay, MIN_DELAY);
633 run_task = GNUNET_SCHEDULER_add_delayed (delay, &scan, NULL);
634 }
635 else
636 {
637 run_task = GNUNET_SCHEDULER_add_now (&work, NULL);
638 }
639}
640
641
642/**
643 * Main function that will be run by the scheduler.
644 *
645 * @param cls closure
646 * @param args remaining command-line arguments
647 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
648 * @param c configuration
649 */
650static void
651run (void *cls,
652 char *const *args,
653 const char *cfgfile,
654 const struct GNUNET_CONFIGURATION_Handle *c)
655{
656 /* check arguments */
657 if ((NULL == args[0]) || (NULL != args[1]) ||
658 (GNUNET_YES != GNUNET_DISK_directory_test (args[0], GNUNET_YES)))
659 {
660 printf (_ (
661 "You must specify one and only one directory name for automatic publication.\n"));
662 ret = -1;
663 return;
664 }
665 cfg_filename = GNUNET_strdup (cfgfile);
666 cfg = c;
667 dir_name = args[0];
668 work_finished = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO);
669 load_state ();
670 run_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
671 &scan,
672 NULL);
673 GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL);
674}
675
676
677/**
678 * Free memory associated with the work item from the work_finished map.
679 *
680 * @param cls NULL (unused)
681 * @param key key of the item in the map (unused)
682 * @param value the `struct WorkItem` to free
683 * @return #GNUNET_OK to continue to iterate
684 */
685static int
686free_item (void *cls, const struct GNUNET_HashCode *key, void *value)
687{
688 struct WorkItem *wi = value;
689
690 GNUNET_free (wi->filename);
691 GNUNET_free (wi);
692 return GNUNET_OK;
693}
694
695
696/**
697 * The main function to automatically publish content to GNUnet.
698 *
699 * @param argc number of arguments from the command line
700 * @param argv command line arguments
701 * @return 0 ok, 1 on error
702 */
703int
704main (int argc, char *const *argv)
705{
706 struct GNUNET_GETOPT_CommandLineOption options[] = {
707 GNUNET_GETOPT_option_uint ('a',
708 "anonymity",
709 "LEVEL",
710 gettext_noop (
711 "set the desired LEVEL of sender-anonymity"),
712 &anonymity_level),
713
714 GNUNET_GETOPT_option_flag (
715 'd',
716 "disable-creation-time",
717 gettext_noop (
718 "disable adding the creation time to the metadata of the uploaded file"),
719 &do_disable_creation_time),
720
721 GNUNET_GETOPT_option_flag (
722 'D',
723 "disable-extractor",
724 gettext_noop ("do not use libextractor to add keywords or metadata"),
725 &disable_extractor),
726
727 GNUNET_GETOPT_option_uint ('p',
728 "priority",
729 "PRIORITY",
730 gettext_noop (
731 "specify the priority of the content"),
732 &content_priority),
733
734 GNUNET_GETOPT_option_uint ('r',
735 "replication",
736 "LEVEL",
737 gettext_noop (
738 "set the desired replication LEVEL"),
739 &replication_level),
740
741 GNUNET_GETOPT_option_verbose (&verbose),
742
743 GNUNET_GETOPT_OPTION_END
744 };
745 struct WorkItem *wi;
746 int ok;
747 struct GNUNET_SIGNAL_Context *shc_chld;
748
749 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
750 return 2;
751 sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
752 GNUNET_assert (NULL != sigpipe);
753 shc_chld =
754 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
755 ok =
756 (GNUNET_OK ==
757 GNUNET_PROGRAM_run (
758 argc,
759 argv,
760 "gnunet-auto-share [OPTIONS] FILENAME",
761 gettext_noop ("Automatically publish files from a directory on GNUnet"),
762 options,
763 &run,
764 NULL))
765 ? ret
766 : 1;
767 if (NULL != work_finished)
768 {
769 (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished,
770 &free_item,
771 NULL);
772 GNUNET_CONTAINER_multihashmap_destroy (work_finished);
773 }
774 while (NULL != (wi = work_head))
775 {
776 GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi);
777 GNUNET_free (wi->filename);
778 GNUNET_free (wi);
779 }
780 GNUNET_SIGNAL_handler_uninstall (shc_chld);
781 shc_chld = NULL;
782 GNUNET_DISK_pipe_close (sigpipe);
783 sigpipe = NULL;
784 GNUNET_free (cfg_filename);
785 cfg_filename = NULL;
786 GNUNET_free_nz ((void *) argv);
787 return ok;
788}
789
790
791/* end of gnunet-auto-share.c */
diff --git a/src/fs/gnunet-daemon-fsprofiler.c b/src/fs/gnunet-daemon-fsprofiler.c
deleted file mode 100644
index fbb7c6028..000000000
--- a/src/fs/gnunet-daemon-fsprofiler.c
+++ /dev/null
@@ -1,672 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 Christian Grothoff
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-daemon-fsprofiler.c
23 * @brief daemon that publishes and downloads (random) files
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - how to signal driver that we're done?
28 */
29#include "platform.h"
30#include "gnunet_fs_service.h"
31#include "gnunet_statistics_service.h"
32
33/**
34 * We use 'patterns' of the form (x,y,t) to specify desired download/publish
35 * activities of a peer. They are stored in a DLL.
36 */
37struct Pattern
38{
39 /**
40 * Kept in a DLL.
41 */
42 struct Pattern *next;
43
44 /**
45 * Kept in a DLL.
46 */
47 struct Pattern *prev;
48
49 /**
50 * Execution context for the pattern (FS-handle to the operation).
51 */
52 void *ctx;
53
54 /**
55 * Secondary execution context for the pattern (FS-handle to the operation).
56 */
57 void *sctx;
58
59 /**
60 * When did the operation start?
61 */
62 struct GNUNET_TIME_Absolute start_time;
63
64 /**
65 * With how much delay should this operation be started?
66 */
67 struct GNUNET_TIME_Relative delay;
68
69 /**
70 * Task to run the operation.
71 */
72 struct GNUNET_SCHEDULER_Task *task;
73
74 /**
75 * Secondary task to run the operation.
76 */
77 struct GNUNET_SCHEDULER_Task *stask;
78
79 /**
80 * X-value.
81 */
82 unsigned long long x;
83
84 /**
85 * Y-value.
86 */
87 unsigned long long y;
88};
89
90
91/**
92 * Return value from 'main'.
93 */
94static int global_ret;
95
96/**
97 * Configuration we use.
98 */
99static const struct GNUNET_CONFIGURATION_Handle *cfg;
100
101/**
102 * Handle to the statistics service.
103 */
104static struct GNUNET_STATISTICS_Handle *stats_handle;
105
106/**
107 * Peer's FS handle.
108 */
109static struct GNUNET_FS_Handle *fs_handle;
110
111/**
112 * Unique number for this peer in the testbed.
113 */
114static unsigned long long my_peerid;
115
116/**
117 * Desired anonymity level.
118 */
119static unsigned long long anonymity_level;
120
121/**
122 * Desired replication level.
123 */
124static unsigned long long replication_level;
125
126/**
127 * String describing which publishing operations this peer should
128 * perform. The format is "(SIZE,SEED,TIME)*", for example:
129 * "(1,5,0)(7,3,13)" means to publish a file with 1 byte and
130 * seed/keyword 5 immediately and another file with 7 bytes and
131 * seed/keyword 3 after 13 ms.
132 */
133static char *publish_pattern;
134
135/**
136 * Head of the DLL of publish patterns.
137 */
138static struct Pattern *publish_head;
139
140/**
141 * Tail of the DLL of publish patterns.
142 */
143static struct Pattern *publish_tail;
144
145/**
146 * String describing which download operations this peer should
147 * perform. The format is "(KEYWORD,SIZE,DELAY)*"; for example,
148 * "(1,7,3)(3,8,8)" means to download one file of 7 bytes under
149 * keyword "1" starting the search after 3 ms; and another one of 8
150 * bytes under keyword '3' starting after 8 ms. The file size is
151 * used to determine which search result(s) should be used or ignored.
152 */
153static char *download_pattern;
154
155/**
156 * Head of the DLL of publish patterns.
157 */
158static struct Pattern *download_head;
159
160/**
161 * Tail of the DLL of publish patterns.
162 */
163static struct Pattern *download_tail;
164
165
166/**
167 * Parse a pattern string and store the corresponding
168 * 'struct Pattern' in the given head/tail.
169 *
170 * @param head where to store the head
171 * @param tail where to store the tail
172 * @param pattern pattern to parse
173 * @return GNUNET_OK on success
174 */
175static int
176parse_pattern (struct Pattern **head,
177 struct Pattern **tail,
178 const char *pattern)
179{
180 struct Pattern *p;
181 unsigned long long x;
182 unsigned long long y;
183 unsigned long long t;
184
185 while (3 == sscanf (pattern,
186 "(%llu,%llu,%llu)",
187 &x, &y, &t))
188 {
189 p = GNUNET_new (struct Pattern);
190 p->x = x;
191 p->y = y;
192 p->delay.rel_value_us = (uint64_t) t;
193 GNUNET_CONTAINER_DLL_insert (*head, *tail, p);
194 pattern = strstr (pattern, ")");
195 GNUNET_assert (NULL != pattern);
196 pattern++;
197 }
198 return (0 == strlen (pattern)) ? GNUNET_OK : GNUNET_SYSERR;
199}
200
201
202/**
203 * Create a KSK URI from a number.
204 *
205 * @param kval the number
206 * @return corresponding KSK URI
207 */
208static struct GNUNET_FS_Uri *
209make_keywords (uint64_t kval)
210{
211 char kw[128];
212
213 GNUNET_snprintf (kw, sizeof(kw),
214 "%llu", (unsigned long long) kval);
215 return GNUNET_FS_uri_ksk_create (kw, NULL);
216}
217
218
219/**
220 * Create a file of the given length with a deterministic amount
221 * of data to be published under keyword 'kval'.
222 *
223 * @param length number of bytes in the file
224 * @param kval keyword value and seed for the data of the file
225 * @param ctx context to pass to 'fi'
226 * @return file information handle for the file
227 */
228static struct GNUNET_FS_FileInformation *
229make_file (uint64_t length,
230 uint64_t kval,
231 void *ctx)
232{
233 struct GNUNET_FS_FileInformation *fi;
234 struct GNUNET_FS_BlockOptions bo;
235 char *data;
236 struct GNUNET_FS_Uri *keywords;
237 unsigned long long i;
238 uint64_t xor;
239
240 data = NULL; /* to make compilers happy */
241 if ((0 != length) &&
242 (NULL == (data = GNUNET_malloc_large ((size_t) length))))
243 return NULL;
244 /* initialize data with 'unique' data only depending on 'kval' and 'size',
245 making sure that blocks do not repeat */
246 for (i = 0; i < length; i += 8)
247 {
248 xor = length ^ kval ^ (uint64_t) (i / 32 / 1024);
249 GNUNET_memcpy (&data[i], &xor, GNUNET_MIN (length - i, sizeof(uint64_t)));
250 }
251 bo.expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
252 bo.anonymity_level = (uint32_t) anonymity_level;
253 bo.content_priority = 128;
254 bo.replication_level = (uint32_t) replication_level;
255 keywords = make_keywords (kval);
256 fi = GNUNET_FS_file_information_create_from_data (fs_handle,
257 ctx,
258 length,
259 data, keywords,
260 NULL, GNUNET_NO, &bo);
261 GNUNET_FS_uri_destroy (keywords);
262 return fi;
263}
264
265
266/**
267 * Task run during shutdown.
268 *
269 * @param cls unused
270 */
271static void
272shutdown_task (void *cls)
273{
274 struct Pattern *p;
275
276 while (NULL != (p = publish_head))
277 {
278 if (NULL != p->task)
279 GNUNET_SCHEDULER_cancel (p->task);
280 if (NULL != p->ctx)
281 GNUNET_FS_publish_stop (p->ctx);
282 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
283 GNUNET_free (p);
284 }
285 while (NULL != (p = download_head))
286 {
287 if (NULL != p->task)
288 GNUNET_SCHEDULER_cancel (p->task);
289 if (NULL != p->stask)
290 GNUNET_SCHEDULER_cancel (p->stask);
291 if (NULL != p->ctx)
292 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
293 if (NULL != p->sctx)
294 GNUNET_FS_search_stop (p->sctx);
295 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
296 GNUNET_free (p);
297 }
298 if (NULL != fs_handle)
299 {
300 GNUNET_FS_stop (fs_handle);
301 fs_handle = NULL;
302 }
303 if (NULL != stats_handle)
304 {
305 GNUNET_STATISTICS_destroy (stats_handle, GNUNET_YES);
306 stats_handle = NULL;
307 }
308}
309
310
311/**
312 * Task run when a publish operation should be stopped.
313 *
314 * @param cls the 'struct Pattern' of the publish operation to stop
315 */
316static void
317publish_stop_task (void *cls)
318{
319 struct Pattern *p = cls;
320
321 p->task = NULL;
322 GNUNET_FS_publish_stop (p->ctx);
323}
324
325
326/**
327 * Task run when a download operation should be stopped.
328 *
329 * @param cls the 'struct Pattern' of the download operation to stop
330 */
331static void
332download_stop_task (void *cls)
333{
334 struct Pattern *p = cls;
335
336 p->task = NULL;
337 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
338}
339
340
341/**
342 * Task run when a download operation should be stopped.
343 *
344 * @param cls the 'struct Pattern' of the download operation to stop
345 */
346static void
347search_stop_task (void *cls)
348{
349 struct Pattern *p = cls;
350
351 p->stask = NULL;
352 GNUNET_FS_search_stop (p->sctx);
353}
354
355
356/**
357 * Notification of FS to a client about the progress of an
358 * operation. Callbacks of this type will be used for uploads,
359 * downloads and searches. Some of the arguments depend a bit
360 * in their meaning on the context in which the callback is used.
361 *
362 * @param cls closure
363 * @param info details about the event, specifying the event type
364 * and various bits about the event
365 * @return client-context (for the next progress call
366 * for this operation; should be set to NULL for
367 * SUSPEND and STOPPED events). The value returned
368 * will be passed to future callbacks in the respective
369 * field in the GNUNET_FS_ProgressInfo struct.
370 */
371static void *
372progress_cb (void *cls,
373 const struct GNUNET_FS_ProgressInfo *info)
374{
375 struct Pattern *p;
376 const struct GNUNET_FS_Uri *uri;
377
378 switch (info->status)
379 {
380 case GNUNET_FS_STATUS_PUBLISH_START:
381 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
382 p = info->value.publish.cctx;
383 return p;
384
385 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
386 p = info->value.publish.cctx;
387 return p;
388
389 case GNUNET_FS_STATUS_PUBLISH_ERROR:
390 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
391 "Publishing failed\n");
392 GNUNET_STATISTICS_update (stats_handle,
393 "# failed publish operations", 1, GNUNET_NO);
394 p = info->value.publish.cctx;
395 p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
396 return p;
397
398 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
399 p = info->value.publish.cctx;
400 GNUNET_STATISTICS_update (stats_handle,
401 "# publishing time (ms)",
402 (long long) GNUNET_TIME_absolute_get_duration (
403 p->start_time).rel_value_us / 1000LL,
404 GNUNET_NO);
405 p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
406 return p;
407
408 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
409 p = info->value.publish.cctx;
410 p->ctx = NULL;
411 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
412 GNUNET_free (p);
413 return NULL;
414
415 case GNUNET_FS_STATUS_DOWNLOAD_START:
416 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
417 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
418 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
419 p = info->value.download.cctx;
420 return p;
421
422 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
423 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
424 "Download failed\n");
425 GNUNET_STATISTICS_update (stats_handle,
426 "# failed downloads", 1, GNUNET_NO);
427 p = info->value.download.cctx;
428 p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
429 return p;
430
431 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
432 p = info->value.download.cctx;
433 GNUNET_STATISTICS_update (stats_handle,
434 "# download time (ms)",
435 (long long) GNUNET_TIME_absolute_get_duration (
436 p->start_time).rel_value_us / 1000LL,
437 GNUNET_NO);
438 p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
439 return p;
440
441 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
442 p = info->value.download.cctx;
443 p->ctx = NULL;
444 if (NULL == p->sctx)
445 {
446 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
447 GNUNET_free (p);
448 }
449 return NULL;
450
451 case GNUNET_FS_STATUS_SEARCH_START:
452 case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE:
453 p = info->value.search.cctx;
454 return p;
455
456 case GNUNET_FS_STATUS_SEARCH_RESULT:
457 p = info->value.search.cctx;
458 uri = info->value.search.specifics.result.uri;
459 if (GNUNET_YES != GNUNET_FS_uri_test_chk (uri))
460 return NULL; /* not what we want */
461 if (p->y != GNUNET_FS_uri_chk_get_file_size (uri))
462 return NULL; /* not what we want */
463 GNUNET_STATISTICS_update (stats_handle,
464 "# search time (ms)",
465 (long long) GNUNET_TIME_absolute_get_duration (
466 p->start_time).rel_value_us / 1000LL,
467 GNUNET_NO);
468 p->start_time = GNUNET_TIME_absolute_get ();
469 p->ctx = GNUNET_FS_download_start (fs_handle, uri,
470 NULL, NULL, NULL,
471 0, GNUNET_FS_uri_chk_get_file_size (uri),
472 anonymity_level,
473 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES,
474 p,
475 NULL);
476 p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
477 return NULL;
478
479 case GNUNET_FS_STATUS_SEARCH_UPDATE:
480 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
481 return NULL; /* don't care */
482
483 case GNUNET_FS_STATUS_SEARCH_ERROR:
484 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
485 "Search failed\n");
486 GNUNET_STATISTICS_update (stats_handle,
487 "# failed searches", 1, GNUNET_NO);
488 p = info->value.search.cctx;
489 p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
490 return p;
491
492 case GNUNET_FS_STATUS_SEARCH_STOPPED:
493 p = info->value.search.cctx;
494 p->sctx = NULL;
495 if (NULL == p->ctx)
496 {
497 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
498 GNUNET_free (p);
499 }
500 return NULL;
501
502 default:
503 /* unexpected event during profiling */
504 GNUNET_break (0);
505 return NULL;
506 }
507}
508
509
510/**
511 * Start publish operation.
512 *
513 * @param cls the 'struct Pattern' specifying the operation to perform
514 */
515static void
516start_publish (void *cls)
517{
518 struct Pattern *p = cls;
519 struct GNUNET_FS_FileInformation *fi;
520
521 p->task = NULL;
522 fi = make_file (p->x, p->y, p);
523 p->start_time = GNUNET_TIME_absolute_get ();
524 p->ctx = GNUNET_FS_publish_start (fs_handle,
525 fi,
526 NULL, NULL, NULL,
527 GNUNET_FS_PUBLISH_OPTION_NONE);
528}
529
530
531/**
532 * Start download operation.
533 *
534 * @param cls the 'struct Pattern' specifying the operation to perform
535 */
536static void
537start_download (void *cls)
538{
539 struct Pattern *p = cls;
540 struct GNUNET_FS_Uri *keywords;
541
542 p->task = NULL;
543 keywords = make_keywords (p->x);
544 p->start_time = GNUNET_TIME_absolute_get ();
545 p->sctx = GNUNET_FS_search_start (fs_handle, keywords,
546 anonymity_level,
547 GNUNET_FS_SEARCH_OPTION_NONE,
548 p);
549}
550
551
552/**
553 * @brief Main function that will be run by the scheduler.
554 *
555 * @param cls closure
556 * @param args remaining command-line arguments
557 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
558 * @param cfg_ configuration
559 */
560static void
561run (void *cls, char *const *args GNUNET_UNUSED,
562 const char *cfgfile GNUNET_UNUSED,
563 const struct GNUNET_CONFIGURATION_Handle *cfg_)
564{
565 char myoptname[128];
566 struct Pattern *p;
567
568 cfg = cfg_;
569 /* Scheduled the task to clean up when shutdown is called */
570 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
571 NULL);
572
573 if (GNUNET_OK !=
574 GNUNET_CONFIGURATION_get_value_number (cfg,
575 "TESTBED", "PEERID",
576 &my_peerid))
577 {
578 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
579 "TESTBED", "PEERID");
580 global_ret = GNUNET_SYSERR;
581 GNUNET_SCHEDULER_shutdown ();
582 return;
583 }
584 if (GNUNET_OK !=
585 GNUNET_CONFIGURATION_get_value_number (cfg,
586 "FSPROFILER", "ANONYMITY_LEVEL",
587 &anonymity_level))
588 anonymity_level = 1;
589 if (GNUNET_OK !=
590 GNUNET_CONFIGURATION_get_value_number (cfg,
591 "FSPROFILER", "REPLICATION_LEVEL",
592 &replication_level))
593 replication_level = 1;
594 GNUNET_snprintf (myoptname, sizeof(myoptname),
595 "DOWNLOAD-PATTERN-%llu", my_peerid);
596 if (GNUNET_OK !=
597 GNUNET_CONFIGURATION_get_value_string (cfg,
598 "FSPROFILER", myoptname,
599 &download_pattern))
600 download_pattern = GNUNET_strdup ("");
601 GNUNET_snprintf (myoptname, sizeof(myoptname),
602 "PUBLISH-PATTERN-%llu", my_peerid);
603 if (GNUNET_OK !=
604 GNUNET_CONFIGURATION_get_value_string (cfg,
605 "FSPROFILER", myoptname,
606 &publish_pattern))
607 publish_pattern = GNUNET_strdup ("");
608 if ((GNUNET_OK !=
609 parse_pattern (&download_head,
610 &download_tail,
611 download_pattern)) ||
612 (GNUNET_OK !=
613 parse_pattern (&publish_head,
614 &publish_tail,
615 publish_pattern)))
616 {
617 GNUNET_SCHEDULER_shutdown ();
618 return;
619 }
620
621 stats_handle = GNUNET_STATISTICS_create ("fsprofiler", cfg);
622 fs_handle =
623 GNUNET_FS_start (cfg,
624 "fsprofiler",
625 &progress_cb, NULL,
626 GNUNET_FS_FLAGS_NONE,
627 GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 1,
628 GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 1,
629 GNUNET_FS_OPTIONS_END);
630 if (NULL == fs_handle)
631 {
632 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
633 "Could not acquire FS handle. Exiting.\n");
634 global_ret = GNUNET_SYSERR;
635 GNUNET_SCHEDULER_shutdown ();
636 return;
637 }
638 for (p = publish_head; NULL != p; p = p->next)
639 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
640 &start_publish, p);
641 for (p = download_head; NULL != p; p = p->next)
642 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
643 &start_download, p);
644}
645
646
647/**
648 * Program that performs various "random" FS activities.
649 *
650 * @param argc number of arguments from the command line
651 * @param argv command line arguments
652 * @return 0 ok, 1 on error
653 */
654int
655main (int argc, char *const *argv)
656{
657 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
658 GNUNET_GETOPT_OPTION_END
659 };
660
661 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
662 return 2;
663 return (GNUNET_OK ==
664 GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-fsprofiler",
665 gettext_noop
666 (
667 "Daemon to use file-sharing to measure its performance."),
668 options, &run, NULL)) ? global_ret : 1;
669}
670
671
672/* end of gnunet-daemon-fsprofiler.c */
diff --git a/src/fs/gnunet-directory.c b/src/fs/gnunet-directory.c
deleted file mode 100644
index ef5c40ae2..000000000
--- a/src/fs/gnunet-directory.c
+++ /dev/null
@@ -1,211 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-directory.c
22 * @brief display content of GNUnet directories
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_fs_service.h"
27
28static int ret;
29
30/**
31 * Print a meta data entry.
32 *
33 * @param cls closure (unused)
34 * @param plugin_name name of the plugin that generated the meta data
35 * @param type type of the keyword
36 * @param format format of data
37 * @param data_mime_type mime type of data
38 * @param data value of the meta data
39 * @param data_size number of bytes in @a data
40 * @return always 0 (to continue iterating)
41 */
42static int
43item_printer (void *cls,
44 const char *plugin_name,
45 enum EXTRACTOR_MetaType type,
46 enum EXTRACTOR_MetaFormat format,
47 const char *data_mime_type,
48 const char *data,
49 size_t data_size)
50{
51 if (type == EXTRACTOR_METATYPE_GNUNET_FULL_DATA)
52 {
53 printf (_ ("\t<original file embedded in %u bytes of meta data>\n"),
54 (unsigned int) data_size);
55 return 0;
56 }
57 if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
58 (format != EXTRACTOR_METAFORMAT_C_STRING))
59 return 0;
60 if (type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME)
61 return 0;
62#if HAVE_LIBEXTRACTOR
63 printf ("\t%20s: %s\n",
64 dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN,
65 EXTRACTOR_metatype_to_string (type)),
66 data);
67#else
68 printf ("\t%20d: %s\n", type, data);
69#endif
70 return 0;
71}
72
73
74/**
75 * Print an entry in a directory.
76 *
77 * @param cls closure (not used)
78 * @param filename name of the file in the directory
79 * @param uri URI of the file
80 * @param meta metadata for the file; metadata for
81 * the directory if everything else is NULL/zero
82 * @param length length of the available data for the file
83 * (of type size_t since data must certainly fit
84 * into memory; if files are larger than size_t
85 * permits, then they will certainly not be
86 * embedded with the directory itself).
87 * @param data data available for the file (length bytes)
88 */
89static void
90print_entry (void *cls,
91 const char *filename,
92 const struct GNUNET_FS_Uri *uri,
93 const struct GNUNET_CONTAINER_MetaData *meta,
94 size_t length,
95 const void *data)
96{
97 char *string;
98 char *name;
99
100 name = GNUNET_CONTAINER_meta_data_get_by_type (
101 meta,
102 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
103 if (uri == NULL)
104 {
105 printf (_ ("Directory `%s' meta data:\n"), name ? name : "");
106 GNUNET_CONTAINER_meta_data_iterate (meta, &item_printer, NULL);
107 printf ("\n");
108 printf (_ ("Directory `%s' contents:\n"), name ? name : "");
109 GNUNET_free (name);
110 return;
111 }
112 string = GNUNET_FS_uri_to_string (uri);
113 printf ("%s (%s):\n", name ? name : "", string);
114 GNUNET_free (string);
115 GNUNET_CONTAINER_meta_data_iterate (meta, &item_printer, NULL);
116 printf ("\n");
117 GNUNET_free (name);
118}
119
120
121/**
122 * Main function that will be run by the scheduler.
123 *
124 * @param cls closure
125 * @param args remaining command-line arguments
126 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
127 * @param cfg configuration
128 */
129static void
130run (void *cls,
131 char *const *args,
132 const char *cfgfile,
133 const struct GNUNET_CONFIGURATION_Handle *cfg)
134{
135 struct GNUNET_DISK_MapHandle *map;
136 struct GNUNET_DISK_FileHandle *h;
137 void *data;
138 size_t len;
139 uint64_t size;
140 const char *filename;
141 int i;
142
143 if (NULL == args[0])
144 {
145 fprintf (stderr, "%s", _ ("You must specify a filename to inspect.\n"));
146 ret = 1;
147 return;
148 }
149 i = 0;
150 while (NULL != (filename = args[i++]))
151 {
152 if ((GNUNET_OK !=
153 GNUNET_DISK_file_size (filename, &size, GNUNET_YES, GNUNET_YES)) ||
154 (NULL == (h = GNUNET_DISK_file_open (filename,
155 GNUNET_DISK_OPEN_READ,
156 GNUNET_DISK_PERM_NONE))))
157 {
158 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
159 _ ("Failed to read directory `%s'\n"),
160 filename);
161 ret = 1;
162 continue;
163 }
164 len = (size_t) size;
165 data = GNUNET_DISK_file_map (h, &map, GNUNET_DISK_MAP_TYPE_READ, len);
166 GNUNET_assert (NULL != data);
167 if (GNUNET_OK !=
168 GNUNET_FS_directory_list_contents (len, data, 0, &print_entry, NULL))
169 fprintf (stdout, _ ("`%s' is not a GNUnet directory\n"), filename);
170 else
171 printf ("\n");
172 GNUNET_DISK_file_unmap (map);
173 GNUNET_DISK_file_close (h);
174 }
175}
176
177
178/**
179 * The main function to inspect GNUnet directories.
180 *
181 * @param argc number of arguments from the command line
182 * @param argv command line arguments
183 * @return 0 ok, 1 on error
184 */
185int
186main (int argc, char *const *argv)
187{
188 static struct GNUNET_GETOPT_CommandLineOption options[] = {
189 GNUNET_GETOPT_OPTION_END
190 };
191
192 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
193 return 2;
194
195 ret = (GNUNET_OK ==
196 GNUNET_PROGRAM_run (argc,
197 argv,
198 "gnunet-directory [OPTIONS] FILENAME",
199 gettext_noop (
200 "Display contents of a GNUnet directory"),
201 options,
202 &run,
203 NULL))
204 ? ret
205 : 1;
206 GNUNET_free_nz ((void *) argv);
207 return ret;
208}
209
210
211/* end of gnunet-directory.c */
diff --git a/src/fs/gnunet-download.c b/src/fs/gnunet-download.c
deleted file mode 100644
index eebf8e993..000000000
--- a/src/fs/gnunet-download.c
+++ /dev/null
@@ -1,384 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-download.c
22 * @brief downloading for files on GNUnet
23 * @author Christian Grothoff
24 * @author Krista Bennett
25 * @author James Blackwell
26 * @author Igor Wronsky
27 */
28#include "platform.h"
29#include "gnunet_fs_service.h"
30
31static int ret;
32
33static unsigned int verbose;
34
35static int delete_incomplete;
36
37static const struct GNUNET_CONFIGURATION_Handle *cfg;
38
39static struct GNUNET_FS_Handle *ctx;
40
41static struct GNUNET_FS_DownloadContext *dc;
42
43static unsigned int anonymity = 1;
44
45static unsigned int parallelism = 16;
46
47static unsigned int request_parallelism = 4092;
48
49static int do_recursive;
50
51static char *filename;
52
53static int local_only;
54
55
56static void
57cleanup_task (void *cls)
58{
59 GNUNET_FS_stop (ctx);
60 ctx = NULL;
61}
62
63
64static void
65shutdown_task (void *cls)
66{
67 if (NULL != dc)
68 {
69 GNUNET_FS_download_stop (dc, delete_incomplete);
70 dc = NULL;
71 }
72}
73
74
75/**
76 * Display progress bar (if tty).
77 *
78 * @param x current position in the download
79 * @param n total size of the download
80 * @param w desired number of steps in the progress bar
81 */
82static void
83display_bar (unsigned long long x, unsigned long long n, unsigned int w)
84{
85 char buf[w + 20];
86 unsigned int p;
87 unsigned int endeq;
88 float ratio_complete;
89
90 if (0 == isatty (1))
91 return;
92 ratio_complete = x / (float) n;
93 endeq = ratio_complete * w;
94 GNUNET_snprintf (buf, sizeof(buf), "%3d%% [", (int) (ratio_complete * 100));
95 for (p = 0; p < endeq; p++)
96 strcat (buf, "=");
97 for (p = endeq; p < w; p++)
98 strcat (buf, " ");
99 strcat (buf, "]\r");
100 printf ("%s", buf);
101 fflush (stdout);
102}
103
104
105/**
106 * Called by FS client to give information about the progress of an
107 * operation.
108 *
109 * @param cls closure
110 * @param info details about the event, specifying the event type
111 * and various bits about the event
112 * @return client-context (for the next progress call
113 * for this operation; should be set to NULL for
114 * SUSPEND and STOPPED events). The value returned
115 * will be passed to future callbacks in the respective
116 * field in the `struct GNUNET_FS_ProgressInfo`
117 */
118static void *
119progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
120{
121 char *s;
122 const char *s2;
123 char *t;
124
125 switch (info->status)
126 {
127 case GNUNET_FS_STATUS_DOWNLOAD_START:
128 if (verbose > 1)
129 fprintf (stderr,
130 _ ("Starting download `%s'.\n"),
131 info->value.download.filename);
132 break;
133
134 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
135 if (verbose)
136 {
137 s = GNUNET_strdup (
138 GNUNET_STRINGS_relative_time_to_string (info->value.download.eta,
139 GNUNET_YES));
140 if (info->value.download.specifics.progress.block_download_duration
141 .rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
142 s2 = _ ("<unknown time>");
143 else
144 s2 = GNUNET_STRINGS_relative_time_to_string (info->value.download
145 .specifics.progress
146 .block_download_duration,
147 GNUNET_YES);
148 t = GNUNET_STRINGS_byte_size_fancy (
149 info->value.download.completed * 1000LL
150 / (info->value.download.duration.rel_value_us + 1));
151 fprintf (
152 stdout,
153 _ (
154 "Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"),
155 info->value.download.filename,
156 (unsigned long long) info->value.download.completed,
157 (unsigned long long) info->value.download.size,
158 s,
159 t,
160 s2);
161 GNUNET_free (s);
162 GNUNET_free (t);
163 }
164 else
165 {
166 display_bar (info->value.download.completed,
167 info->value.download.size,
168 60);
169 }
170 break;
171
172 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
173 if (0 != isatty (1))
174 fprintf (stdout, "\n");
175 fprintf (stderr,
176 _ ("Error downloading: %s.\n"),
177 info->value.download.specifics.error.message);
178 GNUNET_SCHEDULER_shutdown ();
179 break;
180
181 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
182 s = GNUNET_STRINGS_byte_size_fancy (
183 info->value.download.completed * 1000
184 / (info->value.download.duration.rel_value_us + 1));
185 if (0 != isatty (1))
186 fprintf (stdout, "\n");
187 fprintf (stdout,
188 _ ("Downloading `%s' done (%s/s).\n"),
189 info->value.download.filename,
190 s);
191 GNUNET_free (s);
192 if (info->value.download.dc == dc)
193 GNUNET_SCHEDULER_shutdown ();
194 break;
195
196 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
197 if (info->value.download.dc == dc)
198 GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
199 break;
200
201 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
202 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
203 break;
204
205 default:
206 fprintf (stderr, _ ("Unexpected status: %d\n"), info->status);
207 break;
208 }
209 return NULL;
210}
211
212
213/**
214 * Main function that will be run by the scheduler.
215 *
216 * @param cls closure
217 * @param args remaining command-line arguments
218 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
219 * @param c configuration
220 */
221static void
222run (void *cls,
223 char *const *args,
224 const char *cfgfile,
225 const struct GNUNET_CONFIGURATION_Handle *c)
226{
227 struct GNUNET_FS_Uri *uri;
228 char *emsg;
229 enum GNUNET_FS_DownloadOptions options;
230
231 if (NULL == args[0])
232 {
233 fprintf (stderr, "%s", _ ("You need to specify a URI argument.\n"));
234 return;
235 }
236 uri = GNUNET_FS_uri_parse (args[0], &emsg);
237 if (NULL == uri)
238 {
239 fprintf (stderr, _ ("Failed to parse URI: %s\n"), emsg);
240 GNUNET_free (emsg);
241 ret = 1;
242 return;
243 }
244 if ((! GNUNET_FS_uri_test_chk (uri)) && (! GNUNET_FS_uri_test_loc (uri)))
245 {
246 fprintf (stderr, "%s", _ ("Only CHK or LOC URIs supported.\n"));
247 ret = 1;
248 GNUNET_FS_uri_destroy (uri);
249 return;
250 }
251 if (NULL == filename)
252 {
253 fprintf (stderr, "%s", _ ("Target filename must be specified.\n"));
254 ret = 1;
255 GNUNET_FS_uri_destroy (uri);
256 return;
257 }
258 cfg = c;
259 ctx = GNUNET_FS_start (cfg,
260 "gnunet-download",
261 &progress_cb,
262 NULL,
263 GNUNET_FS_FLAGS_NONE,
264 GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM,
265 parallelism,
266 GNUNET_FS_OPTIONS_REQUEST_PARALLELISM,
267 request_parallelism,
268 GNUNET_FS_OPTIONS_END);
269 if (NULL == ctx)
270 {
271 fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS");
272 GNUNET_FS_uri_destroy (uri);
273 ret = 1;
274 return;
275 }
276 options = GNUNET_FS_DOWNLOAD_OPTION_NONE;
277 if (do_recursive)
278 options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE;
279 if (local_only)
280 options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY;
281 dc = GNUNET_FS_download_start (ctx,
282 uri,
283 NULL,
284 filename,
285 NULL,
286 0,
287 GNUNET_FS_uri_chk_get_file_size (uri),
288 anonymity,
289 options,
290 NULL,
291 NULL);
292 GNUNET_FS_uri_destroy (uri);
293 if (dc == NULL)
294 {
295 GNUNET_FS_stop (ctx);
296 ctx = NULL;
297 return;
298 }
299 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
300}
301
302
303/**
304 * The main function to download GNUnet.
305 *
306 * @param argc number of arguments from the command line
307 * @param argv command line arguments
308 * @return 0 ok, 1 on error
309 */
310int
311main (int argc, char *const *argv)
312{
313 struct GNUNET_GETOPT_CommandLineOption options[] =
314 { GNUNET_GETOPT_option_uint ('a',
315 "anonymity",
316 "LEVEL",
317 gettext_noop (
318 "set the desired LEVEL of receiver-anonymity"),
319 &anonymity),
320
321 GNUNET_GETOPT_option_flag (
322 'D',
323 "delete-incomplete",
324 gettext_noop ("delete incomplete downloads (when aborted with CTRL-C)"),
325 &delete_incomplete),
326
327 GNUNET_GETOPT_option_flag (
328 'n',
329 "no-network",
330 gettext_noop ("only search the local peer (no P2P network search)"),
331 &local_only),
332 GNUNET_GETOPT_option_string ('o',
333 "output",
334 "FILENAME",
335 gettext_noop ("write the file to FILENAME"),
336 &filename),
337 GNUNET_GETOPT_option_uint (
338 'p',
339 "parallelism",
340 "DOWNLOADS",
341 gettext_noop (
342 "set the maximum number of parallel downloads that is allowed"),
343 &parallelism),
344 GNUNET_GETOPT_option_uint (
345 'r',
346 "request-parallelism",
347 "REQUESTS",
348 gettext_noop (
349 "set the maximum number of parallel requests for blocks that is allowed"),
350 &request_parallelism),
351 GNUNET_GETOPT_option_flag ('R',
352 "recursive",
353 gettext_noop (
354 "download a GNUnet directory recursively"),
355 &do_recursive),
356 GNUNET_GETOPT_option_increment_uint (
357 'V',
358 "verbose",
359 gettext_noop ("be verbose (print progress information)"),
360 &verbose),
361 GNUNET_GETOPT_OPTION_END };
362
363 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
364 return 2;
365
366 ret =
367 (GNUNET_OK ==
368 GNUNET_PROGRAM_run (
369 argc,
370 argv,
371 "gnunet-download [OPTIONS] URI",
372 gettext_noop (
373 "Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"),
374 options,
375 &run,
376 NULL))
377 ? ret
378 : 1;
379 GNUNET_free_nz ((void *) argv);
380 return ret;
381}
382
383
384/* end of gnunet-download.c */
diff --git a/src/fs/gnunet-fs-profiler.c b/src/fs/gnunet-fs-profiler.c
deleted file mode 100644
index 62da46834..000000000
--- a/src/fs/gnunet-fs-profiler.c
+++ /dev/null
@@ -1,245 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-fs-profiler.c
23 * @brief tool to benchmark/profile file-sharing
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testbed_service.h"
29
30/**
31 * Final status code.
32 */
33static int ret;
34
35/**
36 * Data file with the hosts for the testbed.
37 */
38static char *host_filename;
39
40/**
41 * Number of peers to run in the experiment.
42 */
43static unsigned int num_peers;
44
45/**
46 * After how long do we abort the test?
47 */
48static struct GNUNET_TIME_Relative timeout;
49
50/**
51 * Handle to the task run during termination.
52 */
53static struct GNUNET_SCHEDULER_Task *terminate_taskid;
54
55
56/**
57 * Function called after we've collected the statistics.
58 *
59 * @param cls NULL
60 * @param op the operation that has been finished
61 * @param emsg error message in case the operation has failed; will be NULL if
62 * operation has executed successfully.
63 */
64static void
65shutdown_task (void *cls,
66 struct GNUNET_TESTBED_Operation *op,
67 const char *emsg)
68{
69 if (NULL != emsg)
70 fprintf (stderr,
71 "Error collecting statistics: %s\n",
72 emsg);
73 GNUNET_SCHEDULER_shutdown ();
74}
75
76
77/**
78 * Callback function to process statistic values from all peers.
79 * Prints them out.
80 *
81 * @param cls closure
82 * @param peer the peer the statistic belong to
83 * @param subsystem name of subsystem that created the statistic
84 * @param name the name of the datum
85 * @param value the current value
86 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
87 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
88 */
89static int
90process_stats (void *cls,
91 const struct GNUNET_TESTBED_Peer *peer,
92 const char *subsystem,
93 const char *name,
94 uint64_t value,
95 int is_persistent)
96{
97 fprintf (stdout,
98 "%p-%s: %s = %llu\n",
99 peer,
100 subsystem,
101 name,
102 (unsigned long long) value);
103 return GNUNET_OK;
104}
105
106
107/**
108 * Task run on shutdown to terminate. Triggers printing out
109 * all statistics.
110 *
111 * @param cls NULL
112 */
113static void
114terminate_task (void *cls)
115{
116 if (NULL != terminate_taskid)
117 {
118 GNUNET_SCHEDULER_cancel (terminate_taskid);
119 terminate_taskid = NULL;
120 }
121 GNUNET_TESTBED_get_statistics (0, NULL,
122 NULL, NULL,
123 &process_stats,
124 &shutdown_task,
125 NULL);
126}
127
128
129/**
130 * Task run on timeout to terminate. Triggers printing out
131 * all statistics.
132 *
133 * @param cls NULL
134 */
135static void
136timeout_task (void *cls)
137{
138 terminate_taskid = NULL;
139 GNUNET_SCHEDULER_shutdown ();
140}
141
142
143/**
144 * Signature of a main function for a testcase.
145 *
146 * @param cls closure
147 * @param h the run handle
148 * @param num_peers number of peers in 'peers'
149 * @param peers handle to peers run in the testbed
150 * @param links_succeeded the number of overlay link connection attempts that
151 * succeeded
152 * @param links_failed the number of overlay link connection attempts that
153 * failed
154 */
155static void
156test_master (void *cls,
157 struct GNUNET_TESTBED_RunHandle *h,
158 unsigned int num_peers,
159 struct GNUNET_TESTBED_Peer **peers,
160 unsigned int links_succeeded,
161 unsigned int links_failed)
162{
163 // const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
164 // FIXME: enable clients to signal 'completion' before timeout;
165 // in that case, run the 'terminate_task' "immediately"
166
167 if (0 != timeout.rel_value_us)
168 terminate_taskid = GNUNET_SCHEDULER_add_delayed (timeout,
169 &timeout_task,
170 NULL);
171 GNUNET_SCHEDULER_add_shutdown (&terminate_task,
172 NULL);
173}
174
175
176/**
177 * Main function that will be run by the scheduler.
178 *
179 * @param cls closure
180 * @param args remaining command-line arguments
181 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
182 * @param cfg configuration
183 */
184static void
185run (void *cls, char *const *args, const char *cfgfile,
186 const struct GNUNET_CONFIGURATION_Handle *cfg)
187{
188 GNUNET_TESTBED_run (host_filename,
189 cfg,
190 num_peers,
191 0, NULL, NULL,
192 &test_master, (void *) cfg);
193}
194
195
196/**
197 * Program to run a file-sharing testbed.
198 *
199 * @param argc number of arguments from the command line
200 * @param argv command line arguments
201 * @return 0 ok, 1 on error
202 */
203int
204main (int argc, char *const *argv)
205{
206 struct GNUNET_GETOPT_CommandLineOption options[] = {
207 GNUNET_GETOPT_option_uint ('n',
208 "num-peers",
209 "COUNT",
210 gettext_noop (
211 "run the experiment with COUNT peers"),
212 &num_peers),
213
214 GNUNET_GETOPT_option_string ('H',
215 "hosts",
216 "HOSTFILE",
217 gettext_noop (
218 "specifies name of a file with the HOSTS the testbed should use"),
219 &host_filename),
220
221 GNUNET_GETOPT_option_relative_time ('t',
222 "timeout",
223 "DELAY",
224 gettext_noop (
225 "automatically terminate experiment after DELAY"),
226 &timeout),
227
228 GNUNET_GETOPT_OPTION_END
229 };
230
231 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
232 return 2;
233
234 ret = (GNUNET_OK ==
235 GNUNET_PROGRAM_run (argc, argv, "gnunet-fs-profiler",
236 gettext_noop (
237 "run a testbed to measure file-sharing performance"),
238 options, &run,
239 NULL)) ? ret : 1;
240 GNUNET_free_nz ((void *) argv);
241 return ret;
242}
243
244
245/* end of gnunet-fs-profiler.c */
diff --git a/src/fs/gnunet-fs.c b/src/fs/gnunet-fs.c
deleted file mode 100644
index 70a0034a0..000000000
--- a/src/fs/gnunet-fs.c
+++ /dev/null
@@ -1,152 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-fs.c
22 * @brief special file-sharing functions
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_fs_service.h"
27
28/**
29 * Return value.
30 */
31static int ret;
32
33/**
34 * Handle to FS service.
35 */
36static struct GNUNET_FS_Handle *fs;
37
38/**
39 * Option -i given?
40 */
41static int list_indexed_files;
42
43/**
44 * Option -v given?
45 */
46static unsigned int verbose;
47
48
49/**
50 * Print indexed filenames to stdout.
51 *
52 * @param cls closure
53 * @param filename the name of the file
54 * @param file_id hash of the contents of the indexed file
55 * @return GNUNET_OK to continue iteration
56 */
57static int
58print_indexed (void *cls,
59 const char *filename,
60 const struct GNUNET_HashCode *file_id)
61{
62 if (NULL == filename)
63 {
64 GNUNET_FS_stop (fs);
65 fs = NULL;
66 return GNUNET_OK;
67 }
68 if (verbose)
69 fprintf (stdout, "%s: %s\n", GNUNET_h2s (file_id), filename);
70 else
71 fprintf (stdout, "%s\n", filename);
72 return GNUNET_OK;
73}
74
75
76/**
77 * Main function that will be run by the scheduler.
78 *
79 * @param cls closure
80 * @param args remaining command-line arguments
81 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
82 * @param cfg configuration
83 */
84static void
85run (void *cls,
86 char *const *args,
87 const char *cfgfile,
88 const struct GNUNET_CONFIGURATION_Handle *cfg)
89{
90 if (list_indexed_files)
91 {
92 fs = GNUNET_FS_start (cfg,
93 "gnunet-fs",
94 NULL,
95 NULL,
96 GNUNET_FS_FLAGS_NONE,
97 GNUNET_FS_OPTIONS_END);
98 if (NULL == fs)
99 {
100 ret = 1;
101 return;
102 }
103 if (NULL == GNUNET_FS_get_indexed_files (fs, &print_indexed, NULL))
104 {
105 ret = 2;
106 GNUNET_FS_stop (fs);
107 fs = NULL;
108 return;
109 }
110 }
111}
112
113
114/**
115 * The main function to access special file-sharing functions.
116 *
117 * @param argc number of arguments from the command line
118 * @param argv command line arguments
119 * @return 0 ok, 1 on error
120 */
121int
122main (int argc, char *const *argv)
123{
124 struct GNUNET_GETOPT_CommandLineOption options[] = {
125 GNUNET_GETOPT_option_flag ('i',
126 "list-indexed",
127 gettext_noop (
128 "print a list of all indexed files"),
129 &list_indexed_files),
130
131 GNUNET_GETOPT_option_verbose (&verbose),
132 GNUNET_GETOPT_OPTION_END
133 };
134
135 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
136 return 2;
137 ret = (GNUNET_OK ==
138 GNUNET_PROGRAM_run (argc,
139 argv,
140 "gnunet-fs [OPTIONS]",
141 gettext_noop ("Special file-sharing operations"),
142 options,
143 &run,
144 NULL))
145 ? ret
146 : 1;
147 GNUNET_free_nz ((void *) argv);
148 return ret;
149}
150
151
152/* end of gnunet-fs.c */
diff --git a/src/fs/gnunet-helper-fs-publish.c b/src/fs/gnunet-helper-fs-publish.c
deleted file mode 100644
index ef1a9ce4b..000000000
--- a/src/fs/gnunet-helper-fs-publish.c
+++ /dev/null
@@ -1,578 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file src/fs/gnunet-helper-fs-publish.c
23 * @brief Tool to help extract meta data asynchronously
24 * @author Christian Grothoff
25 *
26 * This program will scan a directory for files with meta data
27 * and report the results to stdout.
28 */
29#include "platform.h"
30#include "gnunet_fs_service.h"
31
32
33/**
34 * A node of a directory tree.
35 */
36struct ScanTreeNode
37{
38 /**
39 * This is a doubly-linked list
40 */
41 struct ScanTreeNode *next;
42
43 /**
44 * This is a doubly-linked list
45 */
46 struct ScanTreeNode *prev;
47
48 /**
49 * Parent of this node, NULL for top-level entries.
50 */
51 struct ScanTreeNode *parent;
52
53 /**
54 * This is a doubly-linked tree
55 * NULL for files and empty directories
56 */
57 struct ScanTreeNode *children_head;
58
59 /**
60 * This is a doubly-linked tree
61 * NULL for files and empty directories
62 */
63 struct ScanTreeNode *children_tail;
64
65 /**
66 * Name of the file/directory
67 */
68 char *filename;
69
70 /**
71 * Size of the file (if it is a file), in bytes.
72 * At the moment it is set to 0 for directories.
73 */
74 uint64_t file_size;
75
76 /**
77 * #GNUNET_YES if this is a directory
78 */
79 int is_directory;
80};
81
82
83#if HAVE_LIBEXTRACTOR
84/**
85 * List of libextractor plugins to use for extracting.
86 */
87static struct EXTRACTOR_PluginList *plugins;
88#endif
89
90/**
91 * File descriptor we use for IPC with the parent.
92 */
93static int output_stream;
94
95
96#if HAVE_LIBEXTRACTOR
97/**
98 * Add meta data that libextractor finds to our meta data
99 * container.
100 *
101 * @param cls closure, our meta data container
102 * @param plugin_name name of the plugin that produced this value;
103 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
104 * used in the main libextractor library and yielding
105 * meta data).
106 * @param type libextractor-type describing the meta data
107 * @param format basic format information about data
108 * @param data_mime_type mime-type of data (not of the original file);
109 * can be NULL (if mime-type is not known)
110 * @param data actual meta-data found
111 * @param data_len number of bytes in @a data
112 * @return always 0 to continue extracting
113 */
114static int
115add_to_md (void *cls,
116 const char *plugin_name,
117 enum EXTRACTOR_MetaType type,
118 enum EXTRACTOR_MetaFormat format,
119 const char *data_mime_type,
120 const char *data,
121 size_t data_len)
122{
123 struct GNUNET_CONTAINER_MetaData *md = cls;
124
125 if (((EXTRACTOR_METAFORMAT_UTF8 == format) ||
126 (EXTRACTOR_METAFORMAT_C_STRING == format)) &&
127 ('\0' != data[data_len - 1]))
128 {
129 char zdata[data_len + 1];
130 GNUNET_memcpy (zdata, data, data_len);
131 zdata[data_len] = '\0';
132 (void) GNUNET_CONTAINER_meta_data_insert (md,
133 plugin_name,
134 type,
135 format,
136 data_mime_type,
137 zdata,
138 data_len + 1);
139 }
140 else
141 {
142 (void) GNUNET_CONTAINER_meta_data_insert (md,
143 plugin_name,
144 type,
145 format,
146 data_mime_type,
147 data,
148 data_len);
149 }
150 return 0;
151}
152
153
154#endif
155
156
157/**
158 * Free memory of the @a tree structure
159 *
160 * @param tree tree to free
161 */
162static void
163free_tree (struct ScanTreeNode *tree)
164{
165 struct ScanTreeNode *pos;
166
167 while (NULL != (pos = tree->children_head))
168 free_tree (pos);
169 if (NULL != tree->parent)
170 GNUNET_CONTAINER_DLL_remove (tree->parent->children_head,
171 tree->parent->children_tail,
172 tree);
173 GNUNET_free (tree->filename);
174 GNUNET_free (tree);
175}
176
177
178/**
179 * Write @a size bytes from @a buf into the #output_stream.
180 *
181 * @param buf buffer with data to write
182 * @param size number of bytes to write
183 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
184 */
185static int
186write_all (const void *buf, size_t size)
187{
188 const char *cbuf = buf;
189 size_t total;
190 ssize_t wr;
191
192 total = 0;
193 do
194 {
195 wr = write (output_stream, &cbuf[total], size - total);
196 if (wr > 0)
197 total += wr;
198 }
199 while ((wr > 0) && (total < size));
200 if (wr <= 0)
201 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
202 "Failed to write to stdout: %s\n",
203 strerror (errno));
204 return (total == size) ? GNUNET_OK : GNUNET_SYSERR;
205}
206
207
208/**
209 * Write message to the master process.
210 *
211 * @param message_type message type to use
212 * @param data data to append, NULL for none
213 * @param data_length number of bytes in @a data
214 * @return #GNUNET_SYSERR to stop scanning (the pipe was broken somehow)
215 */
216static int
217write_message (uint16_t message_type, const char *data, size_t data_length)
218{
219 struct GNUNET_MessageHeader hdr;
220
221#if 0
222 fprintf (stderr,
223 "Helper sends %u-byte message of type %u\n",
224 (unsigned int) (sizeof(struct GNUNET_MessageHeader) + data_length),
225 (unsigned int) message_type);
226#endif
227 hdr.type = htons (message_type);
228 hdr.size = htons (sizeof(struct GNUNET_MessageHeader) + data_length);
229 if ((GNUNET_OK != write_all (&hdr, sizeof(hdr))) ||
230 (GNUNET_OK != write_all (data, data_length)))
231 return GNUNET_SYSERR;
232 return GNUNET_OK;
233}
234
235
236/**
237 * Function called to (recursively) add all of the files in the
238 * directory to the tree. Called by the directory scanner to initiate
239 * the scan. Does NOT yet add any metadata.
240 *
241 * @param filename file or directory to scan
242 * @param dst where to store the resulting share tree item;
243 * NULL is stored in @a dst upon recoverable errors (#GNUNET_OK is returned)
244 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
245 */
246static int
247preprocess_file (const char *filename, struct ScanTreeNode **dst);
248
249
250/**
251 * Closure for the 'scan_callback'
252 */
253struct RecursionContext
254{
255 /**
256 * Parent to add the files to.
257 */
258 struct ScanTreeNode *parent;
259
260 /**
261 * Flag to set to GNUNET_YES on serious errors.
262 */
263 int stop;
264};
265
266
267/**
268 * Function called by the directory iterator to (recursively) add all
269 * of the files in the directory to the tree. Called by the directory
270 * scanner to initiate the scan. Does NOT yet add any metadata.
271 *
272 * @param cls the `struct RecursionContext`
273 * @param filename file or directory to scan
274 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
275 */
276static int
277scan_callback (void *cls, const char *filename)
278{
279 struct RecursionContext *rc = cls;
280 struct ScanTreeNode *chld;
281
282 if (GNUNET_OK != preprocess_file (filename, &chld))
283 {
284 rc->stop = GNUNET_YES;
285 return GNUNET_SYSERR;
286 }
287 if (NULL == chld)
288 return GNUNET_OK;
289 chld->parent = rc->parent;
290 GNUNET_CONTAINER_DLL_insert (rc->parent->children_head,
291 rc->parent->children_tail,
292 chld);
293 return GNUNET_OK;
294}
295
296
297/**
298 * Function called to (recursively) add all of the files in the
299 * directory to the tree. Called by the directory scanner to initiate
300 * the scan. Does NOT yet add any metadata.
301 *
302 * @param filename file or directory to scan
303 * @param dst where to store the resulting share tree item;
304 * NULL is stored in @a dst upon recoverable errors (#GNUNET_OK is returned)
305 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
306 */
307static int
308preprocess_file (const char *filename, struct ScanTreeNode **dst)
309{
310 struct ScanTreeNode *item;
311 struct stat sbuf;
312 uint64_t fsize = 0;
313
314 if ((0 != stat (filename, &sbuf)) ||
315 ((! S_ISDIR (sbuf.st_mode)) &&
316 (GNUNET_OK !=
317 GNUNET_DISK_file_size (filename, &fsize, GNUNET_NO, GNUNET_YES))))
318 {
319 /* If the file doesn't exist (or is not stat-able for any other reason)
320 skip it (but report it), but do continue. */
321 if (GNUNET_OK !=
322 write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_SKIP_FILE,
323 filename,
324 strlen (filename) + 1))
325 return GNUNET_SYSERR;
326 /* recoverable error, store 'NULL' in *dst */
327 *dst = NULL;
328 return GNUNET_OK;
329 }
330
331 /* Report the progress */
332 if (
333 GNUNET_OK !=
334 write_message (S_ISDIR (sbuf.st_mode)
335 ? GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_DIRECTORY
336 : GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_FILE,
337 filename,
338 strlen (filename) + 1))
339 return GNUNET_SYSERR;
340 item = GNUNET_new (struct ScanTreeNode);
341 item->filename = GNUNET_strdup (filename);
342 item->is_directory = (S_ISDIR (sbuf.st_mode)) ? GNUNET_YES : GNUNET_NO;
343 item->file_size = fsize;
344 if (GNUNET_YES == item->is_directory)
345 {
346 struct RecursionContext rc;
347
348 rc.parent = item;
349 rc.stop = GNUNET_NO;
350 GNUNET_DISK_directory_scan (filename, &scan_callback, &rc);
351 if (
352 (GNUNET_YES == rc.stop) ||
353 (GNUNET_OK !=
354 write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_DIRECTORY,
355 "..",
356 3)))
357 {
358 free_tree (item);
359 return GNUNET_SYSERR;
360 }
361 }
362 *dst = item;
363 return GNUNET_OK;
364}
365
366
367/**
368 * Extract metadata from files.
369 *
370 * @param item entry we are processing
371 * @return #GNUNET_OK on success, #GNUNET_SYSERR on fatal errors
372 */
373static int
374extract_files (struct ScanTreeNode *item)
375{
376 struct GNUNET_CONTAINER_MetaData *meta;
377 ssize_t size;
378 size_t slen;
379
380 if (GNUNET_YES == item->is_directory)
381 {
382 /* for directories, we simply only descent, no extraction, no
383 progress reporting */
384 struct ScanTreeNode *pos;
385
386 for (pos = item->children_head; NULL != pos; pos = pos->next)
387 if (GNUNET_OK != extract_files (pos))
388 return GNUNET_SYSERR;
389 return GNUNET_OK;
390 }
391
392 /* this is the expensive operation, *afterwards* we'll check for aborts */
393 meta = GNUNET_CONTAINER_meta_data_create ();
394#if HAVE_LIBEXTRACTOR
395 EXTRACTOR_extract (plugins, item->filename, NULL, 0, &add_to_md, meta);
396#endif
397 slen = strlen (item->filename) + 1;
398 size = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
399 if (-1 == size)
400 {
401 /* no meta data */
402 GNUNET_CONTAINER_meta_data_destroy (meta);
403 if (GNUNET_OK !=
404 write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_META_DATA,
405 item->filename,
406 slen))
407 return GNUNET_SYSERR;
408 return GNUNET_OK;
409 }
410 else if (size > (UINT16_MAX - sizeof(struct GNUNET_MessageHeader) - slen))
411 {
412 /* We can't transfer more than 64k bytes in one message. */
413 size = UINT16_MAX - sizeof(struct GNUNET_MessageHeader) - slen;
414 }
415 {
416 char buf[size + slen];
417 char *dst = &buf[slen];
418
419 GNUNET_memcpy (buf, item->filename, slen);
420 size = GNUNET_CONTAINER_meta_data_serialize (
421 meta,
422 &dst,
423 size,
424 GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
425 if (size < 0)
426 {
427 GNUNET_break (0);
428 size = 0;
429 }
430 GNUNET_CONTAINER_meta_data_destroy (meta);
431 if (GNUNET_OK !=
432 write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_META_DATA,
433 buf,
434 slen + size))
435 return GNUNET_SYSERR;
436 }
437 return GNUNET_OK;
438}
439
440
441/**
442 * Install a signal handler to ignore SIGPIPE.
443 */
444static void
445ignore_sigpipe ()
446{
447 struct sigaction oldsig;
448 struct sigaction sig;
449
450 memset (&sig, 0, sizeof(struct sigaction));
451 sig.sa_handler = SIG_IGN;
452 sigemptyset (&sig.sa_mask);
453#ifdef SA_INTERRUPT
454 sig.sa_flags = SA_INTERRUPT; /* SunOS */
455#else
456 sig.sa_flags = SA_RESTART;
457#endif
458 if (0 != sigaction (SIGPIPE, &sig, &oldsig))
459 fprintf (stderr,
460 "Failed to install SIGPIPE handler: %s\n",
461 strerror (errno));
462}
463
464
465/**
466 * Turn the given file descriptor in to '/dev/null'.
467 *
468 * @param fd fd to bind to /dev/null
469 * @param flags flags to use (O_RDONLY or O_WRONLY)
470 */
471static void
472make_dev_zero (int fd, int flags)
473{
474 int z;
475
476 GNUNET_assert (0 == close (fd));
477 z = open ("/dev/null", flags);
478 GNUNET_assert (-1 != z);
479 if (z == fd)
480 return;
481 GNUNET_break (fd == dup2 (z, fd));
482 GNUNET_assert (0 == close (z));
483}
484
485
486/**
487 * Main function of the helper process to extract meta data.
488 *
489 * @param argc should be 3
490 * @param argv [0] our binary name
491 * [1] name of the file or directory to process
492 * [2] "-" to disable extraction, NULL for defaults,
493 * otherwise custom plugins to load from LE
494 * @return 0 on success
495 */
496int
497main (int argc, char *const *argv)
498{
499 const char *filename_expanded;
500 const char *ex;
501 struct ScanTreeNode *root;
502
503 ignore_sigpipe ();
504 /* move stdout to some other FD for IPC, bind
505 stdout/stderr to /dev/null */
506 output_stream = dup (1);
507 make_dev_zero (1, O_WRONLY);
508 make_dev_zero (2, O_WRONLY);
509
510 /* parse command line */
511 if ((3 != argc) && (2 != argc))
512 {
513 fprintf (stderr,
514 "%s",
515 "gnunet-helper-fs-publish needs exactly one or two arguments\n");
516 return 1;
517 }
518 filename_expanded = argv[1];
519 ex = argv[2];
520 if ((NULL == ex) || (0 != strcmp (ex, "-")))
521 {
522#if HAVE_LIBEXTRACTOR
523 plugins = EXTRACTOR_plugin_add_defaults (EXTRACTOR_OPTION_DEFAULT_POLICY);
524 if (NULL != ex)
525 plugins = EXTRACTOR_plugin_add_config (plugins,
526 ex,
527 EXTRACTOR_OPTION_DEFAULT_POLICY);
528#endif
529 }
530
531 /* scan tree to find out how much work there is to be done */
532 if (GNUNET_OK != preprocess_file (filename_expanded, &root))
533 {
534 (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR, NULL, 0);
535#if HAVE_LIBEXTRACTOR
536 EXTRACTOR_plugin_remove_all (plugins);
537#endif
538 return 2;
539 }
540 /* signal that we're done counting files, so that a percentage of
541 progress can now be calculated */
542 if (GNUNET_OK !=
543 write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_COUNTING_DONE,
544 NULL,
545 0))
546 {
547#if HAVE_LIBEXTRACTOR
548 EXTRACTOR_plugin_remove_all (plugins);
549#endif
550 return 3;
551 }
552 if (NULL != root)
553 {
554 if (GNUNET_OK != extract_files (root))
555 {
556 (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR,
557 NULL,
558 0);
559 free_tree (root);
560#if HAVE_LIBEXTRACTOR
561 EXTRACTOR_plugin_remove_all (plugins);
562#endif
563 return 4;
564 }
565 free_tree (root);
566 }
567 /* enable "clean" shutdown by telling parent that we are done */
568 (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_FINISHED,
569 NULL,
570 0);
571#if HAVE_LIBEXTRACTOR
572 EXTRACTOR_plugin_remove_all (plugins);
573#endif
574 return 0;
575}
576
577
578/* end of gnunet-helper-fs-publish.c */
diff --git a/src/fs/gnunet-publish.c b/src/fs/gnunet-publish.c
deleted file mode 100644
index c62edcd61..000000000
--- a/src/fs/gnunet-publish.c
+++ /dev/null
@@ -1,1008 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-publish.c
22 * @brief publishing files on GNUnet
23 * @author Christian Grothoff
24 * @author Krista Bennett
25 * @author James Blackwell
26 * @author Igor Wronsky
27 */
28#include "platform.h"
29#include "gnunet_fs_service.h"
30#include "gnunet_identity_service.h"
31
32/**
33 * Global return value from #main().
34 */
35static int ret;
36
37/**
38 * Command line option 'verbose' set
39 */
40static unsigned int verbose;
41
42/**
43 * Handle to our configuration.
44 */
45static const struct GNUNET_CONFIGURATION_Handle *cfg;
46
47/**
48 * Handle for interaction with file-sharing service.
49 */
50static struct GNUNET_FS_Handle *ctx;
51
52/**
53 * Handle to FS-publishing operation.
54 */
55static struct GNUNET_FS_PublishContext *pc;
56
57/**
58 * Meta-data provided via command-line option.
59 */
60static struct GNUNET_CONTAINER_MetaData *meta;
61
62/**
63 * Keywords provided via command-line option.
64 */
65static struct GNUNET_FS_Uri *topKeywords;
66
67/**
68 * Options we set for published blocks.
69 */
70static struct GNUNET_FS_BlockOptions bo = { { 0LL }, 1, 365, 1 };
71
72/**
73 * Value of URI provided on command-line (when not publishing
74 * a file but just creating UBlocks to refer to an existing URI).
75 */
76static char *uri_string;
77
78/**
79 * Value of URI provided on command-line (when not publishing
80 * a file but just creating UBlocks to refer to an existing URI);
81 * parsed version of 'uri_string'.
82 */
83static struct GNUNET_FS_Uri *uri;
84
85/**
86 * Command-line option for namespace publishing: identifier for updates
87 * to this publication.
88 */
89static char *next_id;
90
91/**
92 * Command-line option for namespace publishing: identifier for this
93 * publication.
94 */
95static char *this_id;
96
97/**
98 * Command-line option identifying the pseudonym to use for the publication.
99 */
100static char *pseudonym;
101
102/**
103 * Command-line option for 'inserting'
104 */
105static int do_insert;
106
107/**
108 * Command-line option to disable meta data extraction.
109 */
110static int disable_extractor;
111
112/**
113 * Command-line option to merely simulate publishing operation.
114 */
115static int do_simulate;
116
117/**
118 * Command-line option to only perform meta data extraction, but not publish.
119 */
120static int extract_only;
121
122/**
123 * Command-line option to disable adding creation time.
124 */
125static int enable_creation_time;
126
127/**
128 * Handle to the directory scanner (for recursive insertions).
129 */
130static struct GNUNET_FS_DirScanner *ds;
131
132/**
133 * Which namespace do we publish to? NULL if we do not publish to
134 * a namespace.
135 */
136static struct GNUNET_IDENTITY_Ego *namespace;
137
138/**
139 * Handle to identity service.
140 */
141static struct GNUNET_IDENTITY_Handle *identity;
142
143
144/**
145 * We are finished with the publishing operation, clean up all
146 * FS state.
147 *
148 * @param cls NULL
149 */
150static void
151do_stop_task (void *cls)
152{
153 struct GNUNET_FS_PublishContext *p;
154
155 if (NULL != ds)
156 {
157 GNUNET_FS_directory_scan_abort (ds);
158 ds = NULL;
159 }
160 if (NULL != identity)
161 {
162 GNUNET_IDENTITY_disconnect (identity);
163 identity = NULL;
164 }
165 if (NULL != pc)
166 {
167 p = pc;
168 pc = NULL;
169 GNUNET_FS_publish_stop (p);
170 }
171 if (NULL != ctx)
172 {
173 GNUNET_FS_stop (ctx);
174 ctx = NULL;
175 }
176 if (NULL != meta)
177 {
178 GNUNET_CONTAINER_meta_data_destroy (meta);
179 meta = NULL;
180 }
181 if (NULL != uri)
182 {
183 GNUNET_FS_uri_destroy (uri);
184 uri = NULL;
185 }
186}
187
188
189/**
190 * Called by FS client to give information about the progress of an
191 * operation.
192 *
193 * @param cls closure
194 * @param info details about the event, specifying the event type
195 * and various bits about the event
196 * @return client-context (for the next progress call
197 * for this operation; should be set to NULL for
198 * SUSPEND and STOPPED events). The value returned
199 * will be passed to future callbacks in the respective
200 * field in the GNUNET_FS_ProgressInfo struct.
201 */
202static void *
203progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
204{
205 const char *s;
206 char *suri;
207
208 switch (info->status)
209 {
210 case GNUNET_FS_STATUS_PUBLISH_START:
211 break;
212
213 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
214 if (verbose)
215 {
216 s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.eta,
217 GNUNET_YES);
218 fprintf (stdout,
219 _ ("Publishing `%s' at %llu/%llu (%s remaining)\n"),
220 info->value.publish.filename,
221 (unsigned long long) info->value.publish.completed,
222 (unsigned long long) info->value.publish.size,
223 s);
224 }
225 break;
226
227 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
228 if (verbose)
229 {
230 s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.specifics
231 .progress_directory.eta,
232 GNUNET_YES);
233 fprintf (stdout,
234 _ ("Publishing `%s' at %llu/%llu (%s remaining)\n"),
235 info->value.publish.filename,
236 (unsigned long long)
237 info->value.publish.specifics.progress_directory.completed,
238 (unsigned long long)
239 info->value.publish.specifics.progress_directory.total,
240 s);
241 }
242 break;
243
244 case GNUNET_FS_STATUS_PUBLISH_ERROR:
245 fprintf (stderr,
246 _ ("Error publishing: %s.\n"),
247 info->value.publish.specifics.error.message);
248 ret = 1;
249 GNUNET_SCHEDULER_shutdown ();
250 break;
251
252 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
253 fprintf (stdout,
254 _ ("Publishing `%s' done.\n"),
255 info->value.publish.filename);
256 suri =
257 GNUNET_FS_uri_to_string (info->value.publish.specifics.completed.chk_uri);
258 fprintf (stdout, _ ("URI is `%s'.\n"), suri);
259 GNUNET_free (suri);
260 if (NULL != info->value.publish.specifics.completed.sks_uri)
261 {
262 suri = GNUNET_FS_uri_to_string (
263 info->value.publish.specifics.completed.sks_uri);
264 fprintf (stdout, _ ("Namespace URI is `%s'.\n"), suri);
265 GNUNET_free (suri);
266 }
267 if (NULL == info->value.publish.pctx)
268 {
269 ret = 0;
270 GNUNET_SCHEDULER_shutdown ();
271 }
272 break;
273
274 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
275 GNUNET_break (NULL == pc);
276 return NULL;
277
278 case GNUNET_FS_STATUS_UNINDEX_START:
279 fprintf (stderr, "%s", _ ("Starting cleanup after abort\n"));
280 return NULL;
281
282 case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
283 return NULL;
284
285 case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
286 fprintf (stderr, "%s", _ ("Cleanup after abort completed.\n"));
287 GNUNET_FS_unindex_stop (info->value.unindex.uc);
288 return NULL;
289
290 case GNUNET_FS_STATUS_UNINDEX_ERROR:
291 fprintf (stderr, "%s", _ ("Cleanup after abort failed.\n"));
292 GNUNET_FS_unindex_stop (info->value.unindex.uc);
293 return NULL;
294
295 case GNUNET_FS_STATUS_UNINDEX_STOPPED:
296 return NULL;
297
298 default:
299 fprintf (stderr, _ ("Unexpected status: %d\n"), info->status);
300 return NULL;
301 }
302 return ""; /* non-null */
303}
304
305
306/**
307 * Print metadata entries (except binary
308 * metadata and the filename).
309 *
310 * @param cls closure
311 * @param plugin_name name of the plugin that generated the meta data
312 * @param type type of the meta data
313 * @param format format of data
314 * @param data_mime_type mime type of @a data
315 * @param data value of the meta data
316 * @param data_size number of bytes in @a data
317 * @return always 0
318 */
319static int
320meta_printer (void *cls,
321 const char *plugin_name,
322 enum EXTRACTOR_MetaType type,
323 enum EXTRACTOR_MetaFormat format,
324 const char *data_mime_type,
325 const char *data,
326 size_t data_size)
327{
328 if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
329 (EXTRACTOR_METAFORMAT_C_STRING != format))
330 return 0;
331 if (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type)
332 return 0;
333#if HAVE_LIBEXTRACTOR
334 fprintf (stdout, "\t%s - %s\n", EXTRACTOR_metatype_to_string (type), data);
335#else
336 fprintf (stdout, "\t%d - %s\n", type, data);
337#endif
338 return 0;
339}
340
341
342/**
343 * Iterator printing keywords
344 *
345 * @param cls closure
346 * @param keyword the keyword
347 * @param is_mandatory is the keyword mandatory (in a search)
348 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to abort
349 */
350static int
351keyword_printer (void *cls, const char *keyword, int is_mandatory)
352{
353 fprintf (stdout, "\t%s\n", keyword);
354 return GNUNET_OK;
355}
356
357
358/**
359 * Function called on all entries before the publication. This is
360 * where we perform modifications to the default based on command-line
361 * options.
362 *
363 * @param cls closure
364 * @param fi the entry in the publish-structure
365 * @param length length of the file or directory
366 * @param m metadata for the file or directory (can be modified)
367 * @param uri pointer to the keywords that will be used for this entry (can be modified)
368 * @param bo block options
369 * @param do_index should we index?
370 * @param client_info pointer to client context set upon creation (can be modified)
371 * @return #GNUNET_OK to continue, #GNUNET_NO to remove
372 * this entry from the directory, #GNUNET_SYSERR
373 * to abort the iteration
374 */
375static int
376publish_inspector (void *cls,
377 struct GNUNET_FS_FileInformation *fi,
378 uint64_t length,
379 struct GNUNET_CONTAINER_MetaData *m,
380 struct GNUNET_FS_Uri **uri,
381 struct GNUNET_FS_BlockOptions *bo,
382 int *do_index,
383 void **client_info)
384{
385 char *fn;
386 char *fs;
387 struct GNUNET_FS_Uri *new_uri;
388
389 if (cls == fi)
390 return GNUNET_OK;
391 if ((disable_extractor) && (NULL != *uri))
392 {
393 GNUNET_FS_uri_destroy (*uri);
394 *uri = NULL;
395 }
396 if (NULL != topKeywords)
397 {
398 if (NULL != *uri)
399 {
400 new_uri = GNUNET_FS_uri_ksk_merge (topKeywords, *uri);
401 GNUNET_FS_uri_destroy (*uri);
402 *uri = new_uri;
403 GNUNET_FS_uri_destroy (topKeywords);
404 }
405 else
406 {
407 *uri = topKeywords;
408 }
409 topKeywords = NULL;
410 }
411 if (NULL != meta)
412 {
413 GNUNET_CONTAINER_meta_data_merge (m, meta);
414 GNUNET_CONTAINER_meta_data_destroy (meta);
415 meta = NULL;
416 }
417 if (enable_creation_time)
418 GNUNET_CONTAINER_meta_data_add_publication_date (m);
419 if (extract_only)
420 {
421 fn = GNUNET_CONTAINER_meta_data_get_by_type (
422 m,
423 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
424 fs = GNUNET_STRINGS_byte_size_fancy (length);
425 fprintf (stdout, _ ("Meta data for file `%s' (%s)\n"), fn, fs);
426 GNUNET_CONTAINER_meta_data_iterate (m, &meta_printer, NULL);
427 fprintf (stdout, _ ("Keywords for file `%s' (%s)\n"), fn, fs);
428 GNUNET_free (fn);
429 GNUNET_free (fs);
430 if (NULL != *uri)
431 GNUNET_FS_uri_ksk_get_keywords (*uri, &keyword_printer, NULL);
432 fprintf (stdout, "%s", "\n");
433 }
434 if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (m))
435 GNUNET_FS_file_information_inspect (fi, &publish_inspector, fi);
436 return GNUNET_OK;
437}
438
439
440/**
441 * Function called upon completion of the publishing
442 * of the UBLOCK for the SKS URI. As this is the last
443 * step, stop our interaction with FS (clean up).
444 *
445 * @param cls NULL (closure)
446 * @param sks_uri URI for the block that was published
447 * @param emsg error message, NULL on success
448 */
449static void
450uri_sks_continuation (void *cls,
451 const struct GNUNET_FS_Uri *sks_uri,
452 const char *emsg)
453{
454 if (NULL != emsg)
455 {
456 fprintf (stderr, "%s\n", emsg);
457 ret = 1;
458 }
459 GNUNET_SCHEDULER_shutdown ();
460}
461
462
463/**
464 * Function called upon completion of the publishing
465 * of the UBLOCK for the KSK URI. Continue with
466 * publishing the SKS URI (if applicable) or clean up.
467 *
468 * @param cls NULL (closure)
469 * @param ksk_uri URI for the block that was published
470 * @param emsg error message, NULL on success
471 */
472static void
473uri_ksk_continuation (void *cls,
474 const struct GNUNET_FS_Uri *ksk_uri,
475 const char *emsg)
476{
477 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
478 const struct GNUNET_IDENTITY_PrivateKey *pk;
479
480 if (NULL != emsg)
481 {
482 fprintf (stderr, "%s\n", emsg);
483 ret = 1;
484 }
485 if (NULL == namespace)
486 {
487 GNUNET_SCHEDULER_shutdown ();
488 return;
489 }
490 pk = GNUNET_IDENTITY_ego_get_private_key (namespace);
491 if (GNUNET_IDENTITY_TYPE_ECDSA != ntohl (pk->type))
492 return;
493 priv = &pk->ecdsa_key;
494 GNUNET_FS_publish_sks (ctx,
495 priv,
496 this_id,
497 next_id,
498 meta,
499 uri,
500 &bo,
501 GNUNET_FS_PUBLISH_OPTION_NONE,
502 &uri_sks_continuation,
503 NULL);
504}
505
506
507/**
508 * Iterate over the results from the directory scan and extract
509 * the desired information for the publishing operation.
510 *
511 * @param item root with the data from the directory scan
512 * @return handle with the information for the publishing operation
513 */
514static struct GNUNET_FS_FileInformation *
515get_file_information (struct GNUNET_FS_ShareTreeItem *item)
516{
517 struct GNUNET_FS_FileInformation *fi;
518 struct GNUNET_FS_FileInformation *fic;
519 struct GNUNET_FS_ShareTreeItem *child;
520
521 if (GNUNET_YES == item->is_directory)
522 {
523 if (NULL == item->meta)
524 item->meta = GNUNET_CONTAINER_meta_data_create ();
525 GNUNET_CONTAINER_meta_data_delete (item->meta,
526 EXTRACTOR_METATYPE_MIMETYPE,
527 NULL,
528 0);
529 GNUNET_FS_meta_data_make_directory (item->meta);
530 if (NULL == item->ksk_uri)
531 {
532 const char *mime = GNUNET_FS_DIRECTORY_MIME;
533 item->ksk_uri = GNUNET_FS_uri_ksk_create_from_args (1, &mime);
534 }
535 else
536 GNUNET_FS_uri_ksk_add_keyword (item->ksk_uri,
537 GNUNET_FS_DIRECTORY_MIME,
538 GNUNET_NO);
539 fi = GNUNET_FS_file_information_create_empty_directory (ctx,
540 NULL,
541 item->ksk_uri,
542 item->meta,
543 &bo,
544 item->filename);
545 for (child = item->children_head; child; child = child->next)
546 {
547 fic = get_file_information (child);
548 GNUNET_break (GNUNET_OK == GNUNET_FS_file_information_add (fi, fic));
549 }
550 }
551 else
552 {
553 fi = GNUNET_FS_file_information_create_from_file (ctx,
554 NULL,
555 item->filename,
556 item->ksk_uri,
557 item->meta,
558 ! do_insert,
559 &bo);
560 }
561 return fi;
562}
563
564
565/**
566 * We've finished scanning the directory and optimized the meta data.
567 * Begin the publication process.
568 *
569 * @param directory_scan_result result from the directory scan, freed in this function
570 */
571static void
572directory_trim_complete (struct GNUNET_FS_ShareTreeItem *directory_scan_result)
573{
574 struct GNUNET_FS_FileInformation *fi;
575 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
576 const struct GNUNET_IDENTITY_PrivateKey *pk;
577
578 fi = get_file_information (directory_scan_result);
579 GNUNET_FS_share_tree_free (directory_scan_result);
580 if (NULL == fi)
581 {
582 fprintf (stderr, "%s", _ ("Could not publish\n"));
583 ret = 1;
584 GNUNET_SCHEDULER_shutdown ();
585 return;
586 }
587 GNUNET_FS_file_information_inspect (fi, &publish_inspector, NULL);
588 if (extract_only)
589 {
590 GNUNET_FS_file_information_destroy (fi, NULL, NULL);
591 GNUNET_SCHEDULER_shutdown ();
592 return;
593 }
594 priv = NULL;
595 if (NULL != namespace)
596 {
597 pk = GNUNET_IDENTITY_ego_get_private_key (namespace);
598 GNUNET_assert (GNUNET_IDENTITY_TYPE_ECDSA == ntohl (pk->type));
599 priv = &pk->ecdsa_key;
600 }
601 pc = GNUNET_FS_publish_start (ctx,
602 fi,
603 priv,
604 this_id,
605 next_id,
606 (do_simulate)
607 ? GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY
608 : GNUNET_FS_PUBLISH_OPTION_NONE);
609 if (NULL == pc)
610 {
611 fprintf (stderr, "%s", _ ("Could not start publishing.\n"));
612 ret = 1;
613 GNUNET_SCHEDULER_shutdown ();
614 return;
615 }
616}
617
618
619/**
620 * Function called by the directory scanner as we build the tree
621 * that we will need to publish later.
622 *
623 * @param cls closure
624 * @param filename which file we are making progress on
625 * @param is_directory #GNUNET_YES if this is a directory,
626 * #GNUNET_NO if this is a file
627 * #GNUNET_SYSERR if it is neither (or unknown)
628 * @param reason kind of progress we are making
629 */
630static void
631directory_scan_cb (void *cls,
632 const char *filename,
633 int is_directory,
634 enum GNUNET_FS_DirScannerProgressUpdateReason reason)
635{
636 struct GNUNET_FS_ShareTreeItem *directory_scan_result;
637
638 switch (reason)
639 {
640 case GNUNET_FS_DIRSCANNER_FILE_START:
641 if (verbose > 1)
642 {
643 if (is_directory == GNUNET_YES)
644 fprintf (stdout, _ ("Scanning directory `%s'.\n"), filename);
645 else
646 fprintf (stdout, _ ("Scanning file `%s'.\n"), filename);
647 }
648 break;
649
650 case GNUNET_FS_DIRSCANNER_FILE_IGNORED:
651 fprintf (stderr,
652 _ ("There was trouble processing file `%s', skipping it.\n"),
653 filename);
654 break;
655
656 case GNUNET_FS_DIRSCANNER_ALL_COUNTED:
657 if (verbose)
658 fprintf (stdout, "%s", _ ("Preprocessing complete.\n"));
659 break;
660
661 case GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED:
662 if (verbose > 2)
663 fprintf (stdout,
664 _ ("Extracting meta data from file `%s' complete.\n"),
665 filename);
666 break;
667
668 case GNUNET_FS_DIRSCANNER_FINISHED:
669 if (verbose > 1)
670 fprintf (stdout, "%s", _ ("Meta data extraction has finished.\n"));
671 directory_scan_result = GNUNET_FS_directory_scan_get_result (ds);
672 ds = NULL;
673 GNUNET_FS_share_tree_trim (directory_scan_result);
674 directory_trim_complete (directory_scan_result);
675 break;
676
677 case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
678 fprintf (stdout, "%s", _ ("Error scanning directory.\n"));
679 ret = 1;
680 GNUNET_SCHEDULER_shutdown ();
681 break;
682
683 default:
684 GNUNET_assert (0);
685 break;
686 }
687 fflush (stdout);
688}
689
690
691/**
692 * Continuation proceeding with initialization after identity subsystem
693 * has been initialized.
694 *
695 * @param args0 filename to publish
696 */
697static void
698identity_continuation (const char *args0)
699{
700 char *ex;
701 char *emsg;
702
703 if ((NULL != pseudonym) && (NULL == namespace))
704 {
705 fprintf (stderr, _ ("Selected pseudonym `%s' unknown\n"), pseudonym);
706 ret = 1;
707 GNUNET_SCHEDULER_shutdown ();
708 return;
709 }
710 if (NULL != uri_string)
711 {
712 emsg = NULL;
713 if (NULL == (uri = GNUNET_FS_uri_parse (uri_string, &emsg)))
714 {
715 fprintf (stderr, _ ("Failed to parse URI: %s\n"), emsg);
716 GNUNET_free (emsg);
717 ret = 1;
718 GNUNET_SCHEDULER_shutdown ();
719 return;
720 }
721 GNUNET_FS_publish_ksk (ctx,
722 topKeywords,
723 meta,
724 uri,
725 &bo,
726 GNUNET_FS_PUBLISH_OPTION_NONE,
727 &uri_ksk_continuation,
728 NULL);
729 return;
730 }
731 if (GNUNET_OK !=
732 GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS", &ex))
733 ex = NULL;
734 if (0 != access (args0, R_OK))
735 {
736 fprintf (stderr,
737 _ ("Failed to access `%s': %s\n"),
738 args0,
739 strerror (errno));
740 GNUNET_free (ex);
741 return;
742 }
743 ds = GNUNET_FS_directory_scan_start (args0,
744 disable_extractor,
745 ex,
746 &directory_scan_cb,
747 NULL);
748 if (NULL == ds)
749 {
750 fprintf (
751 stderr,
752 "%s",
753 _ (
754 "Failed to start meta directory scanner. Is gnunet-helper-publish-fs installed?\n"));
755 GNUNET_free (ex);
756 return;
757 }
758 GNUNET_free (ex);
759}
760
761
762/**
763 * Function called by identity service with known pseudonyms.
764 *
765 * @param cls closure with 'const char *' of filename to publish
766 * @param ego ego handle
767 * @param ctx context for application to store data for this ego
768 * (during the lifetime of this process, initially NULL)
769 * @param name name assigned by the user for this ego,
770 * NULL if the user just deleted the ego and it
771 * must thus no longer be used
772 */
773static void
774identity_cb (void *cls,
775 struct GNUNET_IDENTITY_Ego *ego,
776 void **ctx,
777 const char *name)
778{
779 const char *args0 = cls;
780
781 if (NULL == ego)
782 {
783 identity_continuation (args0);
784 return;
785 }
786 if (NULL == name)
787 return;
788 if (0 == strcmp (name, pseudonym))
789 namespace = ego;
790}
791
792
793/**
794 * Main function that will be run by the scheduler.
795 *
796 * @param cls closure
797 * @param args remaining command-line arguments
798 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
799 * @param c configuration
800 */
801static void
802run (void *cls,
803 char *const *args,
804 const char *cfgfile,
805 const struct GNUNET_CONFIGURATION_Handle *c)
806{
807 /* check arguments */
808 if ((NULL != uri_string) && (extract_only))
809 {
810 printf (_ ("Cannot extract metadata from a URI!\n"));
811 ret = -1;
812 return;
813 }
814 if (((NULL == uri_string) || (extract_only)) &&
815 ((NULL == args[0]) || (NULL != args[1])))
816 {
817 printf (_ ("You must specify one and only one filename for insertion.\n"));
818 ret = -1;
819 return;
820 }
821 if ((NULL != uri_string) && (NULL != args[0]))
822 {
823 printf (_ ("You must NOT specify an URI and a filename.\n"));
824 ret = -1;
825 return;
826 }
827 if (NULL != pseudonym)
828 {
829 if (NULL == this_id)
830 {
831 fprintf (stderr,
832 _ ("Option `%s' is required when using option `%s'.\n"),
833 "-t",
834 "-P");
835 ret = -1;
836 return;
837 }
838 }
839 else
840 { /* ordinary insertion checks */
841 if (NULL != next_id)
842 {
843 fprintf (stderr,
844 _ ("Option `%s' makes no sense without option `%s'.\n"),
845 "-N",
846 "-P");
847 ret = -1;
848 return;
849 }
850 if (NULL != this_id)
851 {
852 fprintf (stderr,
853 _ ("Option `%s' makes no sense without option `%s'.\n"),
854 "-t",
855 "-P");
856 ret = -1;
857 return;
858 }
859 }
860 cfg = c;
861 ctx = GNUNET_FS_start (cfg,
862 "gnunet-publish",
863 &progress_cb,
864 NULL,
865 GNUNET_FS_FLAGS_NONE,
866 GNUNET_FS_OPTIONS_END);
867 if (NULL == ctx)
868 {
869 fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS");
870 ret = 1;
871 return;
872 }
873 GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL);
874 if (NULL != pseudonym)
875 identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, args[0]);
876 else
877 identity_continuation (args[0]);
878}
879
880
881/**
882 * The main function to publish content to GNUnet.
883 *
884 * @param argc number of arguments from the command line
885 * @param argv command line arguments
886 * @return 0 ok, 1 on error
887 */
888int
889main (int argc, char *const *argv)
890{
891 struct GNUNET_GETOPT_CommandLineOption options[] =
892 { GNUNET_GETOPT_option_uint ('a',
893 "anonymity",
894 "LEVEL",
895 gettext_noop (
896 "set the desired LEVEL of sender-anonymity"),
897 &bo.anonymity_level),
898 GNUNET_GETOPT_option_flag (
899 'D',
900 "disable-extractor",
901 gettext_noop ("do not use libextractor to add keywords or metadata"),
902 &disable_extractor),
903 GNUNET_GETOPT_option_flag ('E',
904 "enable-creation-time",
905 gettext_noop (
906 "enable adding the creation time to the "
907 "metadata of the uploaded file"),
908 &enable_creation_time),
909 GNUNET_GETOPT_option_flag ('e',
910 "extract",
911 gettext_noop (
912 "print list of extracted keywords that would "
913 "be used, but do not perform upload"),
914 &extract_only),
915 GNUNET_FS_GETOPT_KEYWORDS (
916 'k',
917 "key",
918 "KEYWORD",
919 gettext_noop (
920 "add an additional keyword for the top-level "
921 "file or directory (this option can be specified multiple times)"),
922 &topKeywords),
923 GNUNET_FS_GETOPT_METADATA (
924 'm',
925 "meta",
926 "TYPE:VALUE",
927 gettext_noop ("set the meta-data for the given TYPE to the given VALUE"),
928 &meta),
929 GNUNET_GETOPT_option_flag (
930 'n',
931 "noindex",
932 gettext_noop ("do not index, perform full insertion (stores "
933 "entire file in encrypted form in GNUnet database)"),
934 &do_insert),
935 GNUNET_GETOPT_option_string (
936 'N',
937 "next",
938 "ID",
939 gettext_noop ("specify ID of an updated version to be "
940 "published in the future (for namespace insertions only)"),
941 &next_id),
942 GNUNET_GETOPT_option_uint ('p',
943 "priority",
944 "PRIORITY",
945 gettext_noop (
946 "specify the priority of the content"),
947 &bo.content_priority),
948 GNUNET_GETOPT_option_string ('P',
949 "pseudonym",
950 "NAME",
951 gettext_noop (
952 "publish the files under the pseudonym "
953 "NAME (place file into namespace)"),
954 &pseudonym),
955 GNUNET_GETOPT_option_uint ('r',
956 "replication",
957 "LEVEL",
958 gettext_noop (
959 "set the desired replication LEVEL"),
960 &bo.replication_level),
961 GNUNET_GETOPT_option_flag ('s',
962 "simulate-only",
963 gettext_noop (
964 "only simulate the process but do not do "
965 "any actual publishing (useful to compute URIs)"),
966 &do_simulate),
967 GNUNET_GETOPT_option_string ('t',
968 "this",
969 "ID",
970 gettext_noop (
971 "set the ID of this version of the publication "
972 "(for namespace insertions only)"),
973 &this_id),
974 GNUNET_GETOPT_option_string (
975 'u',
976 "uri",
977 "URI",
978 gettext_noop (
979 "URI to be published (can be used instead of passing a "
980 "file to add keywords to the file with the respective URI)"),
981 &uri_string),
982
983 GNUNET_GETOPT_option_verbose (&verbose),
984
985 GNUNET_GETOPT_OPTION_END };
986
987 bo.expiration_time =
988 GNUNET_TIME_year_to_time (GNUNET_TIME_get_current_year () + 2);
989
990 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
991 return 2;
992 ret =
993 (GNUNET_OK ==
994 GNUNET_PROGRAM_run (argc,
995 argv,
996 "gnunet-publish [OPTIONS] FILENAME",
997 gettext_noop ("Publish a file or directory on GNUnet"),
998 options,
999 &run,
1000 NULL))
1001 ? ret
1002 : 1;
1003 GNUNET_free_nz ((void *) argv);
1004 return ret;
1005}
1006
1007
1008/* end of gnunet-publish.c */
diff --git a/src/fs/gnunet-search.c b/src/fs/gnunet-search.c
deleted file mode 100644
index 7e2e4d2a6..000000000
--- a/src/fs/gnunet-search.c
+++ /dev/null
@@ -1,793 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009, 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-search.c
22 * @brief searching for files on GNUnet
23 * @author Christian Grothoff
24 * @author Krista Bennett
25 * @author James Blackwell
26 * @author Igor Wronsky
27 * @author madmurphy
28 */
29#include <ctype.h>
30#include <inttypes.h>
31#include <limits.h>
32#include "platform.h"
33#include "gnunet_fs_service.h"
34
35
36#define GNUNET_SEARCH_log(kind, ...) \
37 GNUNET_log_from(kind, "gnunet-search", __VA_ARGS__)
38
39
40/* The default settings that we use for the printed output */
41
42#define DEFAULT_DIR_FORMAT "#%n:\ngnunet-download -o \"%f\" -R %u\n\n"
43#define HELP_DEFAULT_DIR_FORMAT "#%n:\\ngnunet-download -o \"%f\" -R %u\\n\\n"
44#define DEFAULT_FILE_FORMAT "#%n:\ngnunet-download -o \"%f\" %u\n\n"
45#define HELP_DEFAULT_FILE_FORMAT "#%n:\\ngnunet-download -o \"%f\" %u\\n\\n"
46#define VERB_DEFAULT_DIR_FORMAT DEFAULT_DIR_FORMAT "%a\n"
47#define VERB_DEFAULT_FILE_FORMAT DEFAULT_FILE_FORMAT "%a\n"
48
49#if HAVE_LIBEXTRACTOR
50#define DEFAULT_META_FORMAT " %t: %p\n"
51#define HELP_DEFAULT_META_FORMAT " %t: %p\\n"
52#define HELP_EXTRACTOR_TEXTADD ", %t"
53#else
54#define DEFAULT_META_FORMAT " MetaType #%i: %p\n"
55#define HELP_DEFAULT_META_FORMAT " MetaType #%i: %p\\n"
56#define HELP_EXTRACTOR_TEXTADD ""
57#endif
58
59#define GENERIC_DIRECTORY_NAME "collection"
60#define GENERIC_FILE_NAME "no-name"
61#define GENERIC_FILE_MIMETYPE "application/octet-stream"
62
63
64enum GNUNET_SEARCH_MetadataPrinterFlags {
65 METADATA_PRINTER_FLAG_NONE = 0,
66 METADATA_PRINTER_FLAG_ONE_RUN = 1,
67 METADATA_PRINTER_FLAG_HAVE_TYPE = 2
68};
69
70
71struct GNUNET_SEARCH_MetadataPrinterInfo {
72 unsigned int counter;
73 unsigned int flags;
74 int type;
75};
76
77
78static int ret;
79
80static const struct GNUNET_CONFIGURATION_Handle *cfg;
81
82static struct GNUNET_FS_Handle *ctx;
83
84static struct GNUNET_FS_SearchContext *sc;
85
86static char *output_filename;
87
88static char *format_string;
89
90static char *dir_format_string;
91
92static char *meta_format_string;
93
94static struct GNUNET_FS_DirectoryBuilder *db;
95
96static unsigned int anonymity = 1;
97
98/**
99 * Timeout for the search, 0 means to wait for CTRL-C.
100 */
101static struct GNUNET_TIME_Relative timeout;
102
103static unsigned int results_limit;
104
105static unsigned int results;
106
107static unsigned int verbose;
108
109static int bookmark_only;
110
111static int local_only;
112
113static int silent_mode;
114
115static struct GNUNET_SCHEDULER_Task *tt;
116
117
118/**
119 * Print the escape sequence at the beginning of a string.
120 *
121 * @param esc a string that **must** begin with a backslash (the function only
122 * assumes that it does, but does not check)
123 * @return the fragment that follows what has been printed
124 * @author madmurphy
125 *
126 * If `"\\nfoo"` is passed as argument, this function prints a new line and
127 * returns `"foo"`
128 */
129static const char *
130print_escape_sequence (const char *const esc)
131{
132 unsigned int probe;
133 const char * cursor = esc + 1;
134 char tmp;
135 switch (*cursor)
136 {
137 /* Trivia */
138 case '\\': putchar ('\\'); return cursor + 1;
139 case 'a': putchar ('\a'); return cursor + 1;
140 case 'b': putchar ('\b'); return cursor + 1;
141 case 'e': putchar ('\x1B'); return cursor + 1;
142 case 'f': putchar ('\f'); return cursor + 1;
143 case 'n': putchar ('\n'); return cursor + 1;
144 case 'r': putchar ('\r'); return cursor + 1;
145 case 't': putchar ('\t'); return cursor + 1;
146 case 'v': putchar ('\v'); return cursor + 1;
147
148 /* Possibly hexadecimal code point */
149 case 'x':
150 probe = 0;
151 while (probe < 256 && isxdigit((tmp = *++cursor)))
152 probe = (probe << 4) + tmp - (tmp > 96 ? 87 : tmp > 64 ? 55 : 48);
153 goto maybe_codepoint;
154
155 /* Possibly octal code point */
156 case '0': case '1': case '2': case '3':
157 case '4': case '5': case '6': case '7':
158 probe = *cursor++ - 48;
159 do probe = (probe << 3) + *cursor++ - 48;
160 while (probe < 256 && cursor < esc + 4 && *cursor > 47 && *cursor < 56);
161 goto maybe_codepoint;
162
163 /* Boredom */
164 case '\0': putchar ('\\'); return cursor;
165 default: printf ("\\%c", *cursor); return cursor + 1;
166 }
167
168maybe_codepoint:
169 if (probe < 256)
170 putchar (probe);
171 else
172 fwrite (esc, 1, cursor - esc, stdout);
173 return cursor;
174}
175
176
177/**
178 * Type of a function that libextractor calls for each
179 * meta data item found.
180 *
181 * @param cls closure (user-defined, used for the iteration info)
182 * @param plugin_name name of the plugin that produced this value;
183 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
184 * used in the main libextractor library and yielding
185 * meta data).
186 * @param type libextractor-type describing the meta data
187 * @param format basic format information about data
188 * @param data_mime_type mime-type of data (not of the original file);
189 * can be NULL (if mime-type is not known)
190 * @param data actual meta-data found
191 * @param data_size number of bytes in @a data
192 * @return 0 to continue extracting, 1 to abort
193 */
194static int
195item_printer (void *const cls,
196 const char *const plugin_name,
197 const enum EXTRACTOR_MetaType type,
198 const enum EXTRACTOR_MetaFormat format,
199 const char *const data_mime_type,
200 const char *const data,
201 const size_t data_size)
202{
203#define info ((struct GNUNET_SEARCH_MetadataPrinterInfo *) cls)
204 if ((format != EXTRACTOR_METAFORMAT_UTF8 &&
205 format != EXTRACTOR_METAFORMAT_C_STRING) ||
206 type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME)
207 return 0;
208 info->counter++;
209 if ((info->flags & METADATA_PRINTER_FLAG_HAVE_TYPE) && type != info->type)
210 return 0;
211
212 const char *cursor = meta_format_string;
213 const char *next_spec = strchr(cursor, '%');
214 const char *next_esc = strchr(cursor, '\\');
215
216parse_format:
217
218 /* If an escape sequence exists before the next format specifier... */
219 if (next_esc && (!next_spec || next_esc < next_spec))
220 {
221 if (next_esc > cursor)
222 fwrite (cursor, 1, next_esc - cursor, stdout);
223
224 cursor = print_escape_sequence (next_esc);
225 next_esc = strchr(cursor, '\\');
226 goto parse_format;
227 }
228
229 /* If a format specifier exists before the next escape sequence... */
230 if (next_spec && (!next_esc || next_spec < next_esc))
231 {
232 if (next_spec > cursor)
233 fwrite (cursor, 1, next_spec - cursor, stdout);
234
235 switch (*++next_spec)
236 {
237 case '%': putchar('%'); break;
238 case 'i': printf ("%d", type); break;
239 case 'l': printf ("%lu", (long unsigned int) data_size); break;
240 case 'n': printf ("%u", info->counter); break;
241 case 'p': printf ("%s", data); break;
242#if HAVE_LIBEXTRACTOR
243 case 't':
244 printf ("%s",
245 dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN,
246 EXTRACTOR_metatype_to_string (type)));
247 break;
248#endif
249 case 'w': printf ("%s", plugin_name); break;
250 case '\0': putchar('%'); return 0;
251 default: printf ("%%%c", *next_spec); break;
252 }
253 cursor = next_spec + 1;
254 next_spec = strchr(cursor, '%');
255 goto parse_format;
256 }
257
258 if (*cursor)
259 printf ("%s", cursor);
260
261 return info->flags & METADATA_PRINTER_FLAG_ONE_RUN;
262#undef info
263}
264
265
266/**
267 * Print a search result according to the current formats
268 *
269 * @param filename the filename for this result
270 * @param uri the `struct GNUNET_FS_Uri` this result refers to
271 * @param metadata the `struct GNUNET_CONTAINER_MetaData` associated with this
272 result
273 * @param resultnum the result number
274 * @param is_directory GNUNET_YES if this is a directory, otherwise GNUNET_NO
275 * @author madmurphy
276 */
277static void
278print_search_result (const char *const filename,
279 const struct GNUNET_FS_Uri *const uri,
280 const struct GNUNET_CONTAINER_MetaData *const metadata,
281 const unsigned int resultnum,
282 const int is_directory)
283{
284
285 const char *cursor = GNUNET_YES == is_directory ?
286 dir_format_string
287 : format_string;
288
289 const char *next_spec = strchr(cursor, '%');
290 const char *next_esc = strchr(cursor, '\\');
291 char *placeholder;
292 struct GNUNET_SEARCH_MetadataPrinterInfo info;
293
294parse_format:
295 /* If an escape sequence exists before the next format specifier... */
296 if (next_esc && (!next_spec || next_esc < next_spec))
297 {
298 if (next_esc > cursor)
299 fwrite (cursor, 1, next_esc - cursor, stdout);
300
301 cursor = print_escape_sequence (next_esc);
302 next_esc = strchr(cursor, '\\');
303 goto parse_format;
304 }
305
306 /* If a format specifier exists before the next escape sequence... */
307 if (next_spec && (!next_esc || next_spec < next_esc))
308 {
309 if (next_spec > cursor)
310 fwrite (cursor, 1, next_spec - cursor, stdout);
311
312 switch (*++next_spec)
313 {
314 /* All metadata fields */
315 case 'a':
316 info.flags = METADATA_PRINTER_FLAG_NONE;
317
318iterate_meta:
319 info.counter = 0;
320 GNUNET_CONTAINER_meta_data_iterate (metadata, &item_printer, &info);
321 break;
322 /* File's name */
323 case 'f':
324 if (GNUNET_YES == is_directory)
325 {
326 printf ("%s%s", filename, GNUNET_FS_DIRECTORY_EXT);
327 break;
328 }
329 printf ("%s", filename);
330 break;
331 /* Only the first metadata field */
332 case 'j':
333 info.flags = METADATA_PRINTER_FLAG_ONE_RUN;
334 goto iterate_meta;
335 /* File name's length */
336 case 'l':
337 printf ("%lu",
338 (long unsigned int) ( GNUNET_YES == is_directory ?
339 strlen(filename) +
340 (sizeof(GNUNET_FS_DIRECTORY_EXT) - 1)
341 :
342 strlen(filename)));
343 break;
344 /* File's mime type */
345 case 'm':
346 if (GNUNET_YES == is_directory)
347 {
348 printf ("%s", GNUNET_FS_DIRECTORY_MIME);
349 break;
350 }
351 placeholder = GNUNET_CONTAINER_meta_data_get_by_type (
352 metadata,
353 EXTRACTOR_METATYPE_MIMETYPE);
354 printf ("%s", placeholder ? placeholder : GENERIC_FILE_MIMETYPE);
355 GNUNET_free (placeholder);
356 break;
357 /* Result number */
358 case 'n': printf ("%u", resultnum); break;
359 /* File's size */
360 case 's':
361 printf ("%" PRIu64, GNUNET_FS_uri_chk_get_file_size (uri));
362 break;
363 /* File's URI */
364 case 'u':
365 placeholder = GNUNET_FS_uri_to_string (uri);
366 printf ("%s", placeholder);
367 GNUNET_free (placeholder);
368 break;
369
370 /* We can add as many cases as we want here... */
371
372 /* Handle `%123#a` and `%123#j` (e.g. `%5#j` is a book title) */
373 case '0': case '1': case '2': case '3': case '4':
374 case '5': case '6': case '7': case '8': case '9':
375 cursor = next_spec;
376 info.type = *cursor - 48;
377 while (isdigit(*++cursor) && info.type < (INT_MAX - *cursor + 48) / 10)
378 info.type = info.type * 10 + *cursor - 48;
379 if (info.type == 0 || *cursor != '#')
380 goto not_a_specifier;
381 switch (*++cursor)
382 {
383 /* All metadata fields of type `info.type` */
384 case 'a':
385 next_spec = cursor;
386 info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE;
387 goto iterate_meta;
388
389 /* Only the first metadata field of type `info.type` */
390 case 'j':
391 next_spec = cursor;
392 info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE |
393 METADATA_PRINTER_FLAG_ONE_RUN;
394 goto iterate_meta;
395 }
396 goto not_a_specifier;
397
398 /* All other cases */
399 case '%': putchar('%'); break;
400 case '\0': putchar('%'); return;
401
402not_a_specifier:
403 default: printf ("%%%c", *next_spec); break;
404 }
405 cursor = next_spec + 1;
406 next_spec = strchr(cursor, '%');
407 goto parse_format;
408 }
409
410 if (*cursor)
411 printf ("%s", cursor);
412}
413
414
415static void
416clean_task (void *const cls)
417{
418 size_t dsize;
419 void *ddata;
420
421 GNUNET_FS_stop (ctx);
422 ctx = NULL;
423 if (output_filename == NULL)
424 return;
425 if (GNUNET_OK !=
426 GNUNET_FS_directory_builder_finish (db, &dsize, &ddata))
427 {
428 GNUNET_break (0);
429 GNUNET_free (output_filename);
430 return;
431 }
432 (void) GNUNET_DISK_directory_remove (output_filename);
433 if (GNUNET_OK !=
434 GNUNET_DISK_fn_write (output_filename,
435 ddata,
436 dsize,
437 GNUNET_DISK_PERM_USER_READ
438 | GNUNET_DISK_PERM_USER_WRITE))
439 {
440 GNUNET_SEARCH_log(GNUNET_ERROR_TYPE_ERROR,
441 _ ("Failed to write directory with search results to "
442 "`%s'\n"),
443 output_filename);
444 }
445 GNUNET_free (ddata);
446 GNUNET_free (output_filename);
447}
448
449
450/**
451 * Called by FS client to give information about the progress of an
452 * operation.
453 *
454 * @param cls closure
455 * @param info details about the event, specifying the event type
456 * and various bits about the event
457 * @return client-context (for the next progress call
458 * for this operation; should be set to NULL for
459 * SUSPEND and STOPPED events). The value returned
460 * will be passed to future callbacks in the respective
461 * field in the GNUNET_FS_ProgressInfo struct.
462 */
463static void *
464progress_cb (void *const cls,
465 const struct GNUNET_FS_ProgressInfo *const info)
466{
467 static unsigned int cnt;
468 int is_directory;
469 char *filename;
470
471 switch (info->status)
472 {
473 case GNUNET_FS_STATUS_SEARCH_START:
474 break;
475
476 case GNUNET_FS_STATUS_SEARCH_RESULT:
477 if (silent_mode)
478 break;
479
480 if (db != NULL)
481 GNUNET_FS_directory_builder_add (
482 db,
483 info->value.search.specifics.result.uri,
484 info->value.search.specifics.result.meta,
485 NULL);
486
487 cnt++;
488 filename = GNUNET_CONTAINER_meta_data_get_by_type (
489 info->value.search.specifics.result.meta,
490 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
491 is_directory = GNUNET_FS_meta_data_test_for_directory (
492 info->value.search.specifics.result.meta);
493 if (NULL != filename)
494 {
495 while ((filename[0] != '\0') && ('/' == filename[strlen (filename) - 1]))
496 filename[strlen (filename) - 1] = '\0';
497 GNUNET_DISK_filename_canonicalize (filename);
498 }
499 print_search_result ( filename ?
500 filename
501 : is_directory ?
502 GENERIC_DIRECTORY_NAME
503 :
504 GENERIC_FILE_NAME,
505 info->value.search.specifics.result.uri,
506 info->value.search.specifics.result.meta,
507 cnt,
508 is_directory);
509 fflush (stdout);
510 GNUNET_free (filename);
511 results++;
512 if ((results_limit > 0) && (results >= results_limit))
513 {
514 GNUNET_SCHEDULER_shutdown ();
515 /* otherwise the function might keep printing results for a while... */
516 silent_mode = GNUNET_YES;
517 }
518 break;
519
520 case GNUNET_FS_STATUS_SEARCH_UPDATE:
521 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
522 /* ignore */
523 break;
524
525 case GNUNET_FS_STATUS_SEARCH_ERROR:
526 GNUNET_SEARCH_log(GNUNET_ERROR_TYPE_ERROR,
527 _ ("Error searching: %s.\n"),
528 info->value.search.specifics.error.message);
529 GNUNET_SCHEDULER_shutdown ();
530 break;
531
532 case GNUNET_FS_STATUS_SEARCH_STOPPED:
533 GNUNET_SCHEDULER_add_now (&clean_task, NULL);
534 break;
535
536 default:
537 GNUNET_SEARCH_log(GNUNET_ERROR_TYPE_ERROR,
538 _ ("Unexpected status: %d\n"),
539 info->status);
540 break;
541 }
542 return NULL;
543}
544
545
546static void
547shutdown_task (void *const cls)
548{
549 if (sc != NULL)
550 {
551 GNUNET_FS_search_stop (sc);
552 sc = NULL;
553 }
554}
555
556
557static void
558timeout_task (void *const cls)
559{
560 tt = NULL;
561 silent_mode = GNUNET_YES;
562 GNUNET_SCHEDULER_shutdown ();
563}
564
565
566/**
567 * Main function that will be run by the scheduler.
568 *
569 * @param cls closure
570 * @param args remaining command-line arguments
571 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
572 * @param cfgarg configuration
573 */
574static void
575run (void *const cls,
576 char *const *const args,
577 const char *const cfgfile,
578 const struct GNUNET_CONFIGURATION_Handle *const cfgarg)
579{
580 struct GNUNET_FS_Uri *uri;
581 unsigned int argc;
582 enum GNUNET_FS_SearchOptions options;
583
584 if (silent_mode && bookmark_only)
585 {
586 fprintf (stderr,
587 _ ("Conflicting options --bookmark-only and --silent.\n"));
588 ret = 1;
589 return;
590 }
591 if (bookmark_only && output_filename)
592 {
593 fprintf (stderr,
594 _ ("Conflicting options --bookmark-only and --output.\n"));
595 ret = 1;
596 return;
597 }
598 if (silent_mode && !output_filename)
599 {
600 fprintf (stderr, _ ("An output file is mandatory for silent mode.\n"));
601 ret = 1;
602 return;
603 }
604 if (NULL == dir_format_string)
605 dir_format_string = format_string ? format_string
606 : verbose ? VERB_DEFAULT_DIR_FORMAT
607 : DEFAULT_DIR_FORMAT;
608 if (NULL == format_string)
609 format_string = verbose ? VERB_DEFAULT_FILE_FORMAT
610 : DEFAULT_FILE_FORMAT;
611 if (NULL == meta_format_string)
612 meta_format_string = DEFAULT_META_FORMAT;
613 argc = 0;
614 while (NULL != args[argc])
615 argc++;
616 uri = GNUNET_FS_uri_ksk_create_from_args (argc, (const char **) args);
617 if (NULL == uri)
618 {
619 fprintf (stderr,
620 "%s",
621 _ ("Could not create keyword URI from arguments.\n"));
622 ret = 1;
623 return;
624 }
625 if (!GNUNET_FS_uri_test_ksk (uri) && !GNUNET_FS_uri_test_sks (uri))
626 {
627 fprintf (stderr,
628 "%s",
629 _ ("Invalid URI. Valid URIs for searching are keyword query "
630 "URIs\n(\"gnunet://fs/ksk/...\") and namespace content URIs "
631 "(\"gnunet://fs/sks/...\").\n"));
632 GNUNET_FS_uri_destroy (uri);
633 ret = 1;
634 return;
635 }
636 if (bookmark_only)
637 {
638 char * bmstr = GNUNET_FS_uri_to_string (uri);
639 printf ("%s\n", bmstr);
640 GNUNET_free (bmstr);
641 GNUNET_FS_uri_destroy (uri);
642 ret = 0;
643 return;
644 }
645 cfg = cfgarg;
646 ctx = GNUNET_FS_start (cfg,
647 "gnunet-search",
648 &progress_cb,
649 NULL,
650 GNUNET_FS_FLAGS_NONE,
651 GNUNET_FS_OPTIONS_END);
652 if (NULL == ctx)
653 {
654 fprintf (stderr, _ ("Could not initialize the `%s` subsystem.\n"), "FS");
655 GNUNET_FS_uri_destroy (uri);
656 ret = 1;
657 return;
658 }
659 if (output_filename != NULL)
660 db = GNUNET_FS_directory_builder_create (NULL);
661 options = GNUNET_FS_SEARCH_OPTION_NONE;
662 if (local_only)
663 options |= GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY;
664 sc = GNUNET_FS_search_start (ctx, uri, anonymity, options, NULL);
665 GNUNET_FS_uri_destroy (uri);
666 if (NULL == sc)
667 {
668 fprintf (stderr, "%s", _ ("Could not start searching.\n"));
669 GNUNET_FS_stop (ctx);
670 ret = 1;
671 return;
672 }
673 if (0 != timeout.rel_value_us)
674 tt = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_task, NULL);
675 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
676}
677
678
679/**
680 * The main function to search GNUnet.
681 *
682 * @param argc number of arguments from the command line
683 * @param argv command line arguments
684 * @return 0 ok, an error number on error
685 */
686int
687main (int argc, char *const *argv)
688{
689 struct GNUNET_GETOPT_CommandLineOption options[] =
690 { GNUNET_GETOPT_option_uint (
691 'a',
692 "anonymity",
693 "LEVEL",
694 gettext_noop ("set the desired LEVEL of receiver-anonymity (default: "
695 "1)"),
696 &anonymity),
697 GNUNET_GETOPT_option_flag (
698 'b',
699 "bookmark-only",
700 gettext_noop ("do not search, print only the URI that points to this "
701 "search"),
702 &bookmark_only),
703 GNUNET_GETOPT_option_string (
704 'F',
705 "dir-printf",
706 "FORMAT",
707 gettext_noop ("write search results for directories according to "
708 "FORMAT; accepted placeholders are: %a, %f, %j, %l, %m, "
709 "%n, %s; defaults to the value of --printf when omitted "
710 "or to `" HELP_DEFAULT_DIR_FORMAT "` if --printf is "
711 "omitted too"),
712 &dir_format_string),
713 GNUNET_GETOPT_option_string (
714 'f',
715 "printf",
716 "FORMAT",
717 gettext_noop ("write search results according to FORMAT; accepted "
718 "placeholders are: %a, %f, %j, %l, %m, %n, %s; defaults "
719 "to `" HELP_DEFAULT_FILE_FORMAT "` when omitted"),
720 &format_string),
721 GNUNET_GETOPT_option_string (
722 'i',
723 "iter-printf",
724 "FORMAT",
725 gettext_noop ("when the %a or %j placeholders appear in --printf or "
726 "--dir-printf, list each metadata property according to "
727 "FORMAT; accepted placeholders are: %i, %l, %n, %p"
728 HELP_EXTRACTOR_TEXTADD ", %w; defaults to `"
729 HELP_DEFAULT_META_FORMAT "` when omitted"),
730 &meta_format_string),
731 GNUNET_GETOPT_option_uint ('N',
732 "results",
733 "VALUE",
734 gettext_noop ("automatically terminate search "
735 "after VALUE results are found"),
736 &results_limit),
737 GNUNET_GETOPT_option_flag (
738 'n',
739 "no-network",
740 gettext_noop ("only search the local peer (no P2P network search)"),
741 &local_only),
742 GNUNET_GETOPT_option_string (
743 'o',
744 "output",
745 "FILENAME",
746 gettext_noop ("create a GNUnet directory with search results at "
747 "FILENAME (e.g. `gnunet-search --output=commons"
748 GNUNET_FS_DIRECTORY_EXT " commons`)"),
749 &output_filename),
750 GNUNET_GETOPT_option_flag (
751 's',
752 "silent",
753 gettext_noop ("silent mode (requires the --output argument)"),
754 &silent_mode),
755 GNUNET_GETOPT_option_relative_time (
756 't',
757 "timeout",
758 "DELAY",
759 gettext_noop ("automatically terminate search after DELAY; the value "
760 "given must be a number followed by a space and a time "
761 "unit, for example \"500 ms\"; without a unit it defaults "
762 "to microseconds - 1000000 = 1 second; if 0 or omitted "
763 "it means to wait for CTRL-C"),
764 &timeout),
765 GNUNET_GETOPT_option_increment_uint (
766 'V',
767 "verbose",
768 gettext_noop ("be verbose (append \"%a\\n\" to the default --printf and "
769 "--dir-printf arguments - ignored when these are provided "
770 "by the user)"),
771 &verbose),
772 GNUNET_GETOPT_OPTION_END };
773
774 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
775 return 12;
776
777 if (GNUNET_SYSERR ==
778 GNUNET_PROGRAM_run (argc,
779 argv,
780 "gnunet-search [OPTIONS] KEYWORD1 KEYWORD2 ...",
781 gettext_noop ("Search for files that have been "
782 "published on GNUnet\n"),
783 options,
784 &run,
785 NULL))
786 ret = 1;
787
788 GNUNET_free_nz ((void *) argv);
789 return ret;
790}
791
792
793/* end of gnunet-search.c */
diff --git a/src/fs/gnunet-service-fs.c b/src/fs/gnunet-service-fs.c
deleted file mode 100644
index 05c7e2192..000000000
--- a/src/fs/gnunet-service-fs.c
+++ /dev/null
@@ -1,1425 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2014, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs.c
23 * @brief gnunet anonymity protocol implementation
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <float.h>
28#include "gnunet_constants.h"
29#include "gnunet_core_service.h"
30#include "gnunet_dht_service.h"
31#include "gnunet_datastore_service.h"
32#include "gnunet_load_lib.h"
33#include "gnunet_peer_lib.h"
34#include "gnunet_protocols.h"
35#include "gnunet_signatures.h"
36#include "gnunet_statistics_service.h"
37#include "gnunet_transport_service.h"
38#include "gnunet_util_lib.h"
39#include "gnunet-service-fs_cp.h"
40#include "gnunet-service-fs_indexing.h"
41#include "gnunet-service-fs_pe.h"
42#include "gnunet-service-fs_pr.h"
43#include "gnunet-service-fs_push.h"
44#include "gnunet-service-fs_put.h"
45#include "gnunet-service-fs_cadet.h"
46#include "fs.h"
47#include "fs_api.h"
48
49/**
50 * Size for the hash map for DHT requests from the FS
51 * service. Should be about the number of concurrent
52 * DHT requests we plan to make.
53 */
54#define FS_DHT_HT_SIZE 1024
55
56
57/**
58 * How quickly do we age cover traffic? At the given
59 * time interval, remaining cover traffic counters are
60 * decremented by 1/16th.
61 */
62#define COVER_AGE_FREQUENCY GNUNET_TIME_relative_multiply ( \
63 GNUNET_TIME_UNIT_SECONDS, 5)
64
65/**
66 * Collect an instance number of statistics? May cause excessive IPC.
67 */
68#define INSANE_STATISTICS GNUNET_NO
69
70
71/**
72 * Doubly-linked list of requests we are performing
73 * on behalf of the same client.
74 */
75struct ClientRequest
76{
77 /**
78 * This is a doubly-linked list.
79 */
80 struct ClientRequest *next;
81
82 /**
83 * This is a doubly-linked list.
84 */
85 struct ClientRequest *prev;
86
87 /**
88 * Request this entry represents.
89 */
90 struct GSF_PendingRequest *pr;
91
92 /**
93 * Client list this request belongs to.
94 */
95 struct GSF_LocalClient *lc;
96
97 /**
98 * Task scheduled to destroy the request.
99 */
100 struct GNUNET_SCHEDULER_Task *kill_task;
101};
102
103
104/**
105 * Replies to be transmitted to the client. The actual
106 * response message is allocated after this struct.
107 */
108struct ClientResponse
109{
110 /**
111 * This is a doubly-linked list.
112 */
113 struct ClientResponse *next;
114
115 /**
116 * This is a doubly-linked list.
117 */
118 struct ClientResponse *prev;
119
120 /**
121 * Client list entry this response belongs to.
122 */
123 struct GSF_LocalClient *lc;
124
125 /**
126 * Number of bytes in the response.
127 */
128 size_t msize;
129};
130
131
132/**
133 * Information we track while handling an index
134 * start request from a client.
135 */
136struct IndexStartContext
137{
138 /**
139 * This is a doubly linked list.
140 */
141 struct IndexStartContext *next;
142
143 /**
144 * This is a doubly linked list.
145 */
146 struct IndexStartContext *prev;
147
148 /**
149 * Name of the indexed file.
150 */
151 char *filename;
152
153 /**
154 * Context for transmitting confirmation to client.
155 */
156 struct GSF_LocalClient *lc;
157
158 /**
159 * Context for hashing of the file.
160 */
161 struct GNUNET_CRYPTO_FileHashContext *fhc;
162
163 /**
164 * Hash of the contents of the file.
165 */
166 struct GNUNET_HashCode file_id;
167};
168
169
170/**
171 * A local client.
172 */
173struct GSF_LocalClient
174{
175 /**
176 * ID of the client.
177 */
178 struct GNUNET_SERVICE_Client *client;
179
180 /**
181 * Queue for sending replies.
182 */
183 struct GNUNET_MQ_Handle *mq;
184
185 /**
186 * Head of list of requests performed on behalf
187 * of this client right now.
188 */
189 struct ClientRequest *cr_head;
190
191 /**
192 * Tail of list of requests performed on behalf
193 * of this client right now.
194 */
195 struct ClientRequest *cr_tail;
196
197 /**
198 * This is a doubly linked list.
199 */
200 struct IndexStartContext *isc_head;
201
202 /**
203 * This is a doubly linked list.
204 */
205 struct IndexStartContext *isc_tail;
206
207 /**
208 * Head of linked list of responses.
209 */
210 struct ClientResponse *res_head;
211
212 /**
213 * Tail of linked list of responses.
214 */
215 struct ClientResponse *res_tail;
216};
217
218
219/* ****************************** globals ****************************** */
220
221/**
222 * Our connection to the datastore.
223 */
224struct GNUNET_DATASTORE_Handle *GSF_dsh;
225
226/**
227 * Our configuration.
228 */
229const struct GNUNET_CONFIGURATION_Handle *GSF_cfg;
230
231/**
232 * Handle for reporting statistics.
233 */
234struct GNUNET_STATISTICS_Handle *GSF_stats;
235
236/**
237 * Handle for DHT operations.
238 */
239struct GNUNET_DHT_Handle *GSF_dht;
240
241/**
242 * How long do requests typically stay in the routing table?
243 */
244struct GNUNET_LOAD_Value *GSF_rt_entry_lifetime;
245
246/**
247 * Running average of the observed latency to other peers (round trip).
248 * Initialized to 5s as the initial default.
249 */
250struct GNUNET_TIME_Relative GSF_avg_latency = { 500 };
251
252/**
253 * Handle to ATS service.
254 */
255struct GNUNET_ATS_PerformanceHandle *GSF_ats;
256
257
258/**
259 * Typical priorities we're seeing from other peers right now. Since
260 * most priorities will be zero, this value is the weighted average of
261 * non-zero priorities seen "recently". In order to ensure that new
262 * values do not dramatically change the ratio, values are first
263 * "capped" to a reasonable range (+N of the current value) and then
264 * averaged into the existing value by a ratio of 1:N. Hence
265 * receiving the largest possible priority can still only raise our
266 * "current_priorities" by at most 1.
267 */
268double GSF_current_priorities;
269
270/**
271 * Size of the datastore queue we assume for common requests.
272 */
273unsigned int GSF_datastore_queue_size;
274
275/**
276 * How many query messages have we received 'recently' that
277 * have not yet been claimed as cover traffic?
278 */
279unsigned int GSF_cover_query_count;
280
281/**
282 * How many content messages have we received 'recently' that
283 * have not yet been claimed as cover traffic?
284 */
285unsigned int GSF_cover_content_count;
286
287/**
288 * Our block context.
289 */
290struct GNUNET_BLOCK_Context *GSF_block_ctx;
291
292/**
293 * Pointer to handle to the core service (points to NULL until we've
294 * connected to it).
295 */
296struct GNUNET_CORE_Handle *GSF_core;
297
298/**
299 * Are we introducing randomized delays for better anonymity?
300 */
301int GSF_enable_randomized_delays;
302
303/**
304 * Identity of this peer.
305 */
306struct GNUNET_PeerIdentity GSF_my_id;
307
308/* ***************************** locals ******************************* */
309
310/**
311 * Configuration for block library.
312 */
313static struct GNUNET_CONFIGURATION_Handle *block_cfg;
314
315/**
316 * Private key of this peer. Used to sign LOC URI requests.
317 */
318static struct GNUNET_CRYPTO_EddsaPrivateKey pk;
319
320/**
321 * ID of our task that we use to age the cover counters.
322 */
323static struct GNUNET_SCHEDULER_Task *cover_age_task;
324
325/**
326 * Datastore 'GET' load tracking.
327 */
328static struct GNUNET_LOAD_Value *datastore_get_load;
329
330
331/**
332 * Creates a fresh local client handle.
333 *
334 * @param cls NULL
335 * @param client handle of the client
336 * @param mq message queue for @a client
337 * @return handle to local client entry
338 */
339static void *
340client_connect_cb (void *cls,
341 struct GNUNET_SERVICE_Client *client,
342 struct GNUNET_MQ_Handle *mq)
343{
344 struct GSF_LocalClient *pos;
345
346 pos = GNUNET_new (struct GSF_LocalClient);
347 pos->client = client;
348 pos->mq = mq;
349 return pos;
350}
351
352
353/**
354 * Free the given client request.
355 *
356 * @param cls the client request to free
357 */
358static void
359client_request_destroy (void *cls)
360{
361 struct ClientRequest *cr = cls;
362 struct GSF_LocalClient *lc = cr->lc;
363
364 cr->kill_task = NULL;
365 GNUNET_CONTAINER_DLL_remove (lc->cr_head,
366 lc->cr_tail,
367 cr);
368 GSF_pending_request_cancel_ (cr->pr,
369 GNUNET_YES);
370 GNUNET_STATISTICS_update (GSF_stats,
371 gettext_noop ("# client searches active"),
372 -1,
373 GNUNET_NO);
374 GNUNET_free (cr);
375}
376
377
378/**
379 * Handle a reply to a pending request. Also called if a request
380 * expires (then with data == NULL). The handler may be called
381 * many times (depending on the request type), but will not be
382 * called during or after a call to #GSF_pending_request_cancel()
383 * and will also not be called anymore after a call signalling
384 * expiration.
385 *
386 * @param cls user-specified closure
387 * @param eval evaluation of the result
388 * @param pr handle to the original pending request
389 * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown"
390 * @param expiration when does @a data expire?
391 * @param last_transmission when was the last time we've tried to download this block? (FOREVER if unknown)
392 * @param type type of the block
393 * @param data response data, NULL on request expiration
394 * @param data_len number of bytes in @a data
395 */
396static void
397client_response_handler (void *cls,
398 enum GNUNET_BLOCK_ReplyEvaluationResult eval,
399 struct GSF_PendingRequest *pr,
400 uint32_t reply_anonymity_level,
401 struct GNUNET_TIME_Absolute expiration,
402 struct GNUNET_TIME_Absolute last_transmission,
403 enum GNUNET_BLOCK_Type type,
404 const void *data,
405 size_t data_len)
406{
407 struct ClientRequest *cr = cls;
408 struct GSF_LocalClient *lc;
409 struct GNUNET_MQ_Envelope *env;
410 struct ClientPutMessage *pm;
411 const struct GSF_PendingRequestData *prd;
412
413 if (NULL == data)
414 {
415 /* local-only request, with no result, clean up. */
416 if (NULL == cr->kill_task)
417 cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy,
418 cr);
419 return;
420 }
421 prd = GSF_pending_request_get_data_ (pr);
422 GNUNET_break (type != GNUNET_BLOCK_TYPE_ANY);
423 if ((prd->type != type) && (prd->type != GNUNET_BLOCK_TYPE_ANY))
424 {
425 GNUNET_break (0);
426 return;
427 }
428 GNUNET_STATISTICS_update (GSF_stats,
429 gettext_noop
430 ("# replies received for local clients"), 1,
431 GNUNET_NO);
432 GNUNET_assert (pr == cr->pr);
433 lc = cr->lc;
434 env = GNUNET_MQ_msg_extra (pm,
435 data_len,
436 GNUNET_MESSAGE_TYPE_FS_PUT);
437 pm->type = htonl (type);
438 pm->expiration = GNUNET_TIME_absolute_hton (expiration);
439 pm->last_transmission = GNUNET_TIME_absolute_hton (last_transmission);
440 pm->num_transmissions = htonl (prd->num_transmissions);
441 pm->respect_offered = htonl (prd->respect_offered);
442 GNUNET_memcpy (&pm[1],
443 data,
444 data_len);
445 GNUNET_MQ_send (lc->mq,
446 env);
447 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448 "Queued reply to query `%s' for local client\n",
449 GNUNET_h2s (&prd->query));
450 if (GNUNET_BLOCK_REPLY_OK_LAST != eval)
451 {
452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453 "Evaluation %d - keeping query alive\n",
454 (int) eval);
455 return;
456 }
457 if (NULL == cr->kill_task)
458 cr->kill_task = GNUNET_SCHEDULER_add_now (&client_request_destroy,
459 cr);
460}
461
462
463/**
464 * A client disconnected from us. Tear down the local client
465 * record.
466 *
467 * @param cls unused
468 * @param client handle of the client
469 * @param app_ctx the `struct GSF_LocalClient`
470 */
471static void
472client_disconnect_cb (void *cls,
473 struct GNUNET_SERVICE_Client *client,
474 void *app_ctx)
475{
476 struct GSF_LocalClient *lc = app_ctx;
477 struct IndexStartContext *isc;
478 struct ClientRequest *cr;
479 struct ClientResponse *res;
480
481 while (NULL != (cr = lc->cr_head))
482 {
483 if (NULL != cr->kill_task)
484 GNUNET_SCHEDULER_cancel (cr->kill_task);
485 client_request_destroy (cr);
486 }
487 while (NULL != (res = lc->res_head))
488 {
489 GNUNET_CONTAINER_DLL_remove (lc->res_head,
490 lc->res_tail,
491 res);
492 GNUNET_free (res);
493 }
494 while (NULL != (isc = lc->isc_head))
495 {
496 GNUNET_CONTAINER_DLL_remove (lc->isc_head,
497 lc->isc_tail,
498 isc);
499 GNUNET_CRYPTO_hash_file_cancel (isc->fhc);
500 GNUNET_free (isc);
501 }
502 GNUNET_free (lc);
503}
504
505
506/**
507 * Task that periodically ages our cover traffic statistics.
508 *
509 * @param cls unused closure
510 */
511static void
512age_cover_counters (void *cls)
513{
514 GSF_cover_content_count = (GSF_cover_content_count * 15) / 16;
515 GSF_cover_query_count = (GSF_cover_query_count * 15) / 16;
516 cover_age_task =
517 GNUNET_SCHEDULER_add_delayed (COVER_AGE_FREQUENCY,
518 &age_cover_counters,
519 NULL);
520}
521
522
523/**
524 * We've just now completed a datastore request. Update our
525 * datastore load calculations.
526 *
527 * @param start time when the datastore request was issued
528 */
529void
530GSF_update_datastore_delay_ (struct GNUNET_TIME_Absolute start)
531{
532 struct GNUNET_TIME_Relative delay;
533
534 delay = GNUNET_TIME_absolute_get_duration (start);
535 GNUNET_LOAD_update (datastore_get_load, delay.rel_value_us);
536}
537
538
539/**
540 * Test if the DATABASE (GET) load on this peer is too high
541 * to even consider processing the query at
542 * all.
543 *
544 * @param priority priority of the request (used as a reference point to compare with the load)
545 * @return #GNUNET_YES if the load is too high to do anything (load high)
546 * #GNUNET_NO to process normally (load normal)
547 * #GNUNET_SYSERR to process for free (load low)
548 */
549int
550GSF_test_get_load_too_high_ (uint32_t priority)
551{
552 double ld;
553
554 ld = GNUNET_LOAD_get_load (datastore_get_load);
555 if (ld < 1)
556 return GNUNET_SYSERR;
557 if (ld <= priority)
558 return GNUNET_NO;
559 return GNUNET_YES;
560}
561
562
563/**
564 * We've received peer performance information. Update
565 * our running average for the P2P latency.
566 *
567 * @param cls closure
568 * @param address the address
569 * @param active is this address in active use
570 * @param bandwidth_out assigned outbound bandwidth for the connection
571 * @param bandwidth_in assigned inbound bandwidth for the connection
572 * @param prop performance data for the address (as far as known)
573 */
574static void
575update_latencies (void *cls,
576 const struct GNUNET_HELLO_Address *address,
577 int active,
578 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
579 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
580 const struct GNUNET_ATS_Properties *prop)
581{
582 if (NULL == address)
583 {
584 /* ATS service temporarily disconnected */
585 return;
586 }
587
588 if (GNUNET_YES != active)
589 return;
590 GSF_update_peer_latency_ (&address->peer,
591 prop->delay);
592 GSF_avg_latency.rel_value_us =
593 (GSF_avg_latency.rel_value_us * 31
594 + GNUNET_MIN (5000, prop->delay.rel_value_us)) / 32;
595 GNUNET_STATISTICS_set (GSF_stats,
596 gettext_noop ("# running average P2P latency (ms)"),
597 GSF_avg_latency.rel_value_us / 1000LL,
598 GNUNET_NO);
599}
600
601
602/**
603 * Check P2P "PUT" message.
604 *
605 * @param cls closure with the `struct GSF_ConnectedPeer`
606 * @param message the actual message
607 * @return #GNUNET_OK to keep the connection open,
608 * #GNUNET_SYSERR to close it (signal serious error)
609 */
610static int
611check_p2p_put (void *cls,
612 const struct PutMessage *put)
613{
614 enum GNUNET_BLOCK_Type type;
615
616 type = ntohl (put->type);
617 if (GNUNET_BLOCK_TYPE_FS_ONDEMAND == type)
618 {
619 GNUNET_break_op (0);
620 return GNUNET_SYSERR;
621 }
622 return GNUNET_OK;
623}
624
625
626/**
627 * We have a new request, consider forwarding it to the given
628 * peer.
629 *
630 * @param cls the `struct GSF_PendingRequest`
631 * @param peer identity of the peer
632 * @param cp handle to the connected peer record
633 * @param ppd peer performance data
634 */
635static void
636consider_request_for_forwarding (void *cls,
637 const struct GNUNET_PeerIdentity *peer,
638 struct GSF_ConnectedPeer *cp,
639 const struct GSF_PeerPerformanceData *ppd)
640{
641 struct GSF_PendingRequest *pr = cls;
642
643 if (GNUNET_YES !=
644 GSF_pending_request_test_target_ (pr, peer))
645 {
646#if INSANE_STATISTICS
647 GNUNET_STATISTICS_update (GSF_stats,
648 gettext_noop ("# Loopback routes suppressed"), 1,
649 GNUNET_NO);
650#endif
651 return;
652 }
653 GSF_plan_add_ (cp,
654 pr);
655}
656
657
658/**
659 * Function to be called after we're done processing
660 * replies from the local lookup. If the result status
661 * code indicates that there may be more replies, plan
662 * forwarding the request.
663 *
664 * @param cls closure (NULL)
665 * @param pr the pending request we were processing
666 * @param result final datastore lookup result
667 */
668void
669GSF_consider_forwarding (void *cls,
670 struct GSF_PendingRequest *pr,
671 enum GNUNET_BLOCK_ReplyEvaluationResult result)
672{
673 if (GNUNET_BLOCK_REPLY_OK_LAST == result)
674 return; /* we're done... */
675 if (GNUNET_YES !=
676 GSF_pending_request_test_active_ (pr))
677 return; /* request is not actually active, skip! */
678 GSF_iterate_connected_peers_ (&consider_request_for_forwarding,
679 pr);
680}
681
682
683/**
684 * Check P2P "GET" request.
685 *
686 * @param cls closure
687 * @param gm the actual message
688 * @return #GNUNET_OK to keep the connection open,
689 * #GNUNET_SYSERR to close it (signal serious error)
690 */
691static int
692check_p2p_get (void *cls,
693 const struct GetMessage *gm)
694{
695 size_t msize;
696 unsigned int bm;
697 unsigned int bits;
698 size_t bfsize;
699
700 msize = ntohs (gm->header.size);
701 bm = ntohl (gm->hash_bitmap);
702 bits = 0;
703 while (bm > 0)
704 {
705 if (1 == (bm & 1))
706 bits++;
707 bm >>= 1;
708 }
709 if (msize < sizeof(struct GetMessage) + bits * sizeof(struct
710 GNUNET_PeerIdentity))
711 {
712 GNUNET_break_op (0);
713 return GNUNET_SYSERR;
714 }
715 bfsize = msize - sizeof(struct GetMessage) - bits * sizeof(struct
716 GNUNET_PeerIdentity);
717 /* bfsize must be power of 2, check! */
718 if (0 != ((bfsize - 1) & bfsize))
719 {
720 GNUNET_break_op (0);
721 return GNUNET_SYSERR;
722 }
723 return GNUNET_OK;
724}
725
726
727/**
728 * We're done with the local lookup, now consider
729 * P2P processing (depending on request options and
730 * result status). Also signal that we can now
731 * receive more request information from the client.
732 *
733 * @param cls the client doing the request (`struct GSF_LocalClient`)
734 * @param pr the pending request we were processing
735 * @param result final datastore lookup result
736 */
737static void
738start_p2p_processing (void *cls,
739 struct GSF_PendingRequest *pr,
740 enum GNUNET_BLOCK_ReplyEvaluationResult result)
741{
742 struct GSF_LocalClient *lc = cls;
743 struct GSF_PendingRequestData *prd;
744
745 GNUNET_SERVICE_client_continue (lc->client);
746 if (GNUNET_BLOCK_REPLY_OK_LAST == result)
747 return; /* we're done, 'pr' was already destroyed... */
748 prd = GSF_pending_request_get_data_ (pr);
749 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
750 "Finished database lookup for local request `%s' with result %d\n",
751 GNUNET_h2s (&prd->query),
752 result);
753 if (0 == prd->anonymity_level)
754 {
755 switch (prd->type)
756 {
757 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
758 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
759 /* the above block types MAY be available via 'cadet' */
760 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
761 "Considering cadet-based download for block\n");
762 GSF_cadet_lookup_ (pr);
763 break;
764
765 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
766 /* the above block types are in the DHT */
767 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
768 "Considering DHT-based search for block\n");
769 GSF_dht_lookup_ (pr);
770 break;
771
772 default:
773 GNUNET_break (0);
774 break;
775 }
776 }
777 GSF_consider_forwarding (NULL,
778 pr,
779 result);
780}
781
782
783/**
784 * Check #GNUNET_MESSAGE_TYPE_FS_START_SEARCH-message (search request
785 * from client).
786 *
787 * @param cls identification of the client
788 * @param sm the actual message
789 * @return #GNUNET_OK if @a sm is well-formed
790 */
791static int
792check_client_start_search (void *cls,
793 const struct SearchMessage *sm)
794{
795 uint16_t msize;
796
797 msize = ntohs (sm->header.size) - sizeof(struct SearchMessage);
798 if (0 != msize % sizeof(struct GNUNET_HashCode))
799 {
800 GNUNET_break (0);
801 return GNUNET_SYSERR;
802 }
803 return GNUNET_OK;
804}
805
806
807/**
808 * Handle #GNUNET_MESSAGE_TYPE_FS_START_SEARCH-message (search request
809 * from client).
810 *
811 * Responsible for creating the request entry itself and setting
812 * up reply callback and cancellation on client disconnect.
813 *
814 * @param cls identification of the client
815 * @param sm the actual message
816 */
817static void
818handle_client_start_search (void *cls,
819 const struct SearchMessage *sm)
820{
821 static struct GNUNET_PeerIdentity all_zeros;
822 struct GSF_LocalClient *lc = cls;
823 struct ClientRequest *cr;
824 struct GSF_PendingRequestData *prd;
825 uint16_t msize;
826 unsigned int sc;
827 enum GNUNET_BLOCK_Type type;
828 enum GSF_PendingRequestOptions options;
829
830 GNUNET_STATISTICS_update (GSF_stats,
831 gettext_noop ("# client searches received"),
832 1,
833 GNUNET_NO);
834 msize = ntohs (sm->header.size) - sizeof(struct SearchMessage);
835 sc = msize / sizeof(struct GNUNET_HashCode);
836 type = ntohl (sm->type);
837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
838 "Received request for `%s' of type %u from local client\n",
839 GNUNET_h2s (&sm->query),
840 (unsigned int) type);
841 cr = NULL;
842 /* detect duplicate UBLOCK requests */
843 if ((type == GNUNET_BLOCK_TYPE_FS_UBLOCK) ||
844 (type == GNUNET_BLOCK_TYPE_ANY))
845 {
846 cr = lc->cr_head;
847 while (NULL != cr)
848 {
849 prd = GSF_pending_request_get_data_ (cr->pr);
850 /* only unify with queries that hae not yet started local processing
851 (SEARCH_MESSAGE_OPTION_CONTINUED was always set) and that have a
852 matching query and type */
853 if ((GNUNET_YES != prd->has_started) &&
854 (0 != memcmp (&prd->query,
855 &sm->query,
856 sizeof(struct GNUNET_HashCode))) &&
857 (prd->type == type))
858 break;
859 cr = cr->next;
860 }
861 }
862 if (NULL != cr)
863 {
864 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
865 "Have existing request, merging content-seen lists.\n");
866 GSF_pending_request_update_ (cr->pr,
867 (const struct GNUNET_HashCode *) &sm[1],
868 sc);
869 GNUNET_STATISTICS_update (GSF_stats,
870 gettext_noop (
871 "# client searches updated (merged content seen list)"),
872 1,
873 GNUNET_NO);
874 }
875 else
876 {
877 GNUNET_STATISTICS_update (GSF_stats,
878 gettext_noop ("# client searches active"),
879 1,
880 GNUNET_NO);
881 cr = GNUNET_new (struct ClientRequest);
882 cr->lc = lc;
883 GNUNET_CONTAINER_DLL_insert (lc->cr_head,
884 lc->cr_tail,
885 cr);
886 options = GSF_PRO_LOCAL_REQUEST;
887 if (0 != (SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY & ntohl (sm->options)))
888 options |= GSF_PRO_LOCAL_ONLY;
889 cr->pr = GSF_pending_request_create_ (options, type,
890 &sm->query,
891 (0 !=
892 memcmp (&sm->target,
893 &all_zeros,
894 sizeof(struct
895 GNUNET_PeerIdentity)))
896 ? &sm->target : NULL, NULL,
897 0 /* bf */,
898 ntohl (sm->anonymity_level),
899 0 /* priority */,
900 0 /* ttl */,
901 0 /* sender PID */,
902 0 /* origin PID */,
903 (const struct
904 GNUNET_HashCode *) &sm[1], sc,
905 &client_response_handler,
906 cr);
907 }
908 if (0 != (SEARCH_MESSAGE_OPTION_CONTINUED & ntohl (sm->options)))
909 {
910 GNUNET_SERVICE_client_continue (lc->client);
911 return;
912 }
913 GSF_pending_request_get_data_ (cr->pr)->has_started = GNUNET_YES;
914 GSF_local_lookup_ (cr->pr,
915 &start_p2p_processing,
916 lc);
917}
918
919
920/**
921 * Handle request to sign a LOC URI (from client).
922 *
923 * @param cls identification of the client
924 * @param msg the actual message
925 */
926static void
927handle_client_loc_sign (void *cls,
928 const struct RequestLocSignatureMessage *msg)
929{
930 struct GSF_LocalClient *lc = cls;
931 struct GNUNET_FS_Uri base;
932 struct GNUNET_FS_Uri *loc;
933 struct GNUNET_MQ_Envelope *env;
934 struct ResponseLocSignatureMessage *resp;
935
936 GNUNET_break (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT ==
937 ntohl (msg->purpose));
938 base.type = GNUNET_FS_URI_CHK;
939 base.data.chk.chk = msg->chk;
940 base.data.chk.file_length = GNUNET_ntohll (msg->file_length);
941 loc = GNUNET_FS_uri_loc_create (&base,
942 &pk,
943 GNUNET_TIME_absolute_ntoh (
944 msg->expiration_time));
945 env = GNUNET_MQ_msg (resp,
946 GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGNATURE);
947 resp->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
948 resp->expiration_time = GNUNET_TIME_absolute_hton (
949 loc->data.loc.expirationTime);
950 resp->signature = loc->data.loc.contentSignature;
951 resp->peer = loc->data.loc.peer;
952 GNUNET_FS_uri_destroy (loc);
953 GNUNET_MQ_send (lc->mq,
954 env);
955 GNUNET_SERVICE_client_continue (lc->client);
956}
957
958
959/**
960 * Check INDEX_START-message.
961 *
962 * @param cls identification of the client
963 * @param ism the actual message
964 * @return #GNUNET_OK if @a ism is well-formed
965 */
966static int
967check_client_index_start (void *cls,
968 const struct IndexStartMessage *ism)
969{
970 char *fn;
971
972 GNUNET_MQ_check_zero_termination (ism);
973 if (0 != ism->reserved)
974 {
975 GNUNET_break (0);
976 return GNUNET_SYSERR;
977 }
978 fn = GNUNET_STRINGS_filename_expand ((const char *) &ism[1]);
979 if (NULL == fn)
980 {
981 GNUNET_break (0);
982 return GNUNET_SYSERR;
983 }
984 GNUNET_free (fn);
985 return GNUNET_OK;
986}
987
988
989/**
990 * We've validated the hash of the file we're about to index. Signal
991 * success to the client and update our internal data structures.
992 *
993 * @param isc the data about the index info entry for the request
994 */
995static void
996signal_index_ok (struct IndexStartContext *isc)
997{
998 struct GSF_LocalClient *lc = isc->lc;
999 struct GNUNET_MQ_Envelope *env;
1000 struct GNUNET_MessageHeader *msg;
1001
1002 GNUNET_FS_add_to_index (isc->filename,
1003 &isc->file_id);
1004 env = GNUNET_MQ_msg (msg,
1005 GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK);
1006 GNUNET_MQ_send (lc->mq,
1007 env);
1008 GNUNET_free (isc->filename);
1009 GNUNET_free (isc);
1010 GNUNET_SERVICE_client_continue (lc->client);
1011}
1012
1013
1014/**
1015 * Function called once the hash computation over an
1016 * indexed file has completed.
1017 *
1018 * @param cls closure, our publishing context
1019 * @param res resulting hash, NULL on error
1020 */
1021static void
1022hash_for_index_val (void *cls,
1023 const struct GNUNET_HashCode *res)
1024{
1025 struct IndexStartContext *isc = cls;
1026 struct GSF_LocalClient *lc = isc->lc;
1027 struct GNUNET_MQ_Envelope *env;
1028 struct GNUNET_MessageHeader *msg;
1029
1030 GNUNET_CONTAINER_DLL_remove (lc->isc_head,
1031 lc->isc_tail,
1032 isc);
1033 isc->fhc = NULL;
1034 if ((NULL == res) ||
1035 (0 != memcmp (res,
1036 &isc->file_id,
1037 sizeof(struct GNUNET_HashCode))))
1038 {
1039 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1040 _ (
1041 "Hash mismatch trying to index file `%s' which does not have hash `%s'\n"),
1042 isc->filename,
1043 GNUNET_h2s (&isc->file_id));
1044 env = GNUNET_MQ_msg (msg,
1045 GNUNET_MESSAGE_TYPE_FS_INDEX_START_FAILED);
1046 GNUNET_MQ_send (lc->mq,
1047 env);
1048 GNUNET_SERVICE_client_continue (lc->client);
1049 GNUNET_free (isc);
1050 return;
1051 }
1052 signal_index_ok (isc);
1053}
1054
1055
1056/**
1057 * Handle INDEX_START-message.
1058 *
1059 * @param cls identification of the client
1060 * @param message the actual message
1061 */
1062static void
1063handle_client_index_start (void *cls,
1064 const struct IndexStartMessage *ism)
1065{
1066 struct GSF_LocalClient *lc = cls;
1067 struct IndexStartContext *isc;
1068 char *fn;
1069 uint64_t dev;
1070 uint64_t ino;
1071 uint64_t mydev;
1072 uint64_t myino;
1073
1074 fn = GNUNET_STRINGS_filename_expand ((const char *) &ism[1]);
1075 GNUNET_assert (NULL != fn);
1076 dev = GNUNET_ntohll (ism->device);
1077 ino = GNUNET_ntohll (ism->inode);
1078 isc = GNUNET_new (struct IndexStartContext);
1079 isc->filename = fn;
1080 isc->file_id = ism->file_id;
1081 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1082 "Received START_INDEX message for file `%s'\n",
1083 isc->filename);
1084 isc->lc = lc;
1085 mydev = 0;
1086 myino = 0;
1087 if (((dev != 0) ||
1088 (ino != 0)) &&
1089 (GNUNET_OK == GNUNET_DISK_file_get_identifiers (fn,
1090 &mydev,
1091 &myino)) &&
1092 (dev == mydev) &&
1093 (ino == myino))
1094 {
1095 /* fast validation OK! */
1096 signal_index_ok (isc);
1097 return;
1098 }
1099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1100 "Mismatch in file identifiers (%llu != %llu or %u != %u), need to hash.\n",
1101 (unsigned long long) ino,
1102 (unsigned long long) myino,
1103 (unsigned int) dev,
1104 (unsigned int) mydev);
1105 /* slow validation, need to hash full file (again) */
1106 GNUNET_CONTAINER_DLL_insert (lc->isc_head,
1107 lc->isc_tail,
1108 isc);
1109 isc->fhc = GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE,
1110 isc->filename,
1111 HASHING_BLOCKSIZE,
1112 &hash_for_index_val,
1113 isc);
1114 if (NULL == isc->fhc)
1115 hash_for_index_val (isc,
1116 NULL);
1117}
1118
1119
1120/**
1121 * Handle INDEX_LIST_GET-message.
1122 *
1123 * @param cls closure
1124 * @param message the actual message
1125 */
1126static void
1127handle_client_index_list_get (void *cls,
1128 const struct GNUNET_MessageHeader *message)
1129{
1130 struct GSF_LocalClient *lc = cls;
1131
1132 GNUNET_FS_indexing_send_list (lc->mq);
1133 GNUNET_SERVICE_client_continue (lc->client);
1134}
1135
1136
1137/**
1138 * Handle UNINDEX-message.
1139 *
1140 * @param cls identification of the client
1141 * @param message the actual message
1142 */
1143static void
1144handle_client_unindex (void *cls,
1145 const struct UnindexMessage *um)
1146{
1147 struct GSF_LocalClient *lc = cls;
1148 struct GNUNET_MQ_Envelope *env;
1149 struct GNUNET_MessageHeader *msg;
1150 int found;
1151
1152 GNUNET_break (0 == um->reserved);
1153 found = GNUNET_FS_indexing_do_unindex (&um->file_id);
1154 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1155 "Client requested unindexing of file `%s': %s\n",
1156 GNUNET_h2s (&um->file_id),
1157 found ? "found" : "not found");
1158 env = GNUNET_MQ_msg (msg,
1159 GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK);
1160 GNUNET_MQ_send (lc->mq,
1161 env);
1162 GNUNET_SERVICE_client_continue (lc->client);
1163}
1164
1165
1166/**
1167 * Task run during shutdown.
1168 *
1169 * @param cls unused
1170 */
1171static void
1172shutdown_task (void *cls)
1173{
1174 GSF_cadet_stop_server ();
1175 if (NULL != GSF_core)
1176 {
1177 GNUNET_CORE_disconnect (GSF_core);
1178 GSF_core = NULL;
1179 }
1180 if (NULL != GSF_ats)
1181 {
1182 GNUNET_ATS_performance_done (GSF_ats);
1183 GSF_ats = NULL;
1184 }
1185 GSF_put_done_ ();
1186 GSF_push_done_ ();
1187 GSF_pending_request_done_ ();
1188 GSF_plan_done ();
1189 GSF_connected_peer_done_ ();
1190 GNUNET_DATASTORE_disconnect (GSF_dsh,
1191 GNUNET_NO);
1192 GSF_dsh = NULL;
1193 GNUNET_DHT_disconnect (GSF_dht);
1194 GSF_dht = NULL;
1195 GNUNET_BLOCK_context_destroy (GSF_block_ctx);
1196 GSF_block_ctx = NULL;
1197 GNUNET_CONFIGURATION_destroy (block_cfg);
1198 block_cfg = NULL;
1199 GNUNET_STATISTICS_destroy (GSF_stats, GNUNET_NO);
1200 GSF_stats = NULL;
1201 if (NULL != cover_age_task)
1202 {
1203 GNUNET_SCHEDULER_cancel (cover_age_task);
1204 cover_age_task = NULL;
1205 }
1206 GNUNET_FS_indexing_done ();
1207 GNUNET_LOAD_value_free (datastore_get_load);
1208 datastore_get_load = NULL;
1209 GNUNET_LOAD_value_free (GSF_rt_entry_lifetime);
1210 GSF_rt_entry_lifetime = NULL;
1211}
1212
1213
1214/**
1215 * Function called after GNUNET_CORE_connect has succeeded
1216 * (or failed for good). Note that the private key of the
1217 * peer is intentionally not exposed here; if you need it,
1218 * your process should try to read the private key file
1219 * directly (which should work if you are authorized...).
1220 *
1221 * @param cls closure
1222 * @param my_identity ID of this peer, NULL if we failed
1223 */
1224static void
1225peer_init_handler (void *cls,
1226 const struct GNUNET_PeerIdentity *my_identity)
1227{
1228 if (0 != GNUNET_memcmp (&GSF_my_id,
1229 my_identity))
1230 {
1231 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1232 "Peer identity mismatch, refusing to start!\n");
1233 GNUNET_SCHEDULER_shutdown ();
1234 }
1235}
1236
1237
1238/**
1239 * Process fs requests.
1240 *
1241 * @param c configuration to use
1242 */
1243static int
1244main_init (const struct GNUNET_CONFIGURATION_Handle *c)
1245{
1246 struct GNUNET_MQ_MessageHandler no_p2p_handlers[] = {
1247 GNUNET_MQ_handler_end ()
1248 };
1249 struct GNUNET_MQ_MessageHandler p2p_handlers[] = {
1250 GNUNET_MQ_hd_var_size (p2p_get,
1251 GNUNET_MESSAGE_TYPE_FS_GET,
1252 struct GetMessage,
1253 NULL),
1254 GNUNET_MQ_hd_var_size (p2p_put,
1255 GNUNET_MESSAGE_TYPE_FS_PUT,
1256 struct PutMessage,
1257 NULL),
1258 GNUNET_MQ_hd_fixed_size (p2p_migration_stop,
1259 GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP,
1260 struct MigrationStopMessage,
1261 NULL),
1262 GNUNET_MQ_handler_end ()
1263 };
1264 int anon_p2p_off;
1265 char *keyfile;
1266
1267 /* this option is really only for testcases that need to disable
1268 _anonymous_ file-sharing for some reason */
1269 anon_p2p_off = (GNUNET_YES ==
1270 GNUNET_CONFIGURATION_get_value_yesno (GSF_cfg,
1271 "fs",
1272 "DISABLE_ANON_TRANSFER"));
1273
1274 if (GNUNET_OK !=
1275 GNUNET_CONFIGURATION_get_value_filename (GSF_cfg,
1276 "PEER",
1277 "PRIVATE_KEY",
1278 &keyfile))
1279 {
1280 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1281 _ (
1282 "FS service is lacking HOSTKEY configuration setting. Exiting.\n"));
1283 GNUNET_SCHEDULER_shutdown ();
1284 return GNUNET_SYSERR;
1285 }
1286 if (GNUNET_SYSERR ==
1287 GNUNET_CRYPTO_eddsa_key_from_file (keyfile,
1288 GNUNET_YES,
1289 &pk))
1290 {
1291 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1292 "Failed to setup peer's private key\n");
1293 GNUNET_SCHEDULER_shutdown ();
1294 GNUNET_free (keyfile);
1295 return GNUNET_SYSERR;
1296 }
1297 GNUNET_free (keyfile);
1298 GNUNET_CRYPTO_eddsa_key_get_public (&pk,
1299 &GSF_my_id.public_key);
1300
1301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1302 "I am peer %s\n",
1303 GNUNET_i2s (&GSF_my_id));
1304 GSF_core
1305 = GNUNET_CORE_connect (GSF_cfg,
1306 NULL,
1307 &peer_init_handler,
1308 &GSF_peer_connect_handler,
1309 &GSF_peer_disconnect_handler,
1310 (GNUNET_YES == anon_p2p_off)
1311 ? no_p2p_handlers
1312 : p2p_handlers);
1313 if (NULL == GSF_core)
1314 {
1315 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1316 _ ("Failed to connect to `%s' service.\n"),
1317 "core");
1318 return GNUNET_SYSERR;
1319 }
1320 cover_age_task =
1321 GNUNET_SCHEDULER_add_delayed (COVER_AGE_FREQUENCY,
1322 &age_cover_counters,
1323 NULL);
1324 datastore_get_load = GNUNET_LOAD_value_init (DATASTORE_LOAD_AUTODECLINE);
1325 GSF_cadet_start_server ();
1326 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1327 NULL);
1328 return GNUNET_OK;
1329}
1330
1331
1332/**
1333 * Process fs requests.
1334 *
1335 * @param cls closure
1336 * @param cfg configuration to use
1337 * @param service the initialized service
1338 */
1339static void
1340run (void *cls,
1341 const struct GNUNET_CONFIGURATION_Handle *cfg,
1342 struct GNUNET_SERVICE_Handle *service)
1343{
1344 unsigned long long dqs;
1345
1346 GSF_cfg = cfg;
1347 if (GNUNET_OK !=
1348 GNUNET_CONFIGURATION_get_value_size (GSF_cfg,
1349 "fs",
1350 "DATASTORE_QUEUE_SIZE",
1351 &dqs))
1352 {
1353 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
1354 "fs",
1355 "DATASTORE_QUEUE_SIZE");
1356 dqs = 32;
1357 }
1358 GSF_datastore_queue_size = (unsigned int) dqs;
1359 GSF_enable_randomized_delays =
1360 GNUNET_CONFIGURATION_get_value_yesno (cfg, "fs", "DELAY");
1361 GSF_dsh = GNUNET_DATASTORE_connect (cfg);
1362 if (NULL == GSF_dsh)
1363 {
1364 GNUNET_SCHEDULER_shutdown ();
1365 return;
1366 }
1367 GSF_rt_entry_lifetime = GNUNET_LOAD_value_init (GNUNET_TIME_UNIT_FOREVER_REL);
1368 GSF_stats = GNUNET_STATISTICS_create ("fs", cfg);
1369 block_cfg = GNUNET_CONFIGURATION_create ();
1370 GSF_block_ctx = GNUNET_BLOCK_context_create (block_cfg);
1371 GNUNET_assert (NULL != GSF_block_ctx);
1372 GSF_dht = GNUNET_DHT_connect (cfg, FS_DHT_HT_SIZE);
1373 GSF_plan_init ();
1374 GSF_pending_request_init_ ();
1375 GSF_connected_peer_init_ ();
1376 GSF_ats = GNUNET_ATS_performance_init (GSF_cfg,
1377 &update_latencies,
1378 NULL);
1379 GSF_push_init_ ();
1380 GSF_put_init_ ();
1381 if ((GNUNET_OK != GNUNET_FS_indexing_init (cfg,
1382 GSF_dsh)) ||
1383 (GNUNET_OK != main_init (cfg)))
1384 {
1385 GNUNET_SCHEDULER_shutdown ();
1386 shutdown_task (NULL);
1387 return;
1388 }
1389}
1390
1391
1392/**
1393 * Define "main" method using service macro.
1394 */
1395GNUNET_SERVICE_MAIN
1396 ("fs",
1397 GNUNET_SERVICE_OPTION_NONE,
1398 &run,
1399 &client_connect_cb,
1400 &client_disconnect_cb,
1401 NULL,
1402 GNUNET_MQ_hd_var_size (client_index_start,
1403 GNUNET_MESSAGE_TYPE_FS_INDEX_START,
1404 struct IndexStartMessage,
1405 NULL),
1406 GNUNET_MQ_hd_fixed_size (client_index_list_get,
1407 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET,
1408 struct GNUNET_MessageHeader,
1409 NULL),
1410 GNUNET_MQ_hd_fixed_size (client_unindex,
1411 GNUNET_MESSAGE_TYPE_FS_UNINDEX,
1412 struct UnindexMessage,
1413 NULL),
1414 GNUNET_MQ_hd_var_size (client_start_search,
1415 GNUNET_MESSAGE_TYPE_FS_START_SEARCH,
1416 struct SearchMessage,
1417 NULL),
1418 GNUNET_MQ_hd_fixed_size (client_loc_sign,
1419 GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGN,
1420 struct RequestLocSignatureMessage,
1421 NULL),
1422 GNUNET_MQ_handler_end ());
1423
1424
1425/* end of gnunet-service-fs.c */
diff --git a/src/fs/gnunet-service-fs.h b/src/fs/gnunet-service-fs.h
deleted file mode 100644
index 56d102673..000000000
--- a/src/fs/gnunet-service-fs.h
+++ /dev/null
@@ -1,306 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs.h
23 * @brief shared data structures of gnunet-service-fs.c
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_FS_H
27#define GNUNET_SERVICE_FS_H
28
29#include "gnunet_util_lib.h"
30#include "gnunet_statistics_service.h"
31#include "gnunet_transport_service.h"
32#include "gnunet_core_service.h"
33#include "gnunet_block_lib.h"
34#include "gnunet_ats_service.h"
35#include "fs.h"
36
37
38/**
39 * By which amount do we decrement the TTL for simple forwarding /
40 * indirection of the query; in milli-seconds. Set somewhat in
41 * accordance to your network latency (above the time it'll take you
42 * to send a packet and get a reply).
43 */
44#define TTL_DECREMENT 5000
45
46/**
47 * At what frequency should our datastore load decrease
48 * automatically (since if we don't use it, clearly the
49 * load must be going down).
50 */
51#define DATASTORE_LOAD_AUTODECLINE GNUNET_TIME_relative_multiply ( \
52 GNUNET_TIME_UNIT_MILLISECONDS, 250)
53
54/**
55 * Only the (mandatory) query is included.
56 */
57#define GET_MESSAGE_BIT_QUERY_ONLY 0
58
59/**
60 * The peer identity of a peer waiting for the
61 * reply is included (used if the response
62 * should be transmitted to someone other than
63 * the sender of the GET).
64 */
65#define GET_MESSAGE_BIT_RETURN_TO 1
66
67/**
68 * The peer identity of a peer that had claimed to have the content
69 * previously is included (can be used if responder-anonymity is not
70 * desired; note that the precursor presumably lacked a direct
71 * connection to the specified peer; still, the receiver is in no way
72 * required to limit forwarding only to the specified peer, it should
73 * only prefer it somewhat if possible).
74 */
75#define GET_MESSAGE_BIT_TRANSMIT_TO 4
76
77
78GNUNET_NETWORK_STRUCT_BEGIN
79
80/**
81 * Message sent between peers asking for FS-content.
82 */
83struct GetMessage
84{
85 /**
86 * Message type will be #GNUNET_MESSAGE_TYPE_FS_GET.
87 */
88 struct GNUNET_MessageHeader header;
89
90 /**
91 * Type of the query (block type).
92 */
93 uint32_t type GNUNET_PACKED;
94
95 /**
96 * How important is this request (network byte order)
97 */
98 uint32_t priority GNUNET_PACKED;
99
100 /**
101 * Relative time to live in MILLISECONDS (network byte order)
102 */
103 int32_t ttl GNUNET_PACKED;
104
105 /**
106 * These days not used.
107 */
108 uint32_t reserved GNUNET_PACKED;
109
110 /**
111 * Which of the optional hash codes are present at the end of the
112 * message? See GET_MESSAGE_BIT_xx constants. For each bit that is
113 * set, an additional `struct GNUNET_HashCode` with the respective content
114 * (in order of the bits) will be appended to the end of the GET
115 * message.
116 */
117 uint32_t hash_bitmap GNUNET_PACKED;
118
119 /**
120 * Hashcodes of the file(s) we're looking for.
121 * Details depend on the query type.
122 */
123 struct GNUNET_HashCode query;
124
125 /* this is followed by PeerIdentities as specified in the "hash_bitmap";
126 * after that, an optional bloomfilter (with bits set for replies
127 * that should be suppressed) can be present */
128};
129
130
131/**
132 * Message send by a peer that wants to be excluded
133 * from migration for a while.
134 */
135struct MigrationStopMessage
136{
137 /**
138 * Message type will be
139 * GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP.
140 */
141 struct GNUNET_MessageHeader header;
142
143 /**
144 * Always zero.
145 */
146 uint32_t reserved GNUNET_PACKED;
147
148 /**
149 * How long should the block last?
150 */
151 struct GNUNET_TIME_RelativeNBO duration;
152};
153GNUNET_NETWORK_STRUCT_END
154
155/**
156 * A connected peer.
157 */
158struct GSF_ConnectedPeer;
159
160/**
161 * An active request.
162 */
163struct GSF_PendingRequest;
164
165/**
166 * A local client.
167 */
168struct GSF_LocalClient;
169
170/**
171 * Information kept per plan per request ('pe' module).
172 */
173struct GSF_RequestPlan;
174
175/**
176 * Bijection between request plans and pending requests.
177 */
178struct GSF_PendingRequestPlanBijection;
179
180/**
181 * Our connection to the datastore.
182 */
183extern struct GNUNET_DATASTORE_Handle *GSF_dsh;
184
185/**
186 * Our configuration.
187 */
188extern const struct GNUNET_CONFIGURATION_Handle *GSF_cfg;
189
190/**
191 * Handle for reporting statistics.
192 */
193extern struct GNUNET_STATISTICS_Handle *GSF_stats;
194
195/**
196 * Pointer to handle to the core service (points to NULL until we've
197 * connected to it).
198 */
199extern struct GNUNET_CORE_Handle *GSF_core;
200
201/**
202 * Handle for DHT operations.
203 */
204extern struct GNUNET_DHT_Handle *GSF_dht;
205
206/**
207 * How long do requests typically stay in the routing table?
208 */
209extern struct GNUNET_LOAD_Value *GSF_rt_entry_lifetime;
210
211/**
212 * Running average of the observed latency to other peers (round trip).
213 */
214extern struct GNUNET_TIME_Relative GSF_avg_latency;
215
216/**
217 * Handle to ATS service.
218 */
219extern struct GNUNET_ATS_PerformanceHandle *GSF_ats;
220
221/**
222 * Identity of this peer.
223 */
224extern struct GNUNET_PeerIdentity GSF_my_id;
225
226/**
227 * Typical priorities we're seeing from other peers right now. Since
228 * most priorities will be zero, this value is the weighted average of
229 * non-zero priorities seen "recently". In order to ensure that new
230 * values do not dramatically change the ratio, values are first
231 * "capped" to a reasonable range (+N of the current value) and then
232 * averaged into the existing value by a ratio of 1:N. Hence
233 * receiving the largest possible priority can still only raise our
234 * "current_priorities" by at most 1.
235 */
236extern double GSF_current_priorities;
237
238/**
239 * How many query messages have we received 'recently' that
240 * have not yet been claimed as cover traffic?
241 */
242extern unsigned int GSF_cover_query_count;
243
244/**
245 * How many content messages have we received 'recently' that
246 * have not yet been claimed as cover traffic?
247 */
248extern unsigned int GSF_cover_content_count;
249
250/**
251 * Our block context.
252 */
253extern struct GNUNET_BLOCK_Context *GSF_block_ctx;
254
255/**
256 * Are we introducing randomized delays for better anonymity?
257 */
258extern int GSF_enable_randomized_delays;
259
260/**
261 * Size of the datastore queue we assume for common requests.
262 */
263extern unsigned int GSF_datastore_queue_size;
264
265
266/**
267 * Function to be called after we're done processing
268 * replies from the local lookup. If the result status
269 * code indicates that there may be more replies, plan
270 * forwarding the request.
271 *
272 * @param cls closure (NULL)
273 * @param pr the pending request we were processing
274 * @param result final datastore lookup result
275 */
276void
277GSF_consider_forwarding (void *cls,
278 struct GSF_PendingRequest *pr,
279 enum GNUNET_BLOCK_ReplyEvaluationResult result);
280
281
282/**
283 * Test if the DATABASE (GET) load on this peer is too high
284 * to even consider processing the query at
285 * all.
286 *
287 * @return #GNUNET_YES if the load is too high to do anything (load high)
288 * #GNUNET_NO to process normally (load normal)
289 * #GNUNET_SYSERR to process for free (load low)
290 */
291int
292GSF_test_get_load_too_high_ (uint32_t priority);
293
294
295/**
296 * We've just now completed a datastore request. Update our
297 * datastore load calculations.
298 *
299 * @param start time when the datastore request was issued
300 */
301void
302GSF_update_datastore_delay_ (struct GNUNET_TIME_Absolute start);
303
304
305#endif
306/* end of gnunet-service-fs.h */
diff --git a/src/fs/gnunet-service-fs_cadet.h b/src/fs/gnunet-service-fs_cadet.h
deleted file mode 100644
index c02021a0d..000000000
--- a/src/fs/gnunet-service-fs_cadet.h
+++ /dev/null
@@ -1,168 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_cadet.h
23 * @brief non-anonymous file-transfer
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_FS_CADET_H
27#define GNUNET_SERVICE_FS_CADET_H
28
29/**
30 * Handle for a request that is going out via cadet API.
31 */
32struct GSF_CadetRequest;
33
34
35/**
36 * Function called with a reply from the cadet.
37 *
38 * @param cls closure
39 * @param type type of the block, ANY on error
40 * @param expiration expiration time for the block
41 * @param data_size number of bytes in @a data, 0 on error
42 * @param data reply block data, NULL on error
43 */
44typedef void
45(*GSF_CadetReplyProcessor)(void *cls,
46 enum GNUNET_BLOCK_Type type,
47 struct GNUNET_TIME_Absolute expiration,
48 size_t data_size,
49 const void *data);
50
51
52/**
53 * Look for a block by directly contacting a particular peer.
54 *
55 * @param target peer that should have the block
56 * @param query hash to query for the block
57 * @param type desired type for the block
58 * @param proc function to call with result
59 * @param proc_cls closure for @a proc
60 * @return handle to cancel the operation
61 */
62struct GSF_CadetRequest *
63GSF_cadet_query (const struct GNUNET_PeerIdentity *target,
64 const struct GNUNET_HashCode *query,
65 enum GNUNET_BLOCK_Type type,
66 GSF_CadetReplyProcessor proc,
67 void *proc_cls);
68
69/**
70 * Function called on each active cadets to shut them down.
71 *
72 * @param cls NULL
73 * @param key target peer, unused
74 * @param value the `struct CadetHandle` to destroy
75 * @return #GNUNET_YES (continue to iterate)
76 */
77int
78GSF_cadet_release_clients (void *cls,
79 const struct GNUNET_PeerIdentity *key,
80 void *value);
81
82
83/**
84 * Cancel an active request; must not be called after 'proc'
85 * was called.
86 *
87 * @param sr request to cancel
88 */
89void
90GSF_cadet_query_cancel (struct GSF_CadetRequest *sr);
91
92
93/**
94 * Initialize subsystem for non-anonymous file-sharing.
95 */
96void
97GSF_cadet_start_server (void);
98
99
100/**
101 * Shutdown subsystem for non-anonymous file-sharing.
102 */
103void
104GSF_cadet_stop_server (void);
105
106/**
107 * Cadet channel for creating outbound channels.
108 */
109extern struct GNUNET_CADET_Handle *cadet_handle;
110
111/**
112 * Map from peer identities to 'struct CadetHandles' with cadet
113 * channels to those peers.
114 */
115extern struct GNUNET_CONTAINER_MultiPeerMap *cadet_map;
116
117
118GNUNET_NETWORK_STRUCT_BEGIN
119
120/**
121 * Query from one peer, asking the other for CHK-data.
122 */
123struct CadetQueryMessage
124{
125 /**
126 * Type is GNUNET_MESSAGE_TYPE_FS_CADET_QUERY.
127 */
128 struct GNUNET_MessageHeader header;
129
130 /**
131 * Block type must be DBLOCK or IBLOCK.
132 */
133 uint32_t type GNUNET_PACKED;
134
135 /**
136 * Query hash from CHK (hash of encrypted block).
137 */
138 struct GNUNET_HashCode query;
139};
140
141
142/**
143 * Reply to a CadetQueryMessage.
144 */
145struct CadetReplyMessage
146{
147 /**
148 * Type is GNUNET_MESSAGE_TYPE_FS_CADET_REPLY.
149 */
150 struct GNUNET_MessageHeader header;
151
152 /**
153 * Block type must be DBLOCK or IBLOCK.
154 */
155 uint32_t type GNUNET_PACKED;
156
157 /**
158 * Expiration time for the block.
159 */
160 struct GNUNET_TIME_AbsoluteNBO expiration;
161
162 /* followed by the encrypted block */
163};
164
165GNUNET_NETWORK_STRUCT_END
166
167
168#endif
diff --git a/src/fs/gnunet-service-fs_cadet_client.c b/src/fs/gnunet-service-fs_cadet_client.c
deleted file mode 100644
index 398fcd604..000000000
--- a/src/fs/gnunet-service-fs_cadet_client.c
+++ /dev/null
@@ -1,728 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_cadet_client.c
23 * @brief non-anonymous file-transfer
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - PORT is set to old application type, unsure if we should keep
28 * it that way (fine for now)
29 */
30#include "platform.h"
31#include "gnunet_constants.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_cadet_service.h"
34#include "gnunet_protocols.h"
35#include "gnunet_applications.h"
36#include "gnunet-service-fs.h"
37#include "gnunet-service-fs_indexing.h"
38#include "gnunet-service-fs_cadet.h"
39
40
41/**
42 * After how long do we reset connections without replies?
43 */
44#define CLIENT_RETRY_TIMEOUT \
45 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
46
47
48/**
49 * Handle for a cadet to another peer.
50 */
51struct CadetHandle;
52
53
54/**
55 * Handle for a request that is going out via cadet API.
56 */
57struct GSF_CadetRequest
58{
59 /**
60 * DLL.
61 */
62 struct GSF_CadetRequest *next;
63
64 /**
65 * DLL.
66 */
67 struct GSF_CadetRequest *prev;
68
69 /**
70 * Which cadet is this request associated with?
71 */
72 struct CadetHandle *mh;
73
74 /**
75 * Function to call with the result.
76 */
77 GSF_CadetReplyProcessor proc;
78
79 /**
80 * Closure for @e proc
81 */
82 void *proc_cls;
83
84 /**
85 * Query to transmit to the other peer.
86 */
87 struct GNUNET_HashCode query;
88
89 /**
90 * Desired type for the reply.
91 */
92 enum GNUNET_BLOCK_Type type;
93
94 /**
95 * Did we transmit this request already? #GNUNET_YES if we are
96 * in the 'waiting_map', #GNUNET_NO if we are in the 'pending' DLL.
97 */
98 int was_transmitted;
99};
100
101
102/**
103 * Handle for a cadet to another peer.
104 */
105struct CadetHandle
106{
107 /**
108 * Head of DLL of pending requests on this cadet.
109 */
110 struct GSF_CadetRequest *pending_head;
111
112 /**
113 * Tail of DLL of pending requests on this cadet.
114 */
115 struct GSF_CadetRequest *pending_tail;
116
117 /**
118 * Map from query to `struct GSF_CadetRequest`s waiting for
119 * a reply.
120 */
121 struct GNUNET_CONTAINER_MultiHashMap *waiting_map;
122
123 /**
124 * Channel to the other peer.
125 */
126 struct GNUNET_CADET_Channel *channel;
127
128 /**
129 * Which peer does this cadet go to?
130 */
131 struct GNUNET_PeerIdentity target;
132
133 /**
134 * Task to kill inactive cadets (we keep them around for
135 * a few seconds to give the application a chance to give
136 * us another query).
137 */
138 struct GNUNET_SCHEDULER_Task *timeout_task;
139
140 /**
141 * Task to reset cadets that had errors (asynchronously,
142 * as we may not be able to do it immediately during a
143 * callback from the cadet API).
144 */
145 struct GNUNET_SCHEDULER_Task *reset_task;
146};
147
148
149/**
150 * Cadet channel for creating outbound channels.
151 */
152struct GNUNET_CADET_Handle *cadet_handle;
153
154/**
155 * Map from peer identities to 'struct CadetHandles' with cadet
156 * channels to those peers.
157 */
158struct GNUNET_CONTAINER_MultiPeerMap *cadet_map;
159
160
161/* ********************* client-side code ************************* */
162
163
164/**
165 * Transmit pending requests via the cadet.
166 *
167 * @param cls `struct CadetHandle` to process
168 */
169static void
170transmit_pending (void *cls);
171
172
173/**
174 * Iterator called on each entry in a waiting map to
175 * move it back to the pending list.
176 *
177 * @param cls the `struct CadetHandle`
178 * @param key the key of the entry in the map (the query)
179 * @param value the `struct GSF_CadetRequest` to move to pending
180 * @return #GNUNET_YES (continue to iterate)
181 */
182static int
183move_to_pending (void *cls, const struct GNUNET_HashCode *key, void *value)
184{
185 struct CadetHandle *mh = cls;
186 struct GSF_CadetRequest *sr = value;
187
188 GNUNET_assert (
189 GNUNET_YES ==
190 GNUNET_CONTAINER_multihashmap_remove (mh->waiting_map, key, value));
191 GNUNET_CONTAINER_DLL_insert (mh->pending_head, mh->pending_tail, sr);
192 sr->was_transmitted = GNUNET_NO;
193 return GNUNET_YES;
194}
195
196
197/**
198 * Functions with this signature are called whenever a complete reply
199 * is received.
200 *
201 * @param cls closure with the `struct CadetHandle`
202 * @param srm the actual message
203 * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
204 */
205static int
206check_reply (void *cls, const struct CadetReplyMessage *srm)
207{
208 /* We check later... */
209 return GNUNET_OK;
210}
211
212
213/**
214 * Task called when it is time to reset an cadet.
215 *
216 * @param cls the `struct CadetHandle` to tear down
217 */
218static void
219reset_cadet_task (void *cls);
220
221
222/**
223 * We had a serious error, tear down and re-create cadet from scratch,
224 * but do so asynchronously.
225 *
226 * @param mh cadet to reset
227 */
228static void
229reset_cadet_async (struct CadetHandle *mh)
230{
231 if (NULL != mh->reset_task)
232 GNUNET_SCHEDULER_cancel (mh->reset_task);
233 mh->reset_task = GNUNET_SCHEDULER_add_now (&reset_cadet_task, mh);
234}
235
236
237/**
238 * Closure for handle_reply().
239 */
240struct HandleReplyClosure
241{
242 /**
243 * Reply payload.
244 */
245 const void *data;
246
247 /**
248 * Expiration time for the block.
249 */
250 struct GNUNET_TIME_Absolute expiration;
251
252 /**
253 * Number of bytes in @e data.
254 */
255 size_t data_size;
256
257 /**
258 * Type of the block.
259 */
260 enum GNUNET_BLOCK_Type type;
261
262 /**
263 * Did we have a matching query?
264 */
265 int found;
266};
267
268
269/**
270 * Iterator called on each entry in a waiting map to
271 * process a result.
272 *
273 * @param cls the `struct HandleReplyClosure`
274 * @param key the key of the entry in the map (the query)
275 * @param value the `struct GSF_CadetRequest` to handle result for
276 * @return #GNUNET_YES (continue to iterate)
277 */
278static int
279process_reply (void *cls, const struct GNUNET_HashCode *key, void *value)
280{
281 struct HandleReplyClosure *hrc = cls;
282 struct GSF_CadetRequest *sr = value;
283
284 sr->proc (sr->proc_cls,
285 hrc->type,
286 hrc->expiration,
287 hrc->data_size,
288 hrc->data);
289 sr->proc = NULL;
290 GSF_cadet_query_cancel (sr);
291 hrc->found = GNUNET_YES;
292 return GNUNET_YES;
293}
294
295
296/**
297 * Iterator called on each entry in a waiting map to
298 * call the 'proc' continuation and release associated
299 * resources.
300 *
301 * @param cls the `struct CadetHandle`
302 * @param key the key of the entry in the map (the query)
303 * @param value the `struct GSF_CadetRequest` to clean up
304 * @return #GNUNET_YES (continue to iterate)
305 */
306static int
307free_waiting_entry (void *cls, const struct GNUNET_HashCode *key, void *value)
308{
309 struct GSF_CadetRequest *sr = value;
310
311 GSF_cadet_query_cancel (sr);
312 return GNUNET_YES;
313}
314
315
316/**
317 * Functions with this signature are called whenever a complete reply
318 * is received.
319 *
320 * @param cls closure with the `struct CadetHandle`
321 * @param srm the actual message
322 */
323static void
324handle_reply (void *cls, const struct CadetReplyMessage *srm)
325{
326 struct CadetHandle *mh = cls;
327 struct HandleReplyClosure hrc;
328 uint16_t msize;
329 enum GNUNET_BLOCK_Type type;
330 struct GNUNET_HashCode query;
331
332 msize = ntohs (srm->header.size) - sizeof(struct CadetReplyMessage);
333 type = (enum GNUNET_BLOCK_Type) ntohl (srm->type);
334 if (GNUNET_YES !=
335 GNUNET_BLOCK_get_key (GSF_block_ctx, type, &srm[1], msize, &query))
336 {
337 GNUNET_break_op (0);
338 GNUNET_log (
339 GNUNET_ERROR_TYPE_WARNING,
340 "Received bogus reply of type %u with %u bytes via cadet from peer %s\n",
341 type,
342 msize,
343 GNUNET_i2s (&mh->target));
344 reset_cadet_async (mh);
345 return;
346 }
347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
348 "Received reply `%s' via cadet from peer %s\n",
349 GNUNET_h2s (&query),
350 GNUNET_i2s (&mh->target));
351 GNUNET_CADET_receive_done (mh->channel);
352 GNUNET_STATISTICS_update (GSF_stats,
353 gettext_noop ("# replies received via cadet"),
354 1,
355 GNUNET_NO);
356 hrc.data = &srm[1];
357 hrc.data_size = msize;
358 hrc.expiration = GNUNET_TIME_absolute_ntoh (srm->expiration);
359 hrc.type = type;
360 hrc.found = GNUNET_NO;
361 GNUNET_CONTAINER_multihashmap_get_multiple (mh->waiting_map,
362 &query,
363 &process_reply,
364 &hrc);
365 if (GNUNET_NO == hrc.found)
366 {
367 GNUNET_STATISTICS_update (GSF_stats,
368 gettext_noop (
369 "# replies received via cadet dropped"),
370 1,
371 GNUNET_NO);
372 }
373}
374
375
376/**
377 * Function called by cadet when a client disconnects.
378 * Cleans up our `struct CadetClient` of that channel.
379 *
380 * @param cls our `struct CadetClient`
381 * @param channel channel of the disconnecting client
382 */
383static void
384disconnect_cb (void *cls, const struct GNUNET_CADET_Channel *channel)
385{
386 struct CadetHandle *mh = cls;
387 struct GSF_CadetRequest *sr;
388
389 if (NULL == mh->channel)
390 return; /* being destroyed elsewhere */
391 GNUNET_assert (channel == mh->channel);
392 mh->channel = NULL;
393 while (NULL != (sr = mh->pending_head))
394 GSF_cadet_query_cancel (sr);
395 /* first remove `mh` from the `cadet_map`, so that if the
396 callback from `free_waiting_entry()` happens to re-issue
397 the request, we don't immediately have it back in the
398 `waiting_map`. */
399 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multipeermap_remove (cadet_map,
400 &mh->target,
401 mh));
402 GNUNET_CONTAINER_multihashmap_iterate (mh->waiting_map,
403 &free_waiting_entry,
404 mh);
405 if (NULL != mh->timeout_task)
406 GNUNET_SCHEDULER_cancel (mh->timeout_task);
407 if (NULL != mh->reset_task)
408 GNUNET_SCHEDULER_cancel (mh->reset_task);
409 GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (mh->waiting_map));
410 GNUNET_CONTAINER_multihashmap_destroy (mh->waiting_map);
411 GNUNET_free (mh);
412}
413
414
415/**
416 * Function called whenever an MQ-channel's transmission window size changes.
417 *
418 * The first callback in an outgoing channel will be with a non-zero value
419 * and will mean the channel is connected to the destination.
420 *
421 * For an incoming channel it will be called immediately after the
422 * #GNUNET_CADET_ConnectEventHandler, also with a non-zero value.
423 *
424 * @param cls Channel closure.
425 * @param channel Connection to the other end (henceforth invalid).
426 * @param window_size New window size. If the is more messages than buffer size
427 * this value will be negative..
428 */
429static void
430window_change_cb (void *cls,
431 const struct GNUNET_CADET_Channel *channel,
432 int window_size)
433{
434 /* FIXME: for flow control, implement? */
435#if 0
436 /* Something like this instead of the GNUNET_MQ_notify_sent() in
437 transmit_pending() might be good (once the window change CB works...) */
438 if (0 < window_size) /* test needed? */
439 transmit_pending (mh);
440#endif
441}
442
443
444/**
445 * We had a serious error, tear down and re-create cadet from scratch.
446 *
447 * @param mh cadet to reset
448 */
449static void
450reset_cadet (struct CadetHandle *mh)
451{
452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453 "Resetting cadet channel to %s\n",
454 GNUNET_i2s (&mh->target));
455 if (NULL != mh->channel)
456 {
457 GNUNET_CADET_channel_destroy (mh->channel);
458 mh->channel = NULL;
459 }
460 GNUNET_CONTAINER_multihashmap_iterate (mh->waiting_map, &move_to_pending, mh);
461 {
462 struct GNUNET_MQ_MessageHandler handlers[] =
463 { GNUNET_MQ_hd_var_size (reply,
464 GNUNET_MESSAGE_TYPE_FS_CADET_REPLY,
465 struct CadetReplyMessage,
466 mh),
467 GNUNET_MQ_handler_end () };
468 struct GNUNET_HashCode port;
469
470 GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER,
471 strlen (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER),
472 &port);
473 mh->channel = GNUNET_CADET_channel_create (cadet_handle,
474 mh,
475 &mh->target,
476 &port,
477 &window_change_cb,
478 &disconnect_cb,
479 handlers);
480 }
481 transmit_pending (mh);
482}
483
484
485/**
486 * Task called when it is time to destroy an inactive cadet channel.
487 *
488 * @param cls the `struct CadetHandle` to tear down
489 */
490static void
491cadet_timeout (void *cls)
492{
493 struct CadetHandle *mh = cls;
494 struct GNUNET_CADET_Channel *tun;
495
496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497 "Timeout on cadet channel to %s\n",
498 GNUNET_i2s (&mh->target));
499 mh->timeout_task = NULL;
500 tun = mh->channel;
501 mh->channel = NULL;
502 if (NULL != tun)
503 GNUNET_CADET_channel_destroy (tun);
504}
505
506
507/**
508 * Task called when it is time to reset an cadet.
509 *
510 * @param cls the `struct CadetHandle` to tear down
511 */
512static void
513reset_cadet_task (void *cls)
514{
515 struct CadetHandle *mh = cls;
516
517 mh->reset_task = NULL;
518 reset_cadet (mh);
519}
520
521
522/**
523 * Transmit pending requests via the cadet.
524 *
525 * @param cls `struct CadetHandle` to process
526 */
527static void
528transmit_pending (void *cls)
529{
530 struct CadetHandle *mh = cls;
531 struct GNUNET_MQ_Handle *mq = GNUNET_CADET_get_mq (mh->channel);
532 struct GSF_CadetRequest *sr;
533 struct GNUNET_MQ_Envelope *env;
534 struct CadetQueryMessage *sqm;
535
536 if ((0 != GNUNET_MQ_get_length (mq)) || (NULL == (sr = mh->pending_head)))
537 return;
538 GNUNET_CONTAINER_DLL_remove (mh->pending_head, mh->pending_tail, sr);
539 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (
540 mh->waiting_map,
541 &sr->query,
542 sr,
543 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
544 sr->was_transmitted = GNUNET_YES;
545 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
546 "Sending query for %s via cadet to %s\n",
547 GNUNET_h2s (&sr->query),
548 GNUNET_i2s (&mh->target));
549 env = GNUNET_MQ_msg (sqm, GNUNET_MESSAGE_TYPE_FS_CADET_QUERY);
550 GNUNET_MQ_env_set_options (env,
551 GNUNET_MQ_PREF_GOODPUT
552 | GNUNET_MQ_PREF_CORK_ALLOWED
553 | GNUNET_MQ_PREF_OUT_OF_ORDER);
554 sqm->type = htonl (sr->type);
555 sqm->query = sr->query;
556 GNUNET_MQ_notify_sent (env, &transmit_pending, mh);
557 GNUNET_MQ_send (mq, env);
558}
559
560
561/**
562 * Get (or create) a cadet to talk to the given peer.
563 *
564 * @param target peer we want to communicate with
565 */
566static struct CadetHandle *
567get_cadet (const struct GNUNET_PeerIdentity *target)
568{
569 struct CadetHandle *mh;
570
571 mh = GNUNET_CONTAINER_multipeermap_get (cadet_map, target);
572 if (NULL != mh)
573 {
574 if (NULL != mh->timeout_task)
575 {
576 GNUNET_SCHEDULER_cancel (mh->timeout_task);
577 mh->timeout_task = NULL;
578 }
579 return mh;
580 }
581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
582 "Creating cadet channel to %s\n",
583 GNUNET_i2s (target));
584 mh = GNUNET_new (struct CadetHandle);
585 mh->reset_task =
586 GNUNET_SCHEDULER_add_delayed (CLIENT_RETRY_TIMEOUT, &reset_cadet_task, mh);
587 mh->waiting_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_YES);
588 mh->target = *target;
589 GNUNET_assert (GNUNET_OK ==
590 GNUNET_CONTAINER_multipeermap_put (
591 cadet_map,
592 &mh->target,
593 mh,
594 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
595 {
596 struct GNUNET_MQ_MessageHandler handlers[] =
597 { GNUNET_MQ_hd_var_size (reply,
598 GNUNET_MESSAGE_TYPE_FS_CADET_REPLY,
599 struct CadetReplyMessage,
600 mh),
601 GNUNET_MQ_handler_end () };
602 struct GNUNET_HashCode port;
603
604 GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER,
605 strlen (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER),
606 &port);
607 mh->channel = GNUNET_CADET_channel_create (cadet_handle,
608 mh,
609 &mh->target,
610 &port,
611 &window_change_cb,
612 &disconnect_cb,
613 handlers);
614 }
615 return mh;
616}
617
618
619/**
620 * Look for a block by directly contacting a particular peer.
621 *
622 * @param target peer that should have the block
623 * @param query hash to query for the block
624 * @param type desired type for the block
625 * @param proc function to call with result
626 * @param proc_cls closure for @a proc
627 * @return handle to cancel the operation
628 */
629struct GSF_CadetRequest *
630GSF_cadet_query (const struct GNUNET_PeerIdentity *target,
631 const struct GNUNET_HashCode *query,
632 enum GNUNET_BLOCK_Type type,
633 GSF_CadetReplyProcessor proc,
634 void *proc_cls)
635{
636 struct CadetHandle *mh;
637 struct GSF_CadetRequest *sr;
638
639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640 "Preparing to send query for %s via cadet to %s\n",
641 GNUNET_h2s (query),
642 GNUNET_i2s (target));
643 mh = get_cadet (target);
644 sr = GNUNET_new (struct GSF_CadetRequest);
645 sr->mh = mh;
646 sr->proc = proc;
647 sr->proc_cls = proc_cls;
648 sr->type = type;
649 sr->query = *query;
650 GNUNET_CONTAINER_DLL_insert (mh->pending_head, mh->pending_tail, sr);
651 transmit_pending (mh);
652 return sr;
653}
654
655
656/**
657 * Cancel an active request; must not be called after 'proc'
658 * was called.
659 *
660 * @param sr request to cancel
661 */
662void
663GSF_cadet_query_cancel (struct GSF_CadetRequest *sr)
664{
665 struct CadetHandle *mh = sr->mh;
666 GSF_CadetReplyProcessor p;
667
668 p = sr->proc;
669 sr->proc = NULL;
670 if (NULL != p)
671 {
672 /* signal failure / cancellation to callback */
673 p (sr->proc_cls, GNUNET_BLOCK_TYPE_ANY, GNUNET_TIME_UNIT_ZERO_ABS, 0, NULL);
674 }
675 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
676 "Cancelled query for %s via cadet to %s\n",
677 GNUNET_h2s (&sr->query),
678 GNUNET_i2s (&sr->mh->target));
679 if (GNUNET_YES == sr->was_transmitted)
680 GNUNET_assert (
681 GNUNET_OK ==
682 GNUNET_CONTAINER_multihashmap_remove (mh->waiting_map, &sr->query, sr));
683 else
684 GNUNET_CONTAINER_DLL_remove (mh->pending_head, mh->pending_tail, sr);
685 GNUNET_free (sr);
686 if ((0 == GNUNET_CONTAINER_multihashmap_size (mh->waiting_map)) &&
687 (NULL == mh->pending_head))
688 mh->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
689 &cadet_timeout,
690 mh);
691}
692
693
694/**
695 * Function called on each active cadets to shut them down.
696 *
697 * @param cls NULL
698 * @param key target peer, unused
699 * @param value the `struct CadetHandle` to destroy
700 * @return #GNUNET_YES (continue to iterate)
701 */
702int
703GSF_cadet_release_clients (void *cls,
704 const struct GNUNET_PeerIdentity *key,
705 void *value)
706{
707 struct CadetHandle *mh = value;
708
709 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
710 "Timeout on cadet channel to %s\n",
711 GNUNET_i2s (&mh->target));
712 if (NULL != mh->channel)
713 {
714 struct GNUNET_CADET_Channel *channel = mh->channel;
715
716 mh->channel = NULL;
717 GNUNET_CADET_channel_destroy (channel);
718 }
719 if (NULL != mh->reset_task)
720 {
721 GNUNET_SCHEDULER_cancel (mh->reset_task);
722 mh->reset_task = NULL;
723 }
724 return GNUNET_YES;
725}
726
727
728/* end of gnunet-service-fs_cadet_client.c */
diff --git a/src/fs/gnunet-service-fs_cadet_server.c b/src/fs/gnunet-service-fs_cadet_server.c
deleted file mode 100644
index 395842ebb..000000000
--- a/src/fs/gnunet-service-fs_cadet_server.c
+++ /dev/null
@@ -1,546 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_cadet_server.c
23 * @brief non-anonymous file-transfer
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - PORT is set to old application type, unsure if we should keep
28 * it that way (fine for now)
29 */
30#include "platform.h"
31#include "gnunet_constants.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_cadet_service.h"
34#include "gnunet_protocols.h"
35#include "gnunet_applications.h"
36#include "gnunet-service-fs.h"
37#include "gnunet-service-fs_indexing.h"
38#include "gnunet-service-fs_cadet.h"
39
40/**
41 * After how long do we termiante idle connections?
42 */
43#define IDLE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
44
45
46/**
47 * A message in the queue to be written to the cadet.
48 */
49struct WriteQueueItem
50{
51 /**
52 * Kept in a DLL.
53 */
54 struct WriteQueueItem *next;
55
56 /**
57 * Kept in a DLL.
58 */
59 struct WriteQueueItem *prev;
60
61 /**
62 * Number of bytes of payload, allocated at the end of this struct.
63 */
64 size_t msize;
65};
66
67
68/**
69 * Information we keep around for each active cadeting client.
70 */
71struct CadetClient
72{
73 /**
74 * DLL
75 */
76 struct CadetClient *next;
77
78 /**
79 * DLL
80 */
81 struct CadetClient *prev;
82
83 /**
84 * Channel for communication.
85 */
86 struct GNUNET_CADET_Channel *channel;
87
88 /**
89 * Head of write queue.
90 */
91 struct WriteQueueItem *wqi_head;
92
93 /**
94 * Tail of write queue.
95 */
96 struct WriteQueueItem *wqi_tail;
97
98 /**
99 * Current active request to the datastore, if we have one pending.
100 */
101 struct GNUNET_DATASTORE_QueueEntry *qe;
102
103 /**
104 * Task that is scheduled to asynchronously terminate the connection.
105 */
106 struct GNUNET_SCHEDULER_Task *terminate_task;
107
108 /**
109 * Task that is scheduled to terminate idle connections.
110 */
111 struct GNUNET_SCHEDULER_Task *timeout_task;
112
113 /**
114 * Size of the last write that was initiated.
115 */
116 size_t reply_size;
117};
118
119
120/**
121 * Listen port for incoming requests.
122 */
123static struct GNUNET_CADET_Port *cadet_port;
124
125/**
126 * Head of DLL of cadet clients.
127 */
128static struct CadetClient *sc_head;
129
130/**
131 * Tail of DLL of cadet clients.
132 */
133static struct CadetClient *sc_tail;
134
135/**
136 * Number of active cadet clients in the 'sc_*'-DLL.
137 */
138static unsigned int sc_count;
139
140/**
141 * Maximum allowed number of cadet clients.
142 */
143static unsigned long long sc_count_max;
144
145
146/**
147 * Task run to asynchronously terminate the cadet due to timeout.
148 *
149 * @param cls the 'struct CadetClient'
150 */
151static void
152timeout_cadet_task (void *cls)
153{
154 struct CadetClient *sc = cls;
155 struct GNUNET_CADET_Channel *tun;
156
157 sc->timeout_task = NULL;
158 tun = sc->channel;
159 sc->channel = NULL;
160 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
161 "Timeout for inactive cadet client %p\n",
162 sc);
163 GNUNET_CADET_channel_destroy (tun);
164}
165
166
167/**
168 * Reset the timeout for the cadet client (due to activity).
169 *
170 * @param sc client handle to reset timeout for
171 */
172static void
173refresh_timeout_task (struct CadetClient *sc)
174{
175 if (NULL != sc->timeout_task)
176 GNUNET_SCHEDULER_cancel (sc->timeout_task);
177 sc->timeout_task = GNUNET_SCHEDULER_add_delayed (IDLE_TIMEOUT,
178 &timeout_cadet_task,
179 sc);
180}
181
182
183/**
184 * Check if we are done with the write queue, and if so tell CADET
185 * that we are ready to read more.
186 *
187 * @param cls where to process the write queue
188 */
189static void
190continue_writing (void *cls)
191{
192 struct CadetClient *sc = cls;
193 struct GNUNET_MQ_Handle *mq;
194
195 mq = GNUNET_CADET_get_mq (sc->channel);
196 if (0 != GNUNET_MQ_get_length (mq))
197 {
198 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
199 "Write pending, waiting for it to complete\n");
200 return;
201 }
202 refresh_timeout_task (sc);
203 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
204 "Finished processing cadet request from client %p, ready to receive the next one\n",
205 sc);
206 GNUNET_CADET_receive_done (sc->channel);
207}
208
209
210/**
211 * Process a datum that was stored in the datastore.
212 *
213 * @param cls closure with the `struct CadetClient` which sent the query
214 * @param key key for the content
215 * @param size number of bytes in @a data
216 * @param data content stored
217 * @param type type of the content
218 * @param priority priority of the content
219 * @param anonymity anonymity-level for the content
220 * @param replication replication-level for the content
221 * @param expiration expiration time for the content
222 * @param uid unique identifier for the datum;
223 * maybe 0 if no unique identifier is available
224 */
225static void
226handle_datastore_reply (void *cls,
227 const struct GNUNET_HashCode *key,
228 size_t size,
229 const void *data,
230 enum GNUNET_BLOCK_Type type,
231 uint32_t priority,
232 uint32_t anonymity,
233 uint32_t replication,
234 struct GNUNET_TIME_Absolute expiration,
235 uint64_t uid)
236{
237 struct CadetClient *sc = cls;
238 size_t msize = size + sizeof(struct CadetReplyMessage);
239 struct GNUNET_MQ_Envelope *env;
240 struct CadetReplyMessage *srm;
241
242 sc->qe = NULL;
243 if (NULL == data)
244 {
245 /* no result, this should not really happen, as for
246 non-anonymous routing only peers that HAVE the
247 answers should be queried; OTOH, this is not a
248 hard error as we might have had the answer in the
249 past and the user might have unindexed it. Hence
250 we log at level "INFO" for now. */if (NULL == key)
251 {
252 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
253 "Have no answer and the query was NULL\n");
254 }
255 else
256 {
257 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
258 "Have no answer for query `%s'\n",
259 GNUNET_h2s (key));
260 }
261 GNUNET_STATISTICS_update (GSF_stats,
262 gettext_noop (
263 "# queries received via CADET not answered"),
264 1,
265 GNUNET_NO);
266 continue_writing (sc);
267 return;
268 }
269 if (GNUNET_BLOCK_TYPE_FS_ONDEMAND == type)
270 {
271 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
272 "Performing on-demand encoding for query %s\n",
273 GNUNET_h2s (key));
274 if (GNUNET_OK !=
275 GNUNET_FS_handle_on_demand_block (key,
276 size,
277 data,
278 type,
279 priority,
280 anonymity,
281 replication,
282 expiration,
283 uid,
284 &handle_datastore_reply,
285 sc))
286 {
287 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
288 "On-demand encoding request failed\n");
289 continue_writing (sc);
290 }
291 return;
292 }
293 if (msize > GNUNET_MAX_MESSAGE_SIZE)
294 {
295 GNUNET_break (0);
296 continue_writing (sc);
297 return;
298 }
299 GNUNET_break (GNUNET_BLOCK_TYPE_ANY != type);
300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
301 "Starting transmission of %u byte reply of type %d for query `%s' via cadet to %p\n",
302 (unsigned int) size,
303 (unsigned int) type,
304 GNUNET_h2s (key),
305 sc);
306 env = GNUNET_MQ_msg_extra (srm,
307 size,
308 GNUNET_MESSAGE_TYPE_FS_CADET_REPLY);
309 srm->type = htonl (type);
310 srm->expiration = GNUNET_TIME_absolute_hton (expiration);
311 GNUNET_memcpy (&srm[1],
312 data,
313 size);
314 GNUNET_MQ_notify_sent (env,
315 &continue_writing,
316 sc);
317 GNUNET_STATISTICS_update (GSF_stats,
318 gettext_noop ("# Blocks transferred via cadet"),
319 1,
320 GNUNET_NO);
321 GNUNET_MQ_send (GNUNET_CADET_get_mq (sc->channel),
322 env);
323}
324
325
326/**
327 * Functions with this signature are called whenever a
328 * complete query message is received.
329 *
330 * @param cls closure with the `struct CadetClient`
331 * @param sqm the actual message
332 */
333static void
334handle_request (void *cls,
335 const struct CadetQueryMessage *sqm)
336{
337 struct CadetClient *sc = cls;
338
339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
340 "Received query for `%s' via cadet from client %p\n",
341 GNUNET_h2s (&sqm->query),
342 sc);
343 GNUNET_STATISTICS_update (GSF_stats,
344 gettext_noop ("# queries received via cadet"),
345 1,
346 GNUNET_NO);
347 refresh_timeout_task (sc);
348 sc->qe = GNUNET_DATASTORE_get_key (GSF_dsh,
349 0 /* next_uid */,
350 false /* random */,
351 &sqm->query,
352 ntohl (sqm->type),
353 0 /* priority */,
354 GSF_datastore_queue_size,
355 &handle_datastore_reply,
356 sc);
357 if (NULL == sc->qe)
358 {
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360 "Queueing request with datastore failed (queue full?)\n");
361 continue_writing (sc);
362 }
363}
364
365
366/**
367 * Functions of this type are called upon new cadet connection from other peers.
368 *
369 * @param cls the closure from GNUNET_CADET_connect
370 * @param channel the channel representing the cadet
371 * @param initiator the identity of the peer who wants to establish a cadet
372 * with us; NULL on binding error
373 * @return initial channel context (our `struct CadetClient`)
374 */
375static void *
376connect_cb (void *cls,
377 struct GNUNET_CADET_Channel *channel,
378 const struct GNUNET_PeerIdentity *initiator)
379{
380 struct CadetClient *sc;
381
382 GNUNET_assert (NULL != channel);
383 if (sc_count >= sc_count_max)
384 {
385 GNUNET_STATISTICS_update (GSF_stats,
386 gettext_noop (
387 "# cadet client connections rejected"),
388 1,
389 GNUNET_NO);
390 GNUNET_CADET_channel_destroy (channel);
391 return NULL;
392 }
393 GNUNET_STATISTICS_update (GSF_stats,
394 gettext_noop ("# cadet connections active"),
395 1,
396 GNUNET_NO);
397 sc = GNUNET_new (struct CadetClient);
398 sc->channel = channel;
399 GNUNET_CONTAINER_DLL_insert (sc_head,
400 sc_tail,
401 sc);
402 sc_count++;
403 refresh_timeout_task (sc);
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405 "Accepting inbound cadet connection from `%s' as client %p\n",
406 GNUNET_i2s (initiator),
407 sc);
408 return sc;
409}
410
411
412/**
413 * Function called by cadet when a client disconnects.
414 * Cleans up our `struct CadetClient` of that channel.
415 *
416 * @param cls our `struct CadetClient`
417 * @param channel channel of the disconnecting client
418 * @param channel_ctx
419 */
420static void
421disconnect_cb (void *cls,
422 const struct GNUNET_CADET_Channel *channel)
423{
424 struct CadetClient *sc = cls;
425 struct WriteQueueItem *wqi;
426
427 if (NULL == sc)
428 return;
429 sc->channel = NULL;
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431 "Terminating cadet connection with client %p\n",
432 sc);
433 GNUNET_STATISTICS_update (GSF_stats,
434 gettext_noop ("# cadet connections active"), -1,
435 GNUNET_NO);
436 if (NULL != sc->terminate_task)
437 GNUNET_SCHEDULER_cancel (sc->terminate_task);
438 if (NULL != sc->timeout_task)
439 GNUNET_SCHEDULER_cancel (sc->timeout_task);
440 if (NULL != sc->qe)
441 GNUNET_DATASTORE_cancel (sc->qe);
442 while (NULL != (wqi = sc->wqi_head))
443 {
444 GNUNET_CONTAINER_DLL_remove (sc->wqi_head,
445 sc->wqi_tail,
446 wqi);
447 GNUNET_free (wqi);
448 }
449 GNUNET_CONTAINER_DLL_remove (sc_head,
450 sc_tail,
451 sc);
452 sc_count--;
453 GNUNET_free (sc);
454}
455
456
457/**
458 * Function called whenever an MQ-channel's transmission window size changes.
459 *
460 * The first callback in an outgoing channel will be with a non-zero value
461 * and will mean the channel is connected to the destination.
462 *
463 * For an incoming channel it will be called immediately after the
464 * #GNUNET_CADET_ConnectEventHandler, also with a non-zero value.
465 *
466 * @param cls Channel closure.
467 * @param channel Connection to the other end (henceforth invalid).
468 * @param window_size New window size. If the is more messages than buffer size
469 * this value will be negative..
470 */
471static void
472window_change_cb (void *cls,
473 const struct GNUNET_CADET_Channel *channel,
474 int window_size)
475{
476 /* FIXME: could do flow control here... */
477}
478
479
480/**
481 * Initialize subsystem for non-anonymous file-sharing.
482 */
483void
484GSF_cadet_start_server ()
485{
486 struct GNUNET_MQ_MessageHandler handlers[] = {
487 GNUNET_MQ_hd_fixed_size (request,
488 GNUNET_MESSAGE_TYPE_FS_CADET_QUERY,
489 struct CadetQueryMessage,
490 NULL),
491 GNUNET_MQ_handler_end ()
492 };
493 struct GNUNET_HashCode port;
494
495 if (GNUNET_YES !=
496 GNUNET_CONFIGURATION_get_value_number (GSF_cfg,
497 "fs",
498 "MAX_CADET_CLIENTS",
499 &sc_count_max))
500 return;
501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
502 "Initializing cadet FS server with a limit of %llu connections\n",
503 sc_count_max);
504 cadet_map = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_YES);
505 cadet_handle = GNUNET_CADET_connect (GSF_cfg);
506 GNUNET_assert (NULL != cadet_handle);
507 GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER,
508 strlen (GNUNET_APPLICATION_PORT_FS_BLOCK_TRANSFER),
509 &port);
510 cadet_port = GNUNET_CADET_open_port (cadet_handle,
511 &port,
512 &connect_cb,
513 NULL,
514 &window_change_cb,
515 &disconnect_cb,
516 handlers);
517}
518
519
520/**
521 * Shutdown subsystem for non-anonymous file-sharing.
522 */
523void
524GSF_cadet_stop_server ()
525{
526 GNUNET_CONTAINER_multipeermap_iterate (cadet_map,
527 &GSF_cadet_release_clients,
528 NULL);
529 GNUNET_CONTAINER_multipeermap_destroy (cadet_map);
530 cadet_map = NULL;
531 if (NULL != cadet_port)
532 {
533 GNUNET_CADET_close_port (cadet_port);
534 cadet_port = NULL;
535 }
536 if (NULL != cadet_handle)
537 {
538 GNUNET_CADET_disconnect (cadet_handle);
539 cadet_handle = NULL;
540 }
541 GNUNET_assert (NULL == sc_head);
542 GNUNET_assert (0 == sc_count);
543}
544
545
546/* end of gnunet-service-fs_cadet.c */
diff --git a/src/fs/gnunet-service-fs_cp.c b/src/fs/gnunet-service-fs_cp.c
deleted file mode 100644
index 30b895752..000000000
--- a/src/fs/gnunet-service-fs_cp.c
+++ /dev/null
@@ -1,1826 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-service-fs_cp.c
22 * @brief API to handle 'connected peers'
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_load_lib.h"
28#include "gnunet-service-fs.h"
29#include "gnunet-service-fs_cp.h"
30#include "gnunet-service-fs_pe.h"
31#include "gnunet-service-fs_pr.h"
32#include "gnunet-service-fs_push.h"
33#include "gnunet_peerstore_service.h"
34
35
36/**
37 * Ratio for moving average delay calculation. The previous
38 * average goes in with a factor of (n-1) into the calculation.
39 * Must be > 0.
40 */
41#define RUNAVG_DELAY_N 16
42
43/**
44 * How often do we flush respect values to disk?
45 */
46#define RESPECT_FLUSH_FREQ GNUNET_TIME_relative_multiply ( \
47 GNUNET_TIME_UNIT_MINUTES, 5)
48
49/**
50 * After how long do we discard a reply?
51 */
52#define REPLY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, \
53 2)
54
55/**
56 * Collect an instance number of statistics? May cause excessive IPC.
57 */
58#define INSANE_STATISTICS GNUNET_NO
59
60
61/**
62 * Handle to cancel a transmission request.
63 */
64struct GSF_PeerTransmitHandle
65{
66 /**
67 * Kept in a doubly-linked list.
68 */
69 struct GSF_PeerTransmitHandle *next;
70
71 /**
72 * Kept in a doubly-linked list.
73 */
74 struct GSF_PeerTransmitHandle *prev;
75
76 /**
77 * Time when this transmission request was issued.
78 */
79 struct GNUNET_TIME_Absolute transmission_request_start_time;
80
81 /**
82 * Envelope with the actual message.
83 */
84 struct GNUNET_MQ_Envelope *env;
85
86 /**
87 * Peer this request targets.
88 */
89 struct GSF_ConnectedPeer *cp;
90
91 /**
92 * #GNUNET_YES if this is a query, #GNUNET_NO for content.
93 */
94 int is_query;
95
96 /**
97 * Did we get a reservation already?
98 */
99 int was_reserved;
100
101 /**
102 * Priority of this request.
103 */
104 uint32_t priority;
105};
106
107
108/**
109 * Handle for an entry in our delay list.
110 */
111struct GSF_DelayedHandle
112{
113 /**
114 * Kept in a doubly-linked list.
115 */
116 struct GSF_DelayedHandle *next;
117
118 /**
119 * Kept in a doubly-linked list.
120 */
121 struct GSF_DelayedHandle *prev;
122
123 /**
124 * Peer this transmission belongs to.
125 */
126 struct GSF_ConnectedPeer *cp;
127
128 /**
129 * Envelope of the message that was delayed.
130 */
131 struct GNUNET_MQ_Envelope *env;
132
133 /**
134 * Task for the delay.
135 */
136 struct GNUNET_SCHEDULER_Task *delay_task;
137
138 /**
139 * Size of the message.
140 */
141 size_t msize;
142};
143
144
145/**
146 * Information per peer and request.
147 */
148struct PeerRequest
149{
150 /**
151 * Handle to generic request (generic: from peer or local client).
152 */
153 struct GSF_PendingRequest *pr;
154
155 /**
156 * Which specific peer issued this request?
157 */
158 struct GSF_ConnectedPeer *cp;
159
160 /**
161 * Task for asynchronous stopping of this request.
162 */
163 struct GNUNET_SCHEDULER_Task *kill_task;
164};
165
166
167/**
168 * A connected peer.
169 */
170struct GSF_ConnectedPeer
171{
172 /**
173 * Performance data for this peer.
174 */
175 struct GSF_PeerPerformanceData ppd;
176
177 /**
178 * Time until when we blocked this peer from migrating
179 * data to us.
180 */
181 struct GNUNET_TIME_Absolute last_migration_block;
182
183 /**
184 * Task scheduled to revive migration to this peer.
185 */
186 struct GNUNET_SCHEDULER_Task *mig_revive_task;
187
188 /**
189 * Messages (replies, queries, content migration) we would like to
190 * send to this peer in the near future. Sorted by priority, head.
191 */
192 struct GSF_PeerTransmitHandle *pth_head;
193
194 /**
195 * Messages (replies, queries, content migration) we would like to
196 * send to this peer in the near future. Sorted by priority, tail.
197 */
198 struct GSF_PeerTransmitHandle *pth_tail;
199
200 /**
201 * Messages (replies, queries, content migration) we would like to
202 * send to this peer in the near future. Sorted by priority, head.
203 */
204 struct GSF_DelayedHandle *delayed_head;
205
206 /**
207 * Messages (replies, queries, content migration) we would like to
208 * send to this peer in the near future. Sorted by priority, tail.
209 */
210 struct GSF_DelayedHandle *delayed_tail;
211
212 /**
213 * Context of our GNUNET_ATS_reserve_bandwidth call (or NULL).
214 */
215 struct GNUNET_ATS_ReservationContext *rc;
216
217 /**
218 * Task scheduled if we need to retry bandwidth reservation later.
219 */
220 struct GNUNET_SCHEDULER_Task *rc_delay_task;
221
222 /**
223 * Active requests from this neighbour, map of query to `struct PeerRequest`.
224 */
225 struct GNUNET_CONTAINER_MultiHashMap *request_map;
226
227 /**
228 * Handle for an active request for transmission to this
229 * peer.
230 */
231 struct GNUNET_MQ_Handle *mq;
232
233 /**
234 * Increase in traffic preference still to be submitted
235 * to the core service for this peer.
236 */
237 uint64_t inc_preference;
238
239 /**
240 * Number of entries in @e delayed_head DLL.
241 */
242 unsigned int delay_queue_size;
243
244 /**
245 * Respect rating for this peer on disk.
246 */
247 uint32_t disk_respect;
248
249 /**
250 * Which offset in @e last_p2p_replies will be updated next?
251 * (we go round-robin).
252 */
253 unsigned int last_p2p_replies_woff;
254
255 /**
256 * Which offset in @e last_client_replies will be updated next?
257 * (we go round-robin).
258 */
259 unsigned int last_client_replies_woff;
260
261 /**
262 * Current offset into @e last_request_times ring buffer.
263 */
264 unsigned int last_request_times_off;
265
266 /**
267 * #GNUNET_YES if we did successfully reserve 32k bandwidth,
268 * #GNUNET_NO if not.
269 */
270 int did_reserve;
271
272 /**
273 * Handle to the PEERSTORE iterate request for peer respect value
274 */
275 struct GNUNET_PEERSTORE_IterateContext *respect_iterate_req;
276};
277
278
279/**
280 * Map from peer identities to `struct GSF_ConnectPeer` entries.
281 */
282static struct GNUNET_CONTAINER_MultiPeerMap *cp_map;
283
284/**
285 * Handle to peerstore service.
286 */
287static struct GNUNET_PEERSTORE_Handle *peerstore;
288
289/**
290 * Task used to flush respect values to disk.
291 */
292static struct GNUNET_SCHEDULER_Task *fr_task;
293
294
295/**
296 * Update the latency information kept for the given peer.
297 *
298 * @param id peer record to update
299 * @param latency current latency value
300 */
301void
302GSF_update_peer_latency_ (const struct GNUNET_PeerIdentity *id,
303 struct GNUNET_TIME_Relative latency)
304{
305 struct GSF_ConnectedPeer *cp;
306
307 cp = GSF_peer_get_ (id);
308 if (NULL == cp)
309 return; /* we're not yet connected at the core level, ignore */
310 GNUNET_LOAD_value_set_decline (cp->ppd.transmission_delay,
311 latency);
312}
313
314
315/**
316 * Return the performance data record for the given peer
317 *
318 * @param cp peer to query
319 * @return performance data record for the peer
320 */
321struct GSF_PeerPerformanceData *
322GSF_get_peer_performance_data_ (struct GSF_ConnectedPeer *cp)
323{
324 return &cp->ppd;
325}
326
327
328/**
329 * Core is ready to transmit to a peer, get the message.
330 *
331 * @param cp which peer to send a message to
332 */
333static void
334peer_transmit (struct GSF_ConnectedPeer *cp);
335
336
337/**
338 * Function called by core upon success or failure of our bandwidth reservation request.
339 *
340 * @param cls the `struct GSF_ConnectedPeer` of the peer for which we made the request
341 * @param peer identifies the peer
342 * @param amount set to the amount that was actually reserved or unreserved;
343 * either the full requested amount or zero (no partial reservations)
344 * @param res_delay if the reservation could not be satisfied (amount was 0), how
345 * long should the client wait until re-trying?
346 */
347static void
348ats_reserve_callback (void *cls,
349 const struct GNUNET_PeerIdentity *peer,
350 int32_t amount,
351 struct GNUNET_TIME_Relative res_delay);
352
353
354/**
355 * If ready (bandwidth reserved), try to schedule transmission via
356 * core for the given handle.
357 *
358 * @param pth transmission handle to schedule
359 */
360static void
361schedule_transmission (struct GSF_PeerTransmitHandle *pth)
362{
363 struct GSF_ConnectedPeer *cp;
364 struct GNUNET_PeerIdentity target;
365
366 cp = pth->cp;
367 GNUNET_assert (0 != cp->ppd.pid);
368 GNUNET_PEER_resolve (cp->ppd.pid, &target);
369
370 if (0 != cp->inc_preference)
371 {
372 GNUNET_ATS_performance_change_preference (GSF_ats,
373 &target,
374 GNUNET_ATS_PREFERENCE_BANDWIDTH,
375 (double) cp->inc_preference,
376 GNUNET_ATS_PREFERENCE_END);
377 cp->inc_preference = 0;
378 }
379
380 if ((GNUNET_YES == pth->is_query) &&
381 (GNUNET_YES != pth->was_reserved))
382 {
383 /* query, need reservation */
384 if (GNUNET_YES != cp->did_reserve)
385 return; /* not ready */
386 cp->did_reserve = GNUNET_NO;
387 /* reservation already done! */
388 pth->was_reserved = GNUNET_YES;
389 cp->rc = GNUNET_ATS_reserve_bandwidth (GSF_ats,
390 &target,
391 DBLOCK_SIZE,
392 &ats_reserve_callback,
393 cp);
394 return;
395 }
396 peer_transmit (cp);
397}
398
399
400/**
401 * Core is ready to transmit to a peer, get the message.
402 *
403 * @param cp which peer to send a message to
404 */
405static void
406peer_transmit (struct GSF_ConnectedPeer *cp)
407{
408 struct GSF_PeerTransmitHandle *pth = cp->pth_head;
409 struct GSF_PeerTransmitHandle *pos;
410
411 if (NULL == pth)
412 return;
413 GNUNET_CONTAINER_DLL_remove (cp->pth_head,
414 cp->pth_tail,
415 pth);
416 if (GNUNET_YES == pth->is_query)
417 {
418 cp->ppd.last_request_times[(cp->last_request_times_off++)
419 % MAX_QUEUE_PER_PEER] =
420 GNUNET_TIME_absolute_get ();
421 GNUNET_assert (0 < cp->ppd.pending_queries--);
422 }
423 else if (GNUNET_NO == pth->is_query)
424 {
425 GNUNET_assert (0 < cp->ppd.pending_replies--);
426 }
427 GNUNET_LOAD_update (cp->ppd.transmission_delay,
428 GNUNET_TIME_absolute_get_duration
429 (pth->transmission_request_start_time).rel_value_us);
430 GNUNET_MQ_send (cp->mq,
431 pth->env);
432 GNUNET_free (pth);
433 if (NULL != (pos = cp->pth_head))
434 {
435 GNUNET_assert (pos != pth);
436 schedule_transmission (pos);
437 }
438}
439
440
441/**
442 * (re)try to reserve bandwidth from the given peer.
443 *
444 * @param cls the `struct GSF_ConnectedPeer` to reserve from
445 */
446static void
447retry_reservation (void *cls)
448{
449 struct GSF_ConnectedPeer *cp = cls;
450 struct GNUNET_PeerIdentity target;
451
452 GNUNET_PEER_resolve (cp->ppd.pid, &target);
453 cp->rc_delay_task = NULL;
454 cp->rc =
455 GNUNET_ATS_reserve_bandwidth (GSF_ats,
456 &target,
457 DBLOCK_SIZE,
458 &ats_reserve_callback, cp);
459}
460
461
462/**
463 * Function called by core upon success or failure of our bandwidth reservation request.
464 *
465 * @param cls the `struct GSF_ConnectedPeer` of the peer for which we made the request
466 * @param peer identifies the peer
467 * @param amount set to the amount that was actually reserved or unreserved;
468 * either the full requested amount or zero (no partial reservations)
469 * @param res_delay if the reservation could not be satisfied (amount was 0), how
470 * long should the client wait until re-trying?
471 */
472static void
473ats_reserve_callback (void *cls,
474 const struct GNUNET_PeerIdentity *peer,
475 int32_t amount,
476 struct GNUNET_TIME_Relative res_delay)
477{
478 struct GSF_ConnectedPeer *cp = cls;
479 struct GSF_PeerTransmitHandle *pth;
480
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482 "Reserved %d bytes / need to wait %s for reservation\n",
483 (int) amount,
484 GNUNET_STRINGS_relative_time_to_string (res_delay, GNUNET_YES));
485 cp->rc = NULL;
486 if (0 == amount)
487 {
488 cp->rc_delay_task =
489 GNUNET_SCHEDULER_add_delayed (res_delay,
490 &retry_reservation,
491 cp);
492 return;
493 }
494 cp->did_reserve = GNUNET_YES;
495 pth = cp->pth_head;
496 if (NULL != pth)
497 {
498 /* reservation success, try transmission now! */
499 peer_transmit (cp);
500 }
501}
502
503
504/**
505 * Function called by PEERSTORE with peer respect record
506 *
507 * @param cls handle to connected peer entry
508 * @param record peerstore record information
509 * @param emsg error message, or NULL if no errors
510 */
511static void
512peer_respect_cb (void *cls,
513 const struct GNUNET_PEERSTORE_Record *record,
514 const char *emsg)
515{
516 struct GSF_ConnectedPeer *cp = cls;
517
518 GNUNET_assert (NULL != cp->respect_iterate_req);
519 if ((NULL != record) &&
520 (sizeof(cp->disk_respect) == record->value_size))
521 {
522 cp->disk_respect = *((uint32_t *) record->value);
523 cp->ppd.respect += *((uint32_t *) record->value);
524 }
525 GSF_push_start_ (cp);
526 if (NULL != record)
527 GNUNET_PEERSTORE_iterate_cancel (cp->respect_iterate_req);
528 cp->respect_iterate_req = NULL;
529}
530
531
532/**
533 * Function called for each pending request whenever a new
534 * peer connects, giving us a chance to decide about submitting
535 * the existing request to the new peer.
536 *
537 * @param cls the `struct GSF_ConnectedPeer` of the new peer
538 * @param key query for the request
539 * @param pr handle to the pending request
540 * @return #GNUNET_YES to continue to iterate
541 */
542static int
543consider_peer_for_forwarding (void *cls,
544 const struct GNUNET_HashCode *key,
545 struct GSF_PendingRequest *pr)
546{
547 struct GSF_ConnectedPeer *cp = cls;
548 struct GNUNET_PeerIdentity pid;
549
550 if (GNUNET_YES !=
551 GSF_pending_request_test_active_ (pr))
552 return GNUNET_YES; /* request is not actually active, skip! */
553 GSF_connected_peer_get_identity_ (cp, &pid);
554 if (GNUNET_YES !=
555 GSF_pending_request_test_target_ (pr, &pid))
556 {
557 GNUNET_STATISTICS_update (GSF_stats,
558 gettext_noop ("# Loopback routes suppressed"),
559 1,
560 GNUNET_NO);
561 return GNUNET_YES;
562 }
563 GSF_plan_add_ (cp, pr);
564 return GNUNET_YES;
565}
566
567
568/**
569 * A peer connected to us. Setup the connected peer
570 * records.
571 *
572 * @param cls NULL
573 * @param peer identity of peer that connected
574 * @param mq message queue for talking to @a peer
575 * @return our internal handle for the peer
576 */
577void *
578GSF_peer_connect_handler (void *cls,
579 const struct GNUNET_PeerIdentity *peer,
580 struct GNUNET_MQ_Handle *mq)
581{
582 struct GSF_ConnectedPeer *cp;
583
584 if (0 ==
585 GNUNET_memcmp (&GSF_my_id,
586 peer))
587 return NULL;
588 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
589 "Connected to peer %s\n",
590 GNUNET_i2s (peer));
591 cp = GNUNET_new (struct GSF_ConnectedPeer);
592 cp->ppd.pid = GNUNET_PEER_intern (peer);
593 cp->ppd.peer = peer;
594 cp->mq = mq;
595 cp->ppd.transmission_delay = GNUNET_LOAD_value_init (GNUNET_TIME_UNIT_ZERO);
596 cp->rc =
597 GNUNET_ATS_reserve_bandwidth (GSF_ats,
598 peer,
599 DBLOCK_SIZE,
600 &ats_reserve_callback, cp);
601 cp->request_map = GNUNET_CONTAINER_multihashmap_create (128,
602 GNUNET_YES);
603 GNUNET_break (GNUNET_OK ==
604 GNUNET_CONTAINER_multipeermap_put (cp_map,
605 GSF_connected_peer_get_identity2_ (
606 cp),
607 cp,
608 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
609 GNUNET_STATISTICS_set (GSF_stats,
610 gettext_noop ("# peers connected"),
611 GNUNET_CONTAINER_multipeermap_size (cp_map),
612 GNUNET_NO);
613 cp->respect_iterate_req
614 = GNUNET_PEERSTORE_iterate (peerstore,
615 "fs",
616 peer,
617 "respect",
618 &peer_respect_cb,
619 cp);
620 GSF_iterate_pending_requests_ (&consider_peer_for_forwarding,
621 cp);
622 return cp;
623}
624
625
626/**
627 * It may be time to re-start migrating content to this
628 * peer. Check, and if so, restart migration.
629 *
630 * @param cls the `struct GSF_ConnectedPeer`
631 */
632static void
633revive_migration (void *cls)
634{
635 struct GSF_ConnectedPeer *cp = cls;
636 struct GNUNET_TIME_Relative bt;
637
638 cp->mig_revive_task = NULL;
639 bt = GNUNET_TIME_absolute_get_remaining (cp->ppd.migration_blocked_until);
640 if (0 != bt.rel_value_us)
641 {
642 /* still time left... */
643 cp->mig_revive_task =
644 GNUNET_SCHEDULER_add_delayed (bt, &revive_migration, cp);
645 return;
646 }
647 GSF_push_start_ (cp);
648}
649
650
651/**
652 * Get a handle for a connected peer.
653 *
654 * @param peer peer's identity
655 * @return NULL if the peer is not currently connected
656 */
657struct GSF_ConnectedPeer *
658GSF_peer_get_ (const struct GNUNET_PeerIdentity *peer)
659{
660 if (NULL == cp_map)
661 return NULL;
662 return GNUNET_CONTAINER_multipeermap_get (cp_map, peer);
663}
664
665
666/**
667 * Handle P2P #GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP message.
668 *
669 * @param cls closure, the `struct GSF_ConnectedPeer`
670 * @param msm the actual message
671 */
672void
673handle_p2p_migration_stop (void *cls,
674 const struct MigrationStopMessage *msm)
675{
676 struct GSF_ConnectedPeer *cp = cls;
677 struct GNUNET_TIME_Relative bt;
678
679 GNUNET_STATISTICS_update (GSF_stats,
680 gettext_noop ("# migration stop messages received"),
681 1, GNUNET_NO);
682 bt = GNUNET_TIME_relative_ntoh (msm->duration);
683 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
684 _ ("Migration of content to peer `%s' blocked for %s\n"),
685 GNUNET_i2s (cp->ppd.peer),
686 GNUNET_STRINGS_relative_time_to_string (bt, GNUNET_YES));
687 cp->ppd.migration_blocked_until = GNUNET_TIME_relative_to_absolute (bt);
688 if ((NULL == cp->mig_revive_task) &&
689 (NULL == cp->respect_iterate_req))
690 {
691 GSF_push_stop_ (cp);
692 cp->mig_revive_task =
693 GNUNET_SCHEDULER_add_delayed (bt,
694 &revive_migration, cp);
695 }
696}
697
698
699/**
700 * Free resources associated with the given peer request.
701 *
702 * @param peerreq request to free
703 */
704static void
705free_pending_request (struct PeerRequest *peerreq)
706{
707 struct GSF_ConnectedPeer *cp = peerreq->cp;
708 struct GSF_PendingRequestData *prd;
709
710 prd = GSF_pending_request_get_data_ (peerreq->pr);
711 if (NULL != peerreq->kill_task)
712 {
713 GNUNET_SCHEDULER_cancel (peerreq->kill_task);
714 peerreq->kill_task = NULL;
715 }
716 GNUNET_STATISTICS_update (GSF_stats,
717 gettext_noop ("# P2P searches active"),
718 -1,
719 GNUNET_NO);
720 GNUNET_break (GNUNET_YES ==
721 GNUNET_CONTAINER_multihashmap_remove (cp->request_map,
722 &prd->query,
723 peerreq));
724 GNUNET_free (peerreq);
725}
726
727
728/**
729 * Cancel all requests associated with the peer.
730 *
731 * @param cls unused
732 * @param query hash code of the request
733 * @param value the `struct GSF_PendingRequest`
734 * @return #GNUNET_YES (continue to iterate)
735 */
736static int
737cancel_pending_request (void *cls,
738 const struct GNUNET_HashCode *query,
739 void *value)
740{
741 struct PeerRequest *peerreq = value;
742 struct GSF_PendingRequest *pr = peerreq->pr;
743
744 free_pending_request (peerreq);
745 GSF_pending_request_cancel_ (pr,
746 GNUNET_NO);
747 return GNUNET_OK;
748}
749
750
751/**
752 * Free the given request.
753 *
754 * @param cls the request to free
755 */
756static void
757peer_request_destroy (void *cls)
758{
759 struct PeerRequest *peerreq = cls;
760 struct GSF_PendingRequest *pr = peerreq->pr;
761 struct GSF_PendingRequestData *prd;
762
763 peerreq->kill_task = NULL;
764 prd = GSF_pending_request_get_data_ (pr);
765 cancel_pending_request (NULL,
766 &prd->query,
767 peerreq);
768}
769
770
771/**
772 * The artificial delay is over, transmit the message now.
773 *
774 * @param cls the `struct GSF_DelayedHandle` with the message
775 */
776static void
777transmit_delayed_now (void *cls)
778{
779 struct GSF_DelayedHandle *dh = cls;
780 struct GSF_ConnectedPeer *cp = dh->cp;
781
782 GNUNET_CONTAINER_DLL_remove (cp->delayed_head,
783 cp->delayed_tail,
784 dh);
785 cp->delay_queue_size--;
786 GSF_peer_transmit_ (cp,
787 GNUNET_NO,
788 UINT32_MAX,
789 dh->env);
790 GNUNET_free (dh);
791}
792
793
794/**
795 * Get the randomized delay a response should be subjected to.
796 *
797 * @return desired delay
798 */
799static struct GNUNET_TIME_Relative
800get_randomized_delay ()
801{
802 struct GNUNET_TIME_Relative ret;
803
804 ret =
805 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
806 GNUNET_CRYPTO_random_u32
807 (GNUNET_CRYPTO_QUALITY_WEAK,
808 2 * GSF_avg_latency.rel_value_us + 1));
809#if INSANE_STATISTICS
810 GNUNET_STATISTICS_update (GSF_stats,
811 gettext_noop
812 ("# artificial delays introduced (ms)"),
813 ret.rel_value_us / 1000LL, GNUNET_NO);
814#endif
815 return ret;
816}
817
818
819/**
820 * Handle a reply to a pending request. Also called if a request
821 * expires (then with data == NULL). The handler may be called
822 * many times (depending on the request type), but will not be
823 * called during or after a call to GSF_pending_request_cancel
824 * and will also not be called anymore after a call signalling
825 * expiration.
826 *
827 * @param cls `struct PeerRequest` this is an answer for
828 * @param eval evaluation of the result
829 * @param pr handle to the original pending request
830 * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown"
831 * @param expiration when does @a data expire?
832 * @param last_transmission when did we last transmit a request for this block
833 * @param type type of the block
834 * @param data response data, NULL on request expiration
835 * @param data_len number of bytes in @a data
836 */
837static void
838handle_p2p_reply (void *cls,
839 enum GNUNET_BLOCK_ReplyEvaluationResult eval,
840 struct GSF_PendingRequest *pr,
841 uint32_t reply_anonymity_level,
842 struct GNUNET_TIME_Absolute expiration,
843 struct GNUNET_TIME_Absolute last_transmission,
844 enum GNUNET_BLOCK_Type type,
845 const void *data,
846 size_t data_len)
847{
848 struct PeerRequest *peerreq = cls;
849 struct GSF_ConnectedPeer *cp = peerreq->cp;
850 struct GSF_PendingRequestData *prd;
851 struct GNUNET_MQ_Envelope *env;
852 struct PutMessage *pm;
853 size_t msize;
854
855 GNUNET_assert (data_len + sizeof(struct PutMessage) <
856 GNUNET_MAX_MESSAGE_SIZE);
857 GNUNET_assert (peerreq->pr == pr);
858 prd = GSF_pending_request_get_data_ (pr);
859 if (NULL == data)
860 {
861 free_pending_request (peerreq);
862 return;
863 }
864 GNUNET_break (GNUNET_BLOCK_TYPE_ANY != type);
865 if ( (prd->type != type) &&
866 (GNUNET_BLOCK_TYPE_ANY != prd->type) )
867 {
868 GNUNET_STATISTICS_update (GSF_stats,
869 "# replies dropped due to type mismatch",
870 1, GNUNET_NO);
871 return;
872 }
873 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
874 "Transmitting result for query `%s' to peer\n",
875 GNUNET_h2s (&prd->query));
876 GNUNET_STATISTICS_update (GSF_stats,
877 "# replies received for other peers",
878 1,
879 GNUNET_NO);
880 msize = sizeof(struct PutMessage) + data_len;
881 if (msize >= GNUNET_MAX_MESSAGE_SIZE)
882 {
883 GNUNET_break (0);
884 return;
885 }
886 if ( (UINT32_MAX != reply_anonymity_level) &&
887 (reply_anonymity_level > 1) )
888 {
889 if (reply_anonymity_level - 1 > GSF_cover_content_count)
890 {
891 GNUNET_STATISTICS_update (GSF_stats,
892 "# replies dropped due to insufficient cover traffic",
893 1, GNUNET_NO);
894 return;
895 }
896 GSF_cover_content_count -= (reply_anonymity_level - 1);
897 }
898
899 env = GNUNET_MQ_msg_extra (pm,
900 data_len,
901 GNUNET_MESSAGE_TYPE_FS_PUT);
902 pm->type = htonl (type);
903 pm->expiration = GNUNET_TIME_absolute_hton (expiration);
904 GNUNET_memcpy (&pm[1],
905 data,
906 data_len);
907 if ((UINT32_MAX != reply_anonymity_level) &&
908 (0 != reply_anonymity_level) &&
909 (GNUNET_YES == GSF_enable_randomized_delays))
910 {
911 struct GSF_DelayedHandle *dh;
912
913 dh = GNUNET_new (struct GSF_DelayedHandle);
914 dh->cp = cp;
915 dh->env = env;
916 dh->msize = msize;
917 GNUNET_CONTAINER_DLL_insert (cp->delayed_head,
918 cp->delayed_tail,
919 dh);
920 cp->delay_queue_size++;
921 dh->delay_task =
922 GNUNET_SCHEDULER_add_delayed (get_randomized_delay (),
923 &transmit_delayed_now,
924 dh);
925 }
926 else
927 {
928 GSF_peer_transmit_ (cp,
929 GNUNET_NO,
930 UINT32_MAX,
931 env);
932 }
933 if (GNUNET_BLOCK_REPLY_OK_LAST != eval)
934 return;
935 if (NULL == peerreq->kill_task)
936 {
937 GNUNET_STATISTICS_update (GSF_stats,
938 "# P2P searches destroyed due to ultimate reply",
939 1,
940 GNUNET_NO);
941 peerreq->kill_task =
942 GNUNET_SCHEDULER_add_now (&peer_request_destroy,
943 peerreq);
944 }
945}
946
947
948/**
949 * Increase the peer's respect by a value.
950 *
951 * @param cp which peer to change the respect value on
952 * @param value is the int value by which the
953 * peer's credit is to be increased or decreased
954 * @returns the actual change in respect (positive or negative)
955 */
956static int
957change_peer_respect (struct GSF_ConnectedPeer *cp, int value)
958{
959 if (0 == value)
960 return 0;
961 GNUNET_assert (NULL != cp);
962 if (value > 0)
963 {
964 if (cp->ppd.respect + value < cp->ppd.respect)
965 {
966 value = UINT32_MAX - cp->ppd.respect;
967 cp->ppd.respect = UINT32_MAX;
968 }
969 else
970 cp->ppd.respect += value;
971 }
972 else
973 {
974 if (cp->ppd.respect < -value)
975 {
976 value = -cp->ppd.respect;
977 cp->ppd.respect = 0;
978 }
979 else
980 cp->ppd.respect += value;
981 }
982 return value;
983}
984
985
986/**
987 * We've received a request with the specified priority. Bound it
988 * according to how much we respect the given peer.
989 *
990 * @param prio_in requested priority
991 * @param cp the peer making the request
992 * @return effective priority
993 */
994static int32_t
995bound_priority (uint32_t prio_in,
996 struct GSF_ConnectedPeer *cp)
997{
998#define N ((double) 128.0)
999 uint32_t ret;
1000 double rret;
1001 int ld;
1002
1003 ld = GSF_test_get_load_too_high_ (0);
1004 if (GNUNET_SYSERR == ld)
1005 {
1006#if INSANE_STATISTICS
1007 GNUNET_STATISTICS_update (GSF_stats,
1008 gettext_noop
1009 ("# requests done for free (low load)"), 1,
1010 GNUNET_NO);
1011#endif
1012 return 0; /* excess resources */
1013 }
1014 if (prio_in > INT32_MAX)
1015 prio_in = INT32_MAX;
1016 ret = -change_peer_respect (cp, -(int) prio_in);
1017 if (ret > 0)
1018 {
1019 if (ret > GSF_current_priorities + N)
1020 rret = GSF_current_priorities + N;
1021 else
1022 rret = ret;
1023 GSF_current_priorities = (GSF_current_priorities * (N - 1) + rret) / N;
1024 }
1025 if ((GNUNET_YES == ld) && (ret > 0))
1026 {
1027 /* try with charging */
1028 ld = GSF_test_get_load_too_high_ (ret);
1029 }
1030 if (GNUNET_YES == ld)
1031 {
1032 GNUNET_STATISTICS_update (GSF_stats,
1033 gettext_noop
1034 ("# request dropped, priority insufficient"), 1,
1035 GNUNET_NO);
1036 /* undo charge */
1037 change_peer_respect (cp, (int) ret);
1038 return -1; /* not enough resources */
1039 }
1040 else
1041 {
1042 GNUNET_STATISTICS_update (GSF_stats,
1043 gettext_noop
1044 ("# requests done for a price (normal load)"),
1045 1,
1046 GNUNET_NO);
1047 }
1048#undef N
1049 return ret;
1050}
1051
1052
1053/**
1054 * The priority level imposes a bound on the maximum
1055 * value for the ttl that can be requested.
1056 *
1057 * @param ttl_in requested ttl
1058 * @param prio given priority
1059 * @return @a ttl_in if @a ttl_in is below the limit,
1060 * otherwise the ttl-limit for the given @a prio
1061 */
1062static int32_t
1063bound_ttl (int32_t ttl_in,
1064 uint32_t prio)
1065{
1066 unsigned long long allowed;
1067
1068 if (ttl_in <= 0)
1069 return ttl_in;
1070 allowed = ((unsigned long long) prio) * TTL_DECREMENT / 1000;
1071 if (ttl_in > allowed)
1072 {
1073 if (allowed >= (1 << 30))
1074 return 1 << 30;
1075 return allowed;
1076 }
1077 return ttl_in;
1078}
1079
1080
1081/**
1082 * Closure for #test_exist_cb().
1083 */
1084struct TestExistClosure
1085{
1086 /**
1087 * Priority of the incoming request.
1088 */
1089 int32_t priority;
1090
1091 /**
1092 * Relative TTL of the incoming request.
1093 */
1094 int32_t ttl;
1095
1096 /**
1097 * Type of the incoming request.
1098 */
1099 enum GNUNET_BLOCK_Type type;
1100
1101 /**
1102 * Set to #GNUNET_YES if we are done handling the query.
1103 */
1104 int finished;
1105};
1106
1107
1108/**
1109 * Test if the query already exists. If so, merge it, otherwise
1110 * keep `finished` at #GNUNET_NO.
1111 *
1112 * @param cls our `struct TestExistClosure`
1113 * @param hc the key of the query
1114 * @param value the existing `struct PeerRequest`.
1115 * @return #GNUNET_YES to continue to iterate,
1116 * #GNUNET_NO if we successfully merged
1117 */
1118static int
1119test_exist_cb (void *cls,
1120 const struct GNUNET_HashCode *hc,
1121 void *value)
1122{
1123 struct TestExistClosure *tec = cls;
1124 struct PeerRequest *peerreq = value;
1125 struct GSF_PendingRequest *pr;
1126 struct GSF_PendingRequestData *prd;
1127
1128 pr = peerreq->pr;
1129 prd = GSF_pending_request_get_data_ (pr);
1130 if (prd->type != tec->type)
1131 return GNUNET_YES;
1132 if (prd->ttl.abs_value_us >=
1133 GNUNET_TIME_absolute_get ().abs_value_us + tec->ttl * 1000LL)
1134 {
1135 /* existing request has higher TTL, drop new one! */
1136 prd->priority += tec->priority;
1137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1138 "Have existing request with higher TTL, dropping new request.\n");
1139 GNUNET_STATISTICS_update (GSF_stats,
1140 gettext_noop
1141 ("# requests dropped due to higher-TTL request"),
1142 1, GNUNET_NO);
1143 tec->finished = GNUNET_YES;
1144 return GNUNET_NO;
1145 }
1146 /* existing request has lower TTL, drop old one! */
1147 tec->priority += prd->priority;
1148 free_pending_request (peerreq);
1149 GSF_pending_request_cancel_ (pr,
1150 GNUNET_YES);
1151 return GNUNET_NO;
1152}
1153
1154
1155/**
1156 * Handle P2P "QUERY" message. Creates the pending request entry
1157 * and sets up all of the data structures to that we will
1158 * process replies properly. Does not initiate forwarding or
1159 * local database lookups.
1160 *
1161 * @param cls the other peer involved (sender of the message)
1162 * @param gm the GET message
1163 */
1164void
1165handle_p2p_get (void *cls,
1166 const struct GetMessage *gm)
1167{
1168 struct GSF_ConnectedPeer *cps = cls;
1169 struct PeerRequest *peerreq;
1170 struct GSF_PendingRequest *pr;
1171 struct GSF_ConnectedPeer *cp;
1172 const struct GNUNET_PeerIdentity *target;
1173 enum GSF_PendingRequestOptions options;
1174 uint16_t msize;
1175 unsigned int bits;
1176 const struct GNUNET_PeerIdentity *opt;
1177 uint32_t bm;
1178 size_t bfsize;
1179 uint32_t ttl_decrement;
1180 struct TestExistClosure tec;
1181 GNUNET_PEER_Id spid;
1182 const struct GSF_PendingRequestData *prd;
1183
1184 msize = ntohs (gm->header.size);
1185 tec.type = ntohl (gm->type);
1186 bm = ntohl (gm->hash_bitmap);
1187 bits = 0;
1188 while (bm > 0)
1189 {
1190 if (1 == (bm & 1))
1191 bits++;
1192 bm >>= 1;
1193 }
1194 opt = (const struct GNUNET_PeerIdentity *) &gm[1];
1195 bfsize = msize - sizeof(struct GetMessage) - bits * sizeof(struct
1196 GNUNET_PeerIdentity);
1197 GNUNET_STATISTICS_update (GSF_stats,
1198 gettext_noop
1199 ("# GET requests received (from other peers)"),
1200 1,
1201 GNUNET_NO);
1202 GSF_cover_query_count++;
1203 bm = ntohl (gm->hash_bitmap);
1204 bits = 0;
1205 if (0 != (bm & GET_MESSAGE_BIT_RETURN_TO))
1206 cp = GSF_peer_get_ (&opt[bits++]);
1207 else
1208 cp = cps;
1209 if (NULL == cp)
1210 {
1211 if (0 != (bm & GET_MESSAGE_BIT_RETURN_TO))
1212 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1213 "Failed to find RETURN-TO peer `%s' in connection set. Dropping query.\n",
1214 GNUNET_i2s (&opt[bits - 1]));
1215
1216 else
1217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1218 "Failed to find peer `%s' in connection set. Dropping query.\n",
1219 GNUNET_i2s (cps->ppd.peer));
1220 GNUNET_STATISTICS_update (GSF_stats,
1221 gettext_noop
1222 (
1223 "# requests dropped due to missing reverse route"),
1224 1,
1225 GNUNET_NO);
1226 return;
1227 }
1228 unsigned int queue_size = GNUNET_MQ_get_length (cp->mq);
1229 queue_size += cp->ppd.pending_replies + cp->delay_queue_size;
1230 if (queue_size > MAX_QUEUE_PER_PEER)
1231 {
1232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1233 "Peer `%s' has too many replies queued already. Dropping query.\n",
1234 GNUNET_i2s (cps->ppd.peer));
1235 GNUNET_STATISTICS_update (GSF_stats,
1236 gettext_noop (
1237 "# requests dropped due to full reply queue"),
1238 1,
1239 GNUNET_NO);
1240 return;
1241 }
1242 /* note that we can really only check load here since otherwise
1243 * peers could find out that we are overloaded by not being
1244 * disconnected after sending us a malformed query... */
1245 tec.priority = bound_priority (ntohl (gm->priority),
1246 cps);
1247 if (tec.priority < 0)
1248 {
1249 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1250 "Dropping query from `%s', this peer is too busy.\n",
1251 GNUNET_i2s (cps->ppd.peer));
1252 return;
1253 }
1254 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1255 "Received request for `%s' of type %u from peer `%s' with flags %u\n",
1256 GNUNET_h2s (&gm->query),
1257 (unsigned int) tec.type,
1258 GNUNET_i2s (cps->ppd.peer),
1259 (unsigned int) bm);
1260 target =
1261 (0 !=
1262 (bm & GET_MESSAGE_BIT_TRANSMIT_TO)) ? (&opt[bits++]) : NULL;
1263 options = GSF_PRO_DEFAULTS;
1264 spid = 0;
1265 if ((GNUNET_LOAD_get_load (cp->ppd.transmission_delay) > 3 * (1
1266 + tec.priority))
1267 || (GNUNET_LOAD_get_average (cp->ppd.transmission_delay) >
1268 GNUNET_CONSTANTS_MAX_CORK_DELAY.rel_value_us * 2
1269 + GNUNET_LOAD_get_average (GSF_rt_entry_lifetime)))
1270 {
1271 /* don't have BW to send to peer, or would likely take longer than we have for it,
1272 * so at best indirect the query */
1273 tec.priority = 0;
1274 options |= GSF_PRO_FORWARD_ONLY;
1275 spid = GNUNET_PEER_intern (cps->ppd.peer);
1276 GNUNET_assert (0 != spid);
1277 }
1278 tec.ttl = bound_ttl (ntohl (gm->ttl),
1279 tec.priority);
1280 /* decrement ttl (always) */
1281 ttl_decrement =
1282 2 * TTL_DECREMENT + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1283 TTL_DECREMENT);
1284 if ((tec.ttl < 0) &&
1285 (((int32_t) (tec.ttl - ttl_decrement)) > 0))
1286 {
1287 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1288 "Dropping query from `%s' due to TTL underflow (%d - %u).\n",
1289 GNUNET_i2s (cps->ppd.peer),
1290 tec.ttl,
1291 ttl_decrement);
1292 GNUNET_STATISTICS_update (GSF_stats,
1293 gettext_noop
1294 ("# requests dropped due TTL underflow"), 1,
1295 GNUNET_NO);
1296 /* integer underflow => drop (should be very rare)! */
1297 return;
1298 }
1299 tec.ttl -= ttl_decrement;
1300
1301 /* test if the request already exists */
1302 tec.finished = GNUNET_NO;
1303 GNUNET_CONTAINER_multihashmap_get_multiple (cp->request_map,
1304 &gm->query,
1305 &test_exist_cb,
1306 &tec);
1307 if (GNUNET_YES == tec.finished)
1308 return; /* merged into existing request, we're done */
1309
1310 peerreq = GNUNET_new (struct PeerRequest);
1311 peerreq->cp = cp;
1312 pr = GSF_pending_request_create_ (options,
1313 tec.type,
1314 &gm->query,
1315 target,
1316 (bfsize > 0)
1317 ? (const char *) &opt[bits]
1318 : NULL,
1319 bfsize,
1320 1 /* anonymity */,
1321 (uint32_t) tec.priority,
1322 tec.ttl,
1323 spid,
1324 GNUNET_PEER_intern (cps->ppd.peer),
1325 NULL, 0, /* replies_seen */
1326 &handle_p2p_reply,
1327 peerreq);
1328 GNUNET_assert (NULL != pr);
1329 prd = GSF_pending_request_get_data_ (pr);
1330 peerreq->pr = pr;
1331 GNUNET_break (GNUNET_OK ==
1332 GNUNET_CONTAINER_multihashmap_put (cp->request_map,
1333 &prd->query,
1334 peerreq,
1335 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
1336 GNUNET_STATISTICS_update (GSF_stats,
1337 gettext_noop (
1338 "# P2P query messages received and processed"),
1339 1,
1340 GNUNET_NO);
1341 GNUNET_STATISTICS_update (GSF_stats,
1342 gettext_noop ("# P2P searches active"),
1343 1,
1344 GNUNET_NO);
1345 GSF_pending_request_get_data_ (pr)->has_started = GNUNET_YES;
1346 GSF_local_lookup_ (pr,
1347 &GSF_consider_forwarding,
1348 NULL);
1349}
1350
1351
1352/**
1353 * Transmit a message to the given peer as soon as possible.
1354 * If the peer disconnects before the transmission can happen,
1355 * the callback is invoked with a `NULL` @a buffer.
1356 *
1357 * @param cp target peer
1358 * @param is_query is this a query (#GNUNET_YES) or content (#GNUNET_NO) or neither (#GNUNET_SYSERR)
1359 * @param priority how important is this request?
1360 * @param timeout when does this request timeout
1361 * @param size number of bytes we would like to send to the peer
1362 * @param env message to send
1363 */
1364void
1365GSF_peer_transmit_ (struct GSF_ConnectedPeer *cp,
1366 int is_query,
1367 uint32_t priority,
1368 struct GNUNET_MQ_Envelope *env)
1369{
1370 struct GSF_PeerTransmitHandle *pth;
1371 struct GSF_PeerTransmitHandle *pos;
1372 struct GSF_PeerTransmitHandle *prev;
1373
1374 pth = GNUNET_new (struct GSF_PeerTransmitHandle);
1375 pth->transmission_request_start_time = GNUNET_TIME_absolute_get ();
1376 pth->env = env;
1377 pth->is_query = is_query;
1378 pth->priority = priority;
1379 pth->cp = cp;
1380 /* insertion sort (by priority, descending) */
1381 prev = NULL;
1382 pos = cp->pth_head;
1383 while ((NULL != pos) && (pos->priority > priority))
1384 {
1385 prev = pos;
1386 pos = pos->next;
1387 }
1388 GNUNET_CONTAINER_DLL_insert_after (cp->pth_head,
1389 cp->pth_tail,
1390 prev,
1391 pth);
1392 if (GNUNET_YES == is_query)
1393 cp->ppd.pending_queries++;
1394 else if (GNUNET_NO == is_query)
1395 cp->ppd.pending_replies++;
1396 schedule_transmission (pth);
1397}
1398
1399
1400/**
1401 * Report on receiving a reply; update the performance record of the given peer.
1402 *
1403 * @param cp responding peer (will be updated)
1404 * @param request_time time at which the original query was transmitted
1405 * @param request_priority priority of the original request
1406 */
1407void
1408GSF_peer_update_performance_ (struct GSF_ConnectedPeer *cp,
1409 struct GNUNET_TIME_Absolute request_time,
1410 uint32_t request_priority)
1411{
1412 struct GNUNET_TIME_Relative delay;
1413
1414 delay = GNUNET_TIME_absolute_get_duration (request_time);
1415 cp->ppd.avg_reply_delay.rel_value_us =
1416 (cp->ppd.avg_reply_delay.rel_value_us * (RUNAVG_DELAY_N - 1)
1417 + delay.rel_value_us) / RUNAVG_DELAY_N;
1418 cp->ppd.avg_priority =
1419 (cp->ppd.avg_priority * (RUNAVG_DELAY_N - 1)
1420 + request_priority) / RUNAVG_DELAY_N;
1421}
1422
1423
1424/**
1425 * Report on receiving a reply in response to an initiating client.
1426 * Remember that this peer is good for this client.
1427 *
1428 * @param cp responding peer (will be updated)
1429 * @param initiator_client local client on responsible for query
1430 */
1431void
1432GSF_peer_update_responder_client_ (struct GSF_ConnectedPeer *cp,
1433 struct GSF_LocalClient *initiator_client)
1434{
1435 cp->ppd.last_client_replies[cp->last_client_replies_woff++
1436 % CS2P_SUCCESS_LIST_SIZE] = initiator_client;
1437}
1438
1439
1440/**
1441 * Report on receiving a reply in response to an initiating peer.
1442 * Remember that this peer is good for this initiating peer.
1443 *
1444 * @param cp responding peer (will be updated)
1445 * @param initiator_peer other peer responsible for query
1446 */
1447void
1448GSF_peer_update_responder_peer_ (struct GSF_ConnectedPeer *cp,
1449 const struct GSF_ConnectedPeer *initiator_peer)
1450{
1451 unsigned int woff;
1452
1453 woff = cp->last_p2p_replies_woff % P2P_SUCCESS_LIST_SIZE;
1454 GNUNET_PEER_change_rc (cp->ppd.last_p2p_replies[woff], -1);
1455 cp->ppd.last_p2p_replies[woff] = initiator_peer->ppd.pid;
1456 GNUNET_PEER_change_rc (initiator_peer->ppd.pid, 1);
1457 cp->last_p2p_replies_woff = (woff + 1) % P2P_SUCCESS_LIST_SIZE;
1458}
1459
1460
1461/**
1462 * Write peer-respect information to a file - flush the buffer entry!
1463 *
1464 * @param cls unused
1465 * @param key peer identity
1466 * @param value the `struct GSF_ConnectedPeer` to flush
1467 * @return #GNUNET_OK to continue iteration
1468 */
1469static int
1470flush_respect (void *cls,
1471 const struct GNUNET_PeerIdentity *key,
1472 void *value)
1473{
1474 struct GSF_ConnectedPeer *cp = value;
1475 struct GNUNET_PeerIdentity pid;
1476
1477 if (cp->ppd.respect == cp->disk_respect)
1478 return GNUNET_OK; /* unchanged */
1479 GNUNET_assert (0 != cp->ppd.pid);
1480 GNUNET_PEER_resolve (cp->ppd.pid, &pid);
1481 GNUNET_PEERSTORE_store (peerstore, "fs", &pid, "respect", &cp->ppd.respect,
1482 sizeof(cp->ppd.respect),
1483 GNUNET_TIME_UNIT_FOREVER_ABS,
1484 GNUNET_PEERSTORE_STOREOPTION_REPLACE,
1485 NULL,
1486 NULL);
1487 return GNUNET_OK;
1488}
1489
1490
1491/**
1492 * A peer disconnected from us. Tear down the connected peer
1493 * record.
1494 *
1495 * @param cls unused
1496 * @param peer identity of peer that disconnected
1497 * @param internal_cls the corresponding `struct GSF_ConnectedPeer`
1498 */
1499void
1500GSF_peer_disconnect_handler (void *cls,
1501 const struct GNUNET_PeerIdentity *peer,
1502 void *internal_cls)
1503{
1504 struct GSF_ConnectedPeer *cp = internal_cls;
1505 struct GSF_PeerTransmitHandle *pth;
1506 struct GSF_DelayedHandle *dh;
1507
1508 if (NULL == cp)
1509 return; /* must have been disconnect from core with
1510 * 'peer' == my_id, ignore */
1511 flush_respect (NULL,
1512 peer,
1513 cp);
1514 GNUNET_assert (GNUNET_YES ==
1515 GNUNET_CONTAINER_multipeermap_remove (cp_map,
1516 peer,
1517 cp));
1518 GNUNET_STATISTICS_set (GSF_stats,
1519 gettext_noop ("# peers connected"),
1520 GNUNET_CONTAINER_multipeermap_size (cp_map),
1521 GNUNET_NO);
1522 if (NULL != cp->respect_iterate_req)
1523 {
1524 GNUNET_PEERSTORE_iterate_cancel (cp->respect_iterate_req);
1525 cp->respect_iterate_req = NULL;
1526 }
1527 if (NULL != cp->rc)
1528 {
1529 GNUNET_ATS_reserve_bandwidth_cancel (cp->rc);
1530 cp->rc = NULL;
1531 }
1532 if (NULL != cp->rc_delay_task)
1533 {
1534 GNUNET_SCHEDULER_cancel (cp->rc_delay_task);
1535 cp->rc_delay_task = NULL;
1536 }
1537 GNUNET_CONTAINER_multihashmap_iterate (cp->request_map,
1538 &cancel_pending_request,
1539 cp);
1540 GNUNET_CONTAINER_multihashmap_destroy (cp->request_map);
1541 cp->request_map = NULL;
1542 GSF_plan_notify_peer_disconnect_ (cp);
1543 GNUNET_LOAD_value_free (cp->ppd.transmission_delay);
1544 GNUNET_PEER_decrement_rcs (cp->ppd.last_p2p_replies,
1545 P2P_SUCCESS_LIST_SIZE);
1546 memset (cp->ppd.last_p2p_replies,
1547 0,
1548 sizeof(cp->ppd.last_p2p_replies));
1549 GSF_push_stop_ (cp);
1550 while (NULL != (pth = cp->pth_head))
1551 {
1552 GNUNET_CONTAINER_DLL_remove (cp->pth_head,
1553 cp->pth_tail,
1554 pth);
1555 if (GNUNET_YES == pth->is_query)
1556 GNUNET_assert (0 < cp->ppd.pending_queries--);
1557 else if (GNUNET_NO == pth->is_query)
1558 GNUNET_assert (0 < cp->ppd.pending_replies--);
1559 GNUNET_free (pth);
1560 }
1561 while (NULL != (dh = cp->delayed_head))
1562 {
1563 GNUNET_CONTAINER_DLL_remove (cp->delayed_head,
1564 cp->delayed_tail,
1565 dh);
1566 GNUNET_MQ_discard (dh->env);
1567 cp->delay_queue_size--;
1568 GNUNET_SCHEDULER_cancel (dh->delay_task);
1569 GNUNET_free (dh);
1570 }
1571 GNUNET_PEER_change_rc (cp->ppd.pid, -1);
1572 if (NULL != cp->mig_revive_task)
1573 {
1574 GNUNET_SCHEDULER_cancel (cp->mig_revive_task);
1575 cp->mig_revive_task = NULL;
1576 }
1577 GNUNET_break (0 == cp->ppd.pending_queries);
1578 GNUNET_break (0 == cp->ppd.pending_replies);
1579 GNUNET_free (cp);
1580}
1581
1582
1583/**
1584 * Closure for #call_iterator().
1585 */
1586struct IterationContext
1587{
1588 /**
1589 * Function to call on each entry.
1590 */
1591 GSF_ConnectedPeerIterator it;
1592
1593 /**
1594 * Closure for @e it.
1595 */
1596 void *it_cls;
1597};
1598
1599
1600/**
1601 * Function that calls the callback for each peer.
1602 *
1603 * @param cls the `struct IterationContext *`
1604 * @param key identity of the peer
1605 * @param value the `struct GSF_ConnectedPeer *`
1606 * @return #GNUNET_YES to continue iteration
1607 */
1608static int
1609call_iterator (void *cls,
1610 const struct GNUNET_PeerIdentity *key,
1611 void *value)
1612{
1613 struct IterationContext *ic = cls;
1614 struct GSF_ConnectedPeer *cp = value;
1615
1616 ic->it (ic->it_cls,
1617 key, cp,
1618 &cp->ppd);
1619 return GNUNET_YES;
1620}
1621
1622
1623/**
1624 * Iterate over all connected peers.
1625 *
1626 * @param it function to call for each peer
1627 * @param it_cls closure for @a it
1628 */
1629void
1630GSF_iterate_connected_peers_ (GSF_ConnectedPeerIterator it,
1631 void *it_cls)
1632{
1633 struct IterationContext ic;
1634
1635 ic.it = it;
1636 ic.it_cls = it_cls;
1637 GNUNET_CONTAINER_multipeermap_iterate (cp_map,
1638 &call_iterator,
1639 &ic);
1640}
1641
1642
1643/**
1644 * Obtain the identity of a connected peer.
1645 *
1646 * @param cp peer to get identity of
1647 * @param id identity to set (written to)
1648 */
1649void
1650GSF_connected_peer_get_identity_ (const struct GSF_ConnectedPeer *cp,
1651 struct GNUNET_PeerIdentity *id)
1652{
1653 GNUNET_assert (0 != cp->ppd.pid);
1654 GNUNET_PEER_resolve (cp->ppd.pid, id);
1655}
1656
1657
1658/**
1659 * Obtain the identity of a connected peer.
1660 *
1661 * @param cp peer to get identity of
1662 * @return reference to peer identity, valid until peer disconnects (!)
1663 */
1664const struct GNUNET_PeerIdentity *
1665GSF_connected_peer_get_identity2_ (const struct GSF_ConnectedPeer *cp)
1666{
1667 GNUNET_assert (0 != cp->ppd.pid);
1668 return GNUNET_PEER_resolve2 (cp->ppd.pid);
1669}
1670
1671
1672/**
1673 * Ask a peer to stop migrating data to us until the given point
1674 * in time.
1675 *
1676 * @param cp peer to ask
1677 * @param block_time until when to block
1678 */
1679void
1680GSF_block_peer_migration_ (struct GSF_ConnectedPeer *cp,
1681 struct GNUNET_TIME_Absolute block_time)
1682{
1683 struct GNUNET_MQ_Envelope *env;
1684 struct MigrationStopMessage *msm;
1685
1686 if (cp->last_migration_block.abs_value_us > block_time.abs_value_us)
1687 {
1688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1689 "Migration already blocked for another %s\n",
1690 GNUNET_STRINGS_relative_time_to_string (
1691 GNUNET_TIME_absolute_get_remaining
1692 (cp->
1693 last_migration_block), GNUNET_YES));
1694 return; /* already blocked */
1695 }
1696 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asking to stop migration for %s\n",
1697 GNUNET_STRINGS_relative_time_to_string (
1698 GNUNET_TIME_absolute_get_remaining (block_time),
1699 GNUNET_YES));
1700 cp->last_migration_block = block_time;
1701 env = GNUNET_MQ_msg (msm,
1702 GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP);
1703 msm->reserved = htonl (0);
1704 msm->duration
1705 = GNUNET_TIME_relative_hton (GNUNET_TIME_absolute_get_remaining
1706 (cp->last_migration_block));
1707 GNUNET_STATISTICS_update (GSF_stats,
1708 gettext_noop ("# migration stop messages sent"),
1709 1,
1710 GNUNET_NO);
1711 GSF_peer_transmit_ (cp,
1712 GNUNET_SYSERR,
1713 UINT32_MAX,
1714 env);
1715}
1716
1717
1718/**
1719 * Notify core about a preference we have for the given peer
1720 * (to allocate more resources towards it). The change will
1721 * be communicated the next time we reserve bandwidth with
1722 * core (not instantly).
1723 *
1724 * @param cp peer to reserve bandwidth from
1725 * @param pref preference change
1726 */
1727void
1728GSF_connected_peer_change_preference_ (struct GSF_ConnectedPeer *cp,
1729 uint64_t pref)
1730{
1731 cp->inc_preference += pref;
1732}
1733
1734
1735/**
1736 * Call this method periodically to flush respect information to disk.
1737 *
1738 * @param cls closure, not used
1739 */
1740static void
1741cron_flush_respect (void *cls)
1742{
1743 fr_task = NULL;
1744 GNUNET_CONTAINER_multipeermap_iterate (cp_map,
1745 &flush_respect,
1746 NULL);
1747 fr_task = GNUNET_SCHEDULER_add_delayed_with_priority (RESPECT_FLUSH_FREQ,
1748 GNUNET_SCHEDULER_PRIORITY_HIGH,
1749 &cron_flush_respect,
1750 NULL);
1751}
1752
1753
1754/**
1755 * Initialize peer management subsystem.
1756 */
1757void
1758GSF_connected_peer_init_ ()
1759{
1760 cp_map = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES);
1761 peerstore = GNUNET_PEERSTORE_connect (GSF_cfg);
1762 fr_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_HIGH,
1763 &cron_flush_respect, NULL);
1764}
1765
1766
1767/**
1768 * Shutdown peer management subsystem.
1769 */
1770void
1771GSF_connected_peer_done_ ()
1772{
1773 GNUNET_CONTAINER_multipeermap_iterate (cp_map,
1774 &flush_respect,
1775 NULL);
1776 GNUNET_SCHEDULER_cancel (fr_task);
1777 fr_task = NULL;
1778 GNUNET_CONTAINER_multipeermap_destroy (cp_map);
1779 cp_map = NULL;
1780 GNUNET_PEERSTORE_disconnect (peerstore,
1781 GNUNET_YES);
1782}
1783
1784
1785/**
1786 * Iterator to remove references to LC entry.
1787 *
1788 * @param cls the `struct GSF_LocalClient *` to look for
1789 * @param key current key code
1790 * @param value value in the hash map (peer entry)
1791 * @return #GNUNET_YES (we should continue to iterate)
1792 */
1793static int
1794clean_local_client (void *cls,
1795 const struct GNUNET_PeerIdentity *key,
1796 void *value)
1797{
1798 const struct GSF_LocalClient *lc = cls;
1799 struct GSF_ConnectedPeer *cp = value;
1800 unsigned int i;
1801
1802 for (i = 0; i < CS2P_SUCCESS_LIST_SIZE; i++)
1803 if (cp->ppd.last_client_replies[i] == lc)
1804 cp->ppd.last_client_replies[i] = NULL;
1805 return GNUNET_YES;
1806}
1807
1808
1809/**
1810 * Notification that a local client disconnected. Clean up all of our
1811 * references to the given handle.
1812 *
1813 * @param lc handle to the local client (henceforth invalid)
1814 */
1815void
1816GSF_handle_local_client_disconnect_ (const struct GSF_LocalClient *lc)
1817{
1818 if (NULL == cp_map)
1819 return; /* already cleaned up */
1820 GNUNET_CONTAINER_multipeermap_iterate (cp_map,
1821 &clean_local_client,
1822 (void *) lc);
1823}
1824
1825
1826/* end of gnunet-service-fs_cp.c */
diff --git a/src/fs/gnunet-service-fs_cp.h b/src/fs/gnunet-service-fs_cp.h
deleted file mode 100644
index 54f952778..000000000
--- a/src/fs/gnunet-service-fs_cp.h
+++ /dev/null
@@ -1,415 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_cp.h
23 * @brief API to handle 'connected peers'
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_FS_CP_H
27#define GNUNET_SERVICE_FS_CP_H
28
29#include "fs.h"
30#include "gnunet-service-fs.h"
31
32
33/**
34 * Maximum number of outgoing messages we queue per peer.
35 *
36 * Performance measurements for 2 peer setup for 50 MB file
37 * (using perf_gnunet_service_fs_p2p):
38 *
39 * 24: 2-3 MB/s # ~ 24 MB RAM
40 * 256: 8 MB/s # ~256 MB RAM
41 *
42 * Conclusion: 24 should suffice (reasonable
43 * performance, no excessive memory use).
44 */
45#define MAX_QUEUE_PER_PEER 24
46
47/**
48 * Length of the P2P success tracker. Note that having a very long
49 * list can also hurt performance.
50 */
51#define P2P_SUCCESS_LIST_SIZE 8
52
53/**
54 * Length of the CS-2-P success tracker. Note that
55 * having a very long list can also hurt performance.
56 */
57#define CS2P_SUCCESS_LIST_SIZE 8
58
59
60/**
61 * Performance data kept for a peer.
62 */
63struct GSF_PeerPerformanceData
64{
65 /**
66 * List of the last clients for which this peer successfully
67 * answered a query.
68 */
69 struct GSF_LocalClient *last_client_replies[CS2P_SUCCESS_LIST_SIZE];
70
71 /**
72 * List of the last PIDs for which
73 * this peer successfully answered a query;
74 * We use 0 to indicate no successful reply.
75 */
76 GNUNET_PEER_Id last_p2p_replies[P2P_SUCCESS_LIST_SIZE];
77
78 /**
79 * Average delay between sending the peer a request and
80 * getting a reply (only calculated over the requests for
81 * which we actually got a reply). Calculated
82 * as a moving average: new_delay = ((n-1)*last_delay+curr_delay) / n
83 */
84 struct GNUNET_TIME_Relative avg_reply_delay;
85
86 /**
87 * If we get content we already have from this peer, for how
88 * long do we block it? Adjusted based on the fraction of
89 * redundant data we receive, between 1s and 1h.
90 */
91 struct GNUNET_TIME_Relative migration_delay;
92
93 /**
94 * Point in time until which this peer does not want us to migrate content
95 * to it.
96 */
97 struct GNUNET_TIME_Absolute migration_blocked_until;
98
99 /**
100 * Transmission times for the last MAX_QUEUE_PER_PEER
101 * requests for this peer. Used as a ring buffer, current
102 * offset is stored in 'last_request_times_off'. If the
103 * oldest entry is more recent than the 'avg_delay', we should
104 * not send any more requests right now.
105 */
106 struct GNUNET_TIME_Absolute last_request_times[MAX_QUEUE_PER_PEER];
107
108 /**
109 * How long does it typically take for us to transmit a message
110 * to this peer? (delay between the request being issued and
111 * the callback being invoked).
112 */
113 struct GNUNET_LOAD_Value *transmission_delay;
114
115 /**
116 * Average priority of successful replies. Calculated
117 * as a moving average: new_avg = ((n-1)*last_avg+curr_prio) / n
118 */
119 double avg_priority;
120
121 /**
122 * The peer's identity (interned version).
123 */
124 GNUNET_PEER_Id pid;
125
126 /**
127 * The peer's identity (pointer).
128 */
129 const struct GNUNET_PeerIdentity *peer;
130
131 /**
132 * Respect rating for this peer
133 */
134 uint32_t respect;
135
136 /**
137 * Number of pending queries (replies are not counted)
138 */
139 unsigned int pending_queries;
140
141 /**
142 * Number of pending replies (queries are not counted)
143 */
144 unsigned int pending_replies;
145};
146
147
148/**
149 * Signature of function called on a connected peer.
150 *
151 * @param cls closure
152 * @param peer identity of the peer
153 * @param cp handle to the connected peer record
154 * @param perf peer performance data
155 */
156typedef void
157(*GSF_ConnectedPeerIterator) (void *cls,
158 const struct GNUNET_PeerIdentity *peer,
159 struct GSF_ConnectedPeer *cp,
160 const struct GSF_PeerPerformanceData *ppd);
161
162
163/**
164 * Function called to get a message for transmission.
165 *
166 * @param cls closure
167 * @param buf_size number of bytes available in @a buf
168 * @param buf where to copy the message, NULL on error (peer disconnect)
169 * @return number of bytes copied to @a buf, can be 0 (without indicating an error)
170 */
171typedef size_t
172(*GSF_GetMessageCallback) (void *cls,
173 size_t buf_size,
174 void *buf);
175
176
177/**
178 * Signature of function called on a reservation success or failure.
179 *
180 * @param cls closure
181 * @param cp handle to the connected peer record
182 * @param success #GNUNET_YES on success, #GNUNET_NO on failure
183 */
184typedef void
185(*GSF_PeerReserveCallback) (void *cls,
186 struct GSF_ConnectedPeer *cp,
187 int success);
188
189
190/**
191 * Handle to cancel a transmission request.
192 */
193struct GSF_PeerTransmitHandle;
194
195
196/**
197 * A peer connected to us. Setup the connected peer
198 * records.
199 *
200 * @param cls NULL
201 * @param peer identity of peer that connected
202 * @param mq queue for sending messages to @a peer
203 * @return internal handle for the peer
204 */
205void *
206GSF_peer_connect_handler (void *cls,
207 const struct GNUNET_PeerIdentity *peer,
208 struct GNUNET_MQ_Handle *mq);
209
210
211/**
212 * Get a handle for a connected peer.
213 *
214 * @param peer peer's identity
215 * @return NULL if this peer is not currently connected
216 */
217struct GSF_ConnectedPeer *
218GSF_peer_get_ (const struct GNUNET_PeerIdentity *peer);
219
220
221/**
222 * Update the latency information kept for the given peer.
223 *
224 * @param id peer record to update
225 * @param latency current latency value
226 */
227void
228GSF_update_peer_latency_ (const struct GNUNET_PeerIdentity *id,
229 struct GNUNET_TIME_Relative latency);
230
231
232/**
233 * Transmit a message to the given peer as soon as possible.
234 * If the peer disconnects before the transmission can happen,
235 * the callback is invoked with a 'NULL' buffer.
236 *
237 * @param cp target peer
238 * @param is_query is this a query (#GNUNET_YES) or content (#GNUNET_NO)
239 * @param priority how important is this request?
240 * @param env envelope of message to send
241 */
242void
243GSF_peer_transmit_ (struct GSF_ConnectedPeer *cp,
244 int is_query,
245 uint32_t priority,
246 struct GNUNET_MQ_Envelope *env);
247
248
249/**
250 * Report on receiving a reply; update the performance record of the given peer.
251 *
252 * @param cp responding peer (will be updated)
253 * @param request_time time at which the original query was transmitted
254 * @param request_priority priority of the original request
255 */
256void
257GSF_peer_update_performance_ (struct GSF_ConnectedPeer *cp,
258 struct GNUNET_TIME_Absolute request_time,
259 uint32_t request_priority);
260
261
262/**
263 * Report on receiving a reply in response to an initiating client.
264 * Remember that this peer is good for this client.
265 *
266 * @param cp responding peer (will be updated)
267 * @param initiator_client local client on responsible for query
268 */
269void
270GSF_peer_update_responder_client_ (struct GSF_ConnectedPeer *cp,
271 struct GSF_LocalClient *initiator_client);
272
273
274/**
275 * Report on receiving a reply in response to an initiating peer.
276 * Remember that this peer is good for this initiating peer.
277 *
278 * @param cp responding peer (will be updated)
279 * @param initiator_peer other peer responsible for query
280 */
281void
282GSF_peer_update_responder_peer_ (struct GSF_ConnectedPeer *cp,
283 const struct GSF_ConnectedPeer
284 *initiator_peer);
285
286
287/**
288 * Handle P2P #GNUNET_MESSAGE_TYPE_FS_MIGRATION_STOP message.
289 *
290 * @param cls closure, the `struct GSF_ConnectedPeer`
291 * @param msm the actual message
292 */
293void
294handle_p2p_migration_stop (void *cls,
295 const struct MigrationStopMessage *message);
296
297
298/**
299 * Handle P2P "QUERY" message.
300 *
301 * @param cls the `struct GSF_ConnectedPeer` of the other sender
302 * @param gm the actual message
303 */
304void
305handle_p2p_get (void *cls,
306 const struct GetMessage *gm);
307
308
309/**
310 * Return the performance data record for the given peer
311 *
312 * @param cp peer to query
313 * @return performance data record for the peer
314 */
315struct GSF_PeerPerformanceData *
316GSF_get_peer_performance_data_ (struct GSF_ConnectedPeer *cp);
317
318
319/**
320 * Ask a peer to stop migrating data to us until the given point
321 * in time.
322 *
323 * @param cp peer to ask
324 * @param block_time until when to block
325 */
326void
327GSF_block_peer_migration_ (struct GSF_ConnectedPeer *cp,
328 struct GNUNET_TIME_Absolute block_time);
329
330
331/**
332 * A peer disconnected from us. Tear down the connected peer
333 * record.
334 *
335 * @param cls unused
336 * @param peer identity of peer that connected
337 * @param internal_cls our `struct GSF_ConnectedPeer` for @a peer
338 */
339void
340GSF_peer_disconnect_handler (void *cls,
341 const struct GNUNET_PeerIdentity *peer,
342 void *internal_cls);
343
344
345/**
346 * Notification that a local client disconnected. Clean up all of our
347 * references to the given handle.
348 *
349 * @param lc handle to the local client (henceforth invalid)
350 */
351void
352GSF_handle_local_client_disconnect_ (const struct GSF_LocalClient *lc);
353
354
355/**
356 * Notify core about a preference we have for the given peer
357 * (to allocate more resources towards it). The change will
358 * be communicated the next time we reserve bandwidth with
359 * core (not instantly).
360 *
361 * @param cp peer to reserve bandwidth from
362 * @param pref preference change
363 */
364void
365GSF_connected_peer_change_preference_ (struct GSF_ConnectedPeer *cp,
366 uint64_t pref);
367
368
369/**
370 * Obtain the identity of a connected peer.
371 *
372 * @param cp peer to get identity of
373 * @param id identity to set (written to)
374 */
375void
376GSF_connected_peer_get_identity_ (const struct GSF_ConnectedPeer *cp,
377 struct GNUNET_PeerIdentity *id);
378
379
380/**
381 * Obtain the identity of a connected peer.
382 *
383 * @param cp peer to get identity of
384 * @return reference to peer identity, valid until peer disconnects (!)
385 */
386const struct GNUNET_PeerIdentity *
387GSF_connected_peer_get_identity2_ (const struct GSF_ConnectedPeer *cp);
388
389
390/**
391 * Iterate over all connected peers.
392 *
393 * @param it function to call for each peer
394 * @param it_cls closure for it
395 */
396void
397GSF_iterate_connected_peers_ (GSF_ConnectedPeerIterator it, void *it_cls);
398
399
400/**
401 * Initialize peer management subsystem.
402 */
403void
404GSF_connected_peer_init_ (void);
405
406
407/**
408 * Shutdown peer management subsystem.
409 */
410void
411GSF_connected_peer_done_ (void);
412
413
414#endif
415/* end of gnunet-service-fs_cp.h */
diff --git a/src/fs/gnunet-service-fs_indexing.c b/src/fs/gnunet-service-fs_indexing.c
deleted file mode 100644
index f4d560176..000000000
--- a/src/fs/gnunet-service-fs_indexing.c
+++ /dev/null
@@ -1,522 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_indexing.c
23 * @brief program that provides indexing functions of the file-sharing service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <float.h>
28#include "gnunet_core_service.h"
29#include "gnunet_datastore_service.h"
30#include "gnunet_peer_lib.h"
31#include "gnunet_protocols.h"
32#include "gnunet_signatures.h"
33#include "gnunet_util_lib.h"
34#include "gnunet-service-fs.h"
35#include "gnunet-service-fs_indexing.h"
36#include "fs.h"
37
38/**
39 * In-memory information about indexed files (also available
40 * on-disk).
41 */
42struct IndexInfo
43{
44 /**
45 * This is a doubly linked list.
46 */
47 struct IndexInfo *next;
48
49 /**
50 * This is a doubly linked list.
51 */
52 struct IndexInfo *prev;
53
54 /**
55 * Name of the indexed file. Memory allocated
56 * at the end of this struct (do not free).
57 */
58 const char *filename;
59
60 /**
61 * Context for transmitting confirmation to client,
62 * NULL if we've done this already.
63 */
64 struct GNUNET_SERVER_TransmitContext *tc;
65
66 /**
67 * Context for hashing of the file.
68 */
69 struct GNUNET_CRYPTO_FileHashContext *fhc;
70
71 /**
72 * Hash of the contents of the file.
73 */
74 struct GNUNET_HashCode file_id;
75};
76
77
78/**
79 * Head of linked list of indexed files.
80 * FIXME: we don't need both a DLL and a hashmap here!
81 */
82static struct IndexInfo *indexed_files_head;
83
84/**
85 * Tail of linked list of indexed files.
86 */
87static struct IndexInfo *indexed_files_tail;
88
89/**
90 * Maps hash over content of indexed files to the respective 'struct IndexInfo'.
91 * The filenames are pointers into the indexed_files linked list and
92 * do not need to be freed.
93 */
94static struct GNUNET_CONTAINER_MultiHashMap *ifm;
95
96/**
97 * Our configuration.
98 */
99static const struct GNUNET_CONFIGURATION_Handle *cfg;
100
101/**
102 * Datastore handle. Created and destroyed by code in
103 * gnunet-service-fs (this is an alias).
104 */
105static struct GNUNET_DATASTORE_Handle *dsh;
106
107
108/**
109 * Write the current index information list to disk.
110 */
111static void
112write_index_list ()
113{
114 struct GNUNET_BIO_WriteHandle *wh;
115 char *fn;
116 struct IndexInfo *pos;
117
118 if (GNUNET_OK !=
119 GNUNET_CONFIGURATION_get_value_filename (cfg, "FS", "INDEXDB", &fn))
120 {
121 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
122 "fs",
123 "INDEXDB");
124 return;
125 }
126 wh = GNUNET_BIO_write_open_file (fn);
127 if (NULL == wh)
128 {
129 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
130 _ ("Could not open `%s'.\n"),
131 fn);
132 GNUNET_free (fn);
133 return;
134 }
135 for (pos = indexed_files_head; NULL != pos; pos = pos->next)
136 if ((GNUNET_OK != GNUNET_BIO_write (wh,
137 "fs-indexing-file-id",
138 &pos->file_id,
139 sizeof(struct GNUNET_HashCode))) ||
140 (GNUNET_OK != GNUNET_BIO_write_string (wh,
141 "fs-indexing-filename",
142 pos->filename)))
143 break;
144 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
145 {
146 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
147 _ ("Error writing `%s'.\n"),
148 fn);
149 GNUNET_free (fn);
150 return;
151 }
152 GNUNET_free (fn);
153}
154
155
156/**
157 * Read index information from disk.
158 */
159static void
160read_index_list ()
161{
162 struct GNUNET_BIO_ReadHandle *rh;
163 char *fn;
164 struct IndexInfo *pos;
165 char *fname;
166 struct GNUNET_HashCode hc;
167 size_t slen;
168 char *emsg;
169
170 if (GNUNET_OK !=
171 GNUNET_CONFIGURATION_get_value_filename (cfg, "FS", "INDEXDB", &fn))
172 {
173 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
174 "fs",
175 "INDEXDB");
176 return;
177 }
178 if (GNUNET_NO == GNUNET_DISK_file_test (fn))
179 {
180 /* no index info yet */
181 GNUNET_free (fn);
182 return;
183 }
184 rh = GNUNET_BIO_read_open_file (fn);
185 if (NULL == rh)
186 {
187 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
188 _ ("Could not open `%s'.\n"),
189 fn);
190 GNUNET_free (fn);
191 return;
192 }
193 while (
194 (GNUNET_OK == GNUNET_BIO_read (rh,
195 "Hash of indexed file",
196 &hc,
197 sizeof(struct GNUNET_HashCode))) &&
198 (GNUNET_OK ==
199 GNUNET_BIO_read_string (rh, "Name of indexed file", &fname, 1024 * 16)) &&
200 (fname != NULL))
201 {
202 slen = strlen (fname) + 1;
203 pos = GNUNET_malloc (sizeof(struct IndexInfo) + slen);
204 pos->file_id = hc;
205 pos->filename = (const char *) &pos[1];
206 GNUNET_memcpy (&pos[1], fname, slen);
207 if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put (
208 ifm,
209 &pos->file_id,
210 pos,
211 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
212 {
213 GNUNET_free (pos);
214 }
215 else
216 {
217 GNUNET_CONTAINER_DLL_insert (indexed_files_head, indexed_files_tail, pos);
218 }
219 GNUNET_free (fname);
220 }
221 if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
222 GNUNET_free (emsg);
223 GNUNET_free (fn);
224}
225
226
227/**
228 * Continuation called from datastore's remove
229 * function.
230 *
231 * @param cls unused
232 * @param success did the deletion work?
233 * @param min_expiration minimum expiration time required for content to be stored
234 * @param msg error message
235 */
236static void
237remove_cont (void *cls,
238 int success,
239 struct GNUNET_TIME_Absolute min_expiration,
240 const char *msg)
241{
242 if (GNUNET_OK != success)
243 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
244 _ ("Failed to delete bogus block: %s\n"),
245 msg);
246}
247
248
249/**
250 * We've received an on-demand encoded block from the datastore.
251 * Attempt to do on-demand encoding and (if successful), call the
252 * continuation with the resulting block. On error, clean up and ask
253 * the datastore for more results.
254 *
255 * @param key key for the content
256 * @param size number of bytes in data
257 * @param data content stored
258 * @param type type of the content
259 * @param priority priority of the content
260 * @param anonymity anonymity-level for the content
261 * @param replication replication-level for the content
262 * @param expiration expiration time for the content
263 * @param uid unique identifier for the datum;
264 * maybe 0 if no unique identifier is available
265 * @param cont function to call with the actual block (at most once, on success)
266 * @param cont_cls closure for cont
267 * @return GNUNET_OK on success
268 */
269int
270GNUNET_FS_handle_on_demand_block (const struct GNUNET_HashCode *key,
271 uint32_t size,
272 const void *data,
273 enum GNUNET_BLOCK_Type type,
274 uint32_t priority,
275 uint32_t anonymity,
276 uint32_t replication,
277 struct GNUNET_TIME_Absolute expiration,
278 uint64_t uid,
279 GNUNET_DATASTORE_DatumProcessor cont,
280 void *cont_cls)
281{
282 const struct OnDemandBlock *odb;
283 struct GNUNET_HashCode nkey;
284 struct GNUNET_CRYPTO_SymmetricSessionKey skey;
285 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
286 struct GNUNET_HashCode query;
287 ssize_t nsize;
288 char ndata[DBLOCK_SIZE];
289 char edata[DBLOCK_SIZE];
290 const char *fn;
291 struct GNUNET_DISK_FileHandle *fh;
292 uint64_t off;
293 struct IndexInfo *ii;
294
295 if (size != sizeof(struct OnDemandBlock))
296 {
297 GNUNET_break (0);
298 GNUNET_DATASTORE_remove (dsh, key, size, data, -1, -1, &remove_cont, NULL);
299 return GNUNET_SYSERR;
300 }
301 odb = (const struct OnDemandBlock *) data;
302 off = GNUNET_ntohll (odb->offset);
303 ii = GNUNET_CONTAINER_multihashmap_get (ifm, &odb->file_id);
304 if (NULL == ii)
305 {
306 GNUNET_break (0);
307 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
308 "Failed to find index %s\n",
309 GNUNET_h2s (&odb->file_id));
310 return GNUNET_SYSERR;
311 }
312 fn = ii->filename;
313 if ((NULL == fn) || (0 != access (fn, R_OK)))
314 {
315 GNUNET_STATISTICS_update (
316 GSF_stats,
317 gettext_noop ("# index blocks removed: original file inaccessible"),
318 1,
319 GNUNET_YES);
320 GNUNET_DATASTORE_remove (dsh, key, size, data, -1, -1, &remove_cont, NULL);
321 return GNUNET_SYSERR;
322 }
323 if ((NULL == (fh = GNUNET_DISK_file_open (fn,
324 GNUNET_DISK_OPEN_READ,
325 GNUNET_DISK_PERM_NONE))) ||
326 (off != GNUNET_DISK_file_seek (fh, off, GNUNET_DISK_SEEK_SET)) ||
327 (-1 == (nsize = GNUNET_DISK_file_read (fh, ndata, sizeof(ndata)))))
328 {
329 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
330 _ (
331 "Could not access indexed file `%s' (%s) at offset %llu: %s\n"),
332 GNUNET_h2s (&odb->file_id),
333 fn,
334 (unsigned long long) off,
335 (fn == NULL) ? _ ("not indexed") : strerror (errno));
336 if (fh != NULL)
337 GNUNET_DISK_file_close (fh);
338 GNUNET_DATASTORE_remove (dsh, key, size, data, -1, -1, &remove_cont, NULL);
339 return GNUNET_SYSERR;
340 }
341 GNUNET_DISK_file_close (fh);
342 GNUNET_CRYPTO_hash (ndata, nsize, &nkey);
343 GNUNET_CRYPTO_hash_to_aes_key (&nkey, &skey, &iv);
344 GNUNET_CRYPTO_symmetric_encrypt (ndata, nsize, &skey, &iv, edata);
345 GNUNET_CRYPTO_hash (edata, nsize, &query);
346 if (0 != memcmp (&query, key, sizeof(struct GNUNET_HashCode)))
347 {
348 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
349 _ ("Indexed file `%s' changed at offset %llu\n"),
350 fn,
351 (unsigned long long) off);
352 GNUNET_DATASTORE_remove (dsh, key, size, data, -1, -1, &remove_cont, NULL);
353 return GNUNET_SYSERR;
354 }
355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356 "On-demand encoded block for query `%s'\n",
357 GNUNET_h2s (key));
358 cont (cont_cls,
359 key,
360 nsize,
361 edata,
362 GNUNET_BLOCK_TYPE_FS_DBLOCK,
363 priority,
364 anonymity,
365 replication,
366 expiration,
367 uid);
368 return GNUNET_OK;
369}
370
371
372/**
373 * Transmit information about indexed files to @a mq.
374 *
375 * @param mq message queue to send information to
376 */
377void
378GNUNET_FS_indexing_send_list (struct GNUNET_MQ_Handle *mq)
379{
380 struct GNUNET_MQ_Envelope *env;
381 struct IndexInfoMessage *iim;
382 struct GNUNET_MessageHeader *iem;
383 size_t slen;
384 const char *fn;
385 struct IndexInfo *pos;
386
387 for (pos = indexed_files_head; NULL != pos; pos = pos->next)
388 {
389 fn = pos->filename;
390 slen = strlen (fn) + 1;
391 if (slen + sizeof(struct IndexInfoMessage) >= GNUNET_MAX_MESSAGE_SIZE)
392 {
393 GNUNET_break (0);
394 break;
395 }
396 env =
397 GNUNET_MQ_msg_extra (iim, slen, GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY);
398 iim->reserved = 0;
399 iim->file_id = pos->file_id;
400 GNUNET_memcpy (&iim[1], fn, slen);
401 GNUNET_MQ_send (mq, env);
402 }
403 env = GNUNET_MQ_msg (iem, GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END);
404 GNUNET_MQ_send (mq, env);
405}
406
407
408/**
409 * Remove a file from the index.
410 *
411 * @param fid identifier of the file to remove
412 * @return #GNUNET_YES if the @a fid was found
413 */
414int
415GNUNET_FS_indexing_do_unindex (const struct GNUNET_HashCode *fid)
416{
417 struct IndexInfo *pos;
418
419 for (pos = indexed_files_head; NULL != pos; pos = pos->next)
420 {
421 if (0 == memcmp (&pos->file_id, fid, sizeof(struct GNUNET_HashCode)))
422 {
423 GNUNET_CONTAINER_DLL_remove (indexed_files_head, indexed_files_tail, pos);
424 GNUNET_break (
425 GNUNET_OK ==
426 GNUNET_CONTAINER_multihashmap_remove (ifm, &pos->file_id, pos));
427 GNUNET_free (pos);
428 write_index_list ();
429 return GNUNET_YES;
430 }
431 }
432 return GNUNET_NO;
433}
434
435
436/**
437 * Add the given file to the list of indexed files.
438 *
439 * @param filename name of the file
440 * @param file_id hash identifier for @a filename
441 */
442void
443GNUNET_FS_add_to_index (const char *filename,
444 const struct GNUNET_HashCode *file_id)
445{
446 struct IndexInfo *ii;
447 size_t slen;
448
449 ii = GNUNET_CONTAINER_multihashmap_get (ifm, file_id);
450 if (NULL != ii)
451 {
452 GNUNET_log (
453 GNUNET_ERROR_TYPE_INFO,
454 _ (
455 "Index request received for file `%s' is already indexed as `%s'. Permitting anyway.\n"),
456 filename,
457 ii->filename);
458 return;
459 }
460 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
461 "Adding file %s to index as %s\n",
462 filename,
463 GNUNET_h2s (file_id));
464 slen = strlen (filename) + 1;
465 ii = GNUNET_malloc (sizeof(struct IndexInfo) + slen);
466 ii->file_id = *file_id;
467 ii->filename = (const char *) &ii[1];
468 GNUNET_memcpy (&ii[1], filename, slen);
469 GNUNET_CONTAINER_DLL_insert (indexed_files_head, indexed_files_tail, ii);
470 GNUNET_assert (GNUNET_OK ==
471 GNUNET_CONTAINER_multihashmap_put (
472 ifm,
473 &ii->file_id,
474 ii,
475 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
476 write_index_list ();
477}
478
479
480/**
481 * Shutdown the module.
482 */
483void
484GNUNET_FS_indexing_done ()
485{
486 struct IndexInfo *pos;
487
488 while (NULL != (pos = indexed_files_head))
489 {
490 GNUNET_CONTAINER_DLL_remove (indexed_files_head, indexed_files_tail, pos);
491 if (pos->fhc != NULL)
492 GNUNET_CRYPTO_hash_file_cancel (pos->fhc);
493 GNUNET_break (
494 GNUNET_OK ==
495 GNUNET_CONTAINER_multihashmap_remove (ifm, &pos->file_id, pos));
496 GNUNET_free (pos);
497 }
498 GNUNET_CONTAINER_multihashmap_destroy (ifm);
499 ifm = NULL;
500 cfg = NULL;
501}
502
503
504/**
505 * Initialize the indexing submodule.
506 *
507 * @param c configuration to use
508 * @param d datastore to use
509 */
510int
511GNUNET_FS_indexing_init (const struct GNUNET_CONFIGURATION_Handle *c,
512 struct GNUNET_DATASTORE_Handle *d)
513{
514 cfg = c;
515 dsh = d;
516 ifm = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_YES);
517 read_index_list ();
518 return GNUNET_OK;
519}
520
521
522/* end of gnunet-service-fs_indexing.c */
diff --git a/src/fs/gnunet-service-fs_indexing.h b/src/fs/gnunet-service-fs_indexing.h
deleted file mode 100644
index 546dbb172..000000000
--- a/src/fs/gnunet-service-fs_indexing.h
+++ /dev/null
@@ -1,121 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_indexing.h
23 * @brief indexing for the file-sharing service
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_FS_INDEXING_H
27#define GNUNET_SERVICE_FS_INDEXING_H
28
29#include "gnunet_block_lib.h"
30#include "gnunet_core_service.h"
31#include "gnunet_datastore_service.h"
32#include "gnunet_peer_lib.h"
33#include "gnunet_protocols.h"
34#include "gnunet_signatures.h"
35#include "gnunet_util_lib.h"
36
37
38/**
39 * We've received an on-demand encoded block from the datastore.
40 * Attempt to do on-demand encoding and (if successful), call the
41 * continuation with the resulting block. On error, clean up and ask
42 * the datastore for more results.
43 *
44 * @param key key for the content
45 * @param size number of bytes in data
46 * @param data content stored
47 * @param type type of the content
48 * @param priority priority of the content
49 * @param anonymity anonymity-level for the content
50 * @param replication replication-level for the content
51 * @param expiration expiration time for the content
52 * @param uid unique identifier for the datum;
53 * maybe 0 if no unique identifier is available
54 * @param cont function to call with the actual block (at most once, on success)
55 * @param cont_cls closure for @a cont
56 * @return #GNUNET_OK on success
57 */
58int
59GNUNET_FS_handle_on_demand_block (const struct GNUNET_HashCode *key,
60 uint32_t size,
61 const void *data,
62 enum GNUNET_BLOCK_Type type,
63 uint32_t priority,
64 uint32_t anonymity,
65 uint32_t replication,
66 struct GNUNET_TIME_Absolute expiration,
67 uint64_t uid,
68 GNUNET_DATASTORE_DatumProcessor cont,
69 void *cont_cls);
70
71
72/**
73 * Transmit information about indexed files to @a mq.
74 *
75 * @param mq message queue to send information to
76 */
77void
78GNUNET_FS_indexing_send_list (struct GNUNET_MQ_Handle *mq);
79
80
81/**
82 * Remove a file from the index.
83 *
84 * @param fid identifier of the file to remove
85 * @return #GNUNET_YES if the @a fid was found
86 */
87int
88GNUNET_FS_indexing_do_unindex (const struct GNUNET_HashCode *fid);
89
90
91/**
92 * Add the given file to the list of indexed files.
93 *
94 * @param filename name of the file
95 * @param file_id hash identifier for @a filename
96 */
97void
98GNUNET_FS_add_to_index (const char *filename,
99 const struct GNUNET_HashCode *file_id);
100
101
102/**
103 * Initialize the indexing submodule.
104 *
105 * @param c configuration to use
106 * @param d datastore to use
107 * @return GNUNET_OK on success
108 */
109int
110GNUNET_FS_indexing_init (const struct GNUNET_CONFIGURATION_Handle *c,
111 struct GNUNET_DATASTORE_Handle *d);
112
113
114/**
115 * Shutdown the module.
116 */
117void
118GNUNET_FS_indexing_done (void);
119
120
121#endif
diff --git a/src/fs/gnunet-service-fs_pe.c b/src/fs/gnunet-service-fs_pe.c
deleted file mode 100644
index 60dd0ab70..000000000
--- a/src/fs/gnunet-service-fs_pe.c
+++ /dev/null
@@ -1,814 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_pe.c
23 * @brief API to manage query plan
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet-service-fs.h"
28#include "gnunet-service-fs_cp.h"
29#include "gnunet-service-fs_pe.h"
30#include "gnunet-service-fs_pr.h"
31
32/**
33 * Collect an instance number of statistics? May cause excessive IPC.
34 */
35#define INSANE_STATISTICS GNUNET_NO
36
37/**
38 * List of GSF_PendingRequests this request plan
39 * participates with.
40 */
41struct PendingRequestList;
42
43/**
44 * Transmission plan for a peer.
45 */
46struct PeerPlan;
47
48
49/**
50 * M:N binding of plans to pending requests.
51 * Each pending request can be in a number of plans,
52 * and each plan can have a number of pending requests.
53 * Objects of this type indicate a mapping of a plan to
54 * a particular pending request.
55 *
56 * The corresponding head and tail of the "PE" MDLL
57 * are stored in a `struct GSF_RequestPlan`. (We need
58 * to be able to lookup all pending requests corresponding
59 * to a given plan entry.)
60 *
61 * Similarly head and tail of the "PR" MDLL are stored
62 * with the `struct GSF_PendingRequest`. (We need
63 * to be able to lookup all plan entries corresponding
64 * to a given pending request.)
65 */
66struct GSF_PendingRequestPlanBijection
67{
68 /**
69 * This is a doubly-linked list.
70 */
71 struct GSF_PendingRequestPlanBijection *next_PR;
72
73 /**
74 * This is a doubly-linked list.
75 */
76 struct GSF_PendingRequestPlanBijection *prev_PR;
77
78 /**
79 * This is a doubly-linked list.
80 */
81 struct GSF_PendingRequestPlanBijection *next_PE;
82
83 /**
84 * This is a doubly-linked list.
85 */
86 struct GSF_PendingRequestPlanBijection *prev_PE;
87
88 /**
89 * Associated request plan (tells us one of the peers that
90 * we plan to forward the request to).
91 */
92 struct GSF_RequestPlan *rp;
93
94 /**
95 * Associated pending request (identifies request details
96 * and one of the origins of the request).
97 */
98 struct GSF_PendingRequest *pr;
99};
100
101
102/**
103 * Information we keep per request per peer. This is a doubly-linked
104 * list (with head and tail in the `struct GSF_PendingRequestData`)
105 * with one entry in each heap of each `struct PeerPlan`. Each
106 * entry tracks information relevant for this request and this peer.
107 */
108struct GSF_RequestPlan
109{
110 /**
111 * This is a doubly-linked list.
112 */
113 struct GSF_RequestPlan *next;
114
115 /**
116 * This is a doubly-linked list.
117 */
118 struct GSF_RequestPlan *prev;
119
120 /**
121 * Heap node associated with this request and this peer.
122 */
123 struct GNUNET_CONTAINER_HeapNode *hn;
124
125 /**
126 * The transmission plan for a peer that this request is associated with.
127 */
128 struct PeerPlan *pp;
129
130 /**
131 * Head of list of associated pending requests. This tells us
132 * which incoming requests from other peers this plan entry
133 * corresponds to.
134 */
135 struct GSF_PendingRequestPlanBijection *pe_head;
136
137 /**
138 * Tail of list of associated pending requests.
139 */
140 struct GSF_PendingRequestPlanBijection *pe_tail;
141
142 /**
143 * Earliest time we'd be happy to (re)transmit this request.
144 */
145 struct GNUNET_TIME_Absolute earliest_transmission;
146
147 /**
148 * When was the last time we transmitted this request to this peer? 0 for never.
149 */
150 struct GNUNET_TIME_Absolute last_transmission;
151
152 /**
153 * Current priority for this request for this target.
154 */
155 uint64_t priority;
156
157 /**
158 * How often did we transmit this request to this peer?
159 */
160 unsigned int transmission_counter;
161};
162
163
164/**
165 * Transmission plan for a peer.
166 */
167struct PeerPlan
168{
169 /**
170 * Heap with pending queries (`struct GSF_RequestPlan`), higher weights mean higher priority.
171 */
172 struct GNUNET_CONTAINER_Heap *priority_heap;
173
174 /**
175 * Heap with pending queries (`struct GSF_RequestPlan`), by transmission time, lowest first.
176 */
177 struct GNUNET_CONTAINER_Heap *delay_heap;
178
179 /**
180 * Map of queries to plan entries. All entries in the @e priority_heap
181 * or @e delay_heap should be in the @e plan_map. Note that it is
182 * possible for the @e plan_map to have multiple entries for the same
183 * query.
184 */
185 struct GNUNET_CONTAINER_MultiHashMap *plan_map;
186
187 /**
188 * Peer for which this is the plan.
189 */
190 struct GSF_ConnectedPeer *cp;
191
192 /**
193 * Current task for executing the plan.
194 */
195 struct GNUNET_SCHEDULER_Task *task;
196
197 /**
198 * Current message under transmission for the plan.
199 */
200 struct GNUNET_MQ_Envelope *env;
201};
202
203
204/**
205 * Hash map from peer identities to PeerPlans.
206 */
207static struct GNUNET_CONTAINER_MultiPeerMap *plans;
208
209/**
210 * Sum of all transmission counters (equals total delay for all plan entries).
211 */
212static unsigned long long total_delay;
213
214/**
215 * Number of plan entries.
216 */
217static unsigned long long plan_count;
218
219
220/**
221 * Return the query (key in the plan_map) for the given request plan.
222 * Note that this key may change as there can be multiple pending
223 * requests for the same key and we just return _one_ of them; this
224 * particular one might complete while another one might still be
225 * active, hence the lifetime of the returned hash code is NOT
226 * necessarily identical to that of the `struct GSF_RequestPlan`
227 * given.
228 *
229 * @param rp a request plan
230 * @return the associated query
231 */
232static const struct GNUNET_HashCode *
233get_rp_key (struct GSF_RequestPlan *rp)
234{
235 return &GSF_pending_request_get_data_ (rp->pe_head->pr)->query;
236}
237
238
239/**
240 * Insert the given request plan into the heap with the appropriate weight.
241 *
242 * @param pp associated peer's plan
243 * @param rp request to plan
244 */
245static void
246plan (struct PeerPlan *pp,
247 struct GSF_RequestPlan *rp)
248{
249#define N ((double) 128.0)
250 /**
251 * Running average delay we currently impose.
252 */
253 static double avg_delay;
254
255 struct GSF_PendingRequestData *prd;
256 struct GNUNET_TIME_Relative delay;
257
258 GNUNET_assert (rp->pp == pp);
259 GNUNET_STATISTICS_set (GSF_stats,
260 gettext_noop ("# average retransmission delay (ms)"),
261 total_delay * 1000LL / plan_count, GNUNET_NO);
262 prd = GSF_pending_request_get_data_ (rp->pe_head->pr);
263
264 if (rp->transmission_counter < 8)
265 delay =
266 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
267 rp->transmission_counter);
268 else if (rp->transmission_counter < 32)
269 delay =
270 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
271 8
272 + (1LL << (rp->transmission_counter - 8)));
273 else
274 delay =
275 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
276 8 + (1LL << 24));
277 delay.rel_value_us =
278 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
279 delay.rel_value_us + 1);
280 /* Add 0.01 to avg_delay to avoid division-by-zero later */
281 avg_delay = (((avg_delay * (N - 1.0)) + delay.rel_value_us) / N) + 0.01;
282
283 /*
284 * For the priority, we need to consider a few basic rules:
285 * 1) if we just started requesting (delay is small), we should
286 * virtually always have a priority of zero.
287 * 2) for requests with average latency, our priority should match
288 * the average priority observed on the network
289 * 3) even the longest-running requests should not be WAY out of
290 * the observed average (thus we bound by a factor of 2)
291 * 4) we add +1 to the observed average priority to avoid everyone
292 * staying put at zero (2 * 0 = 0...).
293 *
294 * Using the specific calculation below, we get:
295 *
296 * delay = 0 => priority = 0;
297 * delay = avg delay => priority = running-average-observed-priority;
298 * delay >> avg_delay => priority = 2 * running-average-observed-priority;
299 *
300 * which satisfies all of the rules above.
301 *
302 * Note: M_PI_4 = PI/4 = arctan(1)
303 */rp->priority =
304 round ((GSF_current_priorities
305 + 1.0) * atan (delay.rel_value_us / avg_delay)) / M_PI_4;
306 /* Note: usage of 'round' and 'atan' requires -lm */
307
308 if (rp->transmission_counter != 0)
309 delay.rel_value_us += TTL_DECREMENT * 1000;
310 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
311 "Considering (re)transmission number %u in %s\n",
312 (unsigned int) rp->transmission_counter,
313 GNUNET_STRINGS_relative_time_to_string (delay,
314 GNUNET_YES));
315 rp->earliest_transmission = GNUNET_TIME_relative_to_absolute (delay);
316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317 "Earliest (re)transmission for `%s' in %us\n",
318 GNUNET_h2s (&prd->query),
319 rp->transmission_counter);
320 GNUNET_assert (rp->hn == NULL);
321 if (0 == GNUNET_TIME_absolute_get_remaining (
322 rp->earliest_transmission).rel_value_us)
323 rp->hn = GNUNET_CONTAINER_heap_insert (pp->priority_heap,
324 rp,
325 rp->priority);
326 else
327 rp->hn =
328 GNUNET_CONTAINER_heap_insert (pp->delay_heap,
329 rp,
330 rp->earliest_transmission.abs_value_us);
331 GNUNET_assert (GNUNET_YES ==
332 GNUNET_CONTAINER_multihashmap_contains_value (pp->plan_map,
333 get_rp_key (rp),
334 rp));
335#undef N
336}
337
338
339/**
340 * Get the pending request with the highest TTL from the given plan.
341 *
342 * @param rp plan to investigate
343 * @return pending request with highest TTL
344 */
345struct GSF_PendingRequest *
346get_latest (const struct GSF_RequestPlan *rp)
347{
348 struct GSF_PendingRequest *ret;
349 struct GSF_PendingRequestPlanBijection *bi;
350 const struct GSF_PendingRequestData *rprd;
351 const struct GSF_PendingRequestData *prd;
352
353 bi = rp->pe_head;
354 if (NULL == bi)
355 return NULL; /* should never happen */
356 ret = bi->pr;
357 rprd = GSF_pending_request_get_data_ (ret);
358 for (bi = bi->next_PE; NULL != bi; bi = bi->next_PE)
359 {
360 GNUNET_break (GNUNET_YES ==
361 GSF_pending_request_test_active_ (bi->pr));
362 prd = GSF_pending_request_get_data_ (bi->pr);
363 if (prd->ttl.abs_value_us > rprd->ttl.abs_value_us)
364 {
365 ret = bi->pr;
366 rprd = prd;
367 }
368 }
369 return ret;
370}
371
372
373/**
374 * Figure out when and how to transmit to the given peer.
375 *
376 * @param cls the `struct PeerPlan`
377 */
378static void
379schedule_peer_transmission (void *cls)
380{
381 struct PeerPlan *pp = cls;
382 struct GSF_RequestPlan *rp;
383 struct GNUNET_TIME_Relative delay;
384
385 if (NULL != pp->task)
386 {
387 pp->task = NULL;
388 }
389 else
390 {
391 GNUNET_assert (NULL != pp->env);
392 pp->env = NULL;
393 }
394 /* move ready requests to priority queue */
395 while ((NULL != (rp = GNUNET_CONTAINER_heap_peek (pp->delay_heap))) &&
396 (0 == GNUNET_TIME_absolute_get_remaining
397 (rp->earliest_transmission).rel_value_us))
398 {
399 GNUNET_assert (rp == GNUNET_CONTAINER_heap_remove_root (pp->delay_heap));
400 rp->hn = GNUNET_CONTAINER_heap_insert (pp->priority_heap,
401 rp,
402 rp->priority);
403 }
404 if (0 == GNUNET_CONTAINER_heap_get_size (pp->priority_heap))
405 {
406 /* priority heap (still) empty, check for delay... */
407 rp = GNUNET_CONTAINER_heap_peek (pp->delay_heap);
408 if (NULL == rp)
409 {
410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411 "No active requests for plan %p.\n",
412 pp);
413 return; /* both queues empty */
414 }
415 delay = GNUNET_TIME_absolute_get_remaining (rp->earliest_transmission);
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 "Sleeping for %s before retrying requests on plan %p.\n",
418 GNUNET_STRINGS_relative_time_to_string (delay,
419 GNUNET_YES),
420 pp);
421 GNUNET_STATISTICS_set (GSF_stats,
422 gettext_noop ("# delay heap timeout (ms)"),
423 delay.rel_value_us / 1000LL, GNUNET_NO);
424
425 pp->task
426 = GNUNET_SCHEDULER_add_at (rp->earliest_transmission,
427 &schedule_peer_transmission,
428 pp);
429 return;
430 }
431#if INSANE_STATISTICS
432 GNUNET_STATISTICS_update (GSF_stats,
433 gettext_noop ("# query plans executed"),
434 1,
435 GNUNET_NO);
436#endif
437 /* process from priority heap */
438 rp = GNUNET_CONTAINER_heap_remove_root (pp->priority_heap);
439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440 "Executing query plan %p\n",
441 rp);
442 GNUNET_assert (NULL != rp);
443 rp->hn = NULL;
444 rp->last_transmission = GNUNET_TIME_absolute_get ();
445 rp->transmission_counter++;
446 total_delay++;
447 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448 "Executing plan %p executed %u times, planning retransmission\n",
449 rp,
450 rp->transmission_counter);
451 GNUNET_assert (NULL == pp->env);
452 pp->env = GSF_pending_request_get_message_ (get_latest (rp));
453 GNUNET_MQ_notify_sent (pp->env,
454 &schedule_peer_transmission,
455 pp);
456 GSF_peer_transmit_ (pp->cp,
457 GNUNET_YES,
458 rp->priority,
459 pp->env);
460 GNUNET_STATISTICS_update (GSF_stats,
461 gettext_noop (
462 "# query messages sent to other peers"),
463 1,
464 GNUNET_NO);
465 plan (pp,
466 rp);
467}
468
469
470/**
471 * Closure for merge_pr().
472 */
473struct MergeContext
474{
475 /**
476 * Request we are trying to merge.
477 */
478 struct GSF_PendingRequest *pr;
479
480 /**
481 * Set to #GNUNET_YES if we succeeded to merge.
482 */
483 int merged;
484};
485
486
487/**
488 * Iterator that checks if an equivalent request is already
489 * present for this peer.
490 *
491 * @param cls closure
492 * @param query the query
493 * @param element request plan stored at the node
494 * @return #GNUNET_YES if we should continue to iterate,
495 * #GNUNET_NO if not (merge success)
496 */
497static int
498merge_pr (void *cls,
499 const struct GNUNET_HashCode *query,
500 void *element)
501{
502 struct MergeContext *mpr = cls;
503 struct GSF_RequestPlan *rp = element;
504 struct GSF_PendingRequestData *prd;
505 struct GSF_PendingRequestPlanBijection *bi;
506 struct GSF_PendingRequest *latest;
507
508 GNUNET_break (GNUNET_YES ==
509 GSF_pending_request_test_active_ (mpr->pr));
510 if (GNUNET_OK !=
511 GSF_pending_request_is_compatible_ (mpr->pr,
512 rp->pe_head->pr))
513 return GNUNET_YES;
514 /* merge new request with existing request plan */
515 bi = GNUNET_new (struct GSF_PendingRequestPlanBijection);
516 bi->rp = rp;
517 bi->pr = mpr->pr;
518 prd = GSF_pending_request_get_data_ (mpr->pr);
519 GNUNET_CONTAINER_MDLL_insert (PR,
520 prd->pr_head,
521 prd->pr_tail,
522 bi);
523 GNUNET_CONTAINER_MDLL_insert (PE,
524 rp->pe_head,
525 rp->pe_tail,
526 bi);
527 mpr->merged = GNUNET_YES;
528#if INSANE_STATISTICS
529 GNUNET_STATISTICS_update (GSF_stats,
530 gettext_noop ("# requests merged"),
531 1,
532 GNUNET_NO);
533#endif
534 latest = get_latest (rp);
535 if (GSF_pending_request_get_data_ (latest)->ttl.abs_value_us <
536 prd->ttl.abs_value_us)
537 {
538#if INSANE_STATISTICS
539 GNUNET_STATISTICS_update (GSF_stats,
540 gettext_noop ("# requests refreshed"),
541 1,
542 GNUNET_NO);
543#endif
544 rp->transmission_counter = 0; /* reset */
545 }
546 return GNUNET_NO;
547}
548
549
550/**
551 * Create a new query plan entry.
552 *
553 * @param cp peer with the entry
554 * @param pr request with the entry
555 */
556void
557GSF_plan_add_ (struct GSF_ConnectedPeer *cp,
558 struct GSF_PendingRequest *pr)
559{
560 const struct GNUNET_PeerIdentity *id;
561 struct PeerPlan *pp;
562 struct GSF_PendingRequestData *prd;
563 struct GSF_RequestPlan *rp;
564 struct GSF_PendingRequestPlanBijection *bi;
565 struct MergeContext mpc;
566
567 GNUNET_assert (GNUNET_YES ==
568 GSF_pending_request_test_active_ (pr));
569 GNUNET_assert (NULL != cp);
570 id = GSF_connected_peer_get_identity2_ (cp);
571 pp = GNUNET_CONTAINER_multipeermap_get (plans, id);
572 if (NULL == pp)
573 {
574 pp = GNUNET_new (struct PeerPlan);
575 pp->plan_map = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
576 pp->priority_heap =
577 GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
578 pp->delay_heap =
579 GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
580 pp->cp = cp;
581 GNUNET_assert (GNUNET_OK ==
582 GNUNET_CONTAINER_multipeermap_put (plans,
583 id,
584 pp,
585 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
586 pp->task = GNUNET_SCHEDULER_add_now (&schedule_peer_transmission,
587 pp);
588 }
589 mpc.merged = GNUNET_NO;
590 mpc.pr = pr;
591 prd = GSF_pending_request_get_data_ (pr);
592 GNUNET_CONTAINER_multihashmap_get_multiple (pp->plan_map,
593 &prd->query,
594 &merge_pr,
595 &mpc);
596 if (GNUNET_NO != mpc.merged)
597 return;
598 plan_count++;
599 GNUNET_STATISTICS_update (GSF_stats,
600 gettext_noop ("# query plan entries"),
601 1,
602 GNUNET_NO);
603 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
604 "Planning transmission of query `%s' to peer `%s'\n",
605 GNUNET_h2s (&prd->query),
606 GNUNET_i2s (id));
607 rp = GNUNET_new (struct GSF_RequestPlan);
608 bi = GNUNET_new (struct GSF_PendingRequestPlanBijection);
609 bi->rp = rp;
610 bi->pr = pr;
611 GNUNET_CONTAINER_MDLL_insert (PR,
612 prd->pr_head,
613 prd->pr_tail,
614 bi);
615 GNUNET_CONTAINER_MDLL_insert (PE,
616 rp->pe_head,
617 rp->pe_tail,
618 bi);
619 rp->pp = pp;
620 GNUNET_assert (GNUNET_YES ==
621 GNUNET_CONTAINER_multihashmap_put (pp->plan_map,
622 get_rp_key (rp),
623 rp,
624 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
625 plan (pp,
626 rp);
627}
628
629
630/**
631 * Notify the plan about a peer being no longer available;
632 * destroy all entries associated with this peer.
633 *
634 * @param cp connected peer
635 */
636void
637GSF_plan_notify_peer_disconnect_ (const struct GSF_ConnectedPeer *cp)
638{
639 const struct GNUNET_PeerIdentity *id;
640 struct PeerPlan *pp;
641 struct GSF_RequestPlan *rp;
642 struct GSF_PendingRequestData *prd;
643 struct GSF_PendingRequestPlanBijection *bi;
644
645 id = GSF_connected_peer_get_identity2_ (cp);
646 pp = GNUNET_CONTAINER_multipeermap_get (plans, id);
647 if (NULL == pp)
648 return; /* nothing was ever planned for this peer */
649 GNUNET_assert (GNUNET_YES ==
650 GNUNET_CONTAINER_multipeermap_remove (plans, id,
651 pp));
652 if (NULL != pp->task)
653 {
654 GNUNET_SCHEDULER_cancel (pp->task);
655 pp->task = NULL;
656 }
657 while (NULL != (rp = GNUNET_CONTAINER_heap_remove_root (pp->priority_heap)))
658 {
659 GNUNET_break (GNUNET_YES ==
660 GNUNET_CONTAINER_multihashmap_remove (pp->plan_map,
661 get_rp_key (rp),
662 rp));
663 while (NULL != (bi = rp->pe_head))
664 {
665 GNUNET_CONTAINER_MDLL_remove (PE,
666 rp->pe_head,
667 rp->pe_tail,
668 bi);
669 prd = GSF_pending_request_get_data_ (bi->pr);
670 GNUNET_CONTAINER_MDLL_remove (PR,
671 prd->pr_head,
672 prd->pr_tail,
673 bi);
674 GNUNET_free (bi);
675 }
676 plan_count--;
677 GNUNET_free (rp);
678 }
679 GNUNET_CONTAINER_heap_destroy (pp->priority_heap);
680 while (NULL != (rp = GNUNET_CONTAINER_heap_remove_root (pp->delay_heap)))
681 {
682 GNUNET_break (GNUNET_YES ==
683 GNUNET_CONTAINER_multihashmap_remove (pp->plan_map,
684 get_rp_key (rp),
685 rp));
686 while (NULL != (bi = rp->pe_head))
687 {
688 prd = GSF_pending_request_get_data_ (bi->pr);
689 GNUNET_CONTAINER_MDLL_remove (PE,
690 rp->pe_head,
691 rp->pe_tail,
692 bi);
693 GNUNET_CONTAINER_MDLL_remove (PR,
694 prd->pr_head,
695 prd->pr_tail,
696 bi);
697 GNUNET_free (bi);
698 }
699 plan_count--;
700 GNUNET_free (rp);
701 }
702 GNUNET_STATISTICS_set (GSF_stats,
703 gettext_noop ("# query plan entries"),
704 plan_count,
705 GNUNET_NO);
706 GNUNET_CONTAINER_heap_destroy (pp->delay_heap);
707 GNUNET_CONTAINER_multihashmap_destroy (pp->plan_map);
708 GNUNET_free (pp);
709}
710
711
712/**
713 * Get the last transmission attempt time for the request plan list
714 * referenced by @a pr_head, that was sent to @a sender
715 *
716 * @param pr_head request plan reference list to check.
717 * @param sender the peer that we've sent the request to.
718 * @param result the timestamp to fill, set to #GNUNET_TIME_UNIT_FOREVER_ABS if never transmitted
719 * @return #GNUNET_YES if @a result was changed, #GNUNET_NO otherwise.
720 */
721int
722GSF_request_plan_reference_get_last_transmission_ (struct
723 GSF_PendingRequestPlanBijection
724 *pr_head,
725 struct GSF_ConnectedPeer *
726 sender,
727 struct GNUNET_TIME_Absolute *
728 result)
729{
730 struct GSF_PendingRequestPlanBijection *bi;
731
732 for (bi = pr_head; NULL != bi; bi = bi->next_PR)
733 {
734 if (bi->rp->pp->cp == sender)
735 {
736 if (0 == bi->rp->last_transmission.abs_value_us)
737 *result = GNUNET_TIME_UNIT_FOREVER_ABS;
738 else
739 *result = bi->rp->last_transmission;
740 return GNUNET_YES;
741 }
742 }
743 return GNUNET_NO;
744}
745
746
747/**
748 * Notify the plan about a request being done; destroy all entries
749 * associated with this request.
750 *
751 * @param pr request that is done
752 */
753void
754GSF_plan_notify_request_done_ (struct GSF_PendingRequest *pr)
755{
756 struct GSF_RequestPlan *rp;
757 struct GSF_PendingRequestData *prd;
758 struct GSF_PendingRequestPlanBijection *bi;
759
760 prd = GSF_pending_request_get_data_ (pr);
761 while (NULL != (bi = prd->pr_head))
762 {
763 rp = bi->rp;
764 GNUNET_CONTAINER_MDLL_remove (PR,
765 prd->pr_head,
766 prd->pr_tail,
767 bi);
768 GNUNET_CONTAINER_MDLL_remove (PE,
769 rp->pe_head,
770 rp->pe_tail,
771 bi);
772 GNUNET_assert (bi->pr == pr);
773 if (NULL == rp->pe_head)
774 {
775 GNUNET_CONTAINER_heap_remove_node (rp->hn);
776 plan_count--;
777 GNUNET_break (GNUNET_YES ==
778 GNUNET_CONTAINER_multihashmap_remove (rp->pp->plan_map,
779 &prd->query,
780 rp));
781 GNUNET_free (rp);
782 }
783 GNUNET_free (bi);
784 }
785 GNUNET_STATISTICS_set (GSF_stats,
786 gettext_noop ("# query plan entries"),
787 plan_count,
788 GNUNET_NO);
789}
790
791
792/**
793 * Initialize plan subsystem.
794 */
795void
796GSF_plan_init ()
797{
798 plans = GNUNET_CONTAINER_multipeermap_create (256,
799 GNUNET_YES);
800}
801
802
803/**
804 * Shutdown plan subsystem.
805 */
806void
807GSF_plan_done ()
808{
809 GNUNET_assert (0 == GNUNET_CONTAINER_multipeermap_size (plans));
810 GNUNET_CONTAINER_multipeermap_destroy (plans);
811}
812
813
814/* end of gnunet-service-fs_pe.h */
diff --git a/src/fs/gnunet-service-fs_pe.h b/src/fs/gnunet-service-fs_pe.h
deleted file mode 100644
index d532b6b71..000000000
--- a/src/fs/gnunet-service-fs_pe.h
+++ /dev/null
@@ -1,95 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_pe.h
23 * @brief API to manage query plan
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_FS_PE_H
27#define GNUNET_SERVICE_FS_PE_H
28
29#include "gnunet-service-fs.h"
30
31
32/**
33 * Create a new query plan entry.
34 *
35 * @param cp peer with the entry
36 * @param pr request with the entry
37 */
38void
39GSF_plan_add_ (struct GSF_ConnectedPeer *cp,
40 struct GSF_PendingRequest *pr);
41
42
43/**
44 * Notify the plan about a peer being no longer available;
45 * destroy all entries associated with this peer.
46 *
47 * @param cp connected peer
48 */
49void
50GSF_plan_notify_peer_disconnect_ (const struct GSF_ConnectedPeer *cp);
51
52
53/**
54 * Notify the plan about a request being done;
55 * destroy all entries associated with this request.
56 *
57 * @param pr request that is done
58 */
59void
60GSF_plan_notify_request_done_ (struct GSF_PendingRequest *pr);
61
62/**
63 * Get the last transmission attempt time for the request plan list
64 * referenced by 'rpr_head', that was sent to 'sender'
65 *
66 * @param pr_head request plan reference list to check.
67 * @param sender the peer that we've sent the request to.
68 * @param result the timestamp to fill.
69 * @return GNUNET_YES if 'result' was changed, GNUNET_NO otherwise.
70 */
71int
72GSF_request_plan_reference_get_last_transmission_ (struct
73 GSF_PendingRequestPlanBijection
74 *pr_head,
75 struct GSF_ConnectedPeer *
76 sender,
77 struct GNUNET_TIME_Absolute *
78 result);
79
80/**
81 * Initialize plan subsystem.
82 */
83void
84GSF_plan_init (void);
85
86
87/**
88 * Shutdown plan subsystem.
89 */
90void
91GSF_plan_done (void);
92
93
94#endif
95/* end of gnunet-service-fs_pe.h */
diff --git a/src/fs/gnunet-service-fs_pr.c b/src/fs/gnunet-service-fs_pr.c
deleted file mode 100644
index ee2dbca32..000000000
--- a/src/fs/gnunet-service-fs_pr.c
+++ /dev/null
@@ -1,1921 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_pr.c
23 * @brief API to handle pending requests
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_load_lib.h"
29#include "gnunet-service-fs.h"
30#include "gnunet-service-fs_cp.h"
31#include "gnunet-service-fs_indexing.h"
32#include "gnunet-service-fs_pe.h"
33#include "gnunet-service-fs_pr.h"
34#include "gnunet-service-fs_cadet.h"
35
36
37/**
38 * Desired replication level for GETs.
39 */
40#define DHT_GET_REPLICATION 5
41
42/**
43 * Maximum size of the datastore queue for P2P operations. Needs to
44 * be large enough to queue #MAX_QUEUE_PER_PEER operations for roughly
45 * the number of active (connected) peers.
46 */
47#define MAX_DATASTORE_QUEUE (16 * MAX_QUEUE_PER_PEER)
48
49/**
50 * Bandwidth value of a 0-priority content (must be fairly high
51 * compared to query since content is typically significantly larger
52 * -- and more valuable since it can take many queries to get one
53 * piece of content).
54 */
55#define CONTENT_BANDWIDTH_VALUE 800
56
57/**
58 * Hard limit on the number of results we may get from the datastore per query.
59 */
60#define MAX_RESULTS (100 * 1024)
61
62/**
63 * Collect an instance number of statistics? May cause excessive IPC.
64 */
65#define INSANE_STATISTICS GNUNET_NO
66
67/**
68 * If obtaining a block via cadet fails, how often do we retry it before
69 * giving up for good (and sticking to non-anonymous transfer)?
70 */
71#define CADET_RETRY_MAX 3
72
73
74/**
75 * An active request.
76 */
77struct GSF_PendingRequest
78{
79 /**
80 * Public data for the request.
81 */
82 struct GSF_PendingRequestData public_data;
83
84 /**
85 * Function to call if we encounter a reply.
86 */
87 GSF_PendingRequestReplyHandler rh;
88
89 /**
90 * Closure for @e rh
91 */
92 void *rh_cls;
93
94 /**
95 * Array of hash codes of replies we've already seen.
96 */
97 struct GNUNET_HashCode *replies_seen;
98
99 /**
100 * Block group for filtering replies we've already seen.
101 */
102 struct GNUNET_BLOCK_Group *bg;
103
104 /**
105 * Entry for this pending request in the expiration heap, or NULL.
106 */
107 struct GNUNET_CONTAINER_HeapNode *hnode;
108
109 /**
110 * Datastore queue entry for this request (or NULL for none).
111 */
112 struct GNUNET_DATASTORE_QueueEntry *qe;
113
114 /**
115 * DHT request handle for this request (or NULL for none).
116 */
117 struct GNUNET_DHT_GetHandle *gh;
118
119 /**
120 * Cadet request handle for this request (or NULL for none).
121 */
122 struct GSF_CadetRequest *cadet_request;
123
124 /**
125 * Function to call upon completion of the local get
126 * request, or NULL for none.
127 */
128 GSF_LocalLookupContinuation llc_cont;
129
130 /**
131 * Closure for @e llc_cont.
132 */
133 void *llc_cont_cls;
134
135 /**
136 * Last result from the local datastore lookup evaluation.
137 */
138 enum GNUNET_BLOCK_ReplyEvaluationResult local_result;
139
140 /**
141 * Identity of the peer that we should use for the 'sender'
142 * (recipient of the response) when forwarding (0 for none).
143 */
144 GNUNET_PEER_Id sender_pid;
145
146 /**
147 * Identity of the peer that we should never forward this query
148 * to since it originated this query (0 for none).
149 */
150 GNUNET_PEER_Id origin_pid;
151
152 /**
153 * Time we started the last datastore lookup.
154 */
155 struct GNUNET_TIME_Absolute qe_start;
156
157 /**
158 * Task that warns us if the local datastore lookup takes too long.
159 */
160 struct GNUNET_SCHEDULER_Task *warn_task;
161
162 /**
163 * Do we have a first UID yet?
164 */
165 bool have_first_uid;
166
167 /**
168 * Have we seen a NULL result yet?
169 */
170 bool seen_null;
171
172 /**
173 * Unique ID of the first result from the local datastore;
174 * used to terminate the loop.
175 */
176 uint64_t first_uid;
177
178 /**
179 * Result count.
180 */
181 size_t result_count;
182
183 /**
184 * How often have we retried this request via 'cadet'?
185 * (used to bound overall retries).
186 */
187 unsigned int cadet_retry_count;
188
189 /**
190 * Number of valid entries in the 'replies_seen' array.
191 */
192 unsigned int replies_seen_count;
193
194 /**
195 * Length of the 'replies_seen' array.
196 */
197 unsigned int replies_seen_size;
198};
199
200
201/**
202 * All pending requests, ordered by the query. Entries
203 * are of type 'struct GSF_PendingRequest*'.
204 */
205static struct GNUNET_CONTAINER_MultiHashMap *pr_map;
206
207
208/**
209 * Datastore 'PUT' load tracking.
210 */
211static struct GNUNET_LOAD_Value *datastore_put_load;
212
213
214/**
215 * Are we allowed to migrate content to this peer.
216 */
217static int active_to_migration;
218
219
220/**
221 * Heap with the request that will expire next at the top. Contains
222 * pointers of type "struct PendingRequest*"; these will *also* be
223 * aliased from the "requests_by_peer" data structures and the
224 * "requests_by_query" table. Note that requests from our clients
225 * don't expire and are thus NOT in the "requests_by_expiration"
226 * (or the "requests_by_peer" tables).
227 */
228static struct GNUNET_CONTAINER_Heap *requests_by_expiration_heap;
229
230
231/**
232 * Maximum number of requests (from other peers, overall) that we're
233 * willing to have pending at any given point in time. Can be changed
234 * via the configuration file (32k is just the default).
235 */
236static unsigned long long max_pending_requests = (32 * 1024);
237
238
239/**
240 * Recalculate our bloom filter for filtering replies. This function
241 * will create a new bloom filter from scratch, so it should only be
242 * called if we have no bloomfilter at all (and hence can create a
243 * fresh one of minimal size without problems) OR if our peer is the
244 * initiator (in which case we may resize to larger than minimum size).
245 *
246 * @param type type of the request
247 * @param pr request for which the BF is to be recomputed
248 */
249static void
250refresh_bloomfilter (enum GNUNET_BLOCK_Type type,
251 struct GSF_PendingRequest *pr)
252{
253 if (NULL != pr->bg)
254 {
255 GNUNET_BLOCK_group_destroy (pr->bg);
256 pr->bg = NULL;
257 }
258 if (GNUNET_BLOCK_TYPE_FS_UBLOCK != type)
259 return; /* no need */
260 pr->bg =
261 GNUNET_BLOCK_group_create (GSF_block_ctx,
262 type,
263 NULL,
264 0,
265 "seen-set-size",
266 pr->replies_seen_count,
267 NULL);
268 if (NULL == pr->bg)
269 return;
270 GNUNET_break (GNUNET_OK ==
271 GNUNET_BLOCK_group_set_seen (pr->bg,
272 pr->replies_seen,
273 pr->replies_seen_count));
274}
275
276
277/**
278 * Create a new pending request.
279 *
280 * @param options request options
281 * @param type type of the block that is being requested
282 * @param query key for the lookup
283 * @param target preferred target for the request, NULL for none
284 * @param bf_data raw data for bloom filter for known replies, can be NULL
285 * @param bf_size number of bytes in @a bf_data
286 * @param anonymity_level desired anonymity level
287 * @param priority maximum outgoing cumulative request priority to use
288 * @param ttl current time-to-live for the request
289 * @param sender_pid peer ID to use for the sender when forwarding, 0 for none
290 * @param origin_pid peer ID of origin of query (do not loop back)
291 * @param replies_seen hash codes of known local replies
292 * @param replies_seen_count size of the @a replies_seen array
293 * @param rh handle to call when we get a reply
294 * @param rh_cls closure for @a rh
295 * @return handle for the new pending request
296 */
297struct GSF_PendingRequest *
298GSF_pending_request_create_ (enum GSF_PendingRequestOptions options,
299 enum GNUNET_BLOCK_Type type,
300 const struct GNUNET_HashCode *query,
301 const struct GNUNET_PeerIdentity *target,
302 const char *bf_data,
303 size_t bf_size,
304 uint32_t anonymity_level,
305 uint32_t priority,
306 int32_t ttl,
307 GNUNET_PEER_Id sender_pid,
308 GNUNET_PEER_Id origin_pid,
309 const struct GNUNET_HashCode *replies_seen,
310 unsigned int replies_seen_count,
311 GSF_PendingRequestReplyHandler rh,
312 void *rh_cls)
313{
314 struct GSF_PendingRequest *pr;
315 struct GSF_PendingRequest *dpr;
316 size_t extra;
317 struct GNUNET_HashCode *eptr;
318
319 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
320 "Creating request handle for `%s' of type %d\n",
321 GNUNET_h2s (query),
322 type);
323#if INSANE_STATISTICS
324 GNUNET_STATISTICS_update (GSF_stats,
325 gettext_noop ("# Pending requests created"),
326 1,
327 GNUNET_NO);
328#endif
329 extra = 0;
330 if (NULL != target)
331 extra += sizeof(struct GNUNET_PeerIdentity);
332 pr = GNUNET_malloc (sizeof(struct GSF_PendingRequest) + extra);
333 pr->public_data.query = *query;
334 eptr = (struct GNUNET_HashCode *) &pr[1];
335 if (NULL != target)
336 {
337 pr->public_data.target = (struct GNUNET_PeerIdentity *) eptr;
338 GNUNET_memcpy (eptr, target, sizeof(struct GNUNET_PeerIdentity));
339 }
340 pr->public_data.anonymity_level = anonymity_level;
341 pr->public_data.priority = priority;
342 pr->public_data.original_priority = priority;
343 pr->public_data.options = options;
344 pr->public_data.type = type;
345 pr->public_data.start_time = GNUNET_TIME_absolute_get ();
346 pr->sender_pid = sender_pid;
347 pr->origin_pid = origin_pid;
348 pr->rh = rh;
349 pr->rh_cls = rh_cls;
350 GNUNET_assert ((sender_pid != 0) || (0 == (options & GSF_PRO_FORWARD_ONLY)));
351 if (ttl >= 0)
352 pr->public_data.ttl = GNUNET_TIME_relative_to_absolute (
353 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, (uint32_t) ttl));
354 else
355 pr->public_data.ttl = GNUNET_TIME_absolute_subtract (
356 pr->public_data.start_time,
357 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
358 (uint32_t) (-ttl)));
359 if (replies_seen_count > 0)
360 {
361 pr->replies_seen_size = replies_seen_count;
362 pr->replies_seen =
363 GNUNET_new_array (pr->replies_seen_size, struct GNUNET_HashCode);
364 GNUNET_memcpy (pr->replies_seen,
365 replies_seen,
366 replies_seen_count * sizeof(struct GNUNET_HashCode));
367 pr->replies_seen_count = replies_seen_count;
368 }
369 if ((NULL != bf_data) &&
370 (GNUNET_BLOCK_TYPE_FS_UBLOCK == pr->public_data.type))
371 {
372 pr->bg = GNUNET_BLOCK_group_create (GSF_block_ctx,
373 pr->public_data.type,
374 bf_data,
375 bf_size,
376 "seen-set-size",
377 0,
378 NULL);
379 }
380 else if ((replies_seen_count > 0) &&
381 (0 != (options & GSF_PRO_BLOOMFILTER_FULL_REFRESH)))
382 {
383 refresh_bloomfilter (pr->public_data.type, pr);
384 }
385 GNUNET_CONTAINER_multihashmap_put (pr_map,
386 &pr->public_data.query,
387 pr,
388 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
389 if (0 == (options & GSF_PRO_REQUEST_NEVER_EXPIRES))
390 {
391 pr->hnode = GNUNET_CONTAINER_heap_insert (requests_by_expiration_heap,
392 pr,
393 pr->public_data.ttl.abs_value_us);
394 /* make sure we don't track too many requests */
395 while (GNUNET_CONTAINER_heap_get_size (requests_by_expiration_heap) >
396 max_pending_requests)
397 {
398 dpr = GNUNET_CONTAINER_heap_peek (requests_by_expiration_heap);
399 GNUNET_assert (NULL != dpr);
400 if (pr == dpr)
401 break; /* let the request live briefly... */
402 if (NULL != dpr->rh)
403 dpr->rh (dpr->rh_cls,
404 GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED,
405 dpr,
406 UINT32_MAX,
407 GNUNET_TIME_UNIT_FOREVER_ABS,
408 GNUNET_TIME_UNIT_FOREVER_ABS,
409 GNUNET_BLOCK_TYPE_ANY,
410 NULL,
411 0);
412 GSF_pending_request_cancel_ (dpr, GNUNET_YES);
413 }
414 }
415 GNUNET_STATISTICS_update (GSF_stats,
416 gettext_noop ("# Pending requests active"),
417 1,
418 GNUNET_NO);
419 return pr;
420}
421
422
423/**
424 * Obtain the public data associated with a pending request
425 *
426 * @param pr pending request
427 * @return associated public data
428 */
429struct GSF_PendingRequestData *
430GSF_pending_request_get_data_ (struct GSF_PendingRequest *pr)
431{
432 return &pr->public_data;
433}
434
435
436/**
437 * Test if two pending requests are compatible (would generate
438 * the same query modulo filters and should thus be processed
439 * jointly).
440 *
441 * @param pra a pending request
442 * @param prb another pending request
443 * @return #GNUNET_OK if the requests are compatible
444 */
445int
446GSF_pending_request_is_compatible_ (struct GSF_PendingRequest *pra,
447 struct GSF_PendingRequest *prb)
448{
449 if ((pra->public_data.type != prb->public_data.type) ||
450 (0 != memcmp (&pra->public_data.query,
451 &prb->public_data.query,
452 sizeof(struct GNUNET_HashCode))))
453 return GNUNET_NO;
454 return GNUNET_OK;
455}
456
457
458/**
459 * Update a given pending request with additional replies
460 * that have been seen.
461 *
462 * @param pr request to update
463 * @param replies_seen hash codes of replies that we've seen
464 * @param replies_seen_count size of the replies_seen array
465 */
466void
467GSF_pending_request_update_ (struct GSF_PendingRequest *pr,
468 const struct GNUNET_HashCode *replies_seen,
469 unsigned int replies_seen_count)
470{
471 if (replies_seen_count + pr->replies_seen_count < pr->replies_seen_count)
472 return; /* integer overflow */
473 if (0 != (pr->public_data.options & GSF_PRO_BLOOMFILTER_FULL_REFRESH))
474 {
475 /* we're responsible for the BF, full refresh */
476 if (replies_seen_count + pr->replies_seen_count > pr->replies_seen_size)
477 GNUNET_array_grow (pr->replies_seen,
478 pr->replies_seen_size,
479 replies_seen_count + pr->replies_seen_count);
480 GNUNET_memcpy (&pr->replies_seen[pr->replies_seen_count],
481 replies_seen,
482 sizeof(struct GNUNET_HashCode) * replies_seen_count);
483 pr->replies_seen_count += replies_seen_count;
484 refresh_bloomfilter (pr->public_data.type, pr);
485 }
486 else
487 {
488 if (NULL == pr->bg)
489 {
490 /* we're not the initiator, but the initiator did not give us
491 * any bloom-filter, so we need to create one on-the-fly */
492 refresh_bloomfilter (pr->public_data.type, pr);
493 }
494 else
495 {
496 GNUNET_break (GNUNET_OK ==
497 GNUNET_BLOCK_group_set_seen (pr->bg,
498 replies_seen,
499 pr->replies_seen_count));
500 }
501 }
502 if (NULL != pr->gh)
503 GNUNET_DHT_get_filter_known_results (pr->gh,
504 replies_seen_count,
505 replies_seen);
506}
507
508
509/**
510 * Generate the message corresponding to the given pending request for
511 * transmission to other peers.
512 *
513 * @param pr request to generate the message for
514 * @return envelope with the request message
515 */
516struct GNUNET_MQ_Envelope *
517GSF_pending_request_get_message_ (struct GSF_PendingRequest *pr)
518{
519 struct GNUNET_MQ_Envelope *env;
520 struct GetMessage *gm;
521 struct GNUNET_PeerIdentity *ext;
522 unsigned int k;
523 uint32_t bm;
524 uint32_t prio;
525 size_t bf_size;
526 struct GNUNET_TIME_Absolute now;
527 int64_t ttl;
528 int do_route;
529 void *bf_data;
530
531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
532 "Building request message for `%s' of type %d\n",
533 GNUNET_h2s (&pr->public_data.query),
534 pr->public_data.type);
535 k = 0;
536 bm = 0;
537 do_route = (0 == (pr->public_data.options & GSF_PRO_FORWARD_ONLY));
538 if ((! do_route) && (pr->sender_pid == 0))
539 {
540 GNUNET_break (0);
541 do_route = GNUNET_YES;
542 }
543 if (! do_route)
544 {
545 bm |= GET_MESSAGE_BIT_RETURN_TO;
546 k++;
547 }
548 if (NULL != pr->public_data.target)
549 {
550 bm |= GET_MESSAGE_BIT_TRANSMIT_TO;
551 k++;
552 }
553 if (GNUNET_OK !=
554 GNUNET_BLOCK_group_serialize (pr->bg,
555 &bf_data,
556 &bf_size))
557 {
558 bf_size = 0;
559 bf_data = NULL;
560 }
561 env = GNUNET_MQ_msg_extra (gm,
562 bf_size + k * sizeof(struct GNUNET_PeerIdentity),
563 GNUNET_MESSAGE_TYPE_FS_GET);
564 gm->type = htonl (pr->public_data.type);
565 if (do_route)
566 prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
567 pr->public_data.priority + 1);
568 else
569 prio = 0;
570 pr->public_data.priority -= prio;
571 pr->public_data.num_transmissions++;
572 pr->public_data.respect_offered += prio;
573 gm->priority = htonl (prio);
574 now = GNUNET_TIME_absolute_get ();
575 ttl = (int64_t) (pr->public_data.ttl.abs_value_us - now.abs_value_us);
576 gm->ttl = htonl (ttl / 1000LL / 1000LL);
577 gm->reserved = htonl (0);
578 gm->hash_bitmap = htonl (bm);
579 gm->query = pr->public_data.query;
580 ext = (struct GNUNET_PeerIdentity *) &gm[1];
581 k = 0;
582 if (! do_route)
583 GNUNET_PEER_resolve (pr->sender_pid, &ext[k++]);
584 if (NULL != pr->public_data.target)
585 ext[k++] = *pr->public_data.target;
586 GNUNET_memcpy (&ext[k], bf_data, bf_size);
587 GNUNET_free (bf_data);
588 return env;
589}
590
591
592/**
593 * Iterator to free pending requests.
594 *
595 * @param cls closure, unused
596 * @param key current key code
597 * @param value value in the hash map (pending request)
598 * @return #GNUNET_YES (we should continue to iterate)
599 */
600static int
601clean_request (void *cls, const struct GNUNET_HashCode *key, void *value)
602{
603 struct GSF_PendingRequest *pr = value;
604 GSF_LocalLookupContinuation cont;
605
606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
607 "Cleaning up pending request for `%s'.\n",
608 GNUNET_h2s (key));
609 if (NULL != pr->cadet_request)
610 {
611 pr->cadet_retry_count = CADET_RETRY_MAX;
612 GSF_cadet_query_cancel (pr->cadet_request);
613 pr->cadet_request = NULL;
614 }
615 if (NULL != (cont = pr->llc_cont))
616 {
617 pr->llc_cont = NULL;
618 cont (pr->llc_cont_cls,
619 pr,
620 pr->local_result);
621 }
622 GSF_plan_notify_request_done_ (pr);
623 GNUNET_free (pr->replies_seen);
624 GNUNET_BLOCK_group_destroy (pr->bg);
625 pr->bg = NULL;
626 GNUNET_PEER_change_rc (pr->sender_pid, -1);
627 pr->sender_pid = 0;
628 GNUNET_PEER_change_rc (pr->origin_pid, -1);
629 pr->origin_pid = 0;
630 if (NULL != pr->hnode)
631 {
632 GNUNET_CONTAINER_heap_remove_node (pr->hnode);
633 pr->hnode = NULL;
634 }
635 if (NULL != pr->qe)
636 {
637 GNUNET_DATASTORE_cancel (pr->qe);
638 pr->qe = NULL;
639 }
640 if (NULL != pr->gh)
641 {
642 GNUNET_DHT_get_stop (pr->gh);
643 pr->gh = NULL;
644 }
645 if (NULL != pr->warn_task)
646 {
647 GNUNET_SCHEDULER_cancel (pr->warn_task);
648 pr->warn_task = NULL;
649 }
650 GNUNET_assert (
651 GNUNET_OK ==
652 GNUNET_CONTAINER_multihashmap_remove (pr_map, &pr->public_data.query, pr));
653 GNUNET_STATISTICS_update (GSF_stats,
654 gettext_noop ("# Pending requests active"),
655 -1,
656 GNUNET_NO);
657 GNUNET_free (pr);
658 return GNUNET_YES;
659}
660
661
662/**
663 * Explicitly cancel a pending request.
664 *
665 * @param pr request to cancel
666 * @param full_cleanup fully purge the request
667 */
668void
669GSF_pending_request_cancel_ (struct GSF_PendingRequest *pr, int full_cleanup)
670{
671 GSF_LocalLookupContinuation cont;
672
673 if (NULL == pr_map)
674 return; /* already cleaned up! */
675 if (GNUNET_NO == full_cleanup)
676 {
677 /* make request inactive (we're no longer interested in more results),
678 * but do NOT remove from our data-structures, we still need it there
679 * to prevent the request from looping */
680 pr->rh = NULL;
681 if (NULL != pr->cadet_request)
682 {
683 pr->cadet_retry_count = CADET_RETRY_MAX;
684 GSF_cadet_query_cancel (pr->cadet_request);
685 pr->cadet_request = NULL;
686 }
687 if (NULL != (cont = pr->llc_cont))
688 {
689 pr->llc_cont = NULL;
690 cont (pr->llc_cont_cls,
691 pr,
692 pr->local_result);
693 }
694 GSF_plan_notify_request_done_ (pr);
695 if (NULL != pr->qe)
696 {
697 GNUNET_DATASTORE_cancel (pr->qe);
698 pr->qe = NULL;
699 }
700 if (NULL != pr->gh)
701 {
702 GNUNET_DHT_get_stop (pr->gh);
703 pr->gh = NULL;
704 }
705 if (NULL != pr->warn_task)
706 {
707 GNUNET_SCHEDULER_cancel (pr->warn_task);
708 pr->warn_task = NULL;
709 }
710 return;
711 }
712 GNUNET_assert (GNUNET_YES ==
713 clean_request (NULL, &pr->public_data.query, pr));
714}
715
716
717/**
718 * Iterate over all pending requests.
719 *
720 * @param it function to call for each request
721 * @param cls closure for @a it
722 */
723void
724GSF_iterate_pending_requests_ (GSF_PendingRequestIterator it, void *cls)
725{
726 GNUNET_CONTAINER_multihashmap_iterate (
727 pr_map,
728 (GNUNET_CONTAINER_MulitHashMapIteratorCallback) it,
729 cls);
730}
731
732
733/**
734 * Closure for process_reply() function.
735 */
736struct ProcessReplyClosure
737{
738 /**
739 * The data for the reply.
740 */
741 const void *data;
742
743 /**
744 * Who gave us this reply? NULL for local host (or DHT)
745 */
746 struct GSF_ConnectedPeer *sender;
747
748 /**
749 * When the reply expires.
750 */
751 struct GNUNET_TIME_Absolute expiration;
752
753 /**
754 * Size of data.
755 */
756 size_t size;
757
758 /**
759 * Type of the block.
760 */
761 enum GNUNET_BLOCK_Type type;
762
763 /**
764 * How much was this reply worth to us?
765 */
766 uint32_t priority;
767
768 /**
769 * Anonymity requirements for this reply.
770 */
771 uint32_t anonymity_level;
772
773 /**
774 * Evaluation result (returned).
775 */
776 enum GNUNET_BLOCK_ReplyEvaluationResult eval;
777
778 /**
779 * Did we find a matching request?
780 */
781 int request_found;
782};
783
784
785/**
786 * Update the performance data for the sender (if any) since
787 * the sender successfully answered one of our queries.
788 *
789 * @param prq information about the sender
790 * @param pr request that was satisfied
791 */
792static void
793update_request_performance_data (struct ProcessReplyClosure *prq,
794 struct GSF_PendingRequest *pr)
795{
796 if (prq->sender == NULL)
797 return;
798 GSF_peer_update_performance_ (prq->sender,
799 pr->public_data.start_time,
800 prq->priority);
801}
802
803
804/**
805 * We have received a reply; handle it!
806 *
807 * @param cls response (a `struct ProcessReplyClosure`)
808 * @param key our query
809 * @param value value in the hash map (info about the query)
810 * @return #GNUNET_YES (we should continue to iterate)
811 */
812static enum GNUNET_GenericReturnValue
813process_reply (void *cls,
814 const struct GNUNET_HashCode *key,
815 void *value)
816{
817 struct ProcessReplyClosure *prq = cls;
818 struct GSF_PendingRequest *pr = value;
819 struct GNUNET_HashCode chash;
820 struct GNUNET_TIME_Absolute last_transmission;
821
822 if (NULL == pr->rh)
823 return GNUNET_YES;
824 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
825 "Matched result (type %u) for query `%s' with pending request\n",
826 (unsigned int) prq->type,
827 GNUNET_h2s (key));
828 GNUNET_STATISTICS_update (GSF_stats,
829 gettext_noop ("# replies received and matched"),
830 1,
831 GNUNET_NO);
832 prq->eval = GNUNET_BLOCK_check_reply (GSF_block_ctx,
833 prq->type,
834 pr->bg,
835 key,
836 NULL, 0,
837 prq->data,
838 prq->size);
839 switch (prq->eval)
840 {
841 case GNUNET_BLOCK_REPLY_OK_MORE:
842 update_request_performance_data (prq, pr);
843 break;
844 case GNUNET_BLOCK_REPLY_OK_LAST:
845 /* short cut: stop processing early, no BF-update, etc. */
846 update_request_performance_data (prq, pr);
847 GNUNET_LOAD_update (GSF_rt_entry_lifetime,
848 GNUNET_TIME_absolute_get_duration (
849 pr->public_data.start_time)
850 .rel_value_us);
851 if (GNUNET_YES !=
852 GSF_request_plan_reference_get_last_transmission_ (pr->public_data
853 .pr_head,
854 prq->sender,
855 &last_transmission))
856 last_transmission = GNUNET_TIME_UNIT_FOREVER_ABS;
857 /* pass on to other peers / local clients */
858 pr->rh (pr->rh_cls,
859 prq->eval,
860 pr,
861 prq->anonymity_level,
862 prq->expiration,
863 last_transmission,
864 prq->type,
865 prq->data,
866 prq->size);
867 return GNUNET_YES;
868 case GNUNET_BLOCK_REPLY_OK_DUPLICATE:
869#if INSANE_STATISTICS
870 GNUNET_STATISTICS_update (GSF_stats,
871 "# duplicate replies discarded (bloomfilter)",
872 1,
873 GNUNET_NO);
874#endif
875 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
876 "Duplicate response, discarding.\n");
877 return GNUNET_YES; /* duplicate */
878 case GNUNET_BLOCK_REPLY_IRRELEVANT:
879 GNUNET_STATISTICS_update (GSF_stats,
880 "# irrelevant replies discarded",
881 1,
882 GNUNET_NO);
883 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
884 "Irrelevant response, ignoring.\n");
885 return GNUNET_YES;
886 case GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED:
887 GNUNET_break (0); /* bad installation? */
888 return GNUNET_NO;
889 }
890 /* update bloomfilter */
891 GNUNET_CRYPTO_hash (prq->data,
892 prq->size,
893 &chash);
894 GSF_pending_request_update_ (pr,
895 &chash,
896 1);
897 if (NULL == prq->sender)
898 {
899 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
900 "Found result for query `%s' in local datastore\n",
901 GNUNET_h2s (key));
902 GNUNET_STATISTICS_update (GSF_stats,
903 gettext_noop ("# results found locally"),
904 1,
905 GNUNET_NO);
906 }
907 else
908 {
909 GSF_dht_lookup_ (pr);
910 }
911 prq->priority += pr->public_data.original_priority;
912 pr->public_data.priority = 0;
913 pr->public_data.original_priority = 0;
914 pr->public_data.results_found++;
915 prq->request_found = GNUNET_YES;
916 /* finally, pass on to other peer / local client */
917 if (! GSF_request_plan_reference_get_last_transmission_ (pr->public_data
918 .pr_head,
919 prq->sender,
920 &last_transmission))
921 last_transmission = GNUNET_TIME_UNIT_FOREVER_ABS;
922 pr->rh (pr->rh_cls,
923 prq->eval,
924 pr,
925 prq->anonymity_level,
926 prq->expiration,
927 last_transmission,
928 prq->type,
929 prq->data,
930 prq->size);
931 return GNUNET_YES;
932}
933
934
935/**
936 * Context for put_migration_continuation().
937 */
938struct PutMigrationContext
939{
940 /**
941 * Start time for the operation.
942 */
943 struct GNUNET_TIME_Absolute start;
944
945 /**
946 * Request origin.
947 */
948 struct GNUNET_PeerIdentity origin;
949
950 /**
951 * #GNUNET_YES if we had a matching request for this block,
952 * #GNUNET_NO if not.
953 */
954 int requested;
955};
956
957
958/**
959 * Continuation called to notify client about result of the
960 * operation.
961 *
962 * @param cls closure
963 * @param success #GNUNET_SYSERR on failure
964 * @param min_expiration minimum expiration time required for content to be stored
965 * @param msg NULL on success, otherwise an error message
966 */
967static void
968put_migration_continuation (void *cls,
969 int success,
970 struct GNUNET_TIME_Absolute min_expiration,
971 const char *msg)
972{
973 struct PutMigrationContext *pmc = cls;
974 struct GSF_ConnectedPeer *cp;
975 struct GNUNET_TIME_Relative mig_pause;
976 struct GSF_PeerPerformanceData *ppd;
977
978 if (NULL != datastore_put_load)
979 {
980 if (GNUNET_SYSERR != success)
981 {
982 GNUNET_LOAD_update (datastore_put_load,
983 GNUNET_TIME_absolute_get_duration (pmc->start)
984 .rel_value_us);
985 }
986 else
987 {
988 /* on queue failure / timeout, increase the put load dramatically */
989 GNUNET_LOAD_update (datastore_put_load,
990 GNUNET_TIME_UNIT_MINUTES.rel_value_us);
991 }
992 }
993 cp = GSF_peer_get_ (&pmc->origin);
994 if (GNUNET_OK == success)
995 {
996 if (NULL != cp)
997 {
998 ppd = GSF_get_peer_performance_data_ (cp);
999 ppd->migration_delay.rel_value_us /= 2;
1000 }
1001 GNUNET_free (pmc);
1002 return;
1003 }
1004 if ((GNUNET_NO == success) && (GNUNET_NO == pmc->requested) && (NULL != cp))
1005 {
1006 ppd = GSF_get_peer_performance_data_ (cp);
1007 if (min_expiration.abs_value_us > 0)
1008 {
1009 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1010 "Asking to stop migration for %s because datastore is full\n",
1011 GNUNET_STRINGS_relative_time_to_string (
1012 GNUNET_TIME_absolute_get_remaining (min_expiration),
1013 GNUNET_YES));
1014 GSF_block_peer_migration_ (cp, min_expiration);
1015 }
1016 else
1017 {
1018 ppd->migration_delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_SECONDS,
1019 ppd->migration_delay);
1020 ppd->migration_delay =
1021 GNUNET_TIME_relative_min (GNUNET_TIME_UNIT_HOURS, ppd->migration_delay);
1022 mig_pause.rel_value_us =
1023 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1024 ppd->migration_delay.rel_value_us);
1025 ppd->migration_delay =
1026 GNUNET_TIME_relative_saturating_multiply (ppd->migration_delay, 2);
1027 GNUNET_log (
1028 GNUNET_ERROR_TYPE_DEBUG,
1029 "Replicated content already exists locally, asking to stop migration for %s\n",
1030 GNUNET_STRINGS_relative_time_to_string (mig_pause, GNUNET_YES));
1031 GSF_block_peer_migration_ (cp,
1032 GNUNET_TIME_relative_to_absolute (mig_pause));
1033 }
1034 }
1035 GNUNET_free (pmc);
1036 GNUNET_STATISTICS_update (GSF_stats,
1037 gettext_noop ("# Datastore `PUT' failures"),
1038 1,
1039 GNUNET_NO);
1040}
1041
1042
1043/**
1044 * Test if the DATABASE (PUT) load on this peer is too high
1045 * to even consider processing the query at
1046 * all.
1047 *
1048 * @param priority the priority of the item
1049 * @return #GNUNET_YES if the load is too high to do anything (load high)
1050 * #GNUNET_NO to process normally (load normal or low)
1051 */
1052static int
1053test_put_load_too_high (uint32_t priority)
1054{
1055 double ld;
1056
1057 if (NULL == datastore_put_load)
1058 return GNUNET_NO;
1059 if (GNUNET_LOAD_get_average (datastore_put_load) < 50)
1060 return GNUNET_NO; /* very fast */
1061 ld = GNUNET_LOAD_get_load (datastore_put_load);
1062 if (ld < 2.0 * (1 + priority))
1063 return GNUNET_NO;
1064 GNUNET_STATISTICS_update (GSF_stats,
1065 gettext_noop (
1066 "# storage requests dropped due to high load"),
1067 1,
1068 GNUNET_NO);
1069 return GNUNET_YES;
1070}
1071
1072
1073/**
1074 * Iterator called on each result obtained for a DHT
1075 * operation that expects a reply
1076 *
1077 * @param cls closure
1078 * @param exp when will this value expire
1079 * @param key key of the result
1080 * @param trunc_peer truncated peer, NULL for none
1081 * @param get_path peers on reply path (or NULL if not recorded)
1082 * @param get_path_length number of entries in @a get_path
1083 * @param put_path peers on the PUT path (or NULL if not recorded)
1084 * @param put_path_length number of entries in @a get_path
1085 * @param type type of the result
1086 * @param size number of bytes in @a data
1087 * @param data pointer to the result data
1088 */
1089static void
1090handle_dht_reply (void *cls,
1091 struct GNUNET_TIME_Absolute exp,
1092 const struct GNUNET_HashCode *key,
1093 const struct GNUNET_PeerIdentity *trunc_peer,
1094 const struct GNUNET_DHT_PathElement *get_path,
1095 unsigned int get_path_length,
1096 const struct GNUNET_DHT_PathElement *put_path,
1097 unsigned int put_path_length,
1098 enum GNUNET_BLOCK_Type type,
1099 size_t size,
1100 const void *data)
1101{
1102 struct GSF_PendingRequest *pr = cls;
1103 struct ProcessReplyClosure prq;
1104 struct PutMigrationContext *pmc;
1105
1106 GNUNET_STATISTICS_update (GSF_stats,
1107 gettext_noop ("# Replies received from DHT"),
1108 1,
1109 GNUNET_NO);
1110 memset (&prq, 0, sizeof(prq));
1111 prq.data = data;
1112 prq.expiration = exp;
1113 /* do not allow migrated content to live longer than 1 year */
1114 prq.expiration = GNUNET_TIME_absolute_min (GNUNET_TIME_relative_to_absolute (
1115 GNUNET_TIME_UNIT_YEARS),
1116 prq.expiration);
1117 prq.size = size;
1118 prq.type = type;
1119 process_reply (&prq,
1120 key,
1121 pr);
1122 if ((GNUNET_YES == active_to_migration) &&
1123 (GNUNET_NO == test_put_load_too_high (prq.priority)))
1124 {
1125 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1126 "Replicating result for query `%s' with priority %u\n",
1127 GNUNET_h2s (key),
1128 prq.priority);
1129 pmc = GNUNET_new (struct PutMigrationContext);
1130 pmc->start = GNUNET_TIME_absolute_get ();
1131 pmc->requested = GNUNET_YES;
1132 if (NULL == GNUNET_DATASTORE_put (GSF_dsh,
1133 0,
1134 key,
1135 size,
1136 data,
1137 type,
1138 prq.priority,
1139 1 /* anonymity */,
1140 0 /* replication */,
1141 exp,
1142 1 + prq.priority,
1143 MAX_DATASTORE_QUEUE,
1144 &put_migration_continuation,
1145 pmc))
1146 {
1147 put_migration_continuation (pmc,
1148 GNUNET_SYSERR,
1149 GNUNET_TIME_UNIT_ZERO_ABS,
1150 NULL);
1151 }
1152 }
1153}
1154
1155
1156/**
1157 * Consider looking up the data in the DHT (anonymity-level permitting).
1158 *
1159 * @param pr the pending request to process
1160 */
1161void
1162GSF_dht_lookup_ (struct GSF_PendingRequest *pr)
1163{
1164 const void *xquery;
1165 size_t xquery_size;
1166 struct GNUNET_PeerIdentity pi;
1167 char buf[sizeof(struct GNUNET_HashCode) * 2] GNUNET_ALIGN;
1168
1169 if (0 != pr->public_data.anonymity_level)
1170 return;
1171 if (NULL != pr->gh)
1172 {
1173 GNUNET_DHT_get_stop (pr->gh);
1174 pr->gh = NULL;
1175 }
1176 xquery = NULL;
1177 xquery_size = 0;
1178 if (0 != (pr->public_data.options & GSF_PRO_FORWARD_ONLY))
1179 {
1180 GNUNET_assert (0 != pr->sender_pid);
1181 GNUNET_PEER_resolve (pr->sender_pid, &pi);
1182 GNUNET_memcpy (&buf[xquery_size], &pi, sizeof(struct GNUNET_PeerIdentity));
1183 xquery_size += sizeof(struct GNUNET_PeerIdentity);
1184 }
1185 pr->gh = GNUNET_DHT_get_start (GSF_dht,
1186 pr->public_data.type,
1187 &pr->public_data.query,
1188 DHT_GET_REPLICATION,
1189 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
1190 xquery,
1191 xquery_size,
1192 &handle_dht_reply,
1193 pr);
1194 if ((NULL != pr->gh) && (0 != pr->replies_seen_count))
1195 GNUNET_DHT_get_filter_known_results (pr->gh,
1196 pr->replies_seen_count,
1197 pr->replies_seen);
1198}
1199
1200
1201/**
1202 * Function called with a reply from the cadet.
1203 *
1204 * @param cls the pending request struct
1205 * @param type type of the block, ANY on error
1206 * @param expiration expiration time for the block
1207 * @param data_size number of bytes in @a data, 0 on error
1208 * @param data reply block data, NULL on error
1209 */
1210static void
1211cadet_reply_proc (void *cls,
1212 enum GNUNET_BLOCK_Type type,
1213 struct GNUNET_TIME_Absolute expiration,
1214 size_t data_size,
1215 const void *data)
1216{
1217 struct GSF_PendingRequest *pr = cls;
1218 struct ProcessReplyClosure prq;
1219 struct GNUNET_HashCode query;
1220
1221 pr->cadet_request = NULL;
1222 if (GNUNET_OK !=
1223 GNUNET_BLOCK_check_block (GSF_block_ctx,
1224 type,
1225 data,
1226 data_size))
1227 {
1228 GNUNET_break_op (0);
1229 return;
1230 }
1231 if (GNUNET_BLOCK_TYPE_ANY == type)
1232 {
1233 GNUNET_break (NULL == data);
1234 GNUNET_break (0 == data_size);
1235 pr->cadet_retry_count++;
1236 if (pr->cadet_retry_count >= CADET_RETRY_MAX)
1237 return; /* give up on cadet */
1238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error retrieiving block via cadet\n");
1239 /* retry -- without delay, as this is non-anonymous
1240 and cadet/cadet connect will take some time anyway */
1241 pr->cadet_request = GSF_cadet_query (pr->public_data.target,
1242 &pr->public_data.query,
1243 pr->public_data.type,
1244 &cadet_reply_proc,
1245 pr);
1246 return;
1247 }
1248 if (GNUNET_YES !=
1249 GNUNET_BLOCK_get_key (GSF_block_ctx,
1250 type,
1251 data,
1252 data_size,
1253 &query))
1254 {
1255 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1256 "Failed to derive key for block of type %d\n",
1257 (int) type);
1258 GNUNET_break_op (0);
1259 return;
1260 }
1261 GNUNET_STATISTICS_update (GSF_stats,
1262 gettext_noop ("# Replies received from CADET"),
1263 1,
1264 GNUNET_NO);
1265 memset (&prq, 0, sizeof(prq));
1266 prq.data = data;
1267 prq.expiration = expiration;
1268 /* do not allow migrated content to live longer than 1 year */
1269 prq.expiration = GNUNET_TIME_absolute_min (GNUNET_TIME_relative_to_absolute (
1270 GNUNET_TIME_UNIT_YEARS),
1271 prq.expiration);
1272 prq.size = data_size;
1273 prq.type = type;
1274 process_reply (&prq,
1275 &query,
1276 pr);
1277}
1278
1279
1280/**
1281 * Consider downloading via cadet (if possible)
1282 *
1283 * @param pr the pending request to process
1284 */
1285void
1286GSF_cadet_lookup_ (struct GSF_PendingRequest *pr)
1287{
1288 if (0 != pr->public_data.anonymity_level)
1289 return;
1290 if (0 == pr->public_data.target)
1291 {
1292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1293 "Cannot do cadet-based download, target peer not known\n");
1294 return;
1295 }
1296 if (NULL != pr->cadet_request)
1297 return;
1298 pr->cadet_request = GSF_cadet_query (pr->public_data.target,
1299 &pr->public_data.query,
1300 pr->public_data.type,
1301 &cadet_reply_proc,
1302 pr);
1303}
1304
1305
1306/**
1307 * Task that issues a warning if the datastore lookup takes too long.
1308 *
1309 * @param cls the `struct GSF_PendingRequest`
1310 */
1311static void
1312warn_delay_task (void *cls)
1313{
1314 struct GSF_PendingRequest *pr = cls;
1315
1316 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
1317 _ ("Datastore lookup already took %s!\n"),
1318 GNUNET_STRINGS_relative_time_to_string (
1319 GNUNET_TIME_absolute_get_duration (pr->qe_start),
1320 GNUNET_YES));
1321 pr->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1322 &warn_delay_task,
1323 pr);
1324}
1325
1326
1327/**
1328 * Task that issues a warning if the datastore lookup takes too long.
1329 *
1330 * @param cls the `struct GSF_PendingRequest`
1331 */
1332static void
1333odc_warn_delay_task (void *cls)
1334{
1335 struct GSF_PendingRequest *pr = cls;
1336
1337 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1338 _ ("On-demand lookup already took %s!\n"),
1339 GNUNET_STRINGS_relative_time_to_string (
1340 GNUNET_TIME_absolute_get_duration (pr->qe_start),
1341 GNUNET_YES));
1342 pr->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1343 &odc_warn_delay_task,
1344 pr);
1345}
1346
1347
1348/* Call our continuation (if we have any) */
1349static void
1350call_continuation (struct GSF_PendingRequest *pr)
1351{
1352 GSF_LocalLookupContinuation cont = pr->llc_cont;
1353
1354 GNUNET_assert (NULL == pr->qe);
1355 if (NULL != pr->warn_task)
1356 {
1357 GNUNET_SCHEDULER_cancel (pr->warn_task);
1358 pr->warn_task = NULL;
1359 }
1360 if (NULL == cont)
1361 return; /* no continuation */
1362 pr->llc_cont = NULL;
1363 if (0 != (GSF_PRO_LOCAL_ONLY & pr->public_data.options))
1364 {
1365 if (GNUNET_BLOCK_REPLY_OK_LAST != pr->local_result)
1366 {
1367 /* Signal that we are done and that there won't be any
1368 additional results to allow client to clean up state. */
1369 pr->rh (pr->rh_cls,
1370 GNUNET_BLOCK_REPLY_OK_LAST,
1371 pr,
1372 UINT32_MAX,
1373 GNUNET_TIME_UNIT_ZERO_ABS,
1374 GNUNET_TIME_UNIT_FOREVER_ABS,
1375 GNUNET_BLOCK_TYPE_ANY,
1376 NULL,
1377 0);
1378 }
1379 /* Finally, call our continuation to signal that we are
1380 done with local processing of this request; i.e. to
1381 start reading again from the client. */
1382 cont (pr->llc_cont_cls,
1383 NULL,
1384 GNUNET_BLOCK_REPLY_OK_LAST);
1385 return;
1386 }
1387
1388 cont (pr->llc_cont_cls,
1389 pr,
1390 pr->local_result);
1391}
1392
1393
1394/* Update stats and call continuation */
1395static void
1396no_more_local_results (struct GSF_PendingRequest *pr)
1397{
1398 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1399 "No further local responses available.\n");
1400#if INSANE_STATISTICS
1401 if ((GNUNET_BLOCK_TYPE_FS_DBLOCK == pr->public_data.type) ||
1402 (GNUNET_BLOCK_TYPE_FS_IBLOCK == pr->public_data.type))
1403 GNUNET_STATISTICS_update (GSF_stats,
1404 gettext_noop (
1405 "# requested DBLOCK or IBLOCK not found"),
1406 1,
1407 GNUNET_NO);
1408#endif
1409 call_continuation (pr);
1410}
1411
1412
1413/* forward declaration */
1414static void
1415process_local_reply (void *cls,
1416 const struct GNUNET_HashCode *key,
1417 size_t size,
1418 const void *data,
1419 enum GNUNET_BLOCK_Type type,
1420 uint32_t priority,
1421 uint32_t anonymity,
1422 uint32_t replication,
1423 struct GNUNET_TIME_Absolute expiration,
1424 uint64_t uid);
1425
1426
1427/* Start a local query */
1428static void
1429start_local_query (struct GSF_PendingRequest *pr,
1430 uint64_t next_uid,
1431 bool random)
1432{
1433 pr->qe_start = GNUNET_TIME_absolute_get ();
1434 pr->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1435 &warn_delay_task,
1436 pr);
1437 pr->qe = GNUNET_DATASTORE_get_key (GSF_dsh,
1438 next_uid,
1439 random,
1440 &pr->public_data.query,
1441 pr->public_data.type ==
1442 GNUNET_BLOCK_TYPE_FS_DBLOCK
1443 ? GNUNET_BLOCK_TYPE_ANY
1444 : pr->public_data.type,
1445 (0 != (GSF_PRO_PRIORITY_UNLIMITED
1446 & pr->public_data.options))
1447 ? UINT_MAX
1448 : 1
1449 /* queue priority */,
1450 (0 != (GSF_PRO_PRIORITY_UNLIMITED
1451 & pr->public_data.options))
1452 ? UINT_MAX
1453 : GSF_datastore_queue_size
1454 /* max queue size */,
1455 &process_local_reply,
1456 pr);
1457 if (NULL != pr->qe)
1458 return;
1459 GNUNET_log (
1460 GNUNET_ERROR_TYPE_DEBUG,
1461 "ERROR Requesting `%s' of type %d with next_uid %llu from datastore.\n",
1462 GNUNET_h2s (&pr->public_data.query),
1463 pr->public_data.type,
1464 (unsigned long long) next_uid);
1465 GNUNET_STATISTICS_update (GSF_stats,
1466 gettext_noop (
1467 "# Datastore lookups concluded (error queueing)"),
1468 1,
1469 GNUNET_NO);
1470 call_continuation (pr);
1471}
1472
1473
1474/**
1475 * We're processing (local) results for a search request
1476 * from another peer. Pass applicable results to the
1477 * peer and if we are done either clean up (operation
1478 * complete) or forward to other peers (more results possible).
1479 *
1480 * @param cls our closure (`struct GSF_PendingRequest *`)
1481 * @param key key for the content
1482 * @param size number of bytes in @a data
1483 * @param data content stored
1484 * @param type type of the content
1485 * @param priority priority of the content
1486 * @param anonymity anonymity-level for the content
1487 * @param replication replication-level for the content
1488 * @param expiration expiration time for the content
1489 * @param uid unique identifier for the datum;
1490 * maybe 0 if no unique identifier is available
1491 */
1492static void
1493process_local_reply (void *cls,
1494 const struct GNUNET_HashCode *key,
1495 size_t size,
1496 const void *data,
1497 enum GNUNET_BLOCK_Type type,
1498 uint32_t priority,
1499 uint32_t anonymity,
1500 uint32_t replication,
1501 struct GNUNET_TIME_Absolute expiration,
1502 uint64_t uid)
1503{
1504 struct GSF_PendingRequest *pr = cls;
1505 struct ProcessReplyClosure prq;
1506 struct GNUNET_HashCode query;
1507 unsigned int old_rf;
1508
1509 GNUNET_SCHEDULER_cancel (pr->warn_task);
1510 pr->warn_task = NULL;
1511 if (NULL == pr->qe)
1512 goto called_from_on_demand;
1513 pr->qe = NULL;
1514 if (
1515 (NULL == key) && pr->seen_null &&
1516 ! pr->have_first_uid) /* We have hit the end for the 2nd time with no results */
1517 {
1518 /* No results */
1519#if INSANE_STATISTICS
1520 GNUNET_STATISTICS_update (GSF_stats,
1521 gettext_noop (
1522 "# Datastore lookups concluded (no results)"),
1523 1,
1524 GNUNET_NO);
1525#endif
1526 no_more_local_results (pr);
1527 return;
1528 }
1529 if (((NULL == key) &&
1530 pr->seen_null) || /* We have hit the end for the 2nd time OR */
1531 (pr->seen_null && pr->have_first_uid &&
1532 (uid >= pr->first_uid))) /* We have hit the end and past first UID */
1533 {
1534 /* Seen all results */
1535 GNUNET_STATISTICS_update (GSF_stats,
1536 gettext_noop (
1537 "# Datastore lookups concluded (seen all)"),
1538 1,
1539 GNUNET_NO);
1540 no_more_local_results (pr);
1541 return;
1542 }
1543 if (NULL == key)
1544 {
1545 GNUNET_assert (! pr->seen_null);
1546 pr->seen_null = true;
1547 start_local_query (pr, 0 /* next_uid */, false /* random */);
1548 return;
1549 }
1550 if (! pr->have_first_uid)
1551 {
1552 pr->first_uid = uid;
1553 pr->have_first_uid = true;
1554 }
1555 pr->result_count++;
1556 if (pr->result_count > MAX_RESULTS)
1557 {
1558 GNUNET_STATISTICS_update (
1559 GSF_stats,
1560 gettext_noop ("# Datastore lookups aborted (more than MAX_RESULTS)"),
1561 1,
1562 GNUNET_NO);
1563 no_more_local_results (pr);
1564 return;
1565 }
1566 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1567 "Received reply for `%s' of type %d with UID %llu from datastore.\n",
1568 GNUNET_h2s (key),
1569 type,
1570 (unsigned long long) uid);
1571 if (GNUNET_BLOCK_TYPE_FS_ONDEMAND == type)
1572 {
1573 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1574 "Found ONDEMAND block, performing on-demand encoding\n");
1575 GNUNET_STATISTICS_update (GSF_stats,
1576 gettext_noop (
1577 "# on-demand blocks matched requests"),
1578 1,
1579 GNUNET_NO);
1580 pr->qe_start = GNUNET_TIME_absolute_get ();
1581 pr->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1582 &odc_warn_delay_task,
1583 pr);
1584 if (GNUNET_OK == GNUNET_FS_handle_on_demand_block (key,
1585 size,
1586 data,
1587 type,
1588 priority,
1589 anonymity,
1590 replication,
1591 expiration,
1592 uid,
1593 &process_local_reply,
1594 pr))
1595 {
1596 GNUNET_STATISTICS_update (GSF_stats,
1597 gettext_noop (
1598 "# on-demand lookups performed successfully"),
1599 1,
1600 GNUNET_NO);
1601 return; /* we're done */
1602 }
1603 GNUNET_STATISTICS_update (GSF_stats,
1604 gettext_noop ("# on-demand lookups failed"),
1605 1,
1606 GNUNET_NO);
1607 GNUNET_SCHEDULER_cancel (pr->warn_task);
1608 start_local_query (pr, uid + 1 /* next_uid */, false /* random */);
1609 return;
1610 }
1611called_from_on_demand:
1612 old_rf = pr->public_data.results_found;
1613 memset (&prq, 0, sizeof(prq));
1614 prq.data = data;
1615 prq.expiration = expiration;
1616 prq.size = size;
1617 if (GNUNET_OK !=
1618 GNUNET_BLOCK_get_key (GSF_block_ctx,
1619 type,
1620 data,
1621 size,
1622 &query))
1623 {
1624 GNUNET_break (0);
1625 GNUNET_DATASTORE_remove (GSF_dsh,
1626 key,
1627 size,
1628 data,
1629 UINT_MAX,
1630 UINT_MAX,
1631 NULL,
1632 NULL);
1633 start_local_query (pr, uid + 1 /* next_uid */, false /* random */);
1634 return;
1635 }
1636 prq.type = type;
1637 prq.priority = priority;
1638 prq.request_found = GNUNET_NO;
1639 prq.anonymity_level = anonymity;
1640 if ((0 == old_rf) && (0 == pr->public_data.results_found))
1641 GSF_update_datastore_delay_ (pr->public_data.start_time);
1642 process_reply (&prq,
1643 key,
1644 pr);
1645 pr->local_result = prq.eval;
1646 if (GNUNET_BLOCK_REPLY_OK_LAST == prq.eval)
1647 {
1648 GNUNET_STATISTICS_update (
1649 GSF_stats,
1650 gettext_noop ("# Datastore lookups concluded (found last result)"),
1651 1,
1652 GNUNET_NO);
1653 call_continuation (pr);
1654 return;
1655 }
1656 if ((0 == (GSF_PRO_PRIORITY_UNLIMITED & pr->public_data.options)) &&
1657 ((GNUNET_YES == GSF_test_get_load_too_high_ (0)) ||
1658 (pr->public_data.results_found > 5 + 2 * pr->public_data.priority)))
1659 {
1660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Load too high, done with request\n");
1661 GNUNET_STATISTICS_update (GSF_stats,
1662 gettext_noop (
1663 "# Datastore lookups concluded (load too high)"),
1664 1,
1665 GNUNET_NO);
1666 call_continuation (pr);
1667 return;
1668 }
1669 start_local_query (pr, uid + 1 /* next_uid */, false /* random */);
1670}
1671
1672
1673/**
1674 * Is the given target a legitimate peer for forwarding the given request?
1675 *
1676 * @param pr request
1677 * @param target
1678 * @return #GNUNET_YES if this request could be forwarded to the given peer
1679 */
1680int
1681GSF_pending_request_test_target_ (struct GSF_PendingRequest *pr,
1682 const struct GNUNET_PeerIdentity *target)
1683{
1684 struct GNUNET_PeerIdentity pi;
1685
1686 if (0 == pr->origin_pid)
1687 return GNUNET_YES;
1688 GNUNET_PEER_resolve (pr->origin_pid, &pi);
1689 return (0 == memcmp (&pi, target, sizeof(struct GNUNET_PeerIdentity)))
1690 ? GNUNET_NO
1691 : GNUNET_YES;
1692}
1693
1694
1695/**
1696 * Look up the request in the local datastore.
1697 *
1698 * @param pr the pending request to process
1699 * @param cont function to call at the end
1700 * @param cont_cls closure for @a cont
1701 */
1702void
1703GSF_local_lookup_ (struct GSF_PendingRequest *pr,
1704 GSF_LocalLookupContinuation cont,
1705 void *cont_cls)
1706{
1707 GNUNET_assert (NULL == pr->gh);
1708 GNUNET_assert (NULL == pr->cadet_request);
1709 GNUNET_assert (NULL == pr->llc_cont);
1710 pr->llc_cont = cont;
1711 pr->llc_cont_cls = cont_cls;
1712#if INSANE_STATISTICS
1713 GNUNET_STATISTICS_update (GSF_stats,
1714 gettext_noop ("# Datastore lookups initiated"),
1715 1,
1716 GNUNET_NO);
1717#endif
1718 start_local_query (pr, 0 /* next_uid */, true /* random */);
1719}
1720
1721
1722/**
1723 * Handle P2P "CONTENT" message. Checks that the message is
1724 * well-formed and then checks if there are any pending requests for
1725 * this content and possibly passes it on (to local clients or other
1726 * peers). Does NOT perform migration (content caching at this peer).
1727 *
1728 * @param cls the other peer involved
1729 * @param put the actual message
1730 */
1731void
1732handle_p2p_put (void *cls,
1733 const struct PutMessage *put)
1734{
1735 struct GSF_ConnectedPeer *cp = cls;
1736 uint16_t msize;
1737 size_t dsize;
1738 enum GNUNET_BLOCK_Type type;
1739 struct GNUNET_TIME_Absolute expiration;
1740 struct GNUNET_HashCode query;
1741 struct ProcessReplyClosure prq;
1742 struct GNUNET_TIME_Relative block_time;
1743 double putl;
1744 struct PutMigrationContext *pmc;
1745
1746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1747 "Received P2P PUT from %s\n",
1748 GNUNET_i2s (GSF_get_peer_performance_data_ (cp)->peer));
1749 GSF_cover_content_count++;
1750 msize = ntohs (put->header.size);
1751 dsize = msize - sizeof(struct PutMessage);
1752 type = ntohl (put->type);
1753 expiration = GNUNET_TIME_absolute_ntoh (put->expiration);
1754 /* do not allow migrated content to live longer than 1 year */
1755 expiration = GNUNET_TIME_absolute_min (GNUNET_TIME_relative_to_absolute (
1756 GNUNET_TIME_UNIT_YEARS),
1757 expiration);
1758 if (GNUNET_OK !=
1759 GNUNET_BLOCK_check_block (GSF_block_ctx,
1760 type,
1761 &put[1],
1762 dsize))
1763 {
1764 GNUNET_break_op (0);
1765 return;
1766 }
1767 if (GNUNET_OK !=
1768 GNUNET_BLOCK_get_key (GSF_block_ctx,
1769 type,
1770 &put[1],
1771 dsize,
1772 &query))
1773 {
1774 GNUNET_break_op (0);
1775 return;
1776 }
1777 GNUNET_STATISTICS_update (GSF_stats,
1778 gettext_noop ("# GAP PUT messages received"),
1779 1,
1780 GNUNET_NO);
1781 /* now, lookup 'query' */
1782 prq.data = (const void *) &put[1];
1783 prq.sender = cp;
1784 prq.size = dsize;
1785 prq.type = type;
1786 prq.expiration = expiration;
1787 prq.priority = 0;
1788 prq.anonymity_level = UINT32_MAX;
1789 prq.request_found = GNUNET_NO;
1790 GNUNET_CONTAINER_multihashmap_get_multiple (pr_map,
1791 &query,
1792 &process_reply,
1793 &prq);
1794 if (NULL != cp)
1795 {
1796 GSF_connected_peer_change_preference_ (cp,
1797 CONTENT_BANDWIDTH_VALUE
1798 + 1000 * prq.priority);
1799 GSF_get_peer_performance_data_ (cp)->respect += prq.priority;
1800 }
1801 if ((GNUNET_YES == active_to_migration) && (NULL != cp) &&
1802 (GNUNET_NO == test_put_load_too_high (prq.priority)))
1803 {
1804 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1805 "Replicating result for query `%s' with priority %u\n",
1806 GNUNET_h2s (&query),
1807 prq.priority);
1808 pmc = GNUNET_new (struct PutMigrationContext);
1809 pmc->start = GNUNET_TIME_absolute_get ();
1810 pmc->requested = prq.request_found;
1811 GNUNET_assert (0 != GSF_get_peer_performance_data_ (cp)->pid);
1812 GNUNET_PEER_resolve (GSF_get_peer_performance_data_ (cp)->pid,
1813 &pmc->origin);
1814 if (NULL == GNUNET_DATASTORE_put (GSF_dsh,
1815 0,
1816 &query,
1817 dsize,
1818 &put[1],
1819 type,
1820 prq.priority,
1821 1 /* anonymity */,
1822 0 /* replication */,
1823 expiration,
1824 1 + prq.priority,
1825 MAX_DATASTORE_QUEUE,
1826 &put_migration_continuation,
1827 pmc))
1828 {
1829 put_migration_continuation (pmc,
1830 GNUNET_SYSERR,
1831 GNUNET_TIME_UNIT_ZERO_ABS,
1832 NULL);
1833 }
1834 }
1835 else if (NULL != cp)
1836 {
1837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1838 "Choosing not to keep content `%s' (%d/%d)\n",
1839 GNUNET_h2s (&query),
1840 active_to_migration,
1841 test_put_load_too_high (prq.priority));
1842 }
1843 putl = GNUNET_LOAD_get_load (datastore_put_load);
1844 if ((NULL != cp) && (GNUNET_NO == prq.request_found) &&
1845 ((GNUNET_YES != active_to_migration) ||
1846 (putl > 2.5 * (1 + prq.priority))))
1847 {
1848 if (GNUNET_YES != active_to_migration)
1849 putl = 1.0 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 5);
1850 block_time = GNUNET_TIME_relative_multiply (
1851 GNUNET_TIME_UNIT_MILLISECONDS,
1852 5000 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1853 (unsigned int) (60000 * putl * putl)));
1854 GNUNET_log (
1855 GNUNET_ERROR_TYPE_DEBUG,
1856 "Asking to stop migration for %s because of load %f and events %d/%d\n",
1857 GNUNET_STRINGS_relative_time_to_string (block_time, GNUNET_YES),
1858 putl,
1859 active_to_migration,
1860 (GNUNET_NO == prq.request_found));
1861 GSF_block_peer_migration_ (cp,
1862 GNUNET_TIME_relative_to_absolute (block_time));
1863 }
1864}
1865
1866
1867/**
1868 * Check if the given request is still active.
1869 *
1870 * @param pr pending request
1871 * @return #GNUNET_YES if the request is still active
1872 */
1873int
1874GSF_pending_request_test_active_ (struct GSF_PendingRequest *pr)
1875{
1876 return (NULL != pr->rh) ? GNUNET_YES : GNUNET_NO;
1877}
1878
1879
1880/**
1881 * Setup the subsystem.
1882 */
1883void
1884GSF_pending_request_init_ ()
1885{
1886 if (GNUNET_OK !=
1887 GNUNET_CONFIGURATION_get_value_number (GSF_cfg,
1888 "fs",
1889 "MAX_PENDING_REQUESTS",
1890 &max_pending_requests))
1891 {
1892 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
1893 "fs",
1894 "MAX_PENDING_REQUESTS");
1895 }
1896 active_to_migration =
1897 GNUNET_CONFIGURATION_get_value_yesno (GSF_cfg, "FS", "CONTENT_CACHING");
1898 datastore_put_load = GNUNET_LOAD_value_init (DATASTORE_LOAD_AUTODECLINE);
1899 pr_map = GNUNET_CONTAINER_multihashmap_create (32 * 1024, GNUNET_YES);
1900 requests_by_expiration_heap =
1901 GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
1902}
1903
1904
1905/**
1906 * Shutdown the subsystem.
1907 */
1908void
1909GSF_pending_request_done_ ()
1910{
1911 GNUNET_CONTAINER_multihashmap_iterate (pr_map, &clean_request, NULL);
1912 GNUNET_CONTAINER_multihashmap_destroy (pr_map);
1913 pr_map = NULL;
1914 GNUNET_CONTAINER_heap_destroy (requests_by_expiration_heap);
1915 requests_by_expiration_heap = NULL;
1916 GNUNET_LOAD_value_free (datastore_put_load);
1917 datastore_put_load = NULL;
1918}
1919
1920
1921/* end of gnunet-service-fs_pr.c */
diff --git a/src/fs/gnunet-service-fs_pr.h b/src/fs/gnunet-service-fs_pr.h
deleted file mode 100644
index 339e409c5..000000000
--- a/src/fs/gnunet-service-fs_pr.h
+++ /dev/null
@@ -1,422 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_pr.h
23 * @brief API to handle pending requests
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_FS_PR_H
27#define GNUNET_SERVICE_FS_PR_H
28
29#include "gnunet-service-fs.h"
30
31
32/**
33 * Options for pending requests (bits to be ORed).
34 */
35enum GSF_PendingRequestOptions
36{
37 /**
38 * No special options (P2P-default).
39 */
40 GSF_PRO_DEFAULTS = 0,
41
42 /**
43 * Request must only be processed locally.
44 */
45 GSF_PRO_LOCAL_ONLY = 1,
46
47 /**
48 * Request must only be forwarded (no routing)
49 */
50 GSF_PRO_FORWARD_ONLY = 2,
51
52 /**
53 * Request persists indefinitely (no expiration).
54 */
55 GSF_PRO_REQUEST_NEVER_EXPIRES = 4,
56
57 /**
58 * Request is allowed to refresh bloomfilter and change mingle value.
59 */
60 GSF_PRO_BLOOMFILTER_FULL_REFRESH = 8,
61
62 /**
63 * Request priority is allowed to be exceeded.
64 */
65 GSF_PRO_PRIORITY_UNLIMITED = 16,
66
67 /**
68 * Option mask for typical local requests.
69 */
70 GSF_PRO_LOCAL_REQUEST =
71 (GSF_PRO_BLOOMFILTER_FULL_REFRESH | GSF_PRO_PRIORITY_UNLIMITED
72 | GSF_PRO_REQUEST_NEVER_EXPIRES)
73};
74
75
76/**
77 * Public data (in the sense of not encapsulated within
78 * 'gnunet-service-fs_pr', not in the sense of network-wide
79 * known) associated with each pending request.
80 */
81struct GSF_PendingRequestData
82{
83 /**
84 * Primary query hash for this request.
85 */
86 struct GNUNET_HashCode query;
87
88 /**
89 * Identity of a peer hosting the content, otherwise NULl.
90 * Allocated after struct only if needed. Do not free!
91 */
92 const struct GNUNET_PeerIdentity *target;
93
94 /**
95 * Fields for the plan module to track a DLL with the request.
96 */
97 struct GSF_PendingRequestPlanBijection *pr_head;
98
99 /**
100 * Fields for the plan module to track a DLL with the request.
101 */
102 struct GSF_PendingRequestPlanBijection *pr_tail;
103
104 /**
105 * Current TTL for the request.
106 */
107 struct GNUNET_TIME_Absolute ttl;
108
109 /**
110 * When did we start with the request.
111 */
112 struct GNUNET_TIME_Absolute start_time;
113
114 /**
115 * Desired anonymity level.
116 */
117 uint32_t anonymity_level;
118
119 /**
120 * Priority that this request (still) has for us.
121 */
122 uint32_t priority;
123
124 /**
125 * Priority that this request (originally) had for us.
126 */
127 uint32_t original_priority;
128
129 /**
130 * Counter for how often this request has been transmitted (estimate,
131 * because we might have the same request pending for multiple clients,
132 * and of course because a transmission may have failed at a lower
133 * layer).
134 */
135 uint32_t num_transmissions;
136
137 /**
138 * How much respect did we (in total) offer for this request so far (estimate,
139 * because we might have the same request pending for multiple clients,
140 * and of course because a transmission may have failed at a lower
141 * layer).
142 */
143 uint32_t respect_offered;
144
145 /**
146 * Options for the request.
147 */
148 enum GSF_PendingRequestOptions options;
149
150 /**
151 * Type of the requested block.
152 */
153 enum GNUNET_BLOCK_Type type;
154
155 /**
156 * Number of results we have found for this request so far.
157 */
158 unsigned int results_found;
159
160 /**
161 * Has this request been started yet (local/p2p operations)? Or are
162 * we still constructing it?
163 */
164 int has_started;
165};
166
167
168/**
169 * Handle a reply to a pending request. Also called if a request
170 * expires (then with data == NULL). The handler may be called
171 * many times (depending on the request type), but will not be
172 * called during or after a call to GSF_pending_request_cancel
173 * and will also not be called anymore after a call signalling
174 * expiration.
175 *
176 * @param cls user-specified closure
177 * @param eval evaluation of the result
178 * @param pr handle to the original pending request
179 * @param reply_anonymity_level anonymity level for the reply, UINT32_MAX for "unknown"
180 * @param expiration when does @a data expire?
181 * @param last_transmission the last time we've tried to get this block (FOREVER if unknown)
182 * @param type type of the block
183 * @param data response data, NULL on request expiration
184 * @param data_len number of bytes in @a data
185 */
186typedef void
187(*GSF_PendingRequestReplyHandler) (
188 void *cls,
189 enum GNUNET_BLOCK_ReplyEvaluationResult eval,
190 struct GSF_PendingRequest *pr,
191 uint32_t reply_anonymity_level,
192 struct GNUNET_TIME_Absolute expiration,
193 struct GNUNET_TIME_Absolute
194 last_transmission,
195 enum GNUNET_BLOCK_Type type,
196 const void *data,
197 size_t data_len);
198
199
200/**
201 * Create a new pending request.
202 *
203 * @param options request options
204 * @param type type of the block that is being requested
205 * @param query key for the lookup
206 * @param target preferred target for the request, NULL for none
207 * @param bf_data raw data for bloom filter for known replies, can be NULL
208 * @param bf_size number of bytes in bf_data
209 * @param anonymity_level desired anonymity level
210 * @param priority maximum outgoing cumulative request priority to use
211 * @param ttl current time-to-live for the request
212 * @param sender_pid peer ID to use for the sender when forwarding, 0 for none;
213 * reference counter is taken over by this function
214 * @param origin_pid peer ID of origin of query (do not loop back)
215 * @param replies_seen hash codes of known local replies
216 * @param replies_seen_count size of the 'replies_seen' array
217 * @param rh handle to call when we get a reply
218 * @param rh_cls closure for rh
219 * @return handle for the new pending request
220 */
221struct GSF_PendingRequest *
222GSF_pending_request_create_ (enum GSF_PendingRequestOptions options,
223 enum GNUNET_BLOCK_Type type,
224 const struct GNUNET_HashCode *query,
225 const struct GNUNET_PeerIdentity *target,
226 const char *bf_data,
227 size_t bf_size,
228 uint32_t anonymity_level,
229 uint32_t priority,
230 int32_t ttl,
231 GNUNET_PEER_Id sender_pid,
232 GNUNET_PEER_Id origin_pid,
233 const struct GNUNET_HashCode *replies_seen,
234 unsigned int replies_seen_count,
235 GSF_PendingRequestReplyHandler rh,
236 void *rh_cls);
237
238
239/**
240 * Update a given pending request with additional replies
241 * that have been seen.
242 *
243 * @param pr request to update
244 * @param replies_seen hash codes of replies that we've seen
245 * @param replies_seen_count size of the @a replies_seen array
246 */
247void
248GSF_pending_request_update_ (struct GSF_PendingRequest *pr,
249 const struct GNUNET_HashCode *replies_seen,
250 unsigned int replies_seen_count);
251
252
253/**
254 * Obtain the public data associated with a pending request
255 *
256 * @param pr pending request
257 * @return associated public data
258 */
259struct GSF_PendingRequestData *
260GSF_pending_request_get_data_ (struct GSF_PendingRequest *pr);
261
262
263/**
264 * Check if the given request is still active.
265 *
266 * @param pr pending request
267 * @return #GNUNET_YES if the request is still active
268 */
269int
270GSF_pending_request_test_active_ (struct GSF_PendingRequest *pr);
271
272
273/**
274 * Test if two pending requests are compatible (would generate
275 * the same query modulo filters and should thus be processed
276 * jointly).
277 *
278 * @param pra a pending request
279 * @param prb another pending request
280 * @return #GNUNET_OK if the requests are compatible
281 */
282int
283GSF_pending_request_is_compatible_ (struct GSF_PendingRequest *pra,
284 struct GSF_PendingRequest *prb);
285
286
287/**
288 * Generate the message corresponding to the given pending request for
289 * transmission to other peers.
290 *
291 * @param pr request to generate the message for
292 * @return envelope with the request message
293 */
294struct GNUNET_MQ_Envelope *
295GSF_pending_request_get_message_ (struct GSF_PendingRequest *pr);
296
297
298/**
299 * Explicitly cancel a pending request.
300 *
301 * @param pr request to cancel
302 * @param full_cleanup fully purge the request
303 */
304void
305GSF_pending_request_cancel_ (struct GSF_PendingRequest *pr,
306 int full_cleanup);
307
308
309/**
310 * Signature of function called on each request.
311 * (Note: 'subtype' of GNUNET_CONTAINER_HashMapIterator).
312 *
313 * @param cls closure
314 * @param key query for the request
315 * @param pr handle to the pending request
316 * @return #GNUNET_YES to continue to iterate
317 */
318typedef int
319(*GSF_PendingRequestIterator) (void *cls,
320 const struct GNUNET_HashCode *key,
321 struct GSF_PendingRequest *pr);
322
323
324/**
325 * Iterate over all pending requests.
326 *
327 * @param it function to call for each request
328 * @param cls closure for it
329 */
330void
331GSF_iterate_pending_requests_ (GSF_PendingRequestIterator it,
332 void *cls);
333
334
335/**
336 * Handle P2P "CONTENT" message. Checks that the message is
337 * well-formed and then checks if there are any pending requests for
338 * this content and possibly passes it on (to local clients or other
339 * peers). Does NOT perform migration (content caching at this peer).
340 *
341 * @param cls the other peer involved (sender)
342 * @param put the actual message
343 */
344void
345handle_p2p_put (void *cls,
346 const struct PutMessage *put);
347
348
349/**
350 * Consider looking up the data in the DHT (anonymity-level permitting).
351 *
352 * @param pr the pending request to process
353 */
354void
355GSF_dht_lookup_ (struct GSF_PendingRequest *pr);
356
357
358/**
359 * Consider downloading via cadet (if possible)
360 *
361 * @param pr the pending request to process
362 */
363void
364GSF_cadet_lookup_ (struct GSF_PendingRequest *pr);
365
366
367/**
368 * Function to be called after we're done processing
369 * replies from the local lookup.
370 *
371 * @param cls closure
372 * @param pr the pending request we were processing
373 * @param result final datastore lookup result
374 */
375typedef void
376(*GSF_LocalLookupContinuation) (
377 void *cls,
378 struct GSF_PendingRequest *pr,
379 enum GNUNET_BLOCK_ReplyEvaluationResult result);
380
381
382/**
383 * Look up the request in the local datastore.
384 *
385 * @param pr the pending request to process
386 * @param cont function to call at the end
387 * @param cont_cls closure for @a cont
388 */
389void
390GSF_local_lookup_ (struct GSF_PendingRequest *pr,
391 GSF_LocalLookupContinuation cont,
392 void *cont_cls);
393
394
395/**
396 * Is the given target a legitimate peer for forwarding the given request?
397 *
398 * @param pr request
399 * @param target
400 * @return #GNUNET_YES if this request could be forwarded to the given peer
401 */
402int
403GSF_pending_request_test_target_ (struct GSF_PendingRequest *pr,
404 const struct GNUNET_PeerIdentity *target);
405
406
407/**
408 * Setup the subsystem.
409 */
410void
411GSF_pending_request_init_ (void);
412
413
414/**
415 * Shutdown the subsystem.
416 */
417void
418GSF_pending_request_done_ (void);
419
420
421#endif
422/* end of gnunet-service-fs_pr.h */
diff --git a/src/fs/gnunet-service-fs_push.c b/src/fs/gnunet-service-fs_push.c
deleted file mode 100644
index 1825e8eb3..000000000
--- a/src/fs/gnunet-service-fs_push.c
+++ /dev/null
@@ -1,677 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_push.c
23 * @brief API to push content from our datastore to other peers
24 * ('anonymous'-content P2P migration)
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet-service-fs.h"
29#include "gnunet-service-fs_cp.h"
30#include "gnunet-service-fs_indexing.h"
31#include "gnunet-service-fs_push.h"
32
33
34/**
35 * Maximum number of blocks we keep in memory for migration.
36 */
37#define MAX_MIGRATION_QUEUE 8
38
39/**
40 * Blocks are at most migrated to this number of peers
41 * plus one, each time they are fetched from the database.
42 */
43#define MIGRATION_LIST_SIZE 2
44
45/**
46 * How long must content remain valid for us to consider it for migration?
47 * If content will expire too soon, there is clearly no point in pushing
48 * it to other peers. This value gives the threshold for migration. Note
49 * that if this value is increased, the migration testcase may need to be
50 * adjusted as well (especially the CONTENT_LIFETIME in fs_test_lib.c).
51 */
52#define MIN_MIGRATION_CONTENT_LIFETIME GNUNET_TIME_relative_multiply ( \
53 GNUNET_TIME_UNIT_MINUTES, 30)
54
55
56/**
57 * Block that is ready for migration to other peers. Actual data is at the end of the block.
58 */
59struct MigrationReadyBlock
60{
61 /**
62 * This is a doubly-linked list.
63 */
64 struct MigrationReadyBlock *next;
65
66 /**
67 * This is a doubly-linked list.
68 */
69 struct MigrationReadyBlock *prev;
70
71 /**
72 * Query for the block.
73 */
74 struct GNUNET_HashCode query;
75
76 /**
77 * When does this block expire?
78 */
79 struct GNUNET_TIME_Absolute expiration;
80
81 /**
82 * Peers we already forwarded this
83 * block to. Zero for empty entries.
84 */
85 GNUNET_PEER_Id target_list[MIGRATION_LIST_SIZE];
86
87 /**
88 * Size of the block.
89 */
90 size_t size;
91
92 /**
93 * Number of targets already used.
94 */
95 unsigned int used_targets;
96
97 /**
98 * Type of the block.
99 */
100 enum GNUNET_BLOCK_Type type;
101};
102
103
104/**
105 * Information about a peer waiting for migratable data.
106 */
107struct MigrationReadyPeer
108{
109 /**
110 * This is a doubly-linked list.
111 */
112 struct MigrationReadyPeer *next;
113
114 /**
115 * This is a doubly-linked list.
116 */
117 struct MigrationReadyPeer *prev;
118
119 /**
120 * Handle to peer.
121 */
122 struct GSF_ConnectedPeer *peer;
123
124 /**
125 * Envelope of the currently pushed message.
126 */
127 struct GNUNET_MQ_Envelope *env;
128};
129
130
131/**
132 * Head of linked list of blocks that can be migrated.
133 */
134static struct MigrationReadyBlock *mig_head;
135
136/**
137 * Tail of linked list of blocks that can be migrated.
138 */
139static struct MigrationReadyBlock *mig_tail;
140
141/**
142 * Head of linked list of peers.
143 */
144static struct MigrationReadyPeer *peer_head;
145
146/**
147 * Tail of linked list of peers.
148 */
149static struct MigrationReadyPeer *peer_tail;
150
151/**
152 * Request to datastore for migration (or NULL).
153 */
154static struct GNUNET_DATASTORE_QueueEntry *mig_qe;
155
156/**
157 * ID of task that collects blocks for migration.
158 */
159static struct GNUNET_SCHEDULER_Task *mig_task;
160
161/**
162 * What is the maximum frequency at which we are allowed to
163 * poll the datastore for migration content?
164 */
165static struct GNUNET_TIME_Relative min_migration_delay;
166
167/**
168 * Size of the doubly-linked list of migration blocks.
169 */
170static unsigned int mig_size;
171
172/**
173 * Is this module enabled?
174 */
175static int enabled;
176
177/**
178 * Did we find anything in the datastore?
179 */
180static int value_found;
181
182
183/**
184 * Delete the given migration block.
185 *
186 * @param mb block to delete
187 */
188static void
189delete_migration_block (struct MigrationReadyBlock *mb)
190{
191 GNUNET_CONTAINER_DLL_remove (mig_head,
192 mig_tail,
193 mb);
194 GNUNET_PEER_decrement_rcs (mb->target_list,
195 MIGRATION_LIST_SIZE);
196 mig_size--;
197 GNUNET_free (mb);
198}
199
200
201/**
202 * Find content for migration to this peer.
203 *
204 * @param cls a `struct MigrationReadyPeer *`
205 */
206static void
207find_content (void *cls);
208
209
210/**
211 * Send the given block to the given peer.
212 *
213 * @param peer target peer
214 * @param block the block
215 * @return #GNUNET_YES if the block was deleted (!)
216 */
217static int
218transmit_content (struct MigrationReadyPeer *mrp,
219 struct MigrationReadyBlock *block)
220{
221 struct PutMessage *msg;
222 unsigned int i;
223 struct GSF_PeerPerformanceData *ppd;
224 int ret;
225
226 ppd = GSF_get_peer_performance_data_ (mrp->peer);
227 GNUNET_assert (NULL == mrp->env);
228 mrp->env = GNUNET_MQ_msg_extra (msg,
229 block->size,
230 GNUNET_MESSAGE_TYPE_FS_PUT);
231 msg->type = htonl (block->type);
232 msg->expiration = GNUNET_TIME_absolute_hton (block->expiration);
233 GNUNET_memcpy (&msg[1],
234 &block[1],
235 block->size);
236 for (i = 0; i < MIGRATION_LIST_SIZE; i++)
237 {
238 if (block->target_list[i] == 0)
239 {
240 block->target_list[i] = ppd->pid;
241 GNUNET_PEER_change_rc (block->target_list[i],
242 1);
243 break;
244 }
245 }
246 if (MIGRATION_LIST_SIZE == i)
247 {
248 delete_migration_block (block);
249 ret = GNUNET_YES;
250 }
251 else
252 {
253 ret = GNUNET_NO;
254 }
255 GNUNET_MQ_notify_sent (mrp->env,
256 &find_content,
257 mrp);
258 GSF_peer_transmit_ (mrp->peer,
259 GNUNET_NO,
260 0 /* priority */,
261 mrp->env);
262 return ret;
263}
264
265
266/**
267 * Count the number of peers this block has
268 * already been forwarded to.
269 *
270 * @param block the block
271 * @return number of times block was forwarded
272 */
273static unsigned int
274count_targets (struct MigrationReadyBlock *block)
275{
276 unsigned int i;
277
278 for (i = 0; i < MIGRATION_LIST_SIZE; i++)
279 if (block->target_list[i] == 0)
280 return i;
281 return i;
282}
283
284
285/**
286 * Check if sending this block to this peer would
287 * be a good idea.
288 *
289 * @param mrp target peer
290 * @param block the block
291 * @return score (>= 0: feasible, negative: infeasible)
292 */
293static long
294score_content (struct MigrationReadyPeer *mrp,
295 struct MigrationReadyBlock *block)
296{
297 unsigned int i;
298 struct GSF_PeerPerformanceData *ppd;
299 struct GNUNET_PeerIdentity id;
300 struct GNUNET_HashCode hc;
301 uint32_t dist;
302
303 ppd = GSF_get_peer_performance_data_ (mrp->peer);
304 for (i = 0; i < MIGRATION_LIST_SIZE; i++)
305 if (block->target_list[i] == ppd->pid)
306 return -1;
307 GNUNET_assert (0 != ppd->pid);
308 GNUNET_PEER_resolve (ppd->pid,
309 &id);
310 GNUNET_CRYPTO_hash (&id,
311 sizeof(struct GNUNET_PeerIdentity),
312 &hc);
313 dist = GNUNET_CRYPTO_hash_distance_u32 (&block->query,
314 &hc);
315 /* closer distance, higher score: */
316 return UINT32_MAX - dist;
317}
318
319
320/**
321 * If the migration task is not currently running, consider
322 * (re)scheduling it with the appropriate delay.
323 */
324static void
325consider_gathering (void);
326
327
328/**
329 * Find content for migration to this peer.
330 *
331 * @param cls peer to find content for
332 */
333static void
334find_content (void *cls)
335{
336 struct MigrationReadyPeer *mrp = cls;
337 struct MigrationReadyBlock *pos;
338 long score;
339 long best_score;
340 struct MigrationReadyBlock *best;
341
342 mrp->env = NULL;
343 best = NULL;
344 best_score = -1;
345 pos = mig_head;
346 while (NULL != pos)
347 {
348 score = score_content (mrp, pos);
349 if (score > best_score)
350 {
351 best_score = score;
352 best = pos;
353 }
354 pos = pos->next;
355 }
356 if (NULL == best)
357 {
358 if (mig_size < MAX_MIGRATION_QUEUE)
359 {
360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
361 "No content found for pushing, waiting for queue to fill\n");
362 return; /* will fill up eventually... */
363 }
364 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
365 "No suitable content found, purging content from full queue\n");
366 /* failed to find migration target AND
367 * queue is full, purge most-forwarded
368 * block from queue to make room for more */
369 pos = mig_head;
370 while (NULL != pos)
371 {
372 score = count_targets (pos);
373 if (score >= best_score)
374 {
375 best_score = score;
376 best = pos;
377 }
378 pos = pos->next;
379 }
380 GNUNET_assert (NULL != best);
381 delete_migration_block (best);
382 consider_gathering ();
383 return;
384 }
385 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386 "Preparing to push best content to peer\n");
387 transmit_content (mrp,
388 best);
389}
390
391
392/**
393 * Task that is run periodically to obtain blocks for content
394 * migration
395 *
396 * @param cls unused
397 */
398static void
399gather_migration_blocks (void *cls);
400
401
402/**
403 * If the migration task is not currently running, consider
404 * (re)scheduling it with the appropriate delay.
405 */
406static void
407consider_gathering ()
408{
409 struct GNUNET_TIME_Relative delay;
410
411 if (NULL == GSF_dsh)
412 return;
413 if (NULL != mig_qe)
414 return;
415 if (NULL != mig_task)
416 return;
417 if (mig_size >= MAX_MIGRATION_QUEUE)
418 return;
419 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
420 mig_size);
421 delay = GNUNET_TIME_relative_divide (delay,
422 MAX_MIGRATION_QUEUE);
423 delay = GNUNET_TIME_relative_max (delay,
424 min_migration_delay);
425 if (GNUNET_NO == value_found)
426 {
427 /* wait at least 5s if the datastore is empty */
428 delay = GNUNET_TIME_relative_max (delay,
429 GNUNET_TIME_relative_multiply (
430 GNUNET_TIME_UNIT_SECONDS,
431 5));
432 }
433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434 "Scheduling gathering task (queue size: %u)\n",
435 mig_size);
436 mig_task = GNUNET_SCHEDULER_add_delayed (delay,
437 &gather_migration_blocks,
438 NULL);
439}
440
441
442/**
443 * Process content offered for migration.
444 *
445 * @param cls closure
446 * @param key key for the content
447 * @param size number of bytes in data
448 * @param data content stored
449 * @param type type of the content
450 * @param priority priority of the content
451 * @param anonymity anonymity-level for the content
452 * @param replication replication-level for the content
453 * @param expiration expiration time for the content
454 * @param uid unique identifier for the datum;
455 * maybe 0 if no unique identifier is available
456 */
457static void
458process_migration_content (void *cls,
459 const struct GNUNET_HashCode *key,
460 size_t size,
461 const void *data,
462 enum GNUNET_BLOCK_Type type,
463 uint32_t priority,
464 uint32_t anonymity,
465 uint32_t replication,
466 struct GNUNET_TIME_Absolute expiration,
467 uint64_t uid)
468{
469 struct MigrationReadyBlock *mb;
470 struct MigrationReadyPeer *pos;
471
472 mig_qe = NULL;
473 if (NULL == key)
474 {
475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476 "No content found for migration...\n");
477 consider_gathering ();
478 return;
479 }
480 value_found = GNUNET_YES;
481 if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us <
482 MIN_MIGRATION_CONTENT_LIFETIME.rel_value_us)
483 {
484 /* content will expire soon, don't bother */
485 consider_gathering ();
486 return;
487 }
488 if (type == GNUNET_BLOCK_TYPE_FS_ONDEMAND)
489 {
490 if (GNUNET_OK !=
491 GNUNET_FS_handle_on_demand_block (key,
492 size,
493 data,
494 type,
495 priority,
496 anonymity,
497 replication,
498 expiration,
499 uid,
500 &process_migration_content,
501 NULL))
502 consider_gathering ();
503 return;
504 }
505 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
506 "Retrieved block `%s' of type %u for migration (queue size: %u/%u)\n",
507 GNUNET_h2s (key),
508 type, mig_size + 1,
509 MAX_MIGRATION_QUEUE);
510 mb = GNUNET_malloc (sizeof(struct MigrationReadyBlock) + size);
511 mb->query = *key;
512 mb->expiration = expiration;
513 mb->size = size;
514 mb->type = type;
515 GNUNET_memcpy (&mb[1], data, size);
516 GNUNET_CONTAINER_DLL_insert_after (mig_head,
517 mig_tail,
518 mig_tail,
519 mb);
520 mig_size++;
521 for (pos = peer_head; NULL != pos; pos = pos->next)
522 {
523 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524 "Preparing to push best content to peer %s\n",
525 GNUNET_i2s (GSF_connected_peer_get_identity2_ (pos->peer)));
526 if ((NULL == pos->env) &&
527 (GNUNET_YES == transmit_content (pos,
528 mb)))
529 {
530 break; /* 'mb' was freed! */
531 }
532 }
533 consider_gathering ();
534}
535
536
537/**
538 * Task that is run periodically to obtain blocks for content
539 * migration
540 *
541 * @param cls unused
542 */
543static void
544gather_migration_blocks (void *cls)
545{
546 mig_task = NULL;
547 if (mig_size >= MAX_MIGRATION_QUEUE)
548 return;
549 if (NULL == GSF_dsh)
550 return;
551 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
552 "Asking datastore for content for replication (queue size: %u)\n",
553 mig_size);
554 value_found = GNUNET_NO;
555 mig_qe = GNUNET_DATASTORE_get_for_replication (GSF_dsh,
556 0,
557 UINT_MAX,
558 &process_migration_content,
559 NULL);
560 if (NULL == mig_qe)
561 consider_gathering ();
562}
563
564
565/**
566 * A peer connected to us. Start pushing content
567 * to this peer.
568 *
569 * @param peer handle for the peer that connected
570 */
571void
572GSF_push_start_ (struct GSF_ConnectedPeer *peer)
573{
574 struct MigrationReadyPeer *mrp;
575
576 if (GNUNET_YES != enabled)
577 return;
578 for (mrp = peer_head; NULL != mrp; mrp = mrp->next)
579 if (mrp->peer == peer)
580 break;
581 if (NULL != mrp)
582 {
583 /* same peer added twice, must not happen */
584 GNUNET_break (0);
585 return;
586 }
587
588 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
589 "Adding peer %s to list for pushing\n",
590 GNUNET_i2s (GSF_connected_peer_get_identity2_ (peer)));
591
592 mrp = GNUNET_new (struct MigrationReadyPeer);
593 mrp->peer = peer;
594 find_content (mrp);
595 GNUNET_CONTAINER_DLL_insert (peer_head,
596 peer_tail,
597 mrp);
598}
599
600
601/**
602 * A peer disconnected from us. Stop pushing content
603 * to this peer.
604 *
605 * @param peer handle for the peer that disconnected
606 */
607void
608GSF_push_stop_ (struct GSF_ConnectedPeer *peer)
609{
610 struct MigrationReadyPeer *pos;
611
612 for (pos = peer_head; NULL != pos; pos = pos->next)
613 if (pos->peer == peer)
614 break;
615 if (NULL == pos)
616 return;
617 if (NULL != pos->env)
618 GNUNET_MQ_send_cancel (pos->env);
619 GNUNET_CONTAINER_DLL_remove (peer_head,
620 peer_tail,
621 pos);
622 GNUNET_free (pos);
623}
624
625
626/**
627 * Setup the module.
628 */
629void
630GSF_push_init_ ()
631{
632 enabled =
633 GNUNET_CONFIGURATION_get_value_yesno (GSF_cfg,
634 "FS",
635 "CONTENT_PUSHING");
636 if (GNUNET_YES != enabled)
637 return;
638
639 if (GNUNET_OK !=
640 GNUNET_CONFIGURATION_get_value_time (GSF_cfg,
641 "fs",
642 "MIN_MIGRATION_DELAY",
643 &min_migration_delay))
644 {
645 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
646 "fs",
647 "MIN_MIGRATION_DELAY",
648 _ ("time required, content pushing disabled"));
649 return;
650 }
651 consider_gathering ();
652}
653
654
655/**
656 * Shutdown the module.
657 */
658void
659GSF_push_done_ ()
660{
661 if (NULL != mig_task)
662 {
663 GNUNET_SCHEDULER_cancel (mig_task);
664 mig_task = NULL;
665 }
666 if (NULL != mig_qe)
667 {
668 GNUNET_DATASTORE_cancel (mig_qe);
669 mig_qe = NULL;
670 }
671 while (NULL != mig_head)
672 delete_migration_block (mig_head);
673 GNUNET_assert (0 == mig_size);
674}
675
676
677/* end of gnunet-service-fs_push.c */
diff --git a/src/fs/gnunet-service-fs_push.h b/src/fs/gnunet-service-fs_push.h
deleted file mode 100644
index 2cd621bbb..000000000
--- a/src/fs/gnunet-service-fs_push.h
+++ /dev/null
@@ -1,66 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_push.h
23 * @brief support for pushing out content
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_FS_PUSH_H
27#define GNUNET_SERVICE_FS_PUSH_H
28
29#include "gnunet-service-fs.h"
30
31
32/**
33 * Setup the module.
34 */
35void
36GSF_push_init_ (void);
37
38
39/**
40 * Shutdown the module.
41 */
42void
43GSF_push_done_ (void);
44
45
46/**
47 * A peer connected to us or we are now again allowed to push content.
48 * Start pushing content to this peer.
49 *
50 * @param peer handle for the peer that connected
51 */
52void
53GSF_push_start_ (struct GSF_ConnectedPeer *peer);
54
55
56/**
57 * A peer disconnected from us or asked us to stop pushing content for
58 * a while. Stop pushing content to this peer.
59 *
60 * @param peer handle for the peer that disconnected
61 */
62void
63GSF_push_stop_ (struct GSF_ConnectedPeer *peer);
64
65
66#endif
diff --git a/src/fs/gnunet-service-fs_put.c b/src/fs/gnunet-service-fs_put.c
deleted file mode 100644
index 93333b7a2..000000000
--- a/src/fs/gnunet-service-fs_put.c
+++ /dev/null
@@ -1,296 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_put.c
23 * @brief API to PUT zero-anonymity index data from our datastore into the DHT
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet-service-fs.h"
28#include "gnunet-service-fs_put.h"
29
30
31/**
32 * How often do we at most PUT content into the DHT?
33 */
34#define MAX_DHT_PUT_FREQ GNUNET_TIME_relative_multiply ( \
35 GNUNET_TIME_UNIT_SECONDS, 5)
36
37/**
38 * How many replicas do we try to create per PUT?
39 */
40#define DEFAULT_PUT_REPLICATION 5
41
42
43/**
44 * Context for each zero-anonymity iterator.
45 */
46struct PutOperator
47{
48 /**
49 * Request to datastore for DHT PUTs (or NULL).
50 */
51 struct GNUNET_DATASTORE_QueueEntry *dht_qe;
52
53 /**
54 * Type we request from the datastore.
55 */
56 enum GNUNET_BLOCK_Type dht_put_type;
57
58 /**
59 * Handle to PUT operation.
60 */
61 struct GNUNET_DHT_PutHandle *dht_put;
62
63 /**
64 * ID of task that collects blocks for DHT PUTs.
65 */
66 struct GNUNET_SCHEDULER_Task *dht_task;
67
68 /**
69 * How many entries with zero anonymity of our type do we currently
70 * estimate to have in the database?
71 */
72 uint64_t zero_anonymity_count_estimate;
73
74 /**
75 * Count of results received from the database.
76 */
77 uint64_t result_count;
78
79 /**
80 * Next UID to request when iterating the database.
81 */
82 uint64_t next_uid;
83};
84
85
86/**
87 * ANY-terminated list of our operators (one per type
88 * of block that we're putting into the DHT).
89 */
90static struct PutOperator operators[] = {
91 { NULL, GNUNET_BLOCK_TYPE_FS_UBLOCK, 0, 0, 0 },
92 { NULL, GNUNET_BLOCK_TYPE_ANY, 0, 0, 0 }
93};
94
95
96/**
97 * Task that is run periodically to obtain blocks for DHT PUTs.
98 *
99 * @param cls type of blocks to gather
100 * @param tc scheduler context (unused)
101 */
102static void
103gather_dht_put_blocks (void *cls);
104
105
106/**
107 * Calculate when to run the next PUT operation and schedule it.
108 *
109 * @param po put operator to schedule
110 */
111static void
112schedule_next_put (struct PutOperator *po)
113{
114 struct GNUNET_TIME_Relative delay;
115
116 if (po->zero_anonymity_count_estimate > 0)
117 {
118 delay =
119 GNUNET_TIME_relative_divide (GNUNET_DHT_DEFAULT_REPUBLISH_FREQUENCY,
120 po->zero_anonymity_count_estimate);
121 delay = GNUNET_TIME_relative_min (delay, MAX_DHT_PUT_FREQ);
122 }
123 else
124 {
125 /* if we have NO zero-anonymity content yet, wait 5 minutes for some to
126 * (hopefully) appear */
127 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5);
128 }
129 po->dht_task =
130 GNUNET_SCHEDULER_add_delayed (delay, &gather_dht_put_blocks, po);
131}
132
133
134/**
135 * Continuation called after DHT PUT operation has finished.
136 *
137 * @param cls type of blocks to gather
138 */
139static void
140delay_dht_put_blocks (void *cls)
141{
142 struct PutOperator *po = cls;
143
144 po->dht_put = NULL;
145 schedule_next_put (po);
146}
147
148
149/**
150 * Task that is run periodically to obtain blocks for DHT PUTs.
151 *
152 * @param cls type of blocks to gather
153 */
154static void
155delay_dht_put_task (void *cls)
156{
157 struct PutOperator *po = cls;
158
159 po->dht_task = NULL;
160 schedule_next_put (po);
161}
162
163
164/**
165 * Store content in DHT.
166 *
167 * @param cls closure
168 * @param key key for the content
169 * @param size number of bytes in data
170 * @param data content stored
171 * @param type type of the content
172 * @param priority priority of the content
173 * @param anonymity anonymity-level for the content
174 * @param replication replication-level for the content
175 * @param expiration expiration time for the content
176 * @param uid unique identifier for the datum;
177 * maybe 0 if no unique identifier is available
178 */
179static void
180process_dht_put_content (void *cls,
181 const struct GNUNET_HashCode *key,
182 size_t size,
183 const void *data,
184 enum GNUNET_BLOCK_Type type,
185 uint32_t priority,
186 uint32_t anonymity,
187 uint32_t replication,
188 struct GNUNET_TIME_Absolute expiration,
189 uint64_t uid)
190{
191 struct PutOperator *po = cls;
192
193 po->dht_qe = NULL;
194 if (key == NULL)
195 {
196 po->zero_anonymity_count_estimate = po->result_count;
197 po->result_count = 0;
198 po->next_uid = 0;
199 po->dht_task = GNUNET_SCHEDULER_add_now (&delay_dht_put_task, po);
200 return;
201 }
202 po->result_count++;
203 po->next_uid = uid + 1;
204 po->zero_anonymity_count_estimate =
205 GNUNET_MAX (po->result_count, po->zero_anonymity_count_estimate);
206 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
207 "Retrieved block `%s' of type %u for DHT PUT\n", GNUNET_h2s (key),
208 type);
209 po->dht_put = GNUNET_DHT_put (GSF_dht,
210 key,
211 DEFAULT_PUT_REPLICATION,
212 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
213 type,
214 size,
215 data,
216 expiration,
217 &delay_dht_put_blocks,
218 po);
219}
220
221
222/**
223 * Task that is run periodically to obtain blocks for DHT PUTs.
224 *
225 * @param cls type of blocks to gather
226 */
227static void
228gather_dht_put_blocks (void *cls)
229{
230 struct PutOperator *po = cls;
231
232 po->dht_task = NULL;
233 po->dht_qe =
234 GNUNET_DATASTORE_get_zero_anonymity (GSF_dsh,
235 po->next_uid,
236 0,
237 UINT_MAX,
238 po->dht_put_type,
239 &process_dht_put_content,
240 po);
241 if (NULL == po->dht_qe)
242 po->dht_task = GNUNET_SCHEDULER_add_now (&delay_dht_put_task, po);
243}
244
245
246/**
247 * Setup the module.
248 */
249void
250GSF_put_init_ ()
251{
252 unsigned int i;
253
254 i = 0;
255 while (operators[i].dht_put_type != GNUNET_BLOCK_TYPE_ANY)
256 {
257 operators[i].dht_task =
258 GNUNET_SCHEDULER_add_now (&gather_dht_put_blocks, &operators[i]);
259 i++;
260 }
261}
262
263
264/**
265 * Shutdown the module.
266 */
267void
268GSF_put_done_ ()
269{
270 struct PutOperator *po;
271 unsigned int i;
272
273 i = 0;
274 while ((po = &operators[i])->dht_put_type != GNUNET_BLOCK_TYPE_ANY)
275 {
276 if (NULL != po->dht_task)
277 {
278 GNUNET_SCHEDULER_cancel (po->dht_task);
279 po->dht_task = NULL;
280 }
281 if (NULL != po->dht_put)
282 {
283 GNUNET_DHT_put_cancel (po->dht_put);
284 po->dht_put = NULL;
285 }
286 if (NULL != po->dht_qe)
287 {
288 GNUNET_DATASTORE_cancel (po->dht_qe);
289 po->dht_qe = NULL;
290 }
291 i++;
292 }
293}
294
295
296/* end of gnunet-service-fs_put.c */
diff --git a/src/fs/gnunet-service-fs_put.h b/src/fs/gnunet-service-fs_put.h
deleted file mode 100644
index b6c9ba86f..000000000
--- a/src/fs/gnunet-service-fs_put.h
+++ /dev/null
@@ -1,46 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/gnunet-service-fs_put.h
23 * @brief support for putting content into the DHT
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_FS_PUT_H
27#define GNUNET_SERVICE_FS_PUT_H
28
29#include "gnunet-service-fs.h"
30
31
32/**
33 * Setup the module.
34 */
35void
36GSF_put_init_ (void);
37
38
39/**
40 * Shutdown the module.
41 */
42void
43GSF_put_done_ (void);
44
45
46#endif
diff --git a/src/fs/gnunet-unindex.c b/src/fs/gnunet-unindex.c
deleted file mode 100644
index afc547127..000000000
--- a/src/fs/gnunet-unindex.c
+++ /dev/null
@@ -1,205 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-unindex.c
22 * @brief unindex files published on GNUnet
23 * @author Christian Grothoff
24 * @author Krista Bennett
25 * @author James Blackwell
26 * @author Igor Wronsky
27 */
28#include "platform.h"
29#include "gnunet_fs_service.h"
30
31static int ret;
32
33static unsigned int verbose;
34
35static const struct GNUNET_CONFIGURATION_Handle *cfg;
36
37static struct GNUNET_FS_Handle *ctx;
38
39static struct GNUNET_FS_UnindexContext *uc;
40
41
42static void
43cleanup_task (void *cls)
44{
45 GNUNET_FS_stop (ctx);
46 ctx = NULL;
47}
48
49
50static void
51shutdown_task (void *cls)
52{
53 struct GNUNET_FS_UnindexContext *u;
54
55 if (uc != NULL)
56 {
57 u = uc;
58 uc = NULL;
59 GNUNET_FS_unindex_stop (u);
60 }
61}
62
63
64/**
65 * Called by FS client to give information about the progress of an
66 * operation.
67 *
68 * @param cls closure
69 * @param info details about the event, specifying the event type
70 * and various bits about the event
71 * @return client-context (for the next progress call
72 * for this operation; should be set to NULL for
73 * SUSPEND and STOPPED events). The value returned
74 * will be passed to future callbacks in the respective
75 * field in the GNUNET_FS_ProgressInfo struct.
76 */
77static void *
78progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
79{
80 const char *s;
81
82 switch (info->status)
83 {
84 case GNUNET_FS_STATUS_UNINDEX_START:
85 break;
86
87 case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
88 if (verbose)
89 {
90 s = GNUNET_STRINGS_relative_time_to_string (info->value.unindex.eta,
91 GNUNET_YES);
92 fprintf (stdout,
93 _ ("Unindexing at %llu/%llu (%s remaining)\n"),
94 (unsigned long long) info->value.unindex.completed,
95 (unsigned long long) info->value.unindex.size,
96 s);
97 }
98 break;
99
100 case GNUNET_FS_STATUS_UNINDEX_ERROR:
101 fprintf (stderr,
102 _ ("Error unindexing: %s.\n"),
103 info->value.unindex.specifics.error.message);
104 GNUNET_SCHEDULER_shutdown ();
105 break;
106
107 case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
108 fprintf (stdout, "%s", _ ("Unindexing done.\n"));
109 GNUNET_SCHEDULER_shutdown ();
110 break;
111
112 case GNUNET_FS_STATUS_UNINDEX_STOPPED:
113 GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
114 break;
115
116 default:
117 fprintf (stderr, _ ("Unexpected status: %d\n"), info->status);
118 break;
119 }
120 return NULL;
121}
122
123
124/**
125 * Main function that will be run by the scheduler.
126 *
127 * @param cls closure
128 * @param args remaining command-line arguments
129 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
130 * @param c configuration
131 */
132static void
133run (void *cls,
134 char *const *args,
135 const char *cfgfile,
136 const struct GNUNET_CONFIGURATION_Handle *c)
137{
138 /* check arguments */
139 if ((args[0] == NULL) || (args[1] != NULL))
140 {
141 printf (_ ("You must specify one and only one filename for unindexing.\n"));
142 ret = -1;
143 return;
144 }
145 cfg = c;
146 ctx = GNUNET_FS_start (cfg,
147 "gnunet-unindex",
148 &progress_cb,
149 NULL,
150 GNUNET_FS_FLAGS_NONE,
151 GNUNET_FS_OPTIONS_END);
152 if (NULL == ctx)
153 {
154 fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS");
155 ret = 1;
156 return;
157 }
158 uc = GNUNET_FS_unindex_start (ctx, args[0], NULL);
159 if (NULL == uc)
160 {
161 fprintf (stderr, "%s", _ ("Could not start unindex operation.\n"));
162 GNUNET_FS_stop (ctx);
163 return;
164 }
165 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
166}
167
168
169/**
170 * The main function to unindex content.
171 *
172 * @param argc number of arguments from the command line
173 * @param argv command line arguments
174 * @return 0 ok, 1 on error
175 */
176int
177main (int argc, char *const *argv)
178{
179 struct GNUNET_GETOPT_CommandLineOption options[] = {
180 GNUNET_GETOPT_option_verbose (&verbose),
181
182 GNUNET_GETOPT_OPTION_END
183 };
184
185 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
186 return 2;
187
188 ret = (GNUNET_OK ==
189 GNUNET_PROGRAM_run (
190 argc,
191 argv,
192 "gnunet-unindex [OPTIONS] FILENAME",
193 gettext_noop (
194 "Unindex a file that was previously indexed with gnunet-publish."),
195 options,
196 &run,
197 NULL))
198 ? ret
199 : 1;
200 GNUNET_free_nz ((void *) argv);
201 return ret;
202}
203
204
205/* end of gnunet-unindex.c */
diff --git a/src/fs/perf_gnunet_service_fs_p2p.c b/src/fs/perf_gnunet_service_fs_p2p.c
deleted file mode 100644
index 062f062b5..000000000
--- a/src/fs/perf_gnunet_service_fs_p2p.c
+++ /dev/null
@@ -1,368 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/perf_gnunet_service_fs_p2p.c
23 * @brief profile P2P routing using simple publish + download operation
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "fs_test_lib.h"
28#include "gnunet_testbed_service.h"
29
30#define VERBOSE GNUNET_NO
31
32/**
33 * File-size we use for testing.
34 */
35#define FILESIZE (1024 * 1024 * 10)
36
37/**
38 * How long until we give up on transmitting the message?
39 */
40#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
41
42#define NUM_DAEMONS 2
43
44#define SEED 42
45
46static struct GNUNET_TESTBED_Peer *daemons[NUM_DAEMONS];
47
48static int ok;
49
50static struct GNUNET_TIME_Absolute start_time;
51
52static const char *progname;
53
54static struct GNUNET_TIME_Absolute start_time;
55
56
57/**
58 * Master context for 'stat_run'.
59 */
60struct StatMaster
61{
62 struct GNUNET_STATISTICS_Handle *stat;
63 struct GNUNET_TESTBED_Operation *op;
64 unsigned int daemon;
65 unsigned int value;
66};
67
68struct StatValues
69{
70 const char *subsystem;
71 const char *name;
72};
73
74/**
75 * Statistics we print out.
76 */
77static struct StatValues stats[] = {
78 { "fs", "# queries forwarded" },
79 { "fs", "# replies received and matched" },
80 { "fs", "# results found locally" },
81 { "fs", "# requests forwarded due to high load" },
82 { "fs", "# requests done for free (low load)" },
83 { "fs", "# requests dropped, priority insufficient" },
84 { "fs", "# requests done for a price (normal load)" },
85 { "fs", "# requests dropped by datastore (queue length limit)" },
86 { "fs", "# P2P searches received" },
87 { "fs", "# P2P searches discarded (queue length bound)" },
88 { "fs", "# replies received for local clients" },
89 { "fs", "# queries retransmitted to same target" },
90 { "core", "# bytes decrypted" },
91 { "core", "# bytes encrypted" },
92 { "core", "# discarded CORE_SEND requests" },
93 { "core", "# discarded CORE_SEND request bytes" },
94 { "core", "# discarded lower priority CORE_SEND requests" },
95 { "core", "# discarded lower priority CORE_SEND request bytes" },
96 { "transport", "# bytes received via TCP" },
97 { "transport", "# bytes transmitted via TCP" },
98 { "datacache", "# bytes stored" },
99 { NULL, NULL }
100};
101
102
103/**
104 * Callback function to process statistic values.
105 *
106 * @param cls closure
107 * @param subsystem name of subsystem that created the statistic
108 * @param name the name of the datum
109 * @param value the current value
110 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
111 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
112 */
113static int
114print_stat (void *cls, const char *subsystem, const char *name, uint64_t value,
115 int is_persistent)
116{
117 struct StatMaster *sm = cls;
118
119 fprintf (stderr,
120 "Peer %2u: %12s/%50s = %12llu\n",
121 sm->daemon,
122 subsystem,
123 name,
124 (unsigned long long) value);
125 return GNUNET_OK;
126}
127
128
129/**
130 * Function that gathers stats from all daemons.
131 */
132static void
133stat_run (void *cls,
134 struct GNUNET_TESTBED_Operation *op,
135 void *ca_result,
136 const char *emsg);
137
138
139/**
140 * Function called when GET operation on stats is done.
141 */
142static void
143get_done (void *cls, int success)
144{
145 struct StatMaster *sm = cls;
146
147 GNUNET_break (GNUNET_OK == success);
148 sm->value++;
149 stat_run (sm, sm->op, sm->stat, NULL);
150}
151
152
153/**
154 * Adapter function called to establish a connection to
155 * statistics service.
156 *
157 * @param cls closure
158 * @param cfg configuration of the peer to connect to; will be available until
159 * GNUNET_TESTBED_operation_done() is called on the operation returned
160 * from GNUNET_TESTBED_service_connect()
161 * @return service handle to return in 'op_result', NULL on error
162 */
163static void *
164statistics_connect_adapter (void *cls,
165 const struct GNUNET_CONFIGURATION_Handle *cfg)
166{
167 return GNUNET_STATISTICS_create ("<driver>",
168 cfg);
169}
170
171
172/**
173 * Adapter function called to destroy a connection to
174 * statistics service.
175 *
176 * @param cls closure
177 * @param op_result service handle returned from the connect adapter
178 */
179static void
180statistics_disconnect_adapter (void *cls,
181 void *op_result)
182{
183 GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
184}
185
186
187/**
188 * Function that gathers stats from all daemons.
189 */
190static void
191stat_run (void *cls,
192 struct GNUNET_TESTBED_Operation *op,
193 void *ca_result,
194 const char *emsg)
195{
196 struct StatMaster *sm = cls;
197
198 if (NULL != emsg)
199 {
200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
201 "Failed to statistics service: %s\n",
202 emsg);
203 GNUNET_SCHEDULER_shutdown ();
204 return;
205 }
206 sm->stat = ca_result;
207
208 if (stats[sm->value].name != NULL)
209 {
210 GNUNET_STATISTICS_get (sm->stat,
211#if 0
212 NULL, NULL,
213#else
214 stats[sm->value].subsystem, stats[sm->value].name,
215#endif
216 &get_done, &print_stat,
217 sm);
218 return;
219 }
220 GNUNET_TESTBED_operation_done (sm->op);
221 sm->value = 0;
222 sm->daemon++;
223 if (NUM_DAEMONS == sm->daemon)
224 {
225 GNUNET_free (sm);
226 GNUNET_SCHEDULER_shutdown ();
227 return;
228 }
229 sm->op =
230 GNUNET_TESTBED_service_connect (NULL,
231 daemons[sm->daemon],
232 "statistics",
233 &stat_run, sm,
234 &statistics_connect_adapter,
235 &statistics_disconnect_adapter,
236 NULL);
237}
238
239
240static void
241do_report (void *cls)
242{
243 char *fn = cls;
244 struct GNUNET_TIME_Relative del;
245 char *fancy;
246 struct StatMaster *sm;
247
248 if (NULL != fn)
249 {
250 GNUNET_DISK_directory_remove (fn);
251 GNUNET_free (fn);
252 }
253 if (0 ==
254 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
255 TIMEOUT)).
256 rel_value_us)
257 {
258 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
259 "Timeout during download, shutting down with error\n");
260 ok = 1;
261 GNUNET_SCHEDULER_shutdown ();
262 return;
263 }
264
265 del = GNUNET_TIME_absolute_get_duration (start_time);
266 if (del.rel_value_us == 0)
267 del.rel_value_us = 1;
268 fancy =
269 GNUNET_STRINGS_byte_size_fancy (((unsigned long long) FILESIZE)
270 * 1000000LL / del.rel_value_us);
271 fprintf (stdout,
272 "Download speed was %s/s\n",
273 fancy);
274 GNUNET_free (fancy);
275 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
276 "Finished download, shutting down\n");
277 sm = GNUNET_new (struct StatMaster);
278 sm->op =
279 GNUNET_TESTBED_service_connect (NULL,
280 daemons[sm->daemon],
281 "statistics",
282 &stat_run, sm,
283 &statistics_connect_adapter,
284 &statistics_disconnect_adapter,
285 NULL);
286}
287
288
289static void
290do_download (void *cls,
291 const struct GNUNET_FS_Uri *uri,
292 const char *fn)
293{
294 int anonymity;
295
296 if (NULL == uri)
297 {
298 GNUNET_SCHEDULER_shutdown ();
299 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
300 "Timeout during upload attempt, shutting down with error\n");
301 ok = 1;
302 return;
303 }
304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Downloading %llu bytes\n",
305 (unsigned long long) FILESIZE);
306 start_time = GNUNET_TIME_absolute_get ();
307 if (NULL != strstr (progname, "dht"))
308 anonymity = 0;
309 else
310 anonymity = 1;
311 start_time = GNUNET_TIME_absolute_get ();
312 GNUNET_FS_TEST_download (daemons[0],
313 TIMEOUT,
314 anonymity,
315 SEED,
316 uri,
317 VERBOSE,
318 &do_report,
319 (NULL == fn) ? NULL : GNUNET_strdup (fn));
320}
321
322
323static void
324do_publish (void *cls,
325 struct GNUNET_TESTBED_RunHandle *h,
326 unsigned int num_peers,
327 struct GNUNET_TESTBED_Peer **peers,
328 unsigned int links_succeeded,
329 unsigned int links_failed)
330{
331 unsigned int i;
332 int do_index;
333 int anonymity;
334
335 GNUNET_assert (NUM_DAEMONS == num_peers);
336 for (i = 0; i < num_peers; i++)
337 daemons[i] = peers[i];
338 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
339 (unsigned long long) FILESIZE);
340 if (NULL != strstr (progname, "index"))
341 do_index = GNUNET_YES;
342 else
343 do_index = GNUNET_NO;
344 if (NULL != strstr (progname, "dht"))
345 anonymity = 0;
346 else
347 anonymity = 1;
348 GNUNET_FS_TEST_publish (daemons[NUM_DAEMONS - 1], TIMEOUT, anonymity,
349 do_index, FILESIZE, SEED, VERBOSE, &do_download,
350 NULL);
351}
352
353
354int
355main (int argc, char *argv[])
356{
357 progname = argv[0];
358 (void) GNUNET_TESTBED_test_run ("perf-gnunet-service-fs-p2p",
359 "perf_gnunet_service_fs_p2p.conf",
360 NUM_DAEMONS,
361 0, NULL, NULL,
362 &do_publish, NULL);
363 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-fs-lib/");
364 return ok;
365}
366
367
368/* end of perf_gnunet_service_fs_p2p.c */
diff --git a/src/fs/perf_gnunet_service_fs_p2p.conf b/src/fs/perf_gnunet_service_fs_p2p.conf
deleted file mode 100644
index 00f0f512e..000000000
--- a/src/fs/perf_gnunet_service_fs_p2p.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ fs_test_lib_data.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-fs-test-lib/
4
5[fs]
6GAUGER_HEAP = "2-peer 10 MB P2P download"
7# PREFIX = valgrind
diff --git a/src/fs/perf_gnunet_service_fs_p2p_respect.c b/src/fs/perf_gnunet_service_fs_p2p_respect.c
deleted file mode 100644
index c48db2383..000000000
--- a/src/fs/perf_gnunet_service_fs_p2p_respect.c
+++ /dev/null
@@ -1,479 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2011, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/perf_gnunet_service_fs_p2p_respect.c
23 * @brief profile P2P routing respect mechanism. Creates
24 * a clique of NUM_DAEMONS (at least 3) where two
25 * peers share (seed) different files and download
26 * them from each other while all the other peers
27 * just "leach" those files. Ideally, the seeders
28 * "learn" that they contribute (to each other),
29 * and give the other seeder higher priority;
30 * naturally, this only happens nicely for larger
31 * files; finally, once the seeders are done, the
32 * leachers should see fast download rates as well.
33 * @author Christian Grothoff
34 *
35 * Sample output:
36 * - 10 MB, 3 peers, with delays:
37 * Download speed of type `seeder 1' was 757 KiB/s
38 * Download speed of type `seeder 2' was 613 KiB/s
39 * Download speed of type `leach` was 539 KiB/s
40 *
41 * - 10 MB, 3 peers, without delays:
42 * Download speed of type `seeder 1' was 1784 KiB/s
43 * Download speed of type `seeder 2' was 1604 KiB/s
44 * Download speed of type `leach` was 1384 KiB/s
45 */
46#include "platform.h"
47#include "fs_test_lib.h"
48#include "gnunet_testbed_service.h"
49
50#define VERBOSE GNUNET_NO
51
52/**
53 * File-size we use for testing.
54 */
55#define FILESIZE (1024 * 1024 * 1)
56
57/**
58 * How long until we give up on transmitting the message?
59 */
60#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
61
62/**
63 * Number of daemons in clique, must be at least 3 (!).
64 */
65#define NUM_DAEMONS 3
66
67/**
68 * Seed for first file on offer.
69 */
70#define SEED1 42
71
72/**
73 * Seed for second file on offer.
74 */
75#define SEED2 43
76
77static struct GNUNET_TESTBED_Peer *daemons[NUM_DAEMONS];
78
79static int ok;
80
81static struct GNUNET_TIME_Absolute start_time;
82
83static const char *progname;
84
85static struct GNUNET_FS_Uri *uri1;
86
87static struct GNUNET_FS_Uri *uri2;
88
89static char *fn1;
90
91static char *fn2;
92
93/**
94 * Master context for 'stat_run'.
95 */
96struct StatMaster
97{
98 struct GNUNET_STATISTICS_Handle *stat;
99 struct GNUNET_TESTBED_Operation *op;
100 unsigned int daemon;
101 unsigned int value;
102};
103
104struct StatValues
105{
106 const char *subsystem;
107 const char *name;
108};
109
110/**
111 * Statistics we print out.
112 */
113static struct StatValues stats[] = {
114 { "fs", "# artificial delays introduced (ms)" },
115 { "fs", "# queries forwarded" },
116 { "fs", "# replies received and matched" },
117 { "fs", "# results found locally" },
118 { "fs", "# requests forwarded due to high load" },
119 { "fs", "# requests done for free (low load)" },
120 { "fs", "# requests dropped, priority insufficient" },
121 { "fs", "# requests done for a price (normal load)" },
122 { "fs", "# requests dropped by datastore (queue length limit)" },
123 { "fs", "# P2P searches received" },
124 { "fs", "# P2P searches discarded (queue length bound)" },
125 { "fs", "# replies received for local clients" },
126 { "fs", "# queries retransmitted to same target" },
127 { "core", "# bytes decrypted" },
128 { "core", "# bytes encrypted" },
129 { "core", "# discarded CORE_SEND requests" },
130 { "core", "# discarded lower priority CORE_SEND requests" },
131 { "transport", "# bytes received via TCP" },
132 { "transport", "# bytes transmitted via TCP" },
133 { "datacache", "# bytes stored" },
134 { NULL, NULL }
135};
136
137
138static void
139cleanup ()
140{
141 GNUNET_SCHEDULER_shutdown ();
142 if (NULL != fn1)
143 {
144 GNUNET_DISK_directory_remove (fn1);
145 GNUNET_free (fn1);
146 }
147 if (NULL != fn2)
148 {
149 GNUNET_DISK_directory_remove (fn2);
150 GNUNET_free (fn2);
151 }
152}
153
154
155/**
156 * Callback function to process statistic values.
157 *
158 * @param cls closure
159 * @param subsystem name of subsystem that created the statistic
160 * @param name the name of the datum
161 * @param value the current value
162 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
163 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
164 */
165static int
166print_stat (void *cls, const char *subsystem, const char *name, uint64_t value,
167 int is_persistent)
168{
169 struct StatMaster *sm = cls;
170
171 fprintf (stderr, "Peer %2u: %12s/%50s = %12llu\n", sm->daemon, subsystem,
172 name, (unsigned long long) value);
173 return GNUNET_OK;
174}
175
176
177/**
178 * Function that gathers stats from all daemons.
179 */
180static void
181stat_run (void *cls,
182 struct GNUNET_TESTBED_Operation *op,
183 void *ca_result,
184 const char *emsg);
185
186
187/**
188 * Function called when GET operation on stats is done.
189 */
190static void
191get_done (void *cls, int success)
192{
193 struct StatMaster *sm = cls;
194
195 GNUNET_break (GNUNET_OK == success);
196 sm->value++;
197 stat_run (sm, sm->op, sm->stat, NULL);
198}
199
200
201/**
202 * Adapter function called to establish a connection to
203 * statistics service.
204 *
205 * @param cls closure
206 * @param cfg configuration of the peer to connect to; will be available until
207 * GNUNET_TESTBED_operation_done() is called on the operation returned
208 * from GNUNET_TESTBED_service_connect()
209 * @return service handle to return in 'op_result', NULL on error
210 */
211static void *
212statistics_connect_adapter (void *cls,
213 const struct GNUNET_CONFIGURATION_Handle *cfg)
214{
215 return GNUNET_STATISTICS_create ("<driver>",
216 cfg);
217}
218
219
220/**
221 * Adapter function called to destroy a connection to
222 * statistics service.
223 *
224 * @param cls closure
225 * @param op_result service handle returned from the connect adapter
226 */
227static void
228statistics_disconnect_adapter (void *cls,
229 void *op_result)
230{
231 GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
232}
233
234
235/**
236 * Function that gathers stats from all daemons.
237 */
238static void
239stat_run (void *cls,
240 struct GNUNET_TESTBED_Operation *op,
241 void *ca_result,
242 const char *emsg)
243{
244 struct StatMaster *sm = cls;
245
246 sm->stat = ca_result;
247 GNUNET_assert (NULL != sm->stat);
248 if (NULL != stats[sm->value].name)
249 {
250 GNUNET_STATISTICS_get (sm->stat,
251#if 0
252 NULL, NULL,
253#else
254 stats[sm->value].subsystem, stats[sm->value].name,
255#endif
256 &get_done, &print_stat,
257 sm);
258 return;
259 }
260 GNUNET_TESTBED_operation_done (sm->op);
261 sm->value = 0;
262 sm->daemon++;
263 if (NUM_DAEMONS == sm->daemon)
264 {
265 GNUNET_free (sm);
266 cleanup ();
267 return;
268 }
269 sm->op =
270 GNUNET_TESTBED_service_connect (NULL,
271 daemons[sm->daemon],
272 "statistics",
273 &stat_run, sm,
274 &statistics_connect_adapter,
275 &statistics_disconnect_adapter,
276 NULL);
277}
278
279
280static void
281do_report (void *cls)
282{
283 static int download_counter;
284 const char *type = cls;
285 struct GNUNET_TIME_Relative del;
286 char *fancy;
287 struct StatMaster *sm;
288
289 if (0 ==
290 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
291 TIMEOUT)).
292 rel_value_us)
293 {
294 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
295 "Timeout during download for type `%s', shutting down with error\n",
296 type);
297 ok = 1;
298 cleanup ();
299 return;
300 }
301 del = GNUNET_TIME_absolute_get_duration (start_time);
302 if (del.rel_value_us == 0)
303 del.rel_value_us = 1;
304 fancy =
305 GNUNET_STRINGS_byte_size_fancy (((unsigned long long) FILESIZE)
306 * 1000000LL / del.rel_value_us);
307 fprintf (stderr, "Download speed of type `%s' was %s/s\n", type, fancy);
308 GNUNET_free (fancy);
309 if (NUM_DAEMONS != ++download_counter)
310 return; /* more downloads to come */
311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
312 "Finished all downloads, getting statistics\n");
313 sm = GNUNET_new (struct StatMaster);
314 sm->op =
315 GNUNET_TESTBED_service_connect (NULL,
316 daemons[sm->daemon],
317 "statistics",
318 &stat_run, sm,
319 &statistics_connect_adapter,
320 &statistics_disconnect_adapter,
321 NULL);
322}
323
324
325static void
326do_downloads (void *cls, const struct GNUNET_FS_Uri *u2,
327 const char *fn)
328{
329 int anonymity;
330 unsigned int i;
331
332 if (NULL == u2)
333 {
334 cleanup ();
335 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
336 "Timeout during upload attempt, shutting down with error\n");
337 ok = 1;
338 return;
339 }
340 if (NULL != fn)
341 fn2 = GNUNET_strdup (fn);
342 uri2 = GNUNET_FS_uri_dup (u2);
343 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Downloading %llu bytes\n",
344 (unsigned long long) FILESIZE);
345 start_time = GNUNET_TIME_absolute_get ();
346 if (NULL != strstr (progname, "dht"))
347 anonymity = 0;
348 else
349 anonymity = 1;
350 /* (semi) leach-download(s); not true leaches since
351 * these peers do participate in sharing, they just
352 * don't have to offer anything *initially*. */
353 for (i = 0; i < NUM_DAEMONS - 2; i++)
354 GNUNET_FS_TEST_download (daemons[i], TIMEOUT, anonymity,
355 0 == (i % 2) ? SEED1 : SEED2,
356 0 == (i % 2) ? uri1 : uri2, VERBOSE, &do_report,
357 "leach");
358 /* mutual downloads of (primary) sharing peers */
359 GNUNET_FS_TEST_download (daemons[NUM_DAEMONS - 2], TIMEOUT, anonymity, SEED1,
360 uri1, VERBOSE, &do_report, "seeder 2");
361 GNUNET_FS_TEST_download (daemons[NUM_DAEMONS - 1], TIMEOUT, anonymity, SEED2,
362 uri2, VERBOSE, &do_report, "seeder 1");
363}
364
365
366static void
367do_publish2 (void *cls,
368 const struct GNUNET_FS_Uri *u1,
369 const char *fn)
370{
371 int do_index;
372 int anonymity;
373
374 if (NULL == u1)
375 {
376 cleanup ();
377 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
378 "Timeout during upload attempt, shutting down with error\n");
379 ok = 1;
380 return;
381 }
382 if (NULL != fn)
383 fn1 = GNUNET_strdup (fn);
384 uri1 = GNUNET_FS_uri_dup (u1);
385 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
386 (unsigned long long) FILESIZE);
387 if (NULL != strstr (progname, "index"))
388 do_index = GNUNET_YES;
389 else
390 do_index = GNUNET_NO;
391 if (NULL != strstr (progname, "dht"))
392 anonymity = 0;
393 else
394 anonymity = 1;
395
396 GNUNET_FS_TEST_publish (daemons[NUM_DAEMONS - 2], TIMEOUT, anonymity,
397 do_index, FILESIZE, SEED2, VERBOSE, &do_downloads,
398 NULL);
399}
400
401
402static void
403do_publish1 (void *cls,
404 struct GNUNET_TESTBED_Operation *op,
405 const char *emsg)
406{
407 unsigned int *coco = cls;
408 int do_index;
409 int anonymity;
410
411 GNUNET_TESTBED_operation_done (op);
412 if (NULL != emsg)
413 {
414 cleanup ();
415 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error trying to connect: %s\n", emsg);
416 ok = 1;
417 return;
418 }
419 if (0 != (--(*coco)))
420 return; /* more connections to be created */
421 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
422 (unsigned long long) FILESIZE);
423 if (NULL != strstr (progname, "index"))
424 do_index = GNUNET_YES;
425 else
426 do_index = GNUNET_NO;
427 if (NULL != strstr (progname, "dht"))
428 anonymity = 0;
429 else
430 anonymity = 1;
431 GNUNET_FS_TEST_publish (daemons[NUM_DAEMONS - 1], TIMEOUT, anonymity,
432 do_index, FILESIZE, SEED1, VERBOSE, &do_publish2,
433 NULL);
434}
435
436
437static void
438do_connect (void *cls,
439 struct GNUNET_TESTBED_RunHandle *h,
440 unsigned int num_peers,
441 struct GNUNET_TESTBED_Peer **peers,
442 unsigned int links_succeeded,
443 unsigned int links_failed)
444{
445 static unsigned int coco;
446 unsigned int i;
447 unsigned int j;
448
449 GNUNET_assert (NUM_DAEMONS == num_peers);
450 for (i = 0; i < num_peers; i++)
451 daemons[i] = peers[i];
452 for (i = 0; i < NUM_DAEMONS; i++)
453 for (j = i + 1; j < NUM_DAEMONS; j++)
454 {
455 coco++;
456 GNUNET_TESTBED_overlay_connect (NULL,
457 &do_publish1,
458 &coco,
459 peers[i],
460 peers[j]);
461 }
462}
463
464
465int
466main (int argc, char *argv[])
467{
468 progname = argv[0];
469 (void) GNUNET_TESTBED_test_run ("perf-gnunet-service-fs-p2p-respect",
470 "perf_gnunet_service_fs_p2p.conf",
471 NUM_DAEMONS,
472 0, NULL, NULL,
473 &do_connect, NULL);
474 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-fs-lib/");
475 return ok;
476}
477
478
479/* end of perf_gnunet_service_fs_p2p_respect.c */
diff --git a/src/fs/plugin_block_fs.c b/src/fs/plugin_block_fs.c
deleted file mode 100644
index 18aa289ef..000000000
--- a/src/fs/plugin_block_fs.c
+++ /dev/null
@@ -1,337 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010, 2013, 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/plugin_block_fs.c
23 * @brief blocks used for file-sharing
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_block_plugin.h"
28#include "gnunet_fs_service.h"
29#include "block_fs.h"
30#include "gnunet_signatures.h"
31#include "gnunet_block_group_lib.h"
32
33
34/**
35 * Number of bits we set per entry in the bloomfilter.
36 * Do not change!
37 */
38#define BLOOMFILTER_K 16
39
40
41/**
42 * Create a new block group.
43 *
44 * @param ctx block context in which the block group is created
45 * @param type type of the block for which we are creating the group
46 * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh
47 * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh
48 * @param va variable arguments specific to @a type
49 * @return block group handle, NULL if block groups are not supported
50 * by this @a type of block (this is not an error)
51 */
52static struct GNUNET_BLOCK_Group *
53block_plugin_fs_create_group (void *cls,
54 enum GNUNET_BLOCK_Type type,
55 const void *raw_data,
56 size_t raw_data_size,
57 va_list va)
58{
59 unsigned int size;
60 const char *guard;
61
62 switch (type)
63 {
64 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
65 GNUNET_break (NULL == va_arg (va, const char *));
66 return NULL;
67 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
68 GNUNET_break (NULL == va_arg (va, const char *));
69 return NULL;
70 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
71 guard = va_arg (va, const char *);
72 if (0 == strcmp (guard,
73 "seen-set-size"))
74 {
75 size = GNUNET_BLOCK_GROUP_compute_bloomfilter_size (va_arg (va, unsigned
76 int),
77 BLOOMFILTER_K);
78 }
79 else if (0 == strcmp (guard,
80 "filter-size"))
81 {
82 size = va_arg (va, unsigned int);
83 }
84 else
85 {
86 /* va-args invalid! bad bug, complain! */
87 GNUNET_break (0);
88 size = 8;
89 }
90 if (0 == size)
91 size = raw_data_size; /* not for us to determine, use what we got! */
92 GNUNET_break (NULL == va_arg (va, const char *));
93 return GNUNET_BLOCK_GROUP_bf_create (cls,
94 size,
95 BLOOMFILTER_K,
96 type,
97 raw_data,
98 raw_data_size);
99
100 default:
101 GNUNET_break (NULL == va_arg (va, const char *));
102 GNUNET_break (0);
103 return NULL;
104 }
105}
106
107
108/**
109 * Function called to obtain the key for a block.
110 *
111 * @param cls closure
112 * @param type block type
113 * @param block block to get the key for
114 * @param block_size number of bytes in @a block
115 * @param key set to the key (query) for the given block
116 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
117 * (or if extracting a key from a block of this type does not work)
118 */
119static enum GNUNET_GenericReturnValue
120block_plugin_fs_get_key (void *cls,
121 enum GNUNET_BLOCK_Type type,
122 const void *block,
123 size_t block_size,
124 struct GNUNET_HashCode *key)
125{
126 const struct UBlock *ub;
127
128 switch (type)
129 {
130 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
131 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
132 GNUNET_CRYPTO_hash (block,
133 block_size,
134 key);
135 return GNUNET_OK;
136 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
137 if (block_size < sizeof(struct UBlock))
138 {
139 GNUNET_break_op (0);
140 memset (key,
141 0,
142 sizeof (*key));
143 return GNUNET_OK;
144 }
145 ub = block;
146 GNUNET_CRYPTO_hash (&ub->verification_key,
147 sizeof(ub->verification_key),
148 key);
149 return GNUNET_OK;
150 default:
151 GNUNET_break (0);
152 return GNUNET_SYSERR;
153 }
154}
155
156
157/**
158 * Function called to validate a query.
159 *
160 * @param cls closure
161 * @param ctx block context
162 * @param type block type
163 * @param query original query (hash)
164 * @param xquery extrended query data (can be NULL, depending on type)
165 * @param xquery_size number of bytes in @a xquery
166 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
167 */
168static enum GNUNET_GenericReturnValue
169block_plugin_fs_check_query (void *cls,
170 enum GNUNET_BLOCK_Type type,
171 const struct GNUNET_HashCode *query,
172 const void *xquery,
173 size_t xquery_size)
174{
175 switch (type)
176 {
177 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
178 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
179 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
180 if (0 != xquery_size)
181 {
182 GNUNET_break_op (0);
183 return GNUNET_NO;
184 }
185 return GNUNET_OK;
186 default:
187 GNUNET_break (0);
188 return GNUNET_SYSERR;
189 }
190}
191
192
193/**
194 * Function called to validate a block for storage.
195 *
196 * @param cls closure
197 * @param type block type
198 * @param block block data to validate
199 * @param block_size number of bytes in @a block
200 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
201 */
202static enum GNUNET_GenericReturnValue
203block_plugin_fs_check_block (void *cls,
204 enum GNUNET_BLOCK_Type type,
205 const void *block,
206 size_t block_size)
207{
208 switch (type)
209 {
210 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
211 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
212 return GNUNET_OK;
213 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
214 {
215 const struct UBlock *ub;
216
217 if (block_size < sizeof(struct UBlock))
218 {
219 GNUNET_break_op (0);
220 return GNUNET_NO;
221 }
222 ub = block;
223 if (block_size !=
224 ntohl (ub->purpose.size)
225 + sizeof (struct GNUNET_CRYPTO_EcdsaSignature))
226 {
227 GNUNET_break_op (0);
228 return GNUNET_NO;
229 }
230 if (GNUNET_OK !=
231 GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_FS_UBLOCK,
232 &ub->purpose,
233 &ub->signature,
234 &ub->verification_key))
235 {
236 GNUNET_break_op (0);
237 return GNUNET_NO;
238 }
239 return GNUNET_OK;
240 }
241 default:
242 GNUNET_break (0);
243 return GNUNET_SYSERR;
244 }
245}
246
247
248/**
249 * Function called to validate a reply to a request. Note that it is assumed
250 * that the reply has already been matched to the key (and signatures checked)
251 * as it would be done with the GetKeyFunction and the
252 * BlockEvaluationFunction.
253 *
254 * @param cls closure
255 * @param type block type
256 * @param group which block group to use for evaluation
257 * @param query original query (hash)
258 * @param xquery extrended query data (can be NULL, depending on type)
259 * @param xquery_size number of bytes in @a xquery
260 * @param reply_block response to validate
261 * @param reply_block_size number of bytes in @a reply_block
262 * @return characterization of result
263 */
264static enum GNUNET_BLOCK_ReplyEvaluationResult
265block_plugin_fs_check_reply (void *cls,
266 enum GNUNET_BLOCK_Type type,
267 struct GNUNET_BLOCK_Group *group,
268 const struct GNUNET_HashCode *query,
269 const void *xquery,
270 size_t xquery_size,
271 const void *reply_block,
272 size_t reply_block_size)
273{
274 switch (type)
275 {
276 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
277 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
278 return GNUNET_BLOCK_REPLY_OK_LAST;
279 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
280 {
281 struct GNUNET_HashCode chash;
282
283 GNUNET_CRYPTO_hash (reply_block,
284 reply_block_size,
285 &chash);
286 if (GNUNET_YES ==
287 GNUNET_BLOCK_GROUP_bf_test_and_set (group,
288 &chash))
289 return GNUNET_BLOCK_REPLY_OK_DUPLICATE;
290 return GNUNET_BLOCK_REPLY_OK_MORE;
291 }
292 default:
293 GNUNET_break (0);
294 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
295 }
296}
297
298
299/**
300 * Entry point for the plugin.
301 */
302void *
303libgnunet_plugin_block_fs_init (void *cls)
304{
305 static const enum GNUNET_BLOCK_Type types[] = {
306 GNUNET_BLOCK_TYPE_FS_DBLOCK,
307 GNUNET_BLOCK_TYPE_FS_IBLOCK,
308 GNUNET_BLOCK_TYPE_FS_UBLOCK,
309 GNUNET_BLOCK_TYPE_ANY /* end of list */
310 };
311 struct GNUNET_BLOCK_PluginFunctions *api;
312
313 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
314 api->get_key = &block_plugin_fs_get_key;
315 api->create_group = &block_plugin_fs_create_group;
316 api->check_query = &block_plugin_fs_check_query;
317 api->check_block = &block_plugin_fs_check_block;
318 api->check_reply = &block_plugin_fs_check_reply;
319 api->types = types;
320 return api;
321}
322
323
324/**
325 * Exit point from the plugin.
326 */
327void *
328libgnunet_plugin_block_fs_done (void *cls)
329{
330 struct GNUNET_BLOCK_PluginFunctions *api = cls;
331
332 GNUNET_free (api);
333 return NULL;
334}
335
336
337/* end of plugin_block_fs.c */
diff --git a/src/fs/test_fs.c b/src/fs/test_fs.c
deleted file mode 100644
index 7a57e98b0..000000000
--- a/src/fs/test_fs.c
+++ /dev/null
@@ -1,262 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs.c
23 * @brief testcase for FS (upload-search-download-unindex)
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util.h"
29#include "gnunet_fsui_lib.h"
30
31#define DEBUG_VERBOSE GNUNET_NO
32
33#define CHECK(a) if (! (a)) { ok = GNUNET_NO; GNUNET_GE_BREAK (NULL, 0); \
34 goto FAILURE; }
35
36static char *
37makeName (unsigned int i)
38{
39 char *fn;
40
41 fn = GNUNET_malloc (strlen ("/tmp/gnunet-basic_fsui_test/BASIC_FSUI_TEST")
42 + 14);
43 GNUNET_snprintf (fn,
44 strlen ("/tmp/gnunet-basic_fsui_test/BASIC_FSUI_TEST") + 14,
45 "/tmp/gnunet-basic_fsui_test/BASIC_FSUI_TEST%u", i);
46 GNUNET_disk_directory_create_for_file (NULL, fn);
47 return fn;
48}
49
50
51static enum GNUNET_FSUI_EventType lastEvent;
52
53static struct GNUNET_MetaData *search_meta;
54
55static struct GNUNET_ECRS_URI *search_uri;
56
57static struct GNUNET_FSUI_Context *ctx;
58
59static void *
60eventCallback (void *cls, const GNUNET_FSUI_Event *event)
61{
62 static char unused;
63
64 switch (event->type)
65 {
66 case GNUNET_FSUI_search_resumed:
67 case GNUNET_FSUI_download_resumed:
68 case GNUNET_FSUI_upload_resumed:
69 case GNUNET_FSUI_unindex_resumed:
70 return &unused;
71
72 case GNUNET_FSUI_search_result:
73#if DEBUG_VERBOSE
74 printf ("Received search result\n");
75#endif
76 search_uri = GNUNET_ECRS_uri_duplicate (event->data.SearchResult.fi.uri);
77 search_meta = GNUNET_meta_data_duplicate (event->data.SearchResult.fi.meta);
78 break;
79
80 case GNUNET_FSUI_upload_completed:
81#if DEBUG_VERBOSE
82 printf ("Upload complete.\n");
83#endif
84 break;
85
86 case GNUNET_FSUI_download_completed:
87#if DEBUG_VERBOSE
88 printf ("Download complete.\n");
89#endif
90 break;
91
92 case GNUNET_FSUI_unindex_completed:
93#if DEBUG_VERBOSE
94 printf ("Unindex complete.\n");
95#endif
96 break;
97
98 default:
99 break;
100 }
101 lastEvent = event->type;
102 return NULL;
103}
104
105
106#define START_DAEMON 1
107
108int
109main (int argc, char *argv[])
110{
111#if START_DAEMON
112 struct GNUNET_OS_Process *daemon;
113#endif
114 int ok;
115 struct GNUNET_ECRS_URI *uri;
116 char *filename = NULL;
117
118 char *keywords[] = {
119 "fsui_foo",
120 "fsui_bar",
121 };
122 char keyword[40];
123 char *fn;
124 int prog;
125 struct GNUNET_MetaData *meta;
126 struct GNUNET_ECRS_URI *kuri;
127 struct GNUNET_GC_Configuration *cfg;
128 struct GNUNET_FSUI_UploadList *upload = NULL;
129 struct GNUNET_FSUI_SearchList *search = NULL;
130 struct GNUNET_FSUI_UnindexList *unindex = NULL;
131 struct GNUNET_FSUI_DownloadList *download = NULL;
132
133 cfg = GNUNET_GC_create ();
134 if (-1 == GNUNET_GC_parse_configuration (cfg, "check.conf"))
135 {
136 GNUNET_GC_free (cfg);
137 return -1;
138 }
139#if START_DAEMON
140 daemon = GNUNET_daemon_start (NULL, cfg, "peer.conf", GNUNET_NO);
141 GNUNET_GE_ASSERT (NULL, daemon != NULL);
142 CHECK (GNUNET_OK ==
143 GNUNET_wait_for_daemon_running (NULL, cfg, 60 * GNUNET_CRON_SECONDS));
144#endif
145 GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS); /* give apps time to start */
146 ok = GNUNET_YES;
147
148 /* ACTUAL TEST CODE */
149 ctx = GNUNET_FSUI_start (NULL, cfg, "basic_fsui_test", 32, /* thread pool size */
150 GNUNET_NO, /* no resume */
151 &eventCallback, NULL);
152 CHECK (ctx != NULL);
153 filename = makeName (42);
154 GNUNET_disk_file_write (NULL, filename, "foo bar test!",
155 strlen ("foo bar test!"), "600");
156 meta = GNUNET_meta_data_create ();
157 kuri =
158 GNUNET_ECRS_keyword_command_line_to_uri (NULL, 2,
159 (const char **) keywords);
160 /* upload */
161 upload = GNUNET_FSUI_upload_start (ctx, filename,
162 (GNUNET_FSUI_DirectoryScanCallback)
163 & GNUNET_disk_directory_scan, NULL, 0, /* anonymity */
164 0, /* priority */
165 GNUNET_YES, GNUNET_NO, GNUNET_NO,
166 GNUNET_get_time () + 5 * GNUNET_CRON_HOURS,
167 meta, kuri, kuri);
168 CHECK (upload != NULL);
169 GNUNET_ECRS_uri_destroy (kuri);
170 GNUNET_meta_data_destroy (meta);
171 prog = 0;
172 while (lastEvent != GNUNET_FSUI_upload_completed)
173 {
174 prog++;
175 CHECK (prog < 10000) GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS);
176 if (GNUNET_shutdown_test () == GNUNET_YES)
177 break;
178 }
179
180 /* search */
181 GNUNET_snprintf (keyword, 40, "+%s +%s", keywords[0], keywords[1]);
182 uri = GNUNET_ECRS_keyword_string_to_uri (NULL, keyword);
183 search = GNUNET_FSUI_search_start (ctx, 0, uri);
184 GNUNET_ECRS_uri_destroy (uri);
185 CHECK (search != NULL);
186 prog = 0;
187 while (lastEvent != GNUNET_FSUI_search_result)
188 {
189 prog++;
190 CHECK (prog < 10000);
191 GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS);
192 if (GNUNET_shutdown_test () == GNUNET_YES)
193 break;
194 }
195 GNUNET_FSUI_search_abort (search);
196 GNUNET_FSUI_search_stop (search);
197
198 /* download */
199 fn = makeName (43);
200 download =
201 GNUNET_FSUI_download_start (ctx, 0, GNUNET_NO, search_uri, search_meta,
202 fn, NULL, NULL);
203 GNUNET_free (fn);
204 prog = 0;
205 while (lastEvent != GNUNET_FSUI_download_completed)
206 {
207 prog++;
208 CHECK (prog < 10000);
209 GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS);
210 if (GNUNET_shutdown_test () == GNUNET_YES)
211 break;
212 }
213 GNUNET_FSUI_download_stop (download);
214 download = NULL;
215 GNUNET_ECRS_uri_destroy (search_uri);
216 GNUNET_meta_data_destroy (search_meta);
217 /* unindex */
218 unindex = GNUNET_FSUI_unindex_start (ctx, filename);
219 prog = 0;
220 while (lastEvent != GNUNET_FSUI_unindex_completed)
221 {
222 prog++;
223 CHECK (prog < 10000);
224 GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS);
225 if (GNUNET_shutdown_test () == GNUNET_YES)
226 break;
227 }
228 if (lastEvent != GNUNET_FSUI_unindex_completed)
229 GNUNET_FSUI_unindex_abort (unindex);
230 GNUNET_FSUI_unindex_stop (unindex);
231
232
233 /* END OF TEST CODE */
234FAILURE:
235 if (ctx != NULL)
236 GNUNET_FSUI_stop (ctx);
237 if (filename != NULL)
238 {
239 unlink (filename);
240 GNUNET_free (filename);
241 }
242 if (download != NULL)
243 {
244 GNUNET_FSUI_download_abort (download);
245 GNUNET_FSUI_download_stop (download);
246 }
247 filename = makeName (43);
248 /* TODO: verify file 'filename(42)' == file 'filename(43)' */
249 unlink (filename);
250 GNUNET_free (filename);
251
252#if START_DAEMON
253 GNUNET_GE_ASSERT (NULL, GNUNET_OK == GNUNET_daemon_stop (NULL, daemon));
254 GNUNET_OS_process_destroy (daemon);
255#endif
256 GNUNET_GC_free (cfg);
257
258 return (ok == GNUNET_YES) ? 0 : 1;
259}
260
261
262/* end of test_fs.c */
diff --git a/src/fs/test_fs_data.conf b/src/fs/test_fs_data.conf
deleted file mode 100644
index 993141064..000000000
--- a/src/fs/test_fs_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-data/
4
5[fs]
6ACTIVEMIGRATION = NO
7
diff --git a/src/fs/test_fs_defaults.conf b/src/fs/test_fs_defaults.conf
deleted file mode 100644
index 6ead78257..000000000
--- a/src/fs/test_fs_defaults.conf
+++ /dev/null
@@ -1,47 +0,0 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-lib/
5
6[transport]
7PLUGINS = tcp
8
9[nat]
10DISABLEV6 = YES
11ENABLE_UPNP = NO
12BEHIND_NAT = NO
13ALLOW_NAT = NO
14INTERNAL_ADDRESS = 127.0.0.1
15EXTERNAL_ADDRESS = 127.0.0.1
16USE_LOCALADDR = YES
17RETURN_LOCAL_ADDRESSES = YES
18
19[datastore]
20QUOTA = 100 MB
21
22[transport-tcp]
23BINDTO = 127.0.0.1
24PORT = 54368
25
26[peerinfo]
27NO_IO = YES
28
29[ats]
30WAN_QUOTA_IN = 65536
31WAN_QUOTA_OUT = 65536
32
33[fs]
34CONTENT_CACHING = YES
35CONTENT_PUSHING = YES
36DELAY = YES
37# PREFIX = valgrind --leak-check=full
38
39[dhtcache]
40QUOTA=65536
41DATABASE=heap
42
43[cadet]
44REFRESH_PATH_TIME = 30 min
45
46[nse]
47WORKBITS = 0
diff --git a/src/fs/test_fs_directory.c b/src/fs/test_fs_directory.c
deleted file mode 100644
index 908e7c555..000000000
--- a/src/fs/test_fs_directory.c
+++ /dev/null
@@ -1,186 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2005, 2006, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_directory.c
22 * @brief Test for fs_directory.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#if HAVE_EXTRACTOR_H
27#include <extractor.h>
28#endif
29#include "gnunet_util_lib.h"
30#include "gnunet_fs_service.h"
31#include "fs_api.h"
32
33#define ABORT() { fprintf (stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
34 return 1; }
35
36struct PCLS
37{
38 struct GNUNET_FS_Uri **uri;
39 struct GNUNET_CONTAINER_MetaData **md;
40 unsigned int pos;
41 unsigned int max;
42};
43
44static void
45processor (void *cls, const char *filename, const struct GNUNET_FS_Uri *uri,
46 const struct GNUNET_CONTAINER_MetaData *md, size_t length,
47 const void *data)
48{
49 struct PCLS *p = cls;
50 int i;
51
52 if (NULL == uri)
53 return; /* ignore directory's meta data */
54 for (i = 0; i < p->max; i++)
55 {
56 if (GNUNET_CONTAINER_meta_data_test_equal (p->md[i], md) &&
57 GNUNET_FS_uri_test_equal (p->uri[i], uri))
58 {
59 p->pos++;
60 return;
61 }
62 }
63 fprintf (stderr, "Error at %s:%d\n", __FILE__, __LINE__);
64}
65
66
67static int
68testDirectory (unsigned int i)
69{
70 struct GNUNET_FS_DirectoryBuilder *db;
71 char *data;
72 size_t dlen;
73 struct GNUNET_FS_Uri **uris;
74 struct GNUNET_CONTAINER_MetaData **mds;
75 struct GNUNET_CONTAINER_MetaData *meta;
76 struct PCLS cls;
77 char *emsg;
78 int p;
79 int q;
80 char uri[512];
81 char txt[128];
82 int ret = 0;
83 struct GNUNET_TIME_Absolute start;
84 const char *s;
85
86 cls.max = i;
87 uris = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri *) * i);
88 mds = GNUNET_malloc (sizeof(struct GNUNET_CONTAINER_MetaData *) * i);
89 meta = GNUNET_CONTAINER_meta_data_create ();
90 GNUNET_CONTAINER_meta_data_insert (meta, "<test>", EXTRACTOR_METATYPE_TITLE,
91 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
92 "A title", strlen ("A title") + 1);
93 GNUNET_CONTAINER_meta_data_insert (meta, "<test>",
94 EXTRACTOR_METATYPE_AUTHOR_NAME,
95 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
96 "An author", strlen ("An author") + 1);
97 for (p = 0; p < i; p++)
98 {
99 mds[p] = GNUNET_CONTAINER_meta_data_create ();
100 for (q = 0; q <= p; q++)
101 {
102 GNUNET_snprintf (txt, sizeof(txt), "%u -- %u\n", p, q);
103 GNUNET_CONTAINER_meta_data_insert (mds[p], "<test>",
104#if HAVE_EXTRACTOR_H && HAVE_LIBEXTRACTOR
105 q % EXTRACTOR_metatype_get_max (),
106#else
107 q % 128,
108#endif
109 EXTRACTOR_METAFORMAT_UTF8,
110 "text/plain", txt, strlen (txt) + 1);
111 }
112 GNUNET_snprintf (uri, sizeof(uri),
113 "gnunet://fs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.%u",
114 p);
115 emsg = NULL;
116 uris[p] = GNUNET_FS_uri_parse (uri, &emsg);
117 if (uris[p] == NULL)
118 {
119 GNUNET_CONTAINER_meta_data_destroy (mds[p]);
120 while (--p > 0)
121 {
122 GNUNET_CONTAINER_meta_data_destroy (mds[p]);
123 GNUNET_FS_uri_destroy (uris[p]);
124 }
125 GNUNET_free (mds);
126 GNUNET_free (uris);
127 GNUNET_free (emsg);
128 GNUNET_CONTAINER_meta_data_destroy (meta);
129 ABORT (); /* error in testcase */
130 }
131 GNUNET_assert (emsg == NULL);
132 }
133 start = GNUNET_TIME_absolute_get ();
134 db = GNUNET_FS_directory_builder_create (meta);
135 for (p = 0; p < i; p++)
136 GNUNET_FS_directory_builder_add (db, uris[p], mds[p], NULL);
137 GNUNET_FS_directory_builder_finish (db, &dlen, (void **) &data);
138 s = GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration
139 (start),
140 GNUNET_YES);
141 fprintf (stdout,
142 "Creating directory with %u entries and total size %llu took %s\n",
143 i, (unsigned long long) dlen, s);
144 if (i < 100)
145 {
146 cls.pos = 0;
147 cls.uri = uris;
148 cls.md = mds;
149 GNUNET_FS_directory_list_contents (dlen, data, 0, &processor, &cls);
150 GNUNET_assert (cls.pos == i);
151 }
152 GNUNET_free (data);
153 GNUNET_CONTAINER_meta_data_destroy (meta);
154 for (p = 0; p < i; p++)
155 {
156 GNUNET_CONTAINER_meta_data_destroy (mds[p]);
157 GNUNET_FS_uri_destroy (uris[p]);
158 }
159 GNUNET_free (uris);
160 GNUNET_free (mds);
161 return ret;
162}
163
164
165int
166main (int argc, char *argv[])
167{
168 int failureCount = 0;
169 int i;
170
171 GNUNET_log_setup ("test_fs_directory",
172#if VERBOSE
173 "DEBUG",
174#else
175 "WARNING",
176#endif
177 NULL);
178 for (i = 17; i < 1000; i *= 2)
179 failureCount += testDirectory (i);
180 if (failureCount != 0)
181 return 1;
182 return 0;
183}
184
185
186/* end of test_fs_directory.c */
diff --git a/src/fs/test_fs_download.c b/src/fs/test_fs_download.c
deleted file mode 100644
index 2fe0da77c..000000000
--- a/src/fs/test_fs_download.c
+++ /dev/null
@@ -1,368 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008, 2009, 2011, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_download.c
23 * @brief simple testcase for simple publish + download operation
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_fs_service.h"
30#include "gnunet_testing_lib.h"
31#include <gauger.h>
32
33/**
34 * File-size we use for testing.
35 */
36#define FILESIZE (1024 * 1024 * 2)
37
38/**
39 * How long until we give up on transmitting the message?
40 */
41#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
42
43/**
44 * How long should our test-content live?
45 */
46#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
47
48static unsigned int anonymity_level;
49
50static int indexed;
51
52static struct GNUNET_TIME_Absolute start;
53
54static struct GNUNET_FS_Handle *fs;
55
56static struct GNUNET_FS_DownloadContext *download;
57
58static struct GNUNET_FS_PublishContext *publish;
59
60static struct GNUNET_SCHEDULER_Task *timeout_kill;
61
62static char *fn;
63
64static char *fn1;
65
66static int err;
67
68
69static void
70timeout_kill_task (void *cls)
71{
72 if (NULL != download)
73 {
74 GNUNET_FS_download_stop (download, GNUNET_YES);
75 download = NULL;
76 }
77 else if (NULL != publish)
78 {
79 GNUNET_FS_publish_stop (publish);
80 publish = NULL;
81 }
82 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Timeout downloading file\n");
83 timeout_kill = NULL;
84 err = 1;
85}
86
87
88static void
89abort_publish_task (void *cls)
90{
91 if (NULL != publish)
92 {
93 GNUNET_FS_publish_stop (publish);
94 publish = NULL;
95 }
96}
97
98
99static void
100stop_fs_task (void *cls)
101{
102 GNUNET_FS_stop (fs);
103 fs = NULL;
104}
105
106
107static void
108abort_download_task (void *cls)
109{
110 uint64_t size;
111
112 if (NULL != download)
113 {
114 GNUNET_FS_download_stop (download, GNUNET_YES);
115 download = NULL;
116 }
117 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_size (fn, &size, GNUNET_YES,
118 GNUNET_NO));
119 GNUNET_assert (size == FILESIZE);
120 GNUNET_DISK_directory_remove (fn);
121 GNUNET_free (fn);
122 fn = NULL;
123 GNUNET_SCHEDULER_cancel (timeout_kill);
124 timeout_kill = NULL;
125}
126
127
128static void *
129progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
130{
131 switch (event->status)
132 {
133 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
134 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
135 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
136 (unsigned long long) event->value.publish.completed,
137 (unsigned long long) event->value.publish.size,
138 event->value.publish.specifics.progress.depth,
139 (unsigned long long) event->value.publish.specifics.
140 progress.offset);
141 break;
142
143 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
144 break;
145
146 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
147 fprintf (stdout,
148 "Publishing complete, %llu kb/s.\n",
149 (unsigned long long) (FILESIZE * 1000000LL
150 / (1
151 + GNUNET_TIME_absolute_get_duration
152 (start).rel_value_us) / 1024LL));
153 GAUGER ("FS",
154 (GNUNET_YES == indexed)
155 ? "Publishing speed (indexing)"
156 : "Publishing speed (insertion)",
157 (unsigned long long) (FILESIZE * 1000000LL
158 / (1
159 + GNUNET_TIME_absolute_get_duration
160 (start).rel_value_us) / 1024LL), "kb/s");
161 fn = GNUNET_DISK_mktemp ("gnunet-download-test-dst");
162 start = GNUNET_TIME_absolute_get ();
163 download =
164 GNUNET_FS_download_start (fs,
165 event->value.publish.specifics.
166 completed.chk_uri, NULL, fn, NULL, 0,
167 FILESIZE, anonymity_level,
168 GNUNET_FS_DOWNLOAD_OPTION_NONE,
169 "download", NULL);
170 GNUNET_assert (download != NULL);
171 break;
172
173 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
174 fprintf (stdout,
175 "Download complete, %llu kb/s.\n",
176 (unsigned long long) (FILESIZE * 1000000LL
177 / (1
178 + GNUNET_TIME_absolute_get_duration
179 (start).rel_value_us) / 1024LL));
180 GAUGER ("FS",
181 (GNUNET_YES == indexed)
182 ? "Local download speed (indexed)"
183 : "Local download speed (inserted)",
184 (unsigned long long) (FILESIZE * 1000000LL
185 / (1
186 + GNUNET_TIME_absolute_get_duration
187 (start).rel_value_us) / 1024LL), "kb/s");
188 GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
189 break;
190
191 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
192 GNUNET_assert (download == event->value.download.dc);
193 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
194 "Download is progressing (%llu/%llu at level %u off %llu)...\n",
195 (unsigned long long) event->value.download.completed,
196 (unsigned long long) event->value.download.size,
197 event->value.download.specifics.progress.depth,
198 (unsigned long long) event->value.download.specifics.
199 progress.offset);
200 break;
201
202 case GNUNET_FS_STATUS_PUBLISH_ERROR:
203 fprintf (stderr, "Error publishing file: %s\n",
204 event->value.publish.specifics.error.message);
205 GNUNET_break (0);
206 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
207 GNUNET_SCHEDULER_shutdown ();
208 break;
209
210 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
211 fprintf (stderr, "Error downloading file: %s\n",
212 event->value.download.specifics.error.message);
213 GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
214 GNUNET_SCHEDULER_shutdown ();
215 break;
216
217 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
218 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
219 break;
220
221 case GNUNET_FS_STATUS_PUBLISH_START:
222 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
223 GNUNET_assert (NULL == event->value.publish.pctx);
224 GNUNET_assert (FILESIZE == event->value.publish.size);
225 GNUNET_assert (0 == event->value.publish.completed);
226 GNUNET_assert (1 == event->value.publish.anonymity);
227 break;
228
229 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
230 GNUNET_assert (publish == event->value.publish.pc);
231 GNUNET_assert (FILESIZE == event->value.publish.size);
232 GNUNET_assert (1 == event->value.publish.anonymity);
233 GNUNET_SCHEDULER_add_now (&stop_fs_task, NULL);
234 break;
235
236 case GNUNET_FS_STATUS_DOWNLOAD_START:
237 GNUNET_assert (0 == strcmp ("download", event->value.download.cctx));
238 GNUNET_assert (NULL == event->value.download.pctx);
239 GNUNET_assert (NULL != event->value.download.uri);
240 GNUNET_assert (0 == strcmp (fn, event->value.download.filename));
241 GNUNET_assert (FILESIZE == event->value.download.size);
242 GNUNET_assert (0 == event->value.download.completed);
243 GNUNET_assert (1 == event->value.download.anonymity);
244 break;
245
246 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
247 GNUNET_assert (download == event->value.download.dc);
248 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
249 break;
250
251 default:
252 printf ("Unexpected event: %d\n", event->status);
253 break;
254 }
255 return NULL;
256}
257
258
259static void
260run (void *cls,
261 const struct GNUNET_CONFIGURATION_Handle *cfg,
262 struct GNUNET_TESTING_Peer *peer)
263{
264 const char *binary_name = cls;
265 const char *keywords[] = {
266 "down_foo",
267 "down_bar",
268 };
269 char *buf;
270 struct GNUNET_CONTAINER_MetaData *meta;
271 struct GNUNET_FS_Uri *kuri;
272 struct GNUNET_FS_FileInformation *fi;
273 size_t i;
274 struct GNUNET_FS_BlockOptions bo;
275
276 if (GNUNET_YES ==
277 GNUNET_CONFIGURATION_get_value_yesno (cfg,
278 "download-test",
279 "USE_STREAM"))
280 anonymity_level = 0;
281 else
282 anonymity_level = 1;
283 fs = GNUNET_FS_start (cfg, binary_name, &progress_cb, NULL,
284 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
285 GNUNET_assert (NULL != fs);
286 buf = GNUNET_malloc (FILESIZE);
287 for (i = 0; i < FILESIZE; i++)
288 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
289 meta = GNUNET_CONTAINER_meta_data_create ();
290 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
291 bo.content_priority = 42;
292 bo.anonymity_level = anonymity_level;
293 bo.replication_level = 0;
294 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
295
296 if (GNUNET_YES ==
297 GNUNET_CONFIGURATION_get_value_yesno (cfg,
298 "download-test",
299 "USE_INDEX"))
300 {
301 fn1 = GNUNET_DISK_mktemp ("gnunet-download-indexed-test");
302 (void) GNUNET_DISK_directory_remove (fn1);
303 GNUNET_assert (GNUNET_OK ==
304 GNUNET_DISK_fn_write (fn1,
305 buf,
306 FILESIZE,
307 GNUNET_DISK_PERM_USER_READ
308 | GNUNET_DISK_PERM_USER_WRITE));
309 GNUNET_free (buf);
310 fi = GNUNET_FS_file_information_create_from_file (fs, "publish-context",
311 fn1,
312 kuri, meta, GNUNET_YES,
313 &bo);
314 indexed = GNUNET_YES;
315 }
316 else
317 {
318 fi = GNUNET_FS_file_information_create_from_data (fs, "publish-context",
319 FILESIZE, buf, kuri, meta,
320 GNUNET_NO, &bo);
321 /* note: buf will be free'd as part of 'fi' now */
322 indexed = GNUNET_NO;
323 }
324 GNUNET_FS_uri_destroy (kuri);
325 GNUNET_CONTAINER_meta_data_destroy (meta);
326 GNUNET_assert (NULL != fi);
327 timeout_kill =
328 GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout_kill_task, NULL);
329 start = GNUNET_TIME_absolute_get ();
330 publish =
331 GNUNET_FS_publish_start (fs, fi, NULL, NULL, NULL,
332 GNUNET_FS_PUBLISH_OPTION_NONE);
333 GNUNET_assert (publish != NULL);
334}
335
336
337int
338main (int argc, char *argv[])
339{
340 const char *binary_name;
341 const char *config_name;
342
343 binary_name = "test-fs-download";
344 config_name = "test_fs_download_data.conf";
345 if (NULL != strstr (argv[0], "indexed"))
346 {
347 binary_name = "test-fs-download-indexed";
348 config_name = "test_fs_download_indexed.conf";
349 }
350 if (NULL != strstr (argv[0], "cadet"))
351 {
352 binary_name = "test-fs-download-cadet";
353 config_name = "test_fs_download_cadet.conf";
354 }
355 if (0 != GNUNET_TESTING_peer_run (binary_name,
356 config_name,
357 &run, (void *) binary_name))
358 return 1;
359 if (NULL != fn1)
360 {
361 unlink (fn1);
362 GNUNET_free (fn1);
363 }
364 return err;
365}
366
367
368/* end of test_fs_download.c */
diff --git a/src/fs/test_fs_download_data.conf b/src/fs/test_fs_download_data.conf
deleted file mode 100644
index 160dec3ae..000000000
--- a/src/fs/test_fs_download_data.conf
+++ /dev/null
@@ -1,10 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-download/
4
5[download-test]
6# set to 'YES' to test non-anonymous download
7USE_STREAM = NO
8
9# set to 'YES' to use indexing
10USE_INDEX = NO \ No newline at end of file
diff --git a/src/fs/test_fs_download_indexed.conf b/src/fs/test_fs_download_indexed.conf
deleted file mode 100644
index 7f1e36935..000000000
--- a/src/fs/test_fs_download_indexed.conf
+++ /dev/null
@@ -1,10 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-download/
4
5[download-test]
6# set to 'YES' to test non-anonymous download
7USE_STREAM = NO
8
9# set to 'YES' to use indexing
10USE_INDEX = YES
diff --git a/src/fs/test_fs_download_persistence.c b/src/fs/test_fs_download_persistence.c
deleted file mode 100644
index 35d4ed441..000000000
--- a/src/fs/test_fs_download_persistence.c
+++ /dev/null
@@ -1,351 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_download_persistence.c
23 * @brief simple testcase for persistence of simple download operation
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_fs_service.h"
30
31/**
32 * File-size we use for testing.
33 */
34#define FILESIZE (1024 * 1024 * 2)
35
36/**
37 * How long until we give up on transmitting the message?
38 */
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
40
41/**
42 * How long should our test-content live?
43 */
44#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
45
46
47static struct GNUNET_TIME_Absolute start;
48
49static const struct GNUNET_CONFIGURATION_Handle *cfg;
50
51static struct GNUNET_FS_Handle *fs;
52
53static struct GNUNET_FS_DownloadContext *download;
54
55static struct GNUNET_FS_PublishContext *publish;
56
57static struct GNUNET_SCHEDULER_Task *timeout_kill;
58
59static char *fn;
60
61static int err;
62
63
64static void
65timeout_kill_task (void *cls)
66{
67 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Timeout downloading file\n");
68 if (download != NULL)
69 {
70 GNUNET_FS_download_stop (download, GNUNET_YES);
71 download = NULL;
72 }
73 else if (publish != NULL)
74 {
75 GNUNET_FS_publish_stop (publish);
76 publish = NULL;
77 }
78 timeout_kill = NULL;
79 err = 1;
80}
81
82
83static void
84abort_publish_task (void *cls)
85{
86 if (publish != NULL)
87 {
88 GNUNET_FS_publish_stop (publish);
89 publish = NULL;
90 }
91}
92
93
94static void
95abort_download_task (void *cls)
96{
97 uint64_t size;
98
99 if (download != NULL)
100 {
101 GNUNET_FS_download_stop (download, GNUNET_YES);
102 download = NULL;
103 }
104 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_size (fn, &size, GNUNET_YES,
105 GNUNET_NO));
106 GNUNET_assert (size == FILESIZE);
107 GNUNET_DISK_directory_remove (fn);
108 GNUNET_free (fn);
109 fn = NULL;
110 GNUNET_SCHEDULER_cancel (timeout_kill);
111 timeout_kill = NULL;
112}
113
114
115static void *
116progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event);
117
118
119static void
120restart_fs_task (void *cls)
121{
122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Restarting FS.\n");
123 GNUNET_FS_stop (fs);
124 fs = GNUNET_FS_start (cfg, "test-fs-download-persistence", &progress_cb, NULL,
125 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
126}
127
128
129/**
130 * Consider scheduling the restart-task.
131 * Only runs the restart task once per event
132 * category.
133 *
134 * @param ev type of the event to consider
135 */
136static void
137consider_restart (int ev)
138{
139 static int prev[32];
140 static int off;
141 int i;
142
143 for (i = 0; i < off; i++)
144 if (prev[i] == ev)
145 return;
146 prev[off++] = ev;
147 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_URGENT,
148 &restart_fs_task, NULL);
149}
150
151
152static void *
153progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
154{
155 switch (event->status)
156 {
157 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
159 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
160 (unsigned long long) event->value.publish.completed,
161 (unsigned long long) event->value.publish.size,
162 event->value.publish.specifics.progress.depth,
163 (unsigned long long) event->value.publish.specifics.
164 progress.offset);
165 break;
166
167 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
168 break;
169
170 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
171 printf ("Publishing complete, %llu kbps.\n",
172 (unsigned long long) (FILESIZE * 1000000LL
173 / (1
174 + GNUNET_TIME_absolute_get_duration
175 (start).rel_value_us) / 1024LL));
176 fn = GNUNET_DISK_mktemp ("gnunet-download-test-dst");
177 start = GNUNET_TIME_absolute_get ();
178 GNUNET_assert (download == NULL);
179 GNUNET_FS_download_start (fs,
180 event->value.publish.specifics.completed.chk_uri,
181 NULL, fn, NULL, 0, FILESIZE, 1,
182 GNUNET_FS_DOWNLOAD_OPTION_NONE, "download", NULL);
183 break;
184
185 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
186 printf ("Download complete, %llu kbps.\n",
187 (unsigned long long) (FILESIZE * 1000000LL
188 / (1
189 + GNUNET_TIME_absolute_get_duration
190 (start).rel_value_us) / 1024LL));
191 GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
192 break;
193
194 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
195 consider_restart (event->status);
196 GNUNET_assert (download == event->value.download.dc);
197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
198 "Download is progressing (%llu/%llu at level %u off %llu)...\n",
199 (unsigned long long) event->value.download.completed,
200 (unsigned long long) event->value.download.size,
201 event->value.download.specifics.progress.depth,
202 (unsigned long long) event->value.download.specifics.
203 progress.offset);
204 break;
205
206 case GNUNET_FS_STATUS_PUBLISH_ERROR:
207 fprintf (stderr, "Error publishing file: %s\n",
208 event->value.publish.specifics.error.message);
209 GNUNET_break (0);
210 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
211 break;
212
213 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
214 fprintf (stderr, "Error downloading file: %s\n",
215 event->value.download.specifics.error.message);
216 GNUNET_SCHEDULER_add_now (&abort_download_task, NULL);
217 break;
218
219 case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
220 GNUNET_assert (event->value.publish.pc == publish);
221 publish = NULL;
222 break;
223
224 case GNUNET_FS_STATUS_PUBLISH_RESUME:
225 GNUNET_assert (NULL == publish);
226 publish = event->value.publish.pc;
227 break;
228
229 case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download suspended.\n");
231 GNUNET_assert (event->value.download.dc == download);
232 download = NULL;
233 break;
234
235 case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
236 GNUNET_assert (NULL == download);
237 download = event->value.download.dc;
238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download resumed.\n");
239 break;
240
241 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
242 consider_restart (event->status);
243 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download active.\n");
244 break;
245
246 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
247 consider_restart (event->status);
248 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download inactive.\n");
249 break;
250
251 case GNUNET_FS_STATUS_PUBLISH_START:
252 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
253 GNUNET_assert (NULL == event->value.publish.pctx);
254 GNUNET_assert (FILESIZE == event->value.publish.size);
255 GNUNET_assert (0 == event->value.publish.completed);
256 GNUNET_assert (1 == event->value.publish.anonymity);
257 break;
258
259 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
260 GNUNET_assert (publish == event->value.publish.pc);
261 GNUNET_assert (FILESIZE == event->value.publish.size);
262 GNUNET_assert (1 == event->value.publish.anonymity);
263 GNUNET_FS_stop (fs);
264 fs = NULL;
265 break;
266
267 case GNUNET_FS_STATUS_DOWNLOAD_START:
268 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download started.\n");
269 consider_restart (event->status);
270 GNUNET_assert (download == NULL);
271 download = event->value.download.dc;
272 GNUNET_assert (0 == strcmp ("download", event->value.download.cctx));
273 GNUNET_assert (NULL == event->value.download.pctx);
274 GNUNET_assert (NULL != event->value.download.uri);
275 GNUNET_assert (0 == strcmp (fn, event->value.download.filename));
276 GNUNET_assert (FILESIZE == event->value.download.size);
277 GNUNET_assert (0 == event->value.download.completed);
278 GNUNET_assert (1 == event->value.download.anonymity);
279 break;
280
281 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
282 GNUNET_assert (download == event->value.download.dc);
283 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
284 download = NULL;
285 break;
286
287 default:
288 printf ("Unexpected event: %d\n", event->status);
289 break;
290 }
291 return NULL;
292}
293
294
295static void
296run (void *cls,
297 const struct GNUNET_CONFIGURATION_Handle *c,
298 struct GNUNET_TESTING_Peer *peer)
299{
300 const char *keywords[] = {
301 "down_foo",
302 "down_bar",
303 };
304 char *buf;
305 struct GNUNET_CONTAINER_MetaData *meta;
306 struct GNUNET_FS_Uri *kuri;
307 struct GNUNET_FS_FileInformation *fi;
308 size_t i;
309 struct GNUNET_FS_BlockOptions bo;
310
311 cfg = c;
312 fs = GNUNET_FS_start (cfg, "test-fs-download-persistence", &progress_cb, NULL,
313 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
314 GNUNET_assert (NULL != fs);
315 buf = GNUNET_malloc (FILESIZE);
316 for (i = 0; i < FILESIZE; i++)
317 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
318 meta = GNUNET_CONTAINER_meta_data_create ();
319 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
320 bo.content_priority = 42;
321 bo.anonymity_level = 1;
322 bo.replication_level = 0;
323 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
324 fi = GNUNET_FS_file_information_create_from_data (fs, "publish-context",
325 FILESIZE, buf, kuri, meta,
326 GNUNET_NO, &bo);
327 GNUNET_FS_uri_destroy (kuri);
328 GNUNET_CONTAINER_meta_data_destroy (meta);
329 GNUNET_assert (NULL != fi);
330 timeout_kill =
331 GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout_kill_task, NULL);
332 start = GNUNET_TIME_absolute_get ();
333 publish =
334 GNUNET_FS_publish_start (fs, fi, NULL, NULL, NULL,
335 GNUNET_FS_PUBLISH_OPTION_NONE);
336 GNUNET_assert (publish != NULL);
337}
338
339
340int
341main (int argc, char *argv[])
342{
343 if (0 != GNUNET_TESTING_peer_run ("test-fs-download-persistence",
344 "test_fs_download_data.conf",
345 &run, NULL))
346 return 1;
347 return err;
348}
349
350
351/* end of test_fs_download_persistence.c */
diff --git a/src/fs/test_fs_file_information.c b/src/fs/test_fs_file_information.c
deleted file mode 100644
index 22013db27..000000000
--- a/src/fs/test_fs_file_information.c
+++ /dev/null
@@ -1,163 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_file_information.c
23 * @brief simple testcase for file_information operations
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - test that metadata, etc. are all correct (for example,
28 * there is a known bug with dirname never being set that is
29 * not detected!)
30 * - need to iterate over file-information structure
31 * - other API functions may not yet be tested (such as
32 * filedata-from-callback)
33 */
34#include "platform.h"
35#include "gnunet_util_lib.h"
36#include "gnunet_fs_service.h"
37
38
39/**
40 * File-size we use for testing.
41 */
42#define FILESIZE (1024 * 1024 * 2)
43
44/**
45 * How long should our test-content live?
46 */
47#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
48
49
50static int
51mycleaner (void *cls, struct GNUNET_FS_FileInformation *fi, uint64_t length,
52 struct GNUNET_CONTAINER_MetaData *meta, struct GNUNET_FS_Uri **uri,
53 struct GNUNET_FS_BlockOptions *bo, int *do_index, void **client_info)
54{
55 return GNUNET_OK;
56}
57
58
59static void
60run (void *cls, char *const *args, const char *cfgfile,
61 const struct GNUNET_CONFIGURATION_Handle *cfg)
62{
63 const char *keywords[] = {
64 "down_foo",
65 "down_bar",
66 };
67 char *fn1;
68 char *fn2;
69 char *buf;
70 struct GNUNET_CONTAINER_MetaData *meta;
71 struct GNUNET_FS_Uri *kuri;
72 struct GNUNET_FS_FileInformation *fi1;
73 struct GNUNET_FS_FileInformation *fi2;
74 struct GNUNET_FS_FileInformation *fidir;
75 struct GNUNET_FS_Handle *fs;
76 size_t i;
77 struct GNUNET_FS_BlockOptions bo;
78
79 fs = GNUNET_FS_start (cfg, "test-fs-file-information", NULL, NULL,
80 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
81 fn1 = GNUNET_DISK_mktemp ("gnunet-file_information-test-dst");
82 buf = GNUNET_malloc (FILESIZE);
83 for (i = 0; i < FILESIZE; i++)
84 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
85 (void) GNUNET_DISK_directory_remove (fn1);
86 GNUNET_assert (GNUNET_OK ==
87 GNUNET_DISK_fn_write (fn1, buf, FILESIZE,
88 GNUNET_DISK_PERM_USER_READ
89 | GNUNET_DISK_PERM_USER_WRITE));
90 GNUNET_free (buf);
91
92 fn2 = GNUNET_DISK_mktemp ("gnunet-file_information-test-dst");
93 buf = GNUNET_malloc (FILESIZE);
94 for (i = 0; i < FILESIZE; i++)
95 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
96 (void) GNUNET_DISK_directory_remove (fn2);
97 GNUNET_assert (GNUNET_OK ==
98 GNUNET_DISK_fn_write (fn2, buf, FILESIZE,
99 GNUNET_DISK_PERM_USER_READ
100 | GNUNET_DISK_PERM_USER_WRITE));
101 GNUNET_free (buf);
102
103 meta = GNUNET_CONTAINER_meta_data_create ();
104 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
105 bo.content_priority = 42;
106 bo.anonymity_level = 1;
107 bo.replication_level = 0;
108 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
109 fi1 =
110 GNUNET_FS_file_information_create_from_file (fs,
111 "file_information-context1",
112 fn1, kuri, meta, GNUNET_YES,
113 &bo);
114 GNUNET_assert (fi1 != NULL);
115 fi2 =
116 GNUNET_FS_file_information_create_from_file (fs,
117 "file_information-context2",
118 fn2, kuri, meta, GNUNET_YES,
119 &bo);
120 GNUNET_assert (fi2 != NULL);
121 fidir =
122 GNUNET_FS_file_information_create_empty_directory (fs,
123 "file_information-context-dir",
124 kuri, meta, &bo, NULL);
125 GNUNET_assert (GNUNET_OK == GNUNET_FS_file_information_add (fidir, fi1));
126 GNUNET_assert (GNUNET_OK == GNUNET_FS_file_information_add (fidir, fi2));
127 GNUNET_FS_uri_destroy (kuri);
128 GNUNET_CONTAINER_meta_data_destroy (meta);
129 GNUNET_assert (NULL != fidir);
130 /* FIXME: test more of API! */
131 GNUNET_FS_file_information_destroy (fidir, &mycleaner, NULL);
132 GNUNET_DISK_directory_remove (fn1);
133 GNUNET_DISK_directory_remove (fn2);
134 GNUNET_free (fn1);
135 GNUNET_free (fn2);
136 GNUNET_FS_stop (fs);
137}
138
139
140int
141main (int argc, char *argv[])
142{
143 char *const argvx[] = {
144 "test-fs-file_information",
145 "-c",
146 "test_fs_file_information_data.conf",
147 NULL
148 };
149 struct GNUNET_GETOPT_CommandLineOption options[] = {
150 GNUNET_GETOPT_OPTION_END
151 };
152
153 GNUNET_log_setup ("test_fs_file_information",
154 "WARNING",
155 NULL);
156 GNUNET_PROGRAM_run ((sizeof(argvx) / sizeof(char *)) - 1, argvx,
157 "test-fs-file_information", "nohelp", options, &run,
158 NULL);
159 return 0;
160}
161
162
163/* end of test_fs_file_information.c */
diff --git a/src/fs/test_fs_file_information_data.conf b/src/fs/test_fs_file_information_data.conf
deleted file mode 100644
index c8fc0938c..000000000
--- a/src/fs/test_fs_file_information_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-file-information/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_fs_getopt.c b/src/fs/test_fs_getopt.c
deleted file mode 100644
index 3d0da752b..000000000
--- a/src/fs/test_fs_getopt.c
+++ /dev/null
@@ -1,37 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_getopt.c
22 * @brief test for fs_getopt.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_fs_service.h"
27
28
29int
30main (int argc, char *argv[])
31{
32 GNUNET_log_setup ("test_fs_getopt",
33 "WARNING",
34 NULL);
35 fprintf (stderr, "%s", "WARNING: testcase not yet written.\n");
36 return 0; /* testcase passed */
37}
diff --git a/src/fs/test_fs_list_indexed.c b/src/fs/test_fs_list_indexed.c
deleted file mode 100644
index 2f04a017a..000000000
--- a/src/fs/test_fs_list_indexed.c
+++ /dev/null
@@ -1,265 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_list_indexed.c
23 * @brief simple testcase for list_indexed operation (indexing, listing
24 * indexed)
25 * @author Christian Grothoff
26 *
27 * TODO:
28 * - actually call list_indexed API!
29 */
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_testing_lib.h"
33#include "gnunet_fs_service.h"
34
35/**
36 * File-size we use for testing.
37 */
38#define FILESIZE (1024 * 1024 * 2)
39
40/**
41 * How long until we give up on transmitting the message?
42 */
43#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
44
45/**
46 * How long should our test-content live?
47 */
48#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
49
50
51static struct GNUNET_TIME_Absolute start;
52
53static struct GNUNET_FS_Handle *fs;
54
55static struct GNUNET_FS_PublishContext *publish;
56
57static char *fn1;
58
59static char *fn2;
60
61static int err;
62
63
64static void
65abort_publish_task (void *cls)
66{
67 GNUNET_FS_publish_stop (publish);
68 publish = NULL;
69 GNUNET_DISK_directory_remove (fn1);
70 GNUNET_free (fn1);
71 fn1 = NULL;
72 GNUNET_DISK_directory_remove (fn2);
73 GNUNET_free (fn2);
74 fn2 = NULL;
75}
76
77
78static void
79list_indexed_task (void *cls)
80{
81 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
82}
83
84
85static void *
86progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
87{
88 void *ret;
89
90 ret = NULL;
91 switch (event->status)
92 {
93 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
94 ret = event->value.publish.cctx;
95 printf ("Publish complete, %llu kbps.\n",
96 (unsigned long long) (FILESIZE * 1000000LL
97 / (1
98 + GNUNET_TIME_absolute_get_duration
99 (start).rel_value_us) / 1024));
100 if (0 == strcmp ("list_indexed-context-dir", event->value.publish.cctx))
101 GNUNET_SCHEDULER_add_now (&list_indexed_task, NULL);
102
103 break;
104
105 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
106 ret = event->value.publish.cctx;
107 GNUNET_assert (publish == event->value.publish.pc);
108 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
109 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
110 (unsigned long long) event->value.publish.completed,
111 (unsigned long long) event->value.publish.size,
112 event->value.publish.specifics.progress.depth,
113 (unsigned long long) event->value.publish.specifics.
114 progress.offset);
115 break;
116
117 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
118 ret = event->value.publish.cctx;
119 break;
120
121 case GNUNET_FS_STATUS_PUBLISH_ERROR:
122 ret = event->value.publish.cctx;
123 fprintf (stderr, "Error publishing file: %s\n",
124 event->value.publish.specifics.error.message);
125 err = 1;
126 if (0 == strcmp ("list_indexed-context-dir", event->value.publish.cctx))
127 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
128 break;
129
130 case GNUNET_FS_STATUS_PUBLISH_START:
131 ret = event->value.publish.cctx;
132 if (0 == strcmp ("list_indexed-context1", event->value.publish.cctx))
133 {
134 GNUNET_assert (0 ==
135 strcmp ("list_indexed-context-dir",
136 event->value.publish.pctx));
137 GNUNET_assert (FILESIZE == event->value.publish.size);
138 GNUNET_assert (0 == event->value.publish.completed);
139 GNUNET_assert (1 == event->value.publish.anonymity);
140 }
141 else if (0 == strcmp ("list_indexed-context2", event->value.publish.cctx))
142 {
143 GNUNET_assert (0 ==
144 strcmp ("list_indexed-context-dir",
145 event->value.publish.pctx));
146 GNUNET_assert (FILESIZE == event->value.publish.size);
147 GNUNET_assert (0 == event->value.publish.completed);
148 GNUNET_assert (2 == event->value.publish.anonymity);
149 }
150 else if (0 ==
151 strcmp ("list_indexed-context-dir", event->value.publish.cctx))
152 {
153 GNUNET_assert (0 == event->value.publish.completed);
154 GNUNET_assert (3 == event->value.publish.anonymity);
155 }
156 else
157 GNUNET_assert (0);
158 break;
159
160 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
161 if (0 == strcmp ("list_indexed-context-dir", event->value.publish.cctx))
162 {
163 GNUNET_assert (publish == event->value.publish.pc);
164 publish = NULL;
165 }
166 break;
167
168 default:
169 printf ("Unexpected event: %d\n", event->status);
170 break;
171 }
172 return ret;
173}
174
175
176static void
177run (void *cls,
178 const struct GNUNET_CONFIGURATION_Handle *cfg,
179 struct GNUNET_TESTING_Peer *peer)
180{
181 const char *keywords[] = {
182 "down_foo",
183 "down_bar",
184 };
185 char *buf;
186 struct GNUNET_CONTAINER_MetaData *meta;
187 struct GNUNET_FS_Uri *kuri;
188 struct GNUNET_FS_FileInformation *fi1;
189 struct GNUNET_FS_FileInformation *fi2;
190 struct GNUNET_FS_FileInformation *fidir;
191 size_t i;
192 struct GNUNET_FS_BlockOptions bo;
193
194 fs = GNUNET_FS_start (cfg, "test-fs-list_indexed", &progress_cb, NULL,
195 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
196 GNUNET_assert (NULL != fs);
197 fn1 = GNUNET_DISK_mktemp ("gnunet-list_indexed-test-dst");
198 buf = GNUNET_malloc (FILESIZE);
199 for (i = 0; i < FILESIZE; i++)
200 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
201 (void) GNUNET_DISK_directory_remove (fn1);
202 GNUNET_assert (GNUNET_OK ==
203 GNUNET_DISK_fn_write (fn1, buf, FILESIZE,
204 GNUNET_DISK_PERM_USER_READ
205 | GNUNET_DISK_PERM_USER_WRITE));
206 GNUNET_free (buf);
207
208 fn2 = GNUNET_DISK_mktemp ("gnunet-list_indexed-test-dst");
209 buf = GNUNET_malloc (FILESIZE);
210 for (i = 0; i < FILESIZE; i++)
211 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
212 (void) GNUNET_DISK_directory_remove (fn2);
213 GNUNET_assert (GNUNET_OK ==
214 GNUNET_DISK_fn_write (fn2, buf, FILESIZE,
215 GNUNET_DISK_PERM_USER_READ
216 | GNUNET_DISK_PERM_USER_WRITE));
217 GNUNET_free (buf);
218
219 meta = GNUNET_CONTAINER_meta_data_create ();
220 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
221 bo.content_priority = 42;
222 bo.anonymity_level = 1;
223 bo.replication_level = 0;
224 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
225 fi1 =
226 GNUNET_FS_file_information_create_from_file (fs, "list_indexed-context1",
227 fn1, kuri, meta, GNUNET_YES,
228 &bo);
229 GNUNET_assert (NULL != fi1);
230 bo.anonymity_level = 2;
231 fi2 =
232 GNUNET_FS_file_information_create_from_file (fs, "list_indexed-context2",
233 fn2, kuri, meta, GNUNET_YES,
234 &bo);
235 GNUNET_assert (NULL != fi2);
236 bo.anonymity_level = 3;
237 fidir =
238 GNUNET_FS_file_information_create_empty_directory (fs,
239 "list_indexed-context-dir",
240 kuri, meta, &bo, NULL);
241 GNUNET_assert (GNUNET_OK == GNUNET_FS_file_information_add (fidir, fi1));
242 GNUNET_assert (GNUNET_OK == GNUNET_FS_file_information_add (fidir, fi2));
243 GNUNET_FS_uri_destroy (kuri);
244 GNUNET_CONTAINER_meta_data_destroy (meta);
245 GNUNET_assert (NULL != fidir);
246 start = GNUNET_TIME_absolute_get ();
247 publish =
248 GNUNET_FS_publish_start (fs, fidir, NULL, NULL, NULL,
249 GNUNET_FS_PUBLISH_OPTION_NONE);
250 GNUNET_assert (publish != NULL);
251}
252
253
254int
255main (int argc, char *argv[])
256{
257 if (0 != GNUNET_TESTING_peer_run ("test-fs-list-indexed",
258 "test_fs_list_indexed_data.conf",
259 &run, NULL))
260 return 1;
261 return 0;
262}
263
264
265/* end of test_fs_list_indexed.c */
diff --git a/src/fs/test_fs_list_indexed_data.conf b/src/fs/test_fs_list_indexed_data.conf
deleted file mode 100644
index 941809322..000000000
--- a/src/fs/test_fs_list_indexed_data.conf
+++ /dev/null
@@ -1,10 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-list-indexed/
4
5[transport]
6PLUGINS =
7
8[fs]
9ACTIVEMIGRATION = NO
10
diff --git a/src/fs/test_fs_namespace.c b/src/fs/test_fs_namespace.c
deleted file mode 100644
index c1e10d575..000000000
--- a/src/fs/test_fs_namespace.c
+++ /dev/null
@@ -1,320 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2005-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_namespace.c
23 * @brief Test for fs_namespace.c
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_fs_service.h"
30
31
32static struct GNUNET_CRYPTO_EcdsaPublicKey nsid;
33
34static struct GNUNET_FS_Uri *sks_expect_uri;
35
36static struct GNUNET_FS_Uri *ksk_expect_uri;
37
38static struct GNUNET_FS_Handle *fs;
39
40static struct GNUNET_FS_SearchContext *sks_search;
41
42static struct GNUNET_FS_SearchContext *ksk_search;
43
44static struct GNUNET_SCHEDULER_Task *kill_task;
45
46static int update_started;
47
48static int err;
49
50
51static void
52abort_ksk_search_task (void *cls)
53{
54 if (ksk_search != NULL)
55 {
56 GNUNET_FS_search_stop (ksk_search);
57 ksk_search = NULL;
58 if (sks_search == NULL)
59 {
60 GNUNET_FS_stop (fs);
61 if (NULL != kill_task)
62 GNUNET_SCHEDULER_cancel (kill_task);
63 }
64 }
65}
66
67
68static void
69abort_sks_search_task (void *cls)
70{
71 if (sks_search == NULL)
72 return;
73 GNUNET_FS_search_stop (sks_search);
74 sks_search = NULL;
75 if (ksk_search == NULL)
76 {
77 GNUNET_FS_stop (fs);
78 if (NULL != kill_task)
79 GNUNET_SCHEDULER_cancel (kill_task);
80 }
81}
82
83
84static void
85do_timeout (void *cls)
86{
87 err = 1;
88 fprintf (stderr, "%s", "Operation timed out\n");
89 kill_task = NULL;
90 abort_sks_search_task (NULL);
91 abort_ksk_search_task (NULL);
92}
93
94
95static void *
96progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
97{
98 switch (event->status)
99 {
100 case GNUNET_FS_STATUS_SEARCH_RESULT:
101 if (sks_search == event->value.search.sc)
102 {
103 if (! GNUNET_FS_uri_test_equal
104 (sks_expect_uri, event->value.search.specifics.result.uri))
105 {
106 fprintf (stderr, "%s", "Wrong result for sks search!\n");
107 err = 1;
108 }
109 /* give system 1ms to initiate update search! */
110 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
111 &abort_sks_search_task, NULL);
112 }
113 else if (ksk_search == event->value.search.sc)
114 {
115 if (! GNUNET_FS_uri_test_equal
116 (ksk_expect_uri, event->value.search.specifics.result.uri))
117 {
118 fprintf (stderr, "%s", "Wrong result for ksk search!\n");
119 err = 1;
120 }
121 GNUNET_SCHEDULER_add_now (&abort_ksk_search_task, NULL);
122 }
123 else
124 {
125 fprintf (stderr, "%s", "Unexpected search result received!\n");
126 GNUNET_break (0);
127 }
128 break;
129
130 case GNUNET_FS_STATUS_SEARCH_ERROR:
131 fprintf (stderr, "Error searching file: %s\n",
132 event->value.search.specifics.error.message);
133 if (sks_search == event->value.search.sc)
134 GNUNET_SCHEDULER_add_now (&abort_sks_search_task, NULL);
135 else if (ksk_search == event->value.search.sc)
136 GNUNET_SCHEDULER_add_now (&abort_ksk_search_task, NULL);
137 else
138 GNUNET_break (0);
139 break;
140
141 case GNUNET_FS_STATUS_SEARCH_START:
142 GNUNET_assert ((NULL == event->value.search.cctx) ||
143 (0 == strcmp ("sks_search", event->value.search.cctx)) ||
144 (0 == strcmp ("ksk_search", event->value.search.cctx)));
145 if (NULL == event->value.search.cctx)
146 {
147 GNUNET_assert (0 == strcmp ("sks_search", event->value.search.pctx));
148 update_started = GNUNET_YES;
149 }
150 GNUNET_assert (1 == event->value.search.anonymity);
151 break;
152
153 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
154 return NULL;
155
156 case GNUNET_FS_STATUS_SEARCH_STOPPED:
157 return NULL;
158
159 default:
160 fprintf (stderr, "Unexpected event: %d\n", event->status);
161 break;
162 }
163 return event->value.search.cctx;
164}
165
166
167static void
168publish_cont (void *cls, const struct GNUNET_FS_Uri *ksk_uri, const char *emsg)
169{
170 char *msg;
171 struct GNUNET_FS_Uri *sks_uri;
172 char sbuf[1024];
173 char buf[1024];
174 char *ret;
175
176 if (NULL != emsg)
177 {
178 fprintf (stderr, "Error publishing: %s\n", emsg);
179 err = 1;
180 GNUNET_FS_stop (fs);
181 return;
182 }
183 ret = GNUNET_STRINGS_data_to_string (&nsid, sizeof(nsid), buf, sizeof(buf));
184 GNUNET_assert (NULL != ret);
185 ret[0] = '\0';
186 GNUNET_snprintf (sbuf, sizeof(sbuf), "gnunet://fs/sks/%s/this", buf);
187 sks_uri = GNUNET_FS_uri_parse (sbuf, &msg);
188 if (NULL == sks_uri)
189 {
190 fprintf (stderr, "failed to parse URI `%s': %s\n", sbuf, msg);
191 err = 1;
192 GNUNET_FS_stop (fs);
193 GNUNET_free (msg);
194 return;
195 }
196 ksk_search =
197 GNUNET_FS_search_start (fs, ksk_uri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
198 "ksk_search");
199 sks_search =
200 GNUNET_FS_search_start (fs, sks_uri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
201 "sks_search");
202 GNUNET_FS_uri_destroy (sks_uri);
203}
204
205
206static void
207sks_cont (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg)
208{
209 struct GNUNET_CONTAINER_MetaData *meta;
210 struct GNUNET_FS_Uri *ksk_uri;
211 char *msg;
212 struct GNUNET_FS_BlockOptions bo;
213
214 if (NULL == uri)
215 {
216 fprintf (stderr, "Error publishing: %s\n", emsg);
217 err = 1;
218 GNUNET_FS_stop (fs);
219 return;
220 }
221 meta = GNUNET_CONTAINER_meta_data_create ();
222 msg = NULL;
223 ksk_uri = GNUNET_FS_uri_parse ("gnunet://fs/ksk/ns-search", &msg);
224 GNUNET_assert (NULL == msg);
225 ksk_expect_uri = GNUNET_FS_uri_dup (uri);
226 bo.content_priority = 1;
227 bo.anonymity_level = 1;
228 bo.replication_level = 0;
229 bo.expiration_time =
230 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
231 GNUNET_FS_publish_ksk (fs, ksk_uri, meta, uri, &bo,
232 GNUNET_FS_PUBLISH_OPTION_NONE, &publish_cont, NULL);
233 GNUNET_FS_uri_destroy (ksk_uri);
234 GNUNET_CONTAINER_meta_data_destroy (meta);
235}
236
237
238static void
239adv_cont (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg)
240{
241 struct GNUNET_CONTAINER_MetaData *meta;
242 struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
243 struct GNUNET_FS_BlockOptions bo;
244
245 if (NULL != emsg)
246 {
247 fprintf (stderr, "Error publishing: %s\n", emsg);
248 err = 1;
249 GNUNET_FS_stop (fs);
250 return;
251 }
252 GNUNET_CRYPTO_ecdsa_key_create (&ns);
253 meta = GNUNET_CONTAINER_meta_data_create ();
254 sks_expect_uri = GNUNET_FS_uri_dup (uri);
255 bo.content_priority = 1;
256 bo.anonymity_level = 1;
257 bo.replication_level = 0;
258 bo.expiration_time =
259 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
260 GNUNET_CRYPTO_ecdsa_key_get_public (&ns,
261 &nsid);
262 GNUNET_FS_publish_sks (fs,
263 &ns, "this", "next", meta, uri,
264 &bo, GNUNET_FS_PUBLISH_OPTION_NONE, &sks_cont, NULL);
265 GNUNET_CONTAINER_meta_data_destroy (meta);
266}
267
268
269static void
270testNamespace (void)
271{
272 struct GNUNET_FS_BlockOptions bo;
273 struct GNUNET_CONTAINER_MetaData *meta;
274 struct GNUNET_FS_Uri *ksk_uri;
275 struct GNUNET_FS_Uri *sks_uri;
276
277 meta = GNUNET_CONTAINER_meta_data_create ();
278 ksk_uri = GNUNET_FS_uri_parse ("gnunet://fs/ksk/testnsa", NULL);
279 bo.content_priority = 1;
280 bo.anonymity_level = 1;
281 bo.replication_level = 0;
282 bo.expiration_time =
283 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
284 sks_uri = GNUNET_FS_uri_sks_create (&nsid, "root");
285 GNUNET_FS_publish_ksk (fs,
286 ksk_uri, meta, sks_uri,
287 &bo, GNUNET_FS_PUBLISH_OPTION_NONE,
288 &adv_cont, NULL);
289 GNUNET_FS_uri_destroy (sks_uri);
290 kill_task =
291 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, &do_timeout,
292 NULL);
293 GNUNET_FS_uri_destroy (ksk_uri);
294 GNUNET_CONTAINER_meta_data_destroy (meta);
295}
296
297
298static void
299run (void *cls,
300 const struct GNUNET_CONFIGURATION_Handle *cfg,
301 struct GNUNET_TESTING_Peer *peer)
302{
303 fs = GNUNET_FS_start (cfg, "test-fs-namespace", &progress_cb, NULL,
304 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
305 testNamespace ();
306}
307
308
309int
310main (int argc, char *argv[])
311{
312 if (0 != GNUNET_TESTING_peer_run ("test-fs-namespace",
313 "test_fs_namespace_data.conf",
314 &run, NULL))
315 return 1;
316 return err;
317}
318
319
320/* end of test_fs_namespace.c */
diff --git a/src/fs/test_fs_namespace_data.conf b/src/fs/test_fs_namespace_data.conf
deleted file mode 100644
index 70b954f7d..000000000
--- a/src/fs/test_fs_namespace_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-namespace/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_fs_namespace_list_updateable.c b/src/fs/test_fs_namespace_list_updateable.c
deleted file mode 100644
index 2132220fb..000000000
--- a/src/fs/test_fs_namespace_list_updateable.c
+++ /dev/null
@@ -1,175 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2005-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_namespace_list_updateable.c
23 * @brief Test for fs_namespace_list_updateable.c
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_fs_service.h"
30
31
32static struct GNUNET_FS_Handle *fs;
33
34static int err;
35
36static struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
37
38static struct GNUNET_CONTAINER_MetaData *meta;
39
40static struct GNUNET_FS_Uri *uri_this;
41
42static struct GNUNET_FS_Uri *uri_next;
43
44static struct GNUNET_FS_BlockOptions bo;
45
46
47static void *
48progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
49{
50 return NULL;
51}
52
53
54static void
55do_shutdown ()
56{
57 if (uri_this != NULL)
58 GNUNET_FS_uri_destroy (uri_this);
59 if (uri_next != NULL)
60 GNUNET_FS_uri_destroy (uri_next);
61 if (meta != NULL)
62 GNUNET_CONTAINER_meta_data_destroy (meta);
63}
64
65
66static void
67check_next (void *cls, const char *last_id,
68 const struct GNUNET_FS_Uri *last_uri,
69 const struct GNUNET_CONTAINER_MetaData *last_meta,
70 const char *next_id)
71{
72 GNUNET_break (0 == strcmp (last_id, "next"));
73 GNUNET_break (0 == strcmp (next_id, "future"));
74 err -= 4;
75}
76
77
78static void
79check_this_next (void *cls, const char *last_id,
80 const struct GNUNET_FS_Uri *last_uri,
81 const struct GNUNET_CONTAINER_MetaData *last_meta,
82 const char *next_id)
83{
84 GNUNET_break (0 == strcmp (last_id, "this"));
85 GNUNET_break (0 == strcmp (next_id, "next"));
86 err -= 2;
87 err += 4;
88 GNUNET_FS_namespace_list_updateable (fs, &ns, next_id, &check_next, NULL);
89}
90
91
92static void
93sks_cont_next (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg)
94{
95 GNUNET_assert (NULL == emsg);
96 err += 2;
97 GNUNET_FS_namespace_list_updateable (fs, &ns, NULL, &check_this_next, NULL);
98}
99
100
101static void
102check_this (void *cls, const char *last_id,
103 const struct GNUNET_FS_Uri *last_uri,
104 const struct GNUNET_CONTAINER_MetaData *last_meta,
105 const char *next_id)
106{
107 GNUNET_break (0 == strcmp (last_id, "this"));
108 GNUNET_break (0 == strcmp (next_id, "next"));
109 err -= 1;
110}
111
112
113static void
114sks_cont_this (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg)
115{
116 GNUNET_assert (NULL == emsg);
117 err = 1;
118 GNUNET_FS_namespace_list_updateable (fs, &ns, NULL, &check_this, NULL);
119 GNUNET_FS_publish_sks (fs,
120 &ns, "next", "future", meta, uri_next, &bo,
121 GNUNET_FS_PUBLISH_OPTION_NONE, &sks_cont_next, NULL);
122}
123
124
125static void
126testNamespace (void)
127{
128 GNUNET_CRYPTO_ecdsa_key_create (&ns);
129 bo.content_priority = 1;
130 bo.anonymity_level = 1;
131 bo.replication_level = 0;
132 bo.expiration_time =
133 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
134 meta = GNUNET_CONTAINER_meta_data_create ();
135
136 uri_this =
137 GNUNET_FS_uri_parse
138 (
139 "gnunet://fs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.42",
140 NULL);
141 uri_next =
142 GNUNET_FS_uri_parse
143 (
144 "gnunet://fs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.43",
145 NULL);
146 GNUNET_FS_publish_sks (fs,
147 &ns, "this", "next", meta, uri_this, &bo,
148 GNUNET_FS_PUBLISH_OPTION_NONE, &sks_cont_this, NULL);
149}
150
151
152static void
153run (void *cls,
154 const struct GNUNET_CONFIGURATION_Handle *cfg,
155 struct GNUNET_TESTING_Peer *peer)
156{
157 fs = GNUNET_FS_start (cfg, "test-fs-namespace", &progress_cb, NULL,
158 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
159 testNamespace ();
160}
161
162
163int
164main (int argc, char *argv[])
165{
166 if (0 != GNUNET_TESTING_peer_run ("test-fs-namespace-list-updateable",
167 "test_fs_namespace_data.conf",
168 &run, NULL))
169 return 1;
170 do_shutdown ();
171 return err;
172}
173
174
175/* end of test_fs_namespace_list_updateable.c */
diff --git a/src/fs/test_fs_publish.c b/src/fs/test_fs_publish.c
deleted file mode 100644
index 2ffc722da..000000000
--- a/src/fs/test_fs_publish.c
+++ /dev/null
@@ -1,251 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_publish.c
22 * @brief simple testcase for publish operation (indexing, listing
23 * indexed, directory structure)
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_fs_service.h"
30
31/**
32 * File-size we use for testing.
33 */
34#define FILESIZE (1024 * 1024 * 2)
35
36/**
37 * How long until we give up on transmitting the message?
38 */
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
40
41/**
42 * How long should our test-content live?
43 */
44#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
45
46
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static struct GNUNET_FS_PublishContext *publish;
52
53static char *fn1;
54
55static char *fn2;
56
57static int err;
58
59
60static void
61abort_publish_task (void *cls)
62{
63 GNUNET_FS_publish_stop (publish);
64 publish = NULL;
65 GNUNET_DISK_directory_remove (fn1);
66 GNUNET_free (fn1);
67 fn1 = NULL;
68 GNUNET_DISK_directory_remove (fn2);
69 GNUNET_free (fn2);
70 fn2 = NULL;
71}
72
73
74static void *
75progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
76{
77 void *ret;
78
79 ret = NULL;
80 switch (event->status)
81 {
82 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
83 ret = event->value.publish.cctx;
84 printf ("Publish complete, %llu kbps.\n",
85 (unsigned long long) (FILESIZE * 1000000LL
86 / (1
87 + GNUNET_TIME_absolute_get_duration
88 (start).rel_value_us) / 1024));
89 if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
90 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
91 break;
92
93 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
94 ret = event->value.publish.cctx;
95 GNUNET_assert (publish == event->value.publish.pc);
96 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
97 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
98 (unsigned long long) event->value.publish.completed,
99 (unsigned long long) event->value.publish.size,
100 event->value.publish.specifics.progress.depth,
101 (unsigned long long) event->value.publish.specifics.
102 progress.offset);
103 break;
104
105 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
106 ret = event->value.publish.cctx;
107 break;
108
109 case GNUNET_FS_STATUS_PUBLISH_ERROR:
110 ret = event->value.publish.cctx;
111 fprintf (stderr, "Error publishing file: %s\n",
112 event->value.publish.specifics.error.message);
113 err = 1;
114 if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
115 {
116 fprintf (stderr, "Scheduling abort task for error on `%s'\n",
117 (const char *) event->value.publish.cctx);
118 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
119 }
120 break;
121
122 case GNUNET_FS_STATUS_PUBLISH_START:
123 ret = event->value.publish.cctx;
124 if (0 == strcmp ("publish-context1", event->value.publish.cctx))
125 {
126 GNUNET_assert (0 ==
127 strcmp ("publish-context-dir", event->value.publish.pctx));
128 GNUNET_assert (FILESIZE == event->value.publish.size);
129 GNUNET_assert (0 == event->value.publish.completed);
130 GNUNET_assert (1 == event->value.publish.anonymity);
131 }
132 else if (0 == strcmp ("publish-context2", event->value.publish.cctx))
133 {
134 GNUNET_assert (0 ==
135 strcmp ("publish-context-dir", event->value.publish.pctx));
136 GNUNET_assert (FILESIZE == event->value.publish.size);
137 GNUNET_assert (0 == event->value.publish.completed);
138 GNUNET_assert (2 == event->value.publish.anonymity);
139 }
140 else if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
141 {
142 GNUNET_assert (0 == event->value.publish.completed);
143 GNUNET_assert (3 == event->value.publish.anonymity);
144 }
145 else
146 GNUNET_assert (0);
147 break;
148
149 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
150 if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
151 GNUNET_assert (publish == event->value.publish.pc);
152 break;
153
154 default:
155 printf ("Unexpected event: %d\n", event->status);
156 break;
157 }
158 return ret;
159}
160
161
162static void
163run (void *cls,
164 const struct GNUNET_CONFIGURATION_Handle *cfg,
165 struct GNUNET_TESTING_Peer *peer)
166{
167 const char *keywords[] = {
168 "down_foo",
169 "down_bar",
170 };
171 char *buf;
172 struct GNUNET_CONTAINER_MetaData *meta;
173 struct GNUNET_FS_Uri *kuri;
174 struct GNUNET_FS_FileInformation *fi1;
175 struct GNUNET_FS_FileInformation *fi2;
176 struct GNUNET_FS_FileInformation *fidir;
177 size_t i;
178 struct GNUNET_FS_BlockOptions bo;
179
180 fs = GNUNET_FS_start (cfg, "test-fs-publish", &progress_cb, NULL,
181 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
182 GNUNET_assert (NULL != fs);
183 fn1 = GNUNET_DISK_mktemp ("gnunet-publish-test-dst");
184 buf = GNUNET_malloc (FILESIZE);
185 for (i = 0; i < FILESIZE; i++)
186 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
187 (void) GNUNET_DISK_directory_remove (fn1);
188 GNUNET_assert (GNUNET_OK ==
189 GNUNET_DISK_fn_write (fn1, buf, FILESIZE,
190 GNUNET_DISK_PERM_USER_READ
191 | GNUNET_DISK_PERM_USER_WRITE));
192 GNUNET_free (buf);
193
194 fn2 = GNUNET_DISK_mktemp ("gnunet-publish-test-dst");
195 buf = GNUNET_malloc (FILESIZE);
196 for (i = 0; i < FILESIZE; i++)
197 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
198 (void) GNUNET_DISK_directory_remove (fn2);
199 GNUNET_assert (GNUNET_OK ==
200 GNUNET_DISK_fn_write (fn2, buf, FILESIZE,
201 GNUNET_DISK_PERM_USER_READ
202 | GNUNET_DISK_PERM_USER_WRITE));
203 GNUNET_free (buf);
204
205 meta = GNUNET_CONTAINER_meta_data_create ();
206 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
207 bo.content_priority = 42;
208 bo.anonymity_level = 1;
209 bo.replication_level = 0;
210 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
211
212 fi1 =
213 GNUNET_FS_file_information_create_from_file (fs, "publish-context1", fn1,
214 kuri, meta, GNUNET_YES, &bo);
215
216 GNUNET_assert (NULL != fi1);
217 bo.anonymity_level = 2;
218 fi2 =
219 GNUNET_FS_file_information_create_from_file (fs, "publish-context2", fn2,
220 kuri, meta, GNUNET_YES, &bo);
221 GNUNET_assert (NULL != fi2);
222 bo.anonymity_level = 3;
223 fidir =
224 GNUNET_FS_file_information_create_empty_directory (fs,
225 "publish-context-dir",
226 kuri, meta, &bo, NULL);
227 GNUNET_assert (GNUNET_OK == GNUNET_FS_file_information_add (fidir, fi1));
228 GNUNET_assert (GNUNET_OK == GNUNET_FS_file_information_add (fidir, fi2));
229 GNUNET_FS_uri_destroy (kuri);
230 GNUNET_CONTAINER_meta_data_destroy (meta);
231 GNUNET_assert (NULL != fidir);
232 start = GNUNET_TIME_absolute_get ();
233 publish =
234 GNUNET_FS_publish_start (fs, fidir, NULL, NULL, NULL,
235 GNUNET_FS_PUBLISH_OPTION_NONE);
236 GNUNET_assert (publish != NULL);
237}
238
239
240int
241main (int argc, char *argv[])
242{
243 if (0 != GNUNET_TESTING_peer_run ("test-fs-publish",
244 "test_fs_publish_data.conf",
245 &run, NULL))
246 return 1;
247 return err;
248}
249
250
251/* end of test_fs_publish.c */
diff --git a/src/fs/test_fs_publish_data.conf b/src/fs/test_fs_publish_data.conf
deleted file mode 100644
index 0930cdfed..000000000
--- a/src/fs/test_fs_publish_data.conf
+++ /dev/null
@@ -1,10 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-publish/
4
5[transport]
6PLUGINS =
7
8[fs]
9ACTIVEMIGRATION = NO
10
diff --git a/src/fs/test_fs_publish_persistence.c b/src/fs/test_fs_publish_persistence.c
deleted file mode 100644
index da0209793..000000000
--- a/src/fs/test_fs_publish_persistence.c
+++ /dev/null
@@ -1,322 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_publish_persistence.c
22 * @brief simple testcase for persistence of simple publish operation
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_testing_lib.h"
28#include "gnunet_fs_service.h"
29
30
31/**
32 * File-size we use for testing.
33 */
34#define FILESIZE (1024 * 1024 * 2)
35
36/**
37 * How long until we give up on transmitting the message?
38 */
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
40
41/**
42 * How long should our test-content live?
43 */
44#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
45
46
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static const struct GNUNET_CONFIGURATION_Handle *cfg;
52
53static struct GNUNET_FS_PublishContext *publish;
54
55static struct GNUNET_FS_PublishContext *publish;
56
57static char *fn1;
58
59static char *fn2;
60
61static int err;
62
63static struct GNUNET_SCHEDULER_Task *rtask;
64
65
66static void
67abort_publish_task (void *cls)
68{
69 GNUNET_FS_publish_stop (publish);
70 publish = NULL;
71 GNUNET_DISK_directory_remove (fn1);
72 GNUNET_free (fn1);
73 fn1 = NULL;
74 GNUNET_DISK_directory_remove (fn2);
75 GNUNET_free (fn2);
76 fn2 = NULL;
77 GNUNET_FS_stop (fs);
78 fs = NULL;
79 if (NULL != rtask)
80 {
81 GNUNET_SCHEDULER_cancel (rtask);
82 rtask = NULL;
83 }
84}
85
86
87static void *
88progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event);
89
90
91static void
92restart_fs_task (void *cls)
93{
94 rtask = NULL;
95 GNUNET_FS_stop (fs);
96 fs = GNUNET_FS_start (cfg, "test-fs-publish-persistence",
97 &progress_cb, NULL,
98 GNUNET_FS_FLAGS_PERSISTENCE,
99 GNUNET_FS_OPTIONS_END);
100}
101
102
103/**
104 * Consider scheduling the restart-task.
105 * Only runs the restart task once per event
106 * category.
107 *
108 * @param ev type of the event to consider
109 */
110static void
111consider_restart (int ev)
112{
113 static int prev[32];
114 static int off;
115 int i;
116
117 for (i = 0; i < off; i++)
118 if (prev[i] == ev)
119 return;
120 prev[off++] = ev;
121 rtask =
122 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_URGENT,
123 &restart_fs_task, NULL);
124}
125
126
127static void *
128progress_cb (void *cls,
129 const struct GNUNET_FS_ProgressInfo *event)
130{
131 void *ret;
132
133 ret = NULL;
134 switch (event->status)
135 {
136 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
137 ret = event->value.publish.cctx;
138 printf ("Publish complete, %llu kbps.\n",
139 (unsigned long long) (FILESIZE * 1000000LL
140 / (1
141 + GNUNET_TIME_absolute_get_duration
142 (start).rel_value_us) / 1024));
143 if ((NULL != event->value.publish.cctx) &&
144 (0 == strcmp ("publish-context-dir", event->value.publish.cctx)))
145 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
146 break;
147
148 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
149 ret = event->value.publish.cctx;
150 return ret;
151
152 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
153 consider_restart (event->status);
154 ret = event->value.publish.cctx;
155 GNUNET_assert (publish == event->value.publish.pc);
156#if VERBOSE
157 printf ("Publish is progressing (%llu/%llu at level %u off %llu)...\n",
158 (unsigned long long) event->value.publish.completed,
159 (unsigned long long) event->value.publish.size,
160 event->value.publish.specifics.progress.depth,
161 (unsigned long long) event->value.publish.specifics.
162 progress.offset);
163#endif
164 break;
165
166 case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
167 if (event->value.publish.pc == publish)
168 publish = NULL;
169 break;
170
171 case GNUNET_FS_STATUS_PUBLISH_RESUME:
172 if (NULL == publish)
173 {
174 GNUNET_assert (GNUNET_YES ==
175 GNUNET_FS_file_information_is_directory (event->
176 value.publish.
177 fi));
178 publish = event->value.publish.pc;
179 return "publish-context-dir";
180 }
181 break;
182
183 case GNUNET_FS_STATUS_PUBLISH_ERROR:
184 ret = event->value.publish.cctx;
185 fprintf (stderr, "Error publishing file: %s\n",
186 event->value.publish.specifics.error.message);
187 err = 1;
188 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
189 break;
190
191 case GNUNET_FS_STATUS_PUBLISH_START:
192 consider_restart (event->status);
193 publish = event->value.publish.pc;
194 ret = event->value.publish.cctx;
195 if (0 == strcmp ("publish-context1", event->value.publish.cctx))
196 {
197 GNUNET_assert (0 ==
198 strcmp ("publish-context-dir", event->value.publish.pctx));
199 GNUNET_assert (FILESIZE == event->value.publish.size);
200 GNUNET_assert (0 == event->value.publish.completed);
201 GNUNET_assert (1 == event->value.publish.anonymity);
202 }
203 else if (0 == strcmp ("publish-context2", event->value.publish.cctx))
204 {
205 GNUNET_assert (0 ==
206 strcmp ("publish-context-dir", event->value.publish.pctx));
207 GNUNET_assert (FILESIZE == event->value.publish.size);
208 GNUNET_assert (0 == event->value.publish.completed);
209 GNUNET_assert (2 == event->value.publish.anonymity);
210 }
211 else if (0 == strcmp ("publish-context-dir", event->value.publish.cctx))
212 {
213 GNUNET_assert (0 == event->value.publish.completed);
214 GNUNET_assert (3 == event->value.publish.anonymity);
215 }
216 else
217 GNUNET_assert (0);
218 break;
219
220 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
221 consider_restart (event->status);
222 if ((NULL != event->value.publish.cctx) &&
223 (0 == strcmp ("publish-context-dir", event->value.publish.cctx)))
224 GNUNET_assert (publish == event->value.publish.pc);
225 break;
226
227 default:
228 printf ("Unexpected event: %d\n", event->status);
229 break;
230 }
231 return ret;
232}
233
234
235static void
236run (void *cls,
237 const struct GNUNET_CONFIGURATION_Handle *c,
238 struct GNUNET_TESTING_Peer *peer)
239{
240 const char *keywords[] = {
241 "down_foo",
242 "down_bar",
243 };
244 char *buf;
245 struct GNUNET_CONTAINER_MetaData *meta;
246 struct GNUNET_FS_Uri *kuri;
247 struct GNUNET_FS_FileInformation *fi1;
248 struct GNUNET_FS_FileInformation *fi2;
249 struct GNUNET_FS_FileInformation *fidir;
250 size_t i;
251 struct GNUNET_FS_BlockOptions bo;
252
253 cfg = c;
254 fs = GNUNET_FS_start (cfg, "test-fs-publish-persistence", &progress_cb, NULL,
255 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
256 GNUNET_assert (NULL != fs);
257 fn1 = GNUNET_DISK_mktemp ("gnunet-publish-test-dst");
258 buf = GNUNET_malloc (FILESIZE);
259 for (i = 0; i < FILESIZE; i++)
260 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
261 (void) GNUNET_DISK_directory_remove (fn1);
262 GNUNET_assert (GNUNET_OK ==
263 GNUNET_DISK_fn_write (fn1, buf, FILESIZE,
264 GNUNET_DISK_PERM_USER_READ
265 | GNUNET_DISK_PERM_USER_WRITE));
266 GNUNET_free (buf);
267
268 fn2 = GNUNET_DISK_mktemp ("gnunet-publish-test-dst");
269 buf = GNUNET_malloc (FILESIZE);
270 for (i = 0; i < FILESIZE; i++)
271 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
272 (void) GNUNET_DISK_directory_remove (fn2);
273 GNUNET_assert (GNUNET_OK ==
274 GNUNET_DISK_fn_write (fn2, buf, FILESIZE,
275 GNUNET_DISK_PERM_USER_READ
276 | GNUNET_DISK_PERM_USER_WRITE));
277 GNUNET_free (buf);
278
279 meta = GNUNET_CONTAINER_meta_data_create ();
280 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
281 bo.content_priority = 42;
282 bo.anonymity_level = 1;
283 bo.replication_level = 0;
284 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
285 fi1 =
286 GNUNET_FS_file_information_create_from_file (fs, "publish-context1", fn1,
287 kuri, meta, GNUNET_YES, &bo);
288 GNUNET_assert (NULL != fi1);
289 bo.anonymity_level = 2;
290 fi2 =
291 GNUNET_FS_file_information_create_from_file (fs, "publish-context2", fn2,
292 kuri, meta, GNUNET_YES, &bo);
293 GNUNET_assert (NULL != fi2);
294 bo.anonymity_level = 3;
295 fidir =
296 GNUNET_FS_file_information_create_empty_directory (fs,
297 "publish-context-dir",
298 kuri, meta, &bo, NULL);
299 GNUNET_assert (GNUNET_OK == GNUNET_FS_file_information_add (fidir, fi1));
300 GNUNET_assert (GNUNET_OK == GNUNET_FS_file_information_add (fidir, fi2));
301 GNUNET_FS_uri_destroy (kuri);
302 GNUNET_CONTAINER_meta_data_destroy (meta);
303 GNUNET_assert (NULL != fidir);
304 start = GNUNET_TIME_absolute_get ();
305 GNUNET_FS_publish_start (fs, fidir, NULL, NULL, NULL,
306 GNUNET_FS_PUBLISH_OPTION_NONE);
307 GNUNET_assert (publish != NULL);
308}
309
310
311int
312main (int argc, char *argv[])
313{
314 if (0 != GNUNET_TESTING_peer_run ("test-fs-publish-persistence",
315 "test_fs_publish_data.conf",
316 &run, NULL))
317 return 1;
318 return err;
319}
320
321
322/* end of test_fs_publish_persistence.c */
diff --git a/src/fs/test_fs_search.c b/src/fs/test_fs_search.c
deleted file mode 100644
index b392cc8ac..000000000
--- a/src/fs/test_fs_search.c
+++ /dev/null
@@ -1,252 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_search.c
22 * @brief simple testcase for simple publish + search operation
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_testing_lib.h"
28#include "gnunet_fs_service.h"
29
30
31/**
32 * File-size we use for testing.
33 */
34#define FILESIZE 1024
35
36/**
37 * How long until we give up on transmitting the message?
38 */
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
40
41/**
42 * How long should our test-content live?
43 */
44#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
45
46
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static struct GNUNET_FS_SearchContext *search;
52
53static struct GNUNET_FS_PublishContext *publish;
54
55static struct GNUNET_SCHEDULER_Task *timeout_task;
56
57static int err;
58
59
60static void
61abort_publish_task (void *cls)
62{
63 if (NULL != publish)
64 {
65 GNUNET_FS_publish_stop (publish);
66 publish = NULL;
67 }
68 if (NULL != timeout_task)
69 {
70 GNUNET_SCHEDULER_cancel (timeout_task);
71 timeout_task = NULL;
72 }
73}
74
75
76static void
77abort_error (void *cls)
78{
79 fprintf (stderr,
80 "Timeout\n");
81 timeout_task = NULL;
82 if (NULL != search)
83 {
84 GNUNET_FS_search_stop (search);
85 search = NULL;
86 }
87 if (NULL != publish)
88 {
89 GNUNET_FS_publish_stop (publish);
90 publish = NULL;
91 }
92 err = 1;
93}
94
95
96static void
97abort_search_task (void *cls)
98{
99 if (NULL != search)
100 {
101 GNUNET_FS_search_stop (search);
102 search = NULL;
103 }
104}
105
106
107static void *
108progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
109{
110 const char *keywords[] = {
111 "down_foo"
112 };
113 struct GNUNET_FS_Uri *kuri;
114
115 switch (event->status)
116 {
117 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
118 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
119 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
120 (unsigned long long) event->value.publish.completed,
121 (unsigned long long) event->value.publish.size,
122 event->value.publish.specifics.progress.depth,
123 (unsigned long long) event->value.publish.specifics.
124 progress.offset);
125 break;
126
127 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
128 break;
129
130 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
131 kuri = GNUNET_FS_uri_ksk_create_from_args (1, keywords);
132 start = GNUNET_TIME_absolute_get ();
133 search =
134 GNUNET_FS_search_start (fs, kuri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
135 "search");
136 GNUNET_FS_uri_destroy (kuri);
137 GNUNET_assert (search != NULL);
138 break;
139
140 case GNUNET_FS_STATUS_SEARCH_RESULT:
141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
142 "Search complete.\n");
143 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
144 break;
145
146 case GNUNET_FS_STATUS_PUBLISH_ERROR:
147 fprintf (stderr, "Error publishing file: %s\n",
148 event->value.publish.specifics.error.message);
149 GNUNET_break (0);
150 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
151 break;
152
153 case GNUNET_FS_STATUS_SEARCH_ERROR:
154 fprintf (stderr, "Error searching file: %s\n",
155 event->value.search.specifics.error.message);
156 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
157 break;
158
159 case GNUNET_FS_STATUS_PUBLISH_START:
160 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
161 GNUNET_assert (NULL == event->value.publish.pctx);
162 GNUNET_assert (FILESIZE == event->value.publish.size);
163 GNUNET_assert (0 == event->value.publish.completed);
164 GNUNET_assert (1 == event->value.publish.anonymity);
165 break;
166
167 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
168 GNUNET_assert (publish == event->value.publish.pc);
169 GNUNET_assert (FILESIZE == event->value.publish.size);
170 GNUNET_assert (1 == event->value.publish.anonymity);
171 GNUNET_FS_stop (fs);
172 fs = NULL;
173 break;
174
175 case GNUNET_FS_STATUS_SEARCH_START:
176 GNUNET_assert (search == NULL);
177 GNUNET_assert (0 == strcmp ("search", event->value.search.cctx));
178 GNUNET_assert (1 == event->value.search.anonymity);
179 break;
180
181 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
182 break;
183
184 case GNUNET_FS_STATUS_SEARCH_STOPPED:
185 GNUNET_assert (search == event->value.search.sc);
186 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
187 break;
188
189 default:
190 fprintf (stderr, "Unexpected event: %d\n", event->status);
191 break;
192 }
193 return NULL;
194}
195
196
197static void
198run (void *cls,
199 const struct GNUNET_CONFIGURATION_Handle *cfg,
200 struct GNUNET_TESTING_Peer *peer)
201{
202 const char *keywords[] = {
203 "down_foo",
204 "down_bar"
205 };
206 char *buf;
207 struct GNUNET_CONTAINER_MetaData *meta;
208 struct GNUNET_FS_Uri *kuri;
209 struct GNUNET_FS_BlockOptions bo;
210 struct GNUNET_FS_FileInformation *fi;
211 size_t i;
212
213 fs = GNUNET_FS_start (cfg, "test-fs-search", &progress_cb, NULL,
214 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
215 GNUNET_assert (NULL != fs);
216 buf = GNUNET_malloc (FILESIZE);
217 for (i = 0; i < FILESIZE; i++)
218 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
219 meta = GNUNET_CONTAINER_meta_data_create ();
220 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
221 bo.content_priority = 42;
222 bo.anonymity_level = 1;
223 bo.replication_level = 0;
224 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
225 fi = GNUNET_FS_file_information_create_from_data (fs, "publish-context",
226 FILESIZE, buf, kuri, meta,
227 GNUNET_NO, &bo);
228 GNUNET_FS_uri_destroy (kuri);
229 GNUNET_CONTAINER_meta_data_destroy (meta);
230 GNUNET_assert (NULL != fi);
231 start = GNUNET_TIME_absolute_get ();
232 publish =
233 GNUNET_FS_publish_start (fs, fi, NULL, NULL, NULL,
234 GNUNET_FS_PUBLISH_OPTION_NONE);
235 GNUNET_assert (publish != NULL);
236 timeout_task = GNUNET_SCHEDULER_add_delayed (LIFETIME,
237 &abort_error, NULL);
238}
239
240
241int
242main (int argc, char *argv[])
243{
244 if (0 != GNUNET_TESTING_peer_run ("test-fs-search",
245 "test_fs_search_data.conf",
246 &run, NULL))
247 return 1;
248 return err;
249}
250
251
252/* end of test_fs_search.c */
diff --git a/src/fs/test_fs_search_data.conf b/src/fs/test_fs_search_data.conf
deleted file mode 100644
index 8b24e328d..000000000
--- a/src/fs/test_fs_search_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-search/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_fs_search_persistence.c b/src/fs/test_fs_search_persistence.c
deleted file mode 100644
index d523a6404..000000000
--- a/src/fs/test_fs_search_persistence.c
+++ /dev/null
@@ -1,318 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_search_persistence.c
22 * @brief simple testcase for persistence of search operation
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_testing_lib.h"
28#include "gnunet_fs_service.h"
29
30
31/**
32 * File-size we use for testing.
33 */
34#define FILESIZE 1024
35
36/**
37 * How long until we give up on transmitting the message?
38 */
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
40
41/**
42 * How long should our test-content live?
43 */
44#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
45
46
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static struct GNUNET_FS_SearchContext *search;
52
53static struct GNUNET_FS_PublishContext *publish;
54
55static const struct GNUNET_CONFIGURATION_Handle *cfg;
56
57static struct GNUNET_SCHEDULER_Task *timeout_task;
58
59static int err;
60
61
62static void
63abort_error (void *cls)
64{
65 timeout_task = NULL;
66 fprintf (stderr,
67 "Timeout\n");
68 if (NULL != search)
69 {
70 GNUNET_FS_search_stop (search);
71 search = NULL;
72 }
73 if (NULL != publish)
74 {
75 GNUNET_FS_publish_stop (publish);
76 publish = NULL;
77 }
78 err = 1;
79}
80
81
82static void
83abort_publish_task (void *cls)
84{
85 if (NULL != publish)
86 {
87 GNUNET_FS_publish_stop (publish);
88 publish = NULL;
89 }
90 if (NULL != timeout_task)
91 {
92 GNUNET_SCHEDULER_cancel (timeout_task);
93 timeout_task = NULL;
94 }
95}
96
97
98static void
99abort_search_task (void *cls)
100{
101 if (NULL != search)
102 {
103 GNUNET_FS_search_stop (search);
104 search = NULL;
105 }
106}
107
108
109static void *
110progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event);
111
112
113static void
114restart_fs_task (void *cls)
115{
116 GNUNET_FS_stop (fs);
117 fs = GNUNET_FS_start (cfg, "test-fs-search-persistence", &progress_cb, NULL,
118 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
119}
120
121
122/**
123 * Consider scheduling the restart-task.
124 * Only runs the restart task once per event
125 * category.
126 *
127 * @param ev type of the event to consider
128 */
129static void
130consider_restart (int ev)
131{
132 static int prev[32];
133 static int off;
134 int i;
135
136 for (i = 0; i < off; i++)
137 if (prev[i] == ev)
138 return;
139 prev[off++] = ev;
140 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_URGENT,
141 &restart_fs_task, NULL);
142}
143
144
145static void *
146progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
147{
148 const char *keywords[] = {
149 "down_foo"
150 };
151 struct GNUNET_FS_Uri *kuri;
152
153 switch (event->status)
154 {
155 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
157 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
158 (unsigned long long) event->value.publish.completed,
159 (unsigned long long) event->value.publish.size,
160 event->value.publish.specifics.progress.depth,
161 (unsigned long long) event->value.publish.specifics.
162 progress.offset);
163 break;
164
165 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
166 break;
167
168 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
169 kuri = GNUNET_FS_uri_ksk_create_from_args (1, keywords);
170 start = GNUNET_TIME_absolute_get ();
171 GNUNET_FS_search_start (fs, kuri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
172 "search");
173 GNUNET_FS_uri_destroy (kuri);
174 GNUNET_assert (search != NULL);
175 break;
176
177 case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
178 if (event->value.publish.pc == publish)
179 publish = NULL;
180 break;
181
182 case GNUNET_FS_STATUS_PUBLISH_RESUME:
183 if (NULL == publish)
184 publish = event->value.publish.pc;
185 break;
186
187 case GNUNET_FS_STATUS_SEARCH_RESULT:
188 /* FIXME: consider_restart (event->status); cannot be tested with
189 * search result since we exit here after the first one... */
190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191 "Search complete.\n");
192 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
193 break;
194
195 case GNUNET_FS_STATUS_PUBLISH_ERROR:
196 fprintf (stderr, "Error publishing file: %s\n",
197 event->value.publish.specifics.error.message);
198 GNUNET_break (0);
199 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
200 break;
201
202 case GNUNET_FS_STATUS_SEARCH_ERROR:
203 fprintf (stderr, "Error searching file: %s\n",
204 event->value.search.specifics.error.message);
205 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
206 break;
207
208 case GNUNET_FS_STATUS_SEARCH_SUSPEND:
209 if (event->value.search.sc == search)
210 search = NULL;
211 break;
212
213 case GNUNET_FS_STATUS_SEARCH_RESUME:
214 if (NULL == search)
215 {
216 search = event->value.search.sc;
217 return "search";
218 }
219 break;
220
221 case GNUNET_FS_STATUS_PUBLISH_START:
222 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
223 GNUNET_assert (NULL == event->value.publish.pctx);
224 GNUNET_assert (FILESIZE == event->value.publish.size);
225 GNUNET_assert (0 == event->value.publish.completed);
226 GNUNET_assert (1 == event->value.publish.anonymity);
227 break;
228
229 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
230 GNUNET_assert (publish == event->value.publish.pc);
231 GNUNET_assert (FILESIZE == event->value.publish.size);
232 GNUNET_assert (1 == event->value.publish.anonymity);
233 GNUNET_FS_stop (fs);
234 fs = NULL;
235 break;
236
237 case GNUNET_FS_STATUS_SEARCH_START:
238 consider_restart (event->status);
239 GNUNET_assert (search == NULL);
240 search = event->value.search.sc;
241 GNUNET_assert (0 == strcmp ("search", event->value.search.cctx));
242 GNUNET_assert (1 == event->value.search.anonymity);
243 break;
244
245 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
246 break;
247
248 case GNUNET_FS_STATUS_SEARCH_STOPPED:
249 GNUNET_assert (search == event->value.search.sc);
250 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
251 search = NULL;
252 break;
253
254 default:
255 fprintf (stderr, "Unexpected event: %d\n", event->status);
256 break;
257 }
258 return NULL;
259}
260
261
262static void
263run (void *cls,
264 const struct GNUNET_CONFIGURATION_Handle *c,
265 struct GNUNET_TESTING_Peer *peer)
266{
267 const char *keywords[] = {
268 "down_foo",
269 "down_bar"
270 };
271 char *buf;
272 struct GNUNET_CONTAINER_MetaData *meta;
273 struct GNUNET_FS_Uri *kuri;
274 struct GNUNET_FS_FileInformation *fi;
275 size_t i;
276 struct GNUNET_FS_BlockOptions bo;
277
278 cfg = c;
279 fs = GNUNET_FS_start (cfg, "test-fs-search-persistence", &progress_cb, NULL,
280 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
281 GNUNET_assert (NULL != fs);
282 buf = GNUNET_malloc (FILESIZE);
283 for (i = 0; i < FILESIZE; i++)
284 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
285 meta = GNUNET_CONTAINER_meta_data_create ();
286 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
287 bo.content_priority = 42;
288 bo.anonymity_level = 1;
289 bo.replication_level = 0;
290 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
291 fi = GNUNET_FS_file_information_create_from_data (fs, "publish-context",
292 FILESIZE, buf, kuri, meta,
293 GNUNET_NO, &bo);
294 GNUNET_FS_uri_destroy (kuri);
295 GNUNET_CONTAINER_meta_data_destroy (meta);
296 GNUNET_assert (NULL != fi);
297 start = GNUNET_TIME_absolute_get ();
298 publish =
299 GNUNET_FS_publish_start (fs, fi, NULL, NULL, NULL,
300 GNUNET_FS_PUBLISH_OPTION_NONE);
301 GNUNET_assert (publish != NULL);
302 timeout_task = GNUNET_SCHEDULER_add_delayed (LIFETIME,
303 &abort_error, NULL);
304}
305
306
307int
308main (int argc, char *argv[])
309{
310 if (0 != GNUNET_TESTING_peer_run ("test-fs-search-persistence",
311 "test_fs_search_data.conf",
312 &run, NULL))
313 return 1;
314 return err;
315}
316
317
318/* end of test_fs_search_persistence.c */
diff --git a/src/fs/test_fs_search_probes.c b/src/fs/test_fs_search_probes.c
deleted file mode 100644
index b3c717aa1..000000000
--- a/src/fs/test_fs_search_probes.c
+++ /dev/null
@@ -1,258 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_search_probes.c
23 * @brief simple testcase for publish + search operation with probes
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_fs_service.h"
30
31
32/**
33 * File-size we use for testing.
34 */
35#define FILESIZE 1024
36
37/**
38 * How long until we give up on transmitting the message?
39 */
40#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
41
42/**
43 * How long should our test-content live?
44 */
45#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
46
47
48static struct GNUNET_TIME_Absolute start;
49
50static struct GNUNET_FS_Handle *fs;
51
52static struct GNUNET_FS_SearchContext *search;
53
54static struct GNUNET_FS_PublishContext *publish;
55
56static struct GNUNET_SCHEDULER_Task *timeout_task;
57
58static int err;
59
60
61static void
62abort_error (void *cls)
63{
64 timeout_task = NULL;
65 fprintf (stderr,
66 "Timeout\n");
67 if (NULL != search)
68 {
69 GNUNET_FS_search_stop (search);
70 search = NULL;
71 }
72 if (NULL != publish)
73 {
74 GNUNET_FS_publish_stop (publish);
75 publish = NULL;
76 }
77 err = 1;
78}
79
80
81static void
82abort_publish_task (void *cls)
83{
84 if (NULL != publish)
85 {
86 GNUNET_FS_publish_stop (publish);
87 publish = NULL;
88 }
89 if (NULL != timeout_task)
90 {
91 GNUNET_SCHEDULER_cancel (timeout_task);
92 timeout_task = NULL;
93 }
94}
95
96
97static void
98abort_search_task (void *cls)
99{
100 if (search != NULL)
101 GNUNET_FS_search_stop (search);
102 search = NULL;
103}
104
105
106static void *
107progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
108{
109 const char *keywords[] = {
110 "down_foo"
111 };
112 struct GNUNET_FS_Uri *kuri;
113
114 switch (event->status)
115 {
116 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
117 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
118 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
119 (unsigned long long) event->value.publish.completed,
120 (unsigned long long) event->value.publish.size,
121 event->value.publish.specifics.progress.depth,
122 (unsigned long long) event->value.publish.specifics.
123 progress.offset);
124 break;
125
126 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
127 break;
128
129 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
130 kuri = GNUNET_FS_uri_ksk_create_from_args (1, keywords);
131 start = GNUNET_TIME_absolute_get ();
132 search =
133 GNUNET_FS_search_start (fs, kuri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
134 "search");
135 GNUNET_FS_uri_destroy (kuri);
136 GNUNET_assert (search != NULL);
137 break;
138
139 case GNUNET_FS_STATUS_SEARCH_RESULT:
140 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Search complete.\n");
141 break;
142
143 case GNUNET_FS_STATUS_PUBLISH_ERROR:
144 fprintf (stderr, "Error publishing file: %s\n",
145 event->value.publish.specifics.error.message);
146 GNUNET_break (0);
147 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
148 break;
149
150 case GNUNET_FS_STATUS_SEARCH_ERROR:
151 fprintf (stderr, "Error searching file: %s\n",
152 event->value.search.specifics.error.message);
153 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
154 break;
155
156 case GNUNET_FS_STATUS_PUBLISH_START:
157 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
158 GNUNET_assert (NULL == event->value.publish.pctx);
159 GNUNET_assert (FILESIZE == event->value.publish.size);
160 GNUNET_assert (0 == event->value.publish.completed);
161 GNUNET_assert (1 == event->value.publish.anonymity);
162 break;
163
164 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
165 GNUNET_assert (publish == event->value.publish.pc);
166 GNUNET_assert (FILESIZE == event->value.publish.size);
167 GNUNET_assert (1 == event->value.publish.anonymity);
168 GNUNET_FS_stop (fs);
169 fs = NULL;
170 break;
171
172 case GNUNET_FS_STATUS_SEARCH_UPDATE:
173 if ((0 < event->value.search.specifics.update.availability_rank) &&
174 (0 < event->value.search.specifics.update.availability_certainty))
175 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
176 break;
177
178 case GNUNET_FS_STATUS_SEARCH_START:
179 GNUNET_assert (search == NULL);
180 GNUNET_assert (0 == strcmp ("search", event->value.search.cctx));
181 GNUNET_assert (1 == event->value.search.anonymity);
182 break;
183
184 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
185 break;
186
187 case GNUNET_FS_STATUS_SEARCH_STOPPED:
188 GNUNET_assert (search == event->value.search.sc);
189 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
190 break;
191
192 default:
193 fprintf (stderr,
194 "Unexpected event: %d\n",
195 event->status);
196 break;
197 }
198 return NULL;
199}
200
201
202static void
203run (void *cls,
204 const struct GNUNET_CONFIGURATION_Handle *cfg,
205 struct GNUNET_TESTING_Peer *peer)
206{
207 const char *keywords[] = {
208 "down_foo",
209 "down_bar"
210 };
211 char *buf;
212 struct GNUNET_CONTAINER_MetaData *meta;
213 struct GNUNET_FS_Uri *kuri;
214 struct GNUNET_FS_BlockOptions bo;
215 struct GNUNET_FS_FileInformation *fi;
216 size_t i;
217
218 fs = GNUNET_FS_start (cfg, "test-fs-search", &progress_cb, NULL,
219 GNUNET_FS_FLAGS_DO_PROBES,
220 GNUNET_FS_OPTIONS_END);
221 GNUNET_assert (NULL != fs);
222 buf = GNUNET_malloc (FILESIZE);
223 for (i = 0; i < FILESIZE; i++)
224 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
225 meta = GNUNET_CONTAINER_meta_data_create ();
226 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
227 bo.content_priority = 42;
228 bo.anonymity_level = 1;
229 bo.replication_level = 0;
230 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
231 fi = GNUNET_FS_file_information_create_from_data (fs, "publish-context",
232 FILESIZE, buf, kuri, meta,
233 GNUNET_NO, &bo);
234 GNUNET_FS_uri_destroy (kuri);
235 GNUNET_CONTAINER_meta_data_destroy (meta);
236 GNUNET_assert (NULL != fi);
237 start = GNUNET_TIME_absolute_get ();
238 publish =
239 GNUNET_FS_publish_start (fs, fi, NULL, NULL, NULL,
240 GNUNET_FS_PUBLISH_OPTION_NONE);
241 GNUNET_assert (publish != NULL);
242 timeout_task = GNUNET_SCHEDULER_add_delayed (LIFETIME,
243 &abort_error, NULL);
244}
245
246
247int
248main (int argc, char *argv[])
249{
250 if (0 != GNUNET_TESTING_peer_run ("test-fs-search-probes",
251 "test_fs_search_data.conf",
252 &run, NULL))
253 return 1;
254 return err;
255}
256
257
258/* end of test_fs_search_probes.c */
diff --git a/src/fs/test_fs_search_with_and.c b/src/fs/test_fs_search_with_and.c
deleted file mode 100644
index 2187f4531..000000000
--- a/src/fs/test_fs_search_with_and.c
+++ /dev/null
@@ -1,272 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_search_with_and.c
22 * @brief testcase for publishing multiple files and search with a and operator
23 * @author Bruno Cabral - 99% based on Christian Grothoff code
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_testing_lib.h"
28#include "gnunet_fs_service.h"
29
30
31/**
32 * File-size we use for testing.
33 */
34#define FILESIZE 1024
35
36/**
37 * Number of files for testing.
38 */
39#define NUM_FILES 10
40
41
42/**
43 * How long until we give up on transmitting the message?
44 */
45#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
46
47/**
48 * How long should our test-content live?
49 */
50#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
51
52
53static struct GNUNET_TIME_Absolute start;
54
55static struct GNUNET_FS_Handle *fs;
56
57static struct GNUNET_FS_SearchContext *search;
58
59static struct GNUNET_FS_PublishContext *publish;
60
61static struct GNUNET_SCHEDULER_Task *timeout_task;
62
63static int err;
64
65static int processed_files;
66
67
68static void
69abort_publish_task (void *cls)
70{
71 if (NULL != publish)
72 {
73 GNUNET_FS_publish_stop (publish);
74 publish = NULL;
75 }
76 if (NULL != timeout_task)
77 {
78 GNUNET_SCHEDULER_cancel (timeout_task);
79 timeout_task = NULL;
80 }
81}
82
83
84static void
85abort_error (void *cls)
86{
87 fprintf (stderr,
88 "Timeout\n");
89 timeout_task = NULL;
90 if (NULL != search)
91 {
92 GNUNET_FS_search_stop (search);
93 search = NULL;
94 }
95 if (NULL != publish)
96 {
97 GNUNET_FS_publish_stop (publish);
98 publish = NULL;
99 }
100 err = 1;
101}
102
103
104static void
105abort_search_task (void *cls)
106{
107 if (NULL != search)
108 {
109 GNUNET_FS_search_stop (search);
110 search = NULL;
111 }
112}
113
114
115static void *
116progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
117{
118 struct GNUNET_FS_Uri *kuri;
119
120 switch (event->status)
121 {
122 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
123 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
124 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
125 (unsigned long long) event->value.publish.completed,
126 (unsigned long long) event->value.publish.size,
127 event->value.publish.specifics.progress.depth,
128 (unsigned long long) event->value.publish.specifics.
129 progress.offset);
130 break;
131
132 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
133 break;
134
135 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
136 processed_files++;
137 if (processed_files == NUM_FILES)
138 {
139 char *emsg = NULL;
140 kuri = GNUNET_FS_uri_ksk_create ("+down_foo +down_bar", &emsg);
141 GNUNET_assert (kuri != NULL);
142
143 start = GNUNET_TIME_absolute_get ();
144 search =
145 GNUNET_FS_search_start (fs, kuri, 1, GNUNET_FS_SEARCH_OPTION_NONE,
146 "search");
147 GNUNET_FS_uri_destroy (kuri);
148 GNUNET_assert (search != NULL);
149 }
150 break;
151
152 case GNUNET_FS_STATUS_SEARCH_RESULT:
153 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
154 "Search complete.\n");
155 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
156 break;
157
158 case GNUNET_FS_STATUS_PUBLISH_ERROR:
159 fprintf (stderr, "Error publishing file: %s\n",
160 event->value.publish.specifics.error.message);
161 GNUNET_break (0);
162 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
163 break;
164
165 case GNUNET_FS_STATUS_SEARCH_ERROR:
166 fprintf (stderr, "Error searching file: %s\n",
167 event->value.search.specifics.error.message);
168 GNUNET_SCHEDULER_add_now (&abort_search_task, NULL);
169 break;
170
171 case GNUNET_FS_STATUS_PUBLISH_START:
172 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
173 GNUNET_assert (NULL == event->value.publish.pctx);
174 GNUNET_assert (FILESIZE == event->value.publish.size);
175 GNUNET_assert (0 == event->value.publish.completed);
176 GNUNET_assert (1 == event->value.publish.anonymity);
177 break;
178
179 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
180 GNUNET_assert (publish == event->value.publish.pc);
181 GNUNET_assert (FILESIZE == event->value.publish.size);
182 GNUNET_assert (1 == event->value.publish.anonymity);
183 GNUNET_FS_stop (fs);
184 fs = NULL;
185 break;
186
187 case GNUNET_FS_STATUS_SEARCH_START:
188 GNUNET_assert (search == NULL);
189 GNUNET_assert (0 == strcmp ("search", event->value.search.cctx));
190 GNUNET_assert (1 == event->value.search.anonymity);
191 break;
192
193 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
194 break;
195
196 case GNUNET_FS_STATUS_SEARCH_STOPPED:
197 GNUNET_assert (search == event->value.search.sc);
198 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
199 break;
200
201 default:
202 fprintf (stderr, "Unexpected event: %d\n", event->status);
203 break;
204 }
205 return NULL;
206}
207
208
209static void
210run (void *cls,
211 const struct GNUNET_CONFIGURATION_Handle *cfg,
212 struct GNUNET_TESTING_Peer *peer)
213{
214 const char *keywords[] = {
215 "down_foo",
216 "down_bar"
217 };
218 char *buf;
219 struct GNUNET_CONTAINER_MetaData *meta;
220 struct GNUNET_FS_Uri *kuri;
221 struct GNUNET_FS_BlockOptions bo;
222 struct GNUNET_FS_FileInformation *fi;
223 size_t i;
224 size_t j;
225
226 fs = GNUNET_FS_start (cfg, "test-fs-search", &progress_cb, NULL,
227 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
228 GNUNET_assert (NULL != fs);
229
230 processed_files = 0;
231 for (j = 0; j < NUM_FILES; j++)
232 {
233 buf = GNUNET_malloc (FILESIZE);
234 for (i = 0; i < FILESIZE; i++)
235 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
236 meta = GNUNET_CONTAINER_meta_data_create ();
237 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
238 bo.content_priority = 42;
239 bo.anonymity_level = 1;
240 bo.replication_level = 0;
241 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
242 fi = GNUNET_FS_file_information_create_from_data (fs, "publish-context",
243 FILESIZE, buf, kuri, meta,
244 GNUNET_NO, &bo);
245 GNUNET_FS_uri_destroy (kuri);
246 GNUNET_CONTAINER_meta_data_destroy (meta);
247 GNUNET_assert (NULL != fi);
248 start = GNUNET_TIME_absolute_get ();
249 publish =
250 GNUNET_FS_publish_start (fs, fi, NULL, NULL, NULL,
251 GNUNET_FS_PUBLISH_OPTION_NONE);
252 GNUNET_assert (publish != NULL);
253 }
254
255
256 timeout_task = GNUNET_SCHEDULER_add_delayed (LIFETIME,
257 &abort_error, NULL);
258}
259
260
261int
262main (int argc, char *argv[])
263{
264 if (0 != GNUNET_TESTING_peer_run ("test-fs-search-with-and",
265 "test_fs_search_data.conf",
266 &run, NULL))
267 return 1;
268 return err;
269}
270
271
272/* end of test_fs_search.c */
diff --git a/src/fs/test_fs_start_stop.c b/src/fs/test_fs_start_stop.c
deleted file mode 100644
index 509fbe76a..000000000
--- a/src/fs/test_fs_start_stop.c
+++ /dev/null
@@ -1,64 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_start_stop.c
22 * @brief testcase for fs.c (start-stop only)
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_fs_service.h"
30
31
32static void *
33progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
34{
35 return NULL;
36}
37
38
39static void
40run (void *cls,
41 const struct GNUNET_CONFIGURATION_Handle *cfg,
42 struct GNUNET_TESTING_Peer *peer)
43{
44 struct GNUNET_FS_Handle *fs;
45
46 fs = GNUNET_FS_start (cfg, "test-fs-start-stop", &progress_cb, NULL,
47 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
48 GNUNET_assert (NULL != fs);
49 GNUNET_FS_stop (fs);
50}
51
52
53int
54main (int argc, char *argv[])
55{
56 if (0 != GNUNET_TESTING_peer_run ("test-fs-start-stop",
57 "test_fs_data.conf",
58 &run, NULL))
59 return 1;
60 return 0;
61}
62
63
64/* end of test_fs_start_stop.c */
diff --git a/src/fs/test_fs_test_lib.c b/src/fs/test_fs_test_lib.c
deleted file mode 100644
index 7f0077bdd..000000000
--- a/src/fs/test_fs_test_lib.c
+++ /dev/null
@@ -1,179 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_test_lib.c
23 * @brief test fs test library
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "fs_test_lib.h"
28
29#define VERBOSE GNUNET_NO
30
31/**
32 * File-size we use for testing.
33 */
34#define FILESIZE (1024 * 1024 * 2)
35
36/**
37 * How long until we give up on transmitting the message?
38 */
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
40
41#define NUM_DAEMONS 2
42
43#define SEED 42
44
45static struct GNUNET_TESTBED_Peer *the_peers[NUM_DAEMONS];
46
47static struct GNUNET_TIME_Absolute start_time;
48
49static int ret;
50
51
52static void
53do_stop (void *cls)
54{
55 char *fn = cls;
56
57 if (0 ==
58 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
59 TIMEOUT)).
60 rel_value_us)
61 {
62 GNUNET_break (0);
63 ret = 1;
64 }
65 else
66 {
67 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
68 "Finished download, shutting down\n");
69 }
70 if (NULL != fn)
71 {
72 GNUNET_DISK_directory_remove (fn);
73 GNUNET_free (fn);
74 }
75 GNUNET_SCHEDULER_shutdown ();
76}
77
78
79static void
80do_download (void *cls,
81 const struct GNUNET_FS_Uri *uri,
82 const char *fn)
83{
84 if (NULL == uri)
85 {
86 GNUNET_break (0);
87 GNUNET_SCHEDULER_shutdown ();
88 ret = 1;
89 return;
90 }
91 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
92 "Downloading %llu bytes\n",
93 (unsigned long long) FILESIZE);
94 start_time = GNUNET_TIME_absolute_get ();
95 GNUNET_FS_TEST_download (the_peers[0],
96 TIMEOUT, 1, SEED,
97 uri,
98 VERBOSE,
99 &do_stop,
100 (NULL == fn) ? NULL : GNUNET_strdup (fn));
101}
102
103
104static void
105do_publish (void *cls,
106 struct GNUNET_TESTBED_Operation *op,
107 const char *emsg)
108{
109 GNUNET_TESTBED_operation_done (op);
110 if (NULL != emsg)
111 {
112 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to connect peers: %s\n", emsg);
113 GNUNET_break (0);
114 ret = 1;
115 GNUNET_SCHEDULER_shutdown ();
116 return;
117 }
118 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
119 (unsigned long long) FILESIZE);
120 GNUNET_FS_TEST_publish (the_peers[0], TIMEOUT, 1, GNUNET_NO, FILESIZE, SEED,
121 VERBOSE, &do_download, NULL);
122}
123
124
125/**
126 * Actual main function for the test.
127 *
128 * @param cls closure
129 * @param h the run handle
130 * @param num_peers number of peers in 'peers'
131 * @param peers handle to peers run in the testbed
132 * @param links_succeeded the number of overlay link connection attempts that
133 * succeeded
134 * @param links_failed the number of overlay link connection attempts that
135 * failed
136 */
137static void
138run (void *cls,
139 struct GNUNET_TESTBED_RunHandle *h,
140 unsigned int num_peers,
141 struct GNUNET_TESTBED_Peer **peers,
142 unsigned int links_succeeded,
143 unsigned int links_failed)
144{
145 unsigned int i;
146
147 GNUNET_assert (NUM_DAEMONS == num_peers);
148 for (i = 0; i < num_peers; i++)
149 the_peers[i] = peers[i];
150 GNUNET_TESTBED_overlay_connect (NULL,
151 &do_publish,
152 NULL,
153 peers[0],
154 peers[1]);
155}
156
157
158/**
159 * Main function that initializes the testbed.
160 *
161 * @param argc ignored
162 * @param argv ignored
163 * @return 0 on success
164 */
165int
166main (int argc, char *argv[])
167{
168 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-fs-lib/");
169 (void) GNUNET_TESTBED_test_run ("test_fs_test_lib",
170 "fs_test_lib_data.conf",
171 NUM_DAEMONS,
172 0, NULL, NULL,
173 &run, NULL);
174 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-fs-lib/");
175 return ret;
176}
177
178
179/* end of test_fs_test_lib.c */
diff --git a/src/fs/test_fs_unindex.c b/src/fs/test_fs_unindex.c
deleted file mode 100644
index 992672244..000000000
--- a/src/fs/test_fs_unindex.c
+++ /dev/null
@@ -1,237 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_unindex.c
23 * @brief simple testcase for simple publish + unindex operation
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_fs_service.h"
29#include "gnunet_testing_lib.h"
30
31
32/**
33 * File-size we use for testing.
34 */
35#define FILESIZE (1024 * 1024 * 2)
36
37/**
38 * How long until we give up on transmitting the message?
39 */
40#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
41
42/**
43 * How long should our test-content live?
44 */
45#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
46
47
48static struct GNUNET_TIME_Absolute start;
49
50static struct GNUNET_FS_Handle *fs;
51
52static struct GNUNET_FS_UnindexContext *unindex;
53
54static struct GNUNET_FS_PublishContext *publish;
55
56static char *fn;
57
58
59static void
60abort_publish_task (void *cls)
61{
62 GNUNET_FS_publish_stop (publish);
63 publish = NULL;
64}
65
66
67static void
68abort_unindex_task (void *cls)
69{
70 GNUNET_FS_unindex_stop (unindex);
71 unindex = NULL;
72 GNUNET_DISK_directory_remove (fn);
73 GNUNET_free (fn);
74 fn = NULL;
75}
76
77
78static void *
79progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
80{
81 switch (event->status)
82 {
83 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
84 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
85 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
86 (unsigned long long) event->value.publish.completed,
87 (unsigned long long) event->value.publish.size,
88 event->value.publish.specifics.progress.depth,
89 (unsigned long long) event->value.publish.specifics.
90 progress.offset);
91 break;
92
93 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
94 break;
95
96 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
97 printf ("Publishing complete, %llu kbps.\n",
98 (unsigned long long) (FILESIZE * 1000000LL
99 / (1
100 + GNUNET_TIME_absolute_get_duration
101 (start).rel_value_us) / 1024));
102 start = GNUNET_TIME_absolute_get ();
103 unindex = GNUNET_FS_unindex_start (fs, fn, "unindex");
104 GNUNET_assert (unindex != NULL);
105 break;
106
107 case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
108 printf ("Unindex complete, %llu kbps.\n",
109 (unsigned long long) (FILESIZE * 1000000LL
110 / (1
111 + GNUNET_TIME_absolute_get_duration
112 (start).rel_value_us) / 1024));
113 GNUNET_SCHEDULER_add_now (&abort_unindex_task, NULL);
114 break;
115
116 case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
117 GNUNET_assert (unindex == event->value.unindex.uc);
118 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
119 "Unindex is progressing (%llu/%llu at level %u off %llu)...\n",
120 (unsigned long long) event->value.unindex.completed,
121 (unsigned long long) event->value.unindex.size,
122 event->value.unindex.specifics.progress.depth,
123 (unsigned long long) event->value.unindex.specifics.
124 progress.offset);
125 break;
126
127 case GNUNET_FS_STATUS_PUBLISH_ERROR:
128 fprintf (stderr, "Error publishing file: %s\n",
129 event->value.publish.specifics.error.message);
130 GNUNET_break (0);
131 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
132 break;
133
134 case GNUNET_FS_STATUS_UNINDEX_ERROR:
135 fprintf (stderr, "Error unindexing file: %s\n",
136 event->value.unindex.specifics.error.message);
137 GNUNET_SCHEDULER_add_now (&abort_unindex_task, NULL);
138 break;
139
140 case GNUNET_FS_STATUS_PUBLISH_START:
141 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
142 GNUNET_assert (NULL == event->value.publish.pctx);
143 GNUNET_assert (FILESIZE == event->value.publish.size);
144 GNUNET_assert (0 == event->value.publish.completed);
145 GNUNET_assert (1 == event->value.publish.anonymity);
146 break;
147
148 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
149 GNUNET_assert (publish == event->value.publish.pc);
150 GNUNET_assert (FILESIZE == event->value.publish.size);
151 GNUNET_assert (1 == event->value.publish.anonymity);
152 GNUNET_FS_stop (fs);
153 fs = NULL;
154 break;
155
156 case GNUNET_FS_STATUS_UNINDEX_START:
157 GNUNET_assert (unindex == NULL);
158 GNUNET_assert (0 == strcmp ("unindex", event->value.unindex.cctx));
159 GNUNET_assert (0 == strcmp (fn, event->value.unindex.filename));
160 GNUNET_assert (FILESIZE == event->value.unindex.size);
161 GNUNET_assert (0 == event->value.unindex.completed);
162 break;
163
164 case GNUNET_FS_STATUS_UNINDEX_STOPPED:
165 GNUNET_assert (unindex == event->value.unindex.uc);
166 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
167 break;
168
169 default:
170 printf ("Unexpected event: %d\n", event->status);
171 break;
172 }
173 return NULL;
174}
175
176
177static void
178run (void *cls,
179 const struct GNUNET_CONFIGURATION_Handle *cfg,
180 struct GNUNET_TESTING_Peer *peer)
181{
182 const char *keywords[] = {
183 "down_foo",
184 "down_bar",
185 };
186 char *buf;
187 struct GNUNET_CONTAINER_MetaData *meta;
188 struct GNUNET_FS_Uri *kuri;
189 struct GNUNET_FS_FileInformation *fi;
190 size_t i;
191 struct GNUNET_FS_BlockOptions bo;
192
193 fn = GNUNET_DISK_mktemp ("gnunet-unindex-test-dst");
194 fs = GNUNET_FS_start (cfg, "test-fs-unindex", &progress_cb, NULL,
195 GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
196 GNUNET_assert (NULL != fs);
197 buf = GNUNET_malloc (FILESIZE);
198 for (i = 0; i < FILESIZE; i++)
199 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
200 (void) GNUNET_DISK_directory_remove (fn);
201 GNUNET_assert (GNUNET_OK ==
202 GNUNET_DISK_fn_write (fn, buf, FILESIZE,
203 GNUNET_DISK_PERM_USER_READ
204 | GNUNET_DISK_PERM_USER_WRITE));
205 GNUNET_free (buf);
206 meta = GNUNET_CONTAINER_meta_data_create ();
207 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
208 bo.content_priority = 42;
209 bo.anonymity_level = 1;
210 bo.replication_level = 0;
211 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
212 fi = GNUNET_FS_file_information_create_from_file (fs, "publish-context", fn,
213 kuri, meta, GNUNET_YES,
214 &bo);
215 GNUNET_FS_uri_destroy (kuri);
216 GNUNET_CONTAINER_meta_data_destroy (meta);
217 GNUNET_assert (NULL != fi);
218 start = GNUNET_TIME_absolute_get ();
219 publish =
220 GNUNET_FS_publish_start (fs, fi, NULL, NULL, NULL,
221 GNUNET_FS_PUBLISH_OPTION_NONE);
222 GNUNET_assert (publish != NULL);
223}
224
225
226int
227main (int argc, char *argv[])
228{
229 if (0 != GNUNET_TESTING_peer_run ("test-fs-unindex",
230 "test_fs_unindex_data.conf",
231 &run, NULL))
232 return 1;
233 return 0;
234}
235
236
237/* end of test_fs_unindex.c */
diff --git a/src/fs/test_fs_unindex_data.conf b/src/fs/test_fs_unindex_data.conf
deleted file mode 100644
index dde401857..000000000
--- a/src/fs/test_fs_unindex_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-unindex/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_fs_unindex_persistence.c b/src/fs/test_fs_unindex_persistence.c
deleted file mode 100644
index 802aaf7ca..000000000
--- a/src/fs/test_fs_unindex_persistence.c
+++ /dev/null
@@ -1,307 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_fs_unindex_persistence.c
23 * @brief simple testcase for simple publish + unindex operation
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_fs_service.h"
30
31/**
32 * File-size we use for testing.
33 */
34#define FILESIZE (1024 * 1024 * 2)
35
36/**
37 * How long until we give up on transmitting the message?
38 */
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
40
41/**
42 * How long should our test-content live?
43 */
44#define LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
45
46
47static struct GNUNET_TIME_Absolute start;
48
49static struct GNUNET_FS_Handle *fs;
50
51static struct GNUNET_FS_UnindexContext *unindex;
52
53static struct GNUNET_FS_PublishContext *publish;
54
55static char *fn;
56
57static const struct GNUNET_CONFIGURATION_Handle *cfg;
58
59
60static void
61abort_publish_task (void *cls)
62{
63 GNUNET_FS_publish_stop (publish);
64 publish = NULL;
65}
66
67
68static void
69abort_unindex_task (void *cls)
70{
71 if (unindex != NULL)
72 {
73 GNUNET_FS_unindex_stop (unindex);
74 unindex = NULL;
75 }
76 if (fn != NULL)
77 {
78 GNUNET_DISK_directory_remove (fn);
79 GNUNET_free (fn);
80 fn = NULL;
81 }
82}
83
84
85static void *
86progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event);
87
88
89static void
90restart_fs_task (void *cls)
91{
92 GNUNET_FS_stop (fs);
93 fs = GNUNET_FS_start (cfg, "test-fs-unindex-persistence", &progress_cb, NULL,
94 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
95}
96
97
98/**
99 * Consider scheduling the restart-task.
100 * Only runs the restart task once per event
101 * category.
102 *
103 * @param ev type of the event to consider
104 */
105static void
106consider_restart (int ev)
107{
108 static int prev[32];
109 static int off;
110 int i;
111
112 for (i = 0; i < off; i++)
113 if (prev[i] == ev)
114 return;
115 prev[off++] = ev;
116 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_URGENT,
117 &restart_fs_task, NULL);
118}
119
120
121static void *
122progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event)
123{
124 switch (event->status)
125 {
126 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
128 "Publish is progressing (%llu/%llu at level %u off %llu)...\n",
129 (unsigned long long) event->value.publish.completed,
130 (unsigned long long) event->value.publish.size,
131 event->value.publish.specifics.progress.depth,
132 (unsigned long long) event->value.publish.specifics.
133 progress.offset);
134 break;
135
136 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
137 break;
138
139 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
140 printf ("Publishing complete, %llu kbps.\n",
141 (unsigned long long) (FILESIZE * 1000000LL
142 / (1
143 + GNUNET_TIME_absolute_get_duration
144 (start).rel_value_us) / 1024));
145 start = GNUNET_TIME_absolute_get ();
146 unindex = GNUNET_FS_unindex_start (fs, fn, "unindex");
147 GNUNET_assert (unindex != NULL);
148 break;
149
150 case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
151 printf ("Unindex complete, %llu kbps.\n",
152 (unsigned long long) (FILESIZE * 1000000LL
153 / (1
154 + GNUNET_TIME_absolute_get_duration
155 (start).rel_value_us) / 1024));
156 GNUNET_SCHEDULER_add_now (&abort_unindex_task, NULL);
157 break;
158
159 case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
160 consider_restart (event->status);
161 GNUNET_assert (unindex == event->value.unindex.uc);
162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163 "Unindex is progressing (%llu/%llu at level %u off %llu)...\n",
164 (unsigned long long) event->value.unindex.completed,
165 (unsigned long long) event->value.unindex.size,
166 event->value.unindex.specifics.progress.depth,
167 (unsigned long long) event->value.unindex.specifics.
168 progress.offset);
169 break;
170
171 case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
172 if (event->value.publish.pc == publish)
173 publish = NULL;
174 break;
175
176 case GNUNET_FS_STATUS_PUBLISH_RESUME:
177 if (NULL == publish)
178 {
179 publish = event->value.publish.pc;
180 return "publish-context";
181 }
182 break;
183
184 case GNUNET_FS_STATUS_UNINDEX_SUSPEND:
185 GNUNET_assert (event->value.unindex.uc == unindex);
186 unindex = NULL;
187 break;
188
189 case GNUNET_FS_STATUS_UNINDEX_RESUME:
190 GNUNET_assert (NULL == unindex);
191 unindex = event->value.unindex.uc;
192 return "unindex";
193
194 case GNUNET_FS_STATUS_PUBLISH_ERROR:
195 fprintf (stderr, "Error publishing file: %s\n",
196 event->value.publish.specifics.error.message);
197 GNUNET_break (0);
198 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
199 break;
200
201 case GNUNET_FS_STATUS_UNINDEX_ERROR:
202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
203 "Error unindexing file: %s\n",
204 event->value.unindex.specifics.error.message);
205 GNUNET_SCHEDULER_add_now (&abort_unindex_task, NULL);
206 break;
207
208 case GNUNET_FS_STATUS_PUBLISH_START:
209 GNUNET_assert (0 == strcmp ("publish-context", event->value.publish.cctx));
210 GNUNET_assert (NULL == event->value.publish.pctx);
211 GNUNET_assert (FILESIZE == event->value.publish.size);
212 GNUNET_assert (0 == event->value.publish.completed);
213 GNUNET_assert (1 == event->value.publish.anonymity);
214 break;
215
216 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
217 GNUNET_assert (publish == event->value.publish.pc);
218 GNUNET_assert (FILESIZE == event->value.publish.size);
219 GNUNET_assert (1 == event->value.publish.anonymity);
220 GNUNET_FS_stop (fs);
221 fs = NULL;
222 break;
223
224 case GNUNET_FS_STATUS_UNINDEX_START:
225 consider_restart (event->status);
226 GNUNET_assert (unindex == NULL);
227 GNUNET_assert (0 == strcmp ("unindex", event->value.unindex.cctx));
228 GNUNET_assert (0 == strcmp (fn, event->value.unindex.filename));
229 GNUNET_assert (FILESIZE == event->value.unindex.size);
230 GNUNET_assert (0 == event->value.unindex.completed);
231 break;
232
233 case GNUNET_FS_STATUS_UNINDEX_STOPPED:
234 GNUNET_assert (unindex == event->value.unindex.uc);
235 GNUNET_SCHEDULER_add_now (&abort_publish_task, NULL);
236 break;
237
238 default:
239 printf ("Unexpected event: %d\n", event->status);
240 break;
241 }
242 return NULL;
243}
244
245
246static void
247run (void *cls,
248 const struct GNUNET_CONFIGURATION_Handle *c,
249 struct GNUNET_TESTING_Peer *peer)
250{
251 const char *keywords[] = {
252 "down_foo",
253 "down_bar",
254 };
255 char *buf;
256 struct GNUNET_CONTAINER_MetaData *meta;
257 struct GNUNET_FS_Uri *kuri;
258 struct GNUNET_FS_FileInformation *fi;
259 size_t i;
260 struct GNUNET_FS_BlockOptions bo;
261
262 cfg = c;
263 fn = GNUNET_DISK_mktemp ("gnunet-unindex-test-dst");
264 fs = GNUNET_FS_start (cfg, "test-fs-unindex-persistence", &progress_cb, NULL,
265 GNUNET_FS_FLAGS_PERSISTENCE, GNUNET_FS_OPTIONS_END);
266 GNUNET_assert (NULL != fs);
267 buf = GNUNET_malloc (FILESIZE);
268 for (i = 0; i < FILESIZE; i++)
269 buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
270 (void) GNUNET_DISK_directory_remove (fn);
271 GNUNET_assert (GNUNET_OK ==
272 GNUNET_DISK_fn_write (fn, buf, FILESIZE,
273 GNUNET_DISK_PERM_USER_READ
274 | GNUNET_DISK_PERM_USER_WRITE));
275 GNUNET_free (buf);
276 meta = GNUNET_CONTAINER_meta_data_create ();
277 kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
278 bo.content_priority = 42;
279 bo.anonymity_level = 1;
280 bo.replication_level = 0;
281 bo.expiration_time = GNUNET_TIME_relative_to_absolute (LIFETIME);
282 fi = GNUNET_FS_file_information_create_from_file (fs, "publish-context", fn,
283 kuri, meta, GNUNET_YES,
284 &bo);
285 GNUNET_FS_uri_destroy (kuri);
286 GNUNET_CONTAINER_meta_data_destroy (meta);
287 GNUNET_assert (NULL != fi);
288 start = GNUNET_TIME_absolute_get ();
289 publish =
290 GNUNET_FS_publish_start (fs, fi, NULL, NULL, NULL,
291 GNUNET_FS_PUBLISH_OPTION_NONE);
292 GNUNET_assert (publish != NULL);
293}
294
295
296int
297main (int argc, char *argv[])
298{
299 if (0 != GNUNET_TESTING_peer_run ("test-fs-unindex-persistence",
300 "test_fs_unindex_data.conf",
301 &run, NULL))
302 return 1;
303 return 0;
304}
305
306
307/* end of test_fs_unindex_persistence.c */
diff --git a/src/fs/test_fs_uri.c b/src/fs/test_fs_uri.c
deleted file mode 100644
index aac7b1b97..000000000
--- a/src/fs/test_fs_uri.c
+++ /dev/null
@@ -1,337 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_fs_uri.c
22 * @brief Test for fs_uri.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_fs_service.h"
28#include "fs_api.h"
29
30
31static int
32testKeyword ()
33{
34 char *uri;
35 struct GNUNET_FS_Uri *ret;
36 char *emsg;
37
38 if (NULL != (ret = GNUNET_FS_uri_parse ("gnunet://fs/ksk/++", &emsg)))
39 {
40 GNUNET_FS_uri_destroy (ret);
41 GNUNET_assert (0);
42 }
43 GNUNET_free (emsg);
44 ret = GNUNET_FS_uri_parse ("gnunet://fs/ksk/foo+bar", &emsg);
45 if (NULL == ret)
46 {
47 GNUNET_free (emsg);
48 GNUNET_assert (0);
49 }
50 if (! GNUNET_FS_uri_test_ksk (ret))
51 {
52 GNUNET_FS_uri_destroy (ret);
53 GNUNET_assert (0);
54 }
55 if ((2 != ret->data.ksk.keywordCount) ||
56 (0 != strcmp (" foo", ret->data.ksk.keywords[0])) ||
57 (0 != strcmp (" bar", ret->data.ksk.keywords[1])))
58 {
59 GNUNET_FS_uri_destroy (ret);
60 GNUNET_assert (0);
61 }
62
63 uri = GNUNET_FS_uri_to_string (ret);
64 if (0 != strcmp (uri, "gnunet://fs/ksk/foo+bar"))
65 {
66 GNUNET_free (uri);
67 GNUNET_FS_uri_destroy (ret);
68 GNUNET_assert (0);
69 }
70 GNUNET_free (uri);
71 GNUNET_FS_uri_destroy (ret);
72 return 0;
73}
74
75
76static int
77testLocation ()
78{
79 struct GNUNET_FS_Uri *uri;
80 char *uric;
81 struct GNUNET_FS_Uri *uri2;
82 struct GNUNET_FS_Uri *baseURI;
83 char *emsg = NULL;
84 struct GNUNET_CRYPTO_EddsaPrivateKey pk;
85
86 baseURI =
87 GNUNET_FS_uri_parse
88 (
89 "gnunet://fs/chk/4QZP479A9SKGFNMQ2ZBCYE71YV2QMTVGWTVPB6A10ASVCKXDHB05DKPSC7ZF6E9P9W1VE47394EQY7NXA47Q6R35M7P1MJPGP59D1Z8.D54QD1K5XCG5878T6YZ19AM60MQ6FC0YNVK7QY08KK0KM0FJJ3KQWYG112FN5T07KN7J0X35DF6WVBT9B8ZMZ3X2BXJ22X3KFQ6MV2G.15999",
90 &emsg);
91 GNUNET_assert (baseURI != NULL);
92 GNUNET_assert (emsg == NULL);
93 GNUNET_CRYPTO_eddsa_key_create (&pk);
94 uri = GNUNET_FS_uri_loc_create (baseURI,
95 &pk,
96 GNUNET_TIME_absolute_get ());
97 if (NULL == uri)
98 {
99 GNUNET_break (0);
100 GNUNET_FS_uri_destroy (baseURI);
101 return 1;
102 }
103 if (! GNUNET_FS_uri_test_loc (uri))
104 {
105 GNUNET_break (0);
106 GNUNET_FS_uri_destroy (uri);
107 GNUNET_FS_uri_destroy (baseURI);
108 return 1;
109 }
110 uri2 = GNUNET_FS_uri_loc_get_uri (uri);
111 if (! GNUNET_FS_uri_test_equal (baseURI, uri2))
112 {
113 GNUNET_break (0);
114 GNUNET_FS_uri_destroy (uri);
115 GNUNET_FS_uri_destroy (uri2);
116 GNUNET_FS_uri_destroy (baseURI);
117 return 1;
118 }
119 GNUNET_FS_uri_destroy (uri2);
120 GNUNET_FS_uri_destroy (baseURI);
121 uric = GNUNET_FS_uri_to_string (uri);
122#if 0
123 /* not for the faint of heart: */
124 printf ("URI: `%s'\n", uric);
125#endif
126 uri2 = GNUNET_FS_uri_parse (uric, &emsg);
127 GNUNET_free (uric);
128 if (uri2 == NULL)
129 {
130 fprintf (stderr, "URI parsing failed: %s\n", emsg);
131 GNUNET_break (0);
132 GNUNET_FS_uri_destroy (uri);
133 GNUNET_free (emsg);
134 return 1;
135 }
136 GNUNET_assert (NULL == emsg);
137 if (GNUNET_YES != GNUNET_FS_uri_test_equal (uri, uri2))
138 {
139 GNUNET_break (0);
140 GNUNET_FS_uri_destroy (uri);
141 GNUNET_FS_uri_destroy (uri2);
142 return 1;
143 }
144 GNUNET_FS_uri_destroy (uri2);
145 GNUNET_FS_uri_destroy (uri);
146 return 0;
147}
148
149
150static int
151testNamespace (int i)
152{
153 char *uri;
154 struct GNUNET_FS_Uri *ret;
155 char *emsg;
156 struct GNUNET_CRYPTO_EcdsaPrivateKey ph;
157 struct GNUNET_CRYPTO_EcdsaPublicKey id;
158 char buf[1024];
159 char ubuf[1024];
160 char *sret;
161
162 if (NULL !=
163 (ret =
164 GNUNET_FS_uri_parse (
165 "gnunet://fs/sks/D1KJS9H2A82Q65VKQ0ML3RFU6U1D3VUK",
166 &emsg)))
167 {
168 GNUNET_FS_uri_destroy (ret);
169 GNUNET_assert (0);
170 }
171 GNUNET_free (emsg);
172 if (NULL !=
173 (ret =
174 GNUNET_FS_uri_parse
175 (
176 "gnunet://fs/sks/XQHH4R288W26EBV369F6RCE0PJVJTX2Y74Q2FJPMPGA31HJX2JG/this",
177 &emsg)))
178 {
179 GNUNET_FS_uri_destroy (ret);
180 GNUNET_assert (0);
181 }
182 GNUNET_free (emsg);
183 if (NULL != (ret = GNUNET_FS_uri_parse ("gnunet://fs/sks/test", &emsg)))
184 {
185 GNUNET_FS_uri_destroy (ret);
186 GNUNET_assert (0);
187 }
188 GNUNET_free (emsg);
189 GNUNET_CRYPTO_ecdsa_key_create (&ph);
190 GNUNET_CRYPTO_ecdsa_key_get_public (&ph,
191 &id);
192 sret = GNUNET_STRINGS_data_to_string (&id, sizeof(id),
193 ubuf, sizeof(ubuf) - 1);
194 GNUNET_assert (NULL != sret);
195 sret[0] = '\0';
196 GNUNET_snprintf (buf, sizeof(buf),
197 "gnunet://fs/sks/%s/test",
198 ubuf);
199 ret = GNUNET_FS_uri_parse (buf, &emsg);
200 if (NULL == ret)
201 {
202 GNUNET_free (emsg);
203 GNUNET_assert (0);
204 }
205 if (GNUNET_FS_uri_test_ksk (ret))
206 {
207 GNUNET_FS_uri_destroy (ret);
208 GNUNET_assert (0);
209 }
210 if (! GNUNET_FS_uri_test_sks (ret))
211 {
212 GNUNET_FS_uri_destroy (ret);
213 GNUNET_assert (0);
214 }
215
216 uri = GNUNET_FS_uri_to_string (ret);
217 if (0 !=
218 strcmp (uri,
219 buf))
220 {
221 GNUNET_FS_uri_destroy (ret);
222 GNUNET_free (uri);
223 GNUNET_assert (0);
224 }
225 GNUNET_free (uri);
226 GNUNET_FS_uri_destroy (ret);
227 return 0;
228}
229
230
231static int
232testFile (int i)
233{
234 char *uri;
235 struct GNUNET_FS_Uri *ret;
236 char *emsg;
237
238 if (NULL !=
239 (ret =
240 GNUNET_FS_uri_parse
241 (
242 "gnunet://fs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H00000440000.42",
243 &emsg)))
244 {
245 GNUNET_FS_uri_destroy (ret);
246 GNUNET_assert (0);
247 }
248 GNUNET_free (emsg);
249 if (NULL !=
250 (ret =
251 GNUNET_FS_uri_parse
252 (
253 "gnunet://fs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000",
254 &emsg)))
255 {
256 GNUNET_FS_uri_destroy (ret);
257 GNUNET_assert (0);
258 }
259 GNUNET_free (emsg);
260 if (NULL !=
261 (ret =
262 GNUNET_FS_uri_parse
263 (
264 "gnunet://fs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.FGH",
265 &emsg)))
266 {
267 GNUNET_FS_uri_destroy (ret);
268 GNUNET_assert (0);
269 }
270 GNUNET_free (emsg);
271 ret =
272 GNUNET_FS_uri_parse
273 (
274 "gnunet://fs/chk/4QZP479A9SKGFNMQ2ZBCYE71YV2QMTVGWTVPB6A10ASVCKXDHB05DKPSC7ZF6E9P9W1VE47394EQY7NXA47Q6R35M7P1MJPGP59D1Z8.D54QD1K5XCG5878T6YZ19AM60MQ6FC0YNVK7QY08KK0KM0FJJ3KQWYG112FN5T07KN7J0X35DF6WVBT9B8ZMZ3X2BXJ22X3KFQ6MV2G.42",
275 &emsg);
276 if (ret == NULL)
277 {
278 GNUNET_free (emsg);
279 GNUNET_assert (0);
280 }
281 if (GNUNET_FS_uri_test_ksk (ret))
282 {
283 GNUNET_FS_uri_destroy (ret);
284 GNUNET_assert (0);
285 }
286 if (GNUNET_FS_uri_test_sks (ret))
287 {
288 GNUNET_FS_uri_destroy (ret);
289 GNUNET_assert (0);
290 }
291 if (GNUNET_ntohll (ret->data.chk.file_length) != 42)
292 {
293 GNUNET_FS_uri_destroy (ret);
294 GNUNET_assert (0);
295 }
296
297 uri = GNUNET_FS_uri_to_string (ret);
298 if (0 !=
299 strcmp (uri,
300 "gnunet://fs/chk/4QZP479A9SKGFNMQ2ZBCYE71YV2QMTVGWTVPB6A10ASVCKXDHB05DKPSC7ZF6E9P9W1VE47394EQY7NXA47Q6R35M7P1MJPGP59D1Z8.D54QD1K5XCG5878T6YZ19AM60MQ6FC0YNVK7QY08KK0KM0FJJ3KQWYG112FN5T07KN7J0X35DF6WVBT9B8ZMZ3X2BXJ22X3KFQ6MV2G.42"))
301 {
302 GNUNET_free (uri);
303 GNUNET_FS_uri_destroy (ret);
304 GNUNET_assert (0);
305 }
306 GNUNET_free (uri);
307 GNUNET_FS_uri_destroy (ret);
308 return 0;
309}
310
311
312int
313main (int argc, char *argv[])
314{
315 int failureCount = 0;
316 int i;
317
318 GNUNET_log_setup ("test_fs_uri",
319 "WARNING",
320 NULL);
321 failureCount += testKeyword ();
322 failureCount += testLocation ();
323 for (i = 0; i < 255; i++)
324 {
325 /* fprintf (stderr, "%s", "."); */
326 failureCount += testNamespace (i);
327 failureCount += testFile (i);
328 }
329 /* fprintf (stderr, "%s", "\n"); */
330 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-fs-uri");
331 if (failureCount != 0)
332 return 1;
333 return 0;
334}
335
336
337/* end of test_fs_uri.c */
diff --git a/src/fs/test_gnunet_fs_idx.py.in b/src/fs/test_gnunet_fs_idx.py.in
deleted file mode 100755
index 564dd68f2..000000000
--- a/src/fs/test_gnunet_fs_idx.py.in
+++ /dev/null
@@ -1,113 +0,0 @@
1#!@PYTHONEXE@
2# This file is part of GNUnet.
3# (C) 2010 Christian Grothoff (and other contributing authors)
4#
5# GNUnet is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# GNUnet is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19#
20# Testcase for file-sharing command-line tools (indexing and unindexing)
21import sys
22import os
23import subprocess
24import re
25import shutil
26
27srcdir = "../.."
28gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts")
29if gnunet_pyexpect_dir not in sys.path:
30 sys.path.append(gnunet_pyexpect_dir)
31
32from gnunet_pyexpect import pexpect
33
34if os.name == 'posix':
35 download = './gnunet-download'
36 gnunetarm = 'gnunet-arm'
37 publish = './gnunet-publish'
38 unindex = './gnunet-unindex'
39elif os.name == 'nt':
40 download = './gnunet-download.exe'
41 gnunetarm = 'gnunet-arm.exe'
42 publish = './gnunet-publish.exe'
43 unindex = './gnunet-unindex.exe'
44
45if os.name == "nt":
46 shutil.rmtree(
47 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-idx"), True
48 )
49else:
50 shutil.rmtree("/tmp/gnunet-test-fs-py-idx", True)
51
52arm = subprocess.Popen([gnunetarm, '-sq', '-c', 'test_gnunet_fs_idx_data.conf'])
53arm.communicate()
54
55try:
56 pub = pexpect()
57
58 pub.spawn(
59 None, [
60 publish, '-c', 'test_gnunet_fs_idx_data.conf', '-m',
61 "description:Test archive", '-k', 'tst',
62 'test_gnunet_fs_rec_data.tgz'
63 ],
64 stdout=subprocess.PIPE,
65 stderr=subprocess.STDOUT
66 )
67 pub.expect(
68 "stdout",
69 re.compile(
70 r"URI is `gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR\.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG\.17822'\.\r?\n"
71 )
72 )
73
74 down = pexpect()
75 down.spawn(
76 None, [
77 download, '-c', 'test_gnunet_fs_idx_data.conf', '-o',
78 'test_gnunet_fs_rec_data.tar.gz',
79 'gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG.17822'
80 ],
81 stdout=subprocess.PIPE,
82 stderr=subprocess.STDOUT
83 )
84 down.expect(
85 "stdout",
86 re.compile(
87 r"Downloading `test_gnunet_fs_rec_data.tar.gz' done (.*).\r?\n"
88 )
89 )
90 os.remove("test_gnunet_fs_rec_data.tar.gz")
91
92 un = pexpect()
93 un.spawn(
94 None, [
95 unindex, '-c', 'test_gnunet_fs_idx_data.conf',
96 'test_gnunet_fs_rec_data.tgz'
97 ],
98 stdout=subprocess.PIPE,
99 stderr=subprocess.STDOUT
100 )
101 un.expect("stdout", re.compile(r'Unindexing done\.\r?\n'))
102
103finally:
104 arm = subprocess.Popen([
105 gnunetarm, '-eq', '-c', 'test_gnunet_fs_idx_data.conf'
106 ])
107 arm.communicate()
108 if os.name == "nt":
109 shutil.rmtree(
110 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-idx"), True
111 )
112 else:
113 shutil.rmtree("/tmp/gnunet-test-fs-py-idx", True)
diff --git a/src/fs/test_gnunet_fs_idx_data.conf b/src/fs/test_gnunet_fs_idx_data.conf
deleted file mode 100644
index ba2a872dd..000000000
--- a/src/fs/test_gnunet_fs_idx_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-py-idx/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_gnunet_fs_psd.py.in b/src/fs/test_gnunet_fs_psd.py.in
deleted file mode 100755
index a25e4eaef..000000000
--- a/src/fs/test_gnunet_fs_psd.py.in
+++ /dev/null
@@ -1,149 +0,0 @@
1#!@PYTHONEXE@
2# This file is part of GNUnet.
3# (C) 2010, 2018 Christian Grothoff (and other contributing authors)
4#
5# GNUnet is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# GNUnet is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19#
20# Testcase for file-sharing command-line tools (publish, search, download)
21
22import sys
23import os
24import subprocess
25import re
26import shutil
27try:
28 # Python 2.7
29 reload
30except NameError:
31 try:
32 # Python 3.4+:
33 from importlib import reload
34 except ImportError:
35 # Python 3.0 - 3.3
36 from imp import reload
37
38reload(sys)
39
40# Force encoding to utf-8, as this test otherwise fails
41# on some systems (see #5094). In Python 3+ there is no attribute
42# sys.setdefaultencoding anymore.
43if (3 < sys.version_info[0]):
44 sys.setdefaultencoding('utf8')
45
46srcdir = "../.."
47gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts")
48if gnunet_pyexpect_dir not in sys.path:
49 sys.path.append(gnunet_pyexpect_dir)
50
51from gnunet_pyexpect import pexpect
52
53if os.name == 'posix':
54 download = './gnunet-download'
55 gnunetarm = 'gnunet-arm'
56 publish = './gnunet-publish'
57 unindex = './gnunet-unindex'
58 search = './gnunet-search'
59elif os.name == 'nt':
60 download = './gnunet-download.exe'
61 gnunetarm = 'gnunet-arm.exe'
62 publish = './gnunet-publish.exe'
63 unindex = './gnunet-unindex.exe'
64 search = './gnunet-search.exe'
65
66if "GNUNET_PREFIX" in os.environ:
67 pass
68else:
69 print("You need to export GNUNET_PREFIX")
70 sys.exit(1)
71
72if os.name == "nt":
73 shutil.rmtree(
74 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-psd"), True
75 )
76else:
77 shutil.rmtree("/tmp/gnunet-test-fs-py-psd", True)
78
79arm = subprocess.Popen([gnunetarm, '-sq', '-c', 'test_gnunet_fs_psd_data.conf'])
80arm.communicate()
81
82# first, basic publish-search-download run
83try:
84 pub = pexpect()
85 pub.spawn(
86 None, [
87 publish, '-c', 'test_gnunet_fs_psd_data.conf', '-n', '-m',
88 "description:Test archive", '-k', 'tst',
89 'test_gnunet_fs_rec_data.tgz'
90 ],
91 stdout=subprocess.PIPE,
92 stderr=subprocess.STDOUT
93 )
94 pub.expect(
95 "stdout",
96 re.compile(r"Publishing `.+test_gnunet_fs_rec_data.tgz' done\.\r?\n")
97 )
98 pub.expect(
99 "stdout",
100 re.compile(
101 r"URI is `gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR\.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG\.17822'\.\r?\n"
102 )
103 )
104
105 s = pexpect()
106 s.spawn(
107 None, [
108 search, '-V', '-t', '1000 ms', '-N', '1', '-c',
109 'test_gnunet_fs_psd_data.conf', 'tst'
110 ],
111 stdout=subprocess.PIPE,
112 stderr=subprocess.STDOUT
113 )
114 s.expect(
115 "stdout",
116 re.compile(
117 r'gnunet-download -o "test_gnunet_fs_rec_data.tgz" gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR\.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG\.17822\r?\n'
118 )
119 )
120
121 down = pexpect()
122 down.spawn(
123 None, [
124 download, '-c', 'test_gnunet_fs_psd_data.conf', '-o',
125 'test_gnunet_fs_rec_data.tar.gz',
126 'gnunet://fs/chk/2ZMHKPV74CB6GB1GFKQRR95BXJQA2SER25FN48GAW7WSBPA0GDEM5Y74V1ZJHM0NA6919TVW376BHTFDRE3RYS0KRY92M1QJVKPHFCR.49BT3V5C10KA1695JF71FCT8ZZG4JMJSH04BD9CT22R6KEM915A7CEST17RD0QYTHXV5M4HHEGJMEZSFRDB7JAYC0EMJAN2V781E9DG.17822'
127 ],
128 stdout=subprocess.PIPE,
129 stderr=subprocess.STDOUT
130 )
131 down.expect(
132 "stdout",
133 re.compile(
134 r"Downloading `test_gnunet_fs_rec_data.tar.gz' done (.*).\r?\n"
135 )
136 )
137 os.remove("test_gnunet_fs_rec_data.tar.gz")
138
139finally:
140 arm = subprocess.Popen([
141 gnunetarm, '-eq', '-c', 'test_gnunet_fs_psd_data.conf'
142 ])
143 arm.communicate()
144 if os.name == "nt":
145 shutil.rmtree(
146 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-psd"), True
147 )
148 else:
149 shutil.rmtree("/tmp/gnunet-test-fs-py-psd", True)
diff --git a/src/fs/test_gnunet_fs_psd_data.conf b/src/fs/test_gnunet_fs_psd_data.conf
deleted file mode 100644
index f6c05ecdd..000000000
--- a/src/fs/test_gnunet_fs_psd_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-py-psd/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_gnunet_fs_rec.py.in b/src/fs/test_gnunet_fs_rec.py.in
deleted file mode 100755
index f7e84e3da..000000000
--- a/src/fs/test_gnunet_fs_rec.py.in
+++ /dev/null
@@ -1,171 +0,0 @@
1#!@PYTHONEXE@
2# This file is part of GNUnet.
3# (C) 2010 Christian Grothoff (and other contributing authors)
4#
5# GNUnet is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# GNUnet is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19#
20# Testcase for file-sharing command-line tools (recursive publishing & download)
21import sys
22import os
23import subprocess
24import re
25import shutil
26import tarfile
27import filecmp
28
29srcdir = "../.."
30gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts")
31if gnunet_pyexpect_dir not in sys.path:
32 sys.path.append(gnunet_pyexpect_dir)
33
34from gnunet_pyexpect import pexpect
35from pydiffer import dcdiff
36
37if os.name == 'posix':
38 download = './gnunet-download'
39 gnunetarm = 'gnunet-arm'
40 publish = './gnunet-publish'
41 unindex = './gnunet-unindex'
42 search = './gnunet-search'
43 directory = './gnunet-directory'
44elif os.name == 'nt':
45 download = './gnunet-download.exe'
46 gnunetarm = 'gnunet-arm.exe'
47 publish = './gnunet-publish.exe'
48 unindex = './gnunet-unindex.exe'
49 search = './gnunet-search.exe'
50 directory = './gnunet-directory.exe'
51
52if os.name == "nt":
53 shutil.rmtree(
54 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-rec"), True
55 )
56else:
57 shutil.rmtree("/tmp/gnunet-test-fs-py-rec", True)
58
59arm = subprocess.Popen([gnunetarm, '-sq', '-c', 'test_gnunet_fs_rec_data.conf'])
60arm.communicate()
61
62tar = tarfile.open('test_gnunet_fs_rec_data.tgz')
63tar.extractall()
64# first, basic publish-search-download run
65try:
66 pub = pexpect()
67 pub.spawn(
68 None, [
69 publish, '-c', 'test_gnunet_fs_rec_data.conf', '-k', 'testdir',
70 'dir/'
71 ],
72 stdout=subprocess.PIPE,
73 stderr=subprocess.STDOUT
74 )
75 # Can't say much for publishing, except that the last one is the toplevel directory
76 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
77 pub.expect(
78 "stdout",
79 re.compile(
80 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
81 )
82 )
83 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
84 pub.expect(
85 "stdout",
86 re.compile(
87 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
88 )
89 )
90 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
91 pub.expect(
92 "stdout",
93 re.compile(
94 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
95 )
96 )
97 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
98 pub.expect(
99 "stdout",
100 re.compile(
101 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
102 )
103 )
104 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
105 pub.expect(
106 "stdout",
107 re.compile(
108 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
109 )
110 )
111 pub.expect("stdout", re.compile(r"Publishing `.+' done\.\r?\n"))
112 pub.expect(
113 "stdout",
114 re.compile(
115 r"URI is `gnunet://fs/chk/[A-Z0-9]{103}\.[A-Z0-9]{103}\.\d+'\.\r?\n"
116 )
117 )
118 pub.expect(
119 "stdout", re.compile(r"Publishing `.+[\\/]dir[\\/]' done\.\r?\n")
120 )
121 m = pub.expect("stdout", re.compile(r".+\r?\n"))
122 if not m:
123 sys.exit(3)
124 output = m.string
125 url = output[output.find("`") + 1:output.find("'")]
126
127 down = pexpect()
128 down.spawn(
129 None, [
130 download, '-c', 'test_gnunet_fs_rec_data.conf', '-R', '-o',
131 'rdir.gnd', url
132 ],
133 stdout=subprocess.PIPE,
134 stderr=subprocess.STDOUT
135 )
136 down.expect("stdout", re.compile(r"Downloading `rdir.gnd' done (.*).\r?\n"))
137
138 d = pexpect()
139 d.spawn(
140 None, [directory, '-c', 'test_gnunet_fs_rec_data.conf', 'rdir/a.gnd'],
141 stdout=subprocess.PIPE,
142 stderr=subprocess.STDOUT
143 )
144 d.expect("stdout", re.compile(r"Directory `a/' meta data:\r?\n"))
145 d.expect("stdout", re.compile(r"Directory `a/' contents:\r?\n"))
146 d.expect("stdout", re.compile(r"COPYING (.*)\r?\n"))
147 d.expect("stdout", re.compile(r"INSTALL (.*)\r?\n"))
148
149 os.remove("rdir/b.gnd")
150 os.remove("rdir/a.gnd")
151 diff = dcdiff('dir', 'rdir')
152 if len(diff) != 0:
153 raise Exception(
154 "Unexpected difference between source directory and downloaded result:\n{}"
155 .format(diff)
156 )
157
158finally:
159 arm = subprocess.Popen([
160 gnunetarm, '-eq', '-c', 'test_gnunet_fs_rec_data.conf'
161 ])
162 arm.communicate()
163 if os.name == "nt":
164 shutil.rmtree(
165 os.path.join(os.getenv("TEMP"), "gnunet-test-fs-py-rec"), True
166 )
167 else:
168 shutil.rmtree("/tmp/gnunet-test-fs-py-rec", True)
169 shutil.rmtree("dir", True)
170 shutil.rmtree("rdir", True)
171 shutil.rmtree("rdir.gnd", True)
diff --git a/src/fs/test_gnunet_fs_rec_data.conf b/src/fs/test_gnunet_fs_rec_data.conf
deleted file mode 100644
index 82ddb8f49..000000000
--- a/src/fs/test_gnunet_fs_rec_data.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-fs-py-rec/
4
5[transport]
6PLUGINS =
7
diff --git a/src/fs/test_gnunet_fs_rec_data.tgz b/src/fs/test_gnunet_fs_rec_data.tgz
deleted file mode 100644
index 697794306..000000000
--- a/src/fs/test_gnunet_fs_rec_data.tgz
+++ /dev/null
Binary files differ
diff --git a/src/fs/test_gnunet_service_fs_migration.c b/src/fs/test_gnunet_service_fs_migration.c
deleted file mode 100644
index d4a1c38b5..000000000
--- a/src/fs/test_gnunet_service_fs_migration.c
+++ /dev/null
@@ -1,221 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2012, 2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_gnunet_service_fs_migration.c
23 * @brief test content migration between two peers
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "fs_test_lib.h"
28#include "gnunet_testbed_service.h"
29
30#define VERBOSE GNUNET_NO
31
32/**
33 * File-size we use for testing.
34 */
35#define FILESIZE (2 * 32 * 1024)
36
37/**
38 * How long until we give up on transmitting the message?
39 */
40#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
41
42/**
43 * How long do we give the peers for content migration?
44 */
45#define MIGRATION_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \
46 90)
47
48#define SEED 42
49
50static struct GNUNET_TESTBED_Peer *daemons[2];
51
52static int ok;
53
54static struct GNUNET_TIME_Absolute start_time;
55
56static struct GNUNET_TESTBED_Operation *op;
57
58
59struct DownloadContext
60{
61 char *fn;
62
63 struct GNUNET_FS_Uri *uri;
64};
65
66
67static void
68do_stop (void *cls)
69{
70 struct GNUNET_TIME_Relative del;
71 char *fancy;
72
73 GNUNET_SCHEDULER_shutdown ();
74 if (0 ==
75 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
76 TIMEOUT)).
77 rel_value_us)
78 {
79 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
80 "Timeout during download, shutting down with error\n");
81 ok = 1;
82 }
83 else
84 {
85 del = GNUNET_TIME_absolute_get_duration (start_time);
86 if (del.rel_value_us == 0)
87 del.rel_value_us = 1;
88 fancy =
89 GNUNET_STRINGS_byte_size_fancy (((unsigned long long) FILESIZE)
90 * 1000000LL / del.rel_value_us);
91 fprintf (stdout,
92 "Download speed was %s/s\n",
93 fancy);
94 GNUNET_free (fancy);
95 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
96 "Finished download, shutting down\n");
97 }
98}
99
100
101static void
102do_download (void *cls,
103 const char *emsg)
104{
105 struct DownloadContext *dc = cls;
106 struct GNUNET_FS_Uri *uri = dc->uri;
107
108 GNUNET_TESTBED_operation_done (op);
109 op = NULL;
110 if (NULL != dc->fn)
111 {
112 GNUNET_DISK_directory_remove (dc->fn);
113 GNUNET_free (dc->fn);
114 }
115 GNUNET_free (dc);
116 if (NULL != emsg)
117 {
118 GNUNET_SCHEDULER_shutdown ();
119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
120 "Failed to stop source daemon: %s\n",
121 emsg);
122 GNUNET_FS_uri_destroy (uri);
123 ok = 1;
124 return;
125 }
126 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
127 "Downloading %llu bytes\n",
128 (unsigned long long) FILESIZE);
129 start_time = GNUNET_TIME_absolute_get ();
130 GNUNET_FS_TEST_download (daemons[0],
131 TIMEOUT,
132 1,
133 SEED,
134 uri,
135 VERBOSE,
136 &do_stop,
137 NULL);
138 GNUNET_FS_uri_destroy (uri);
139}
140
141
142static void
143stop_source_peer (void *cls)
144{
145 struct DownloadContext *dc = cls;
146
147 /* FIXME: We should not interact with testbed when shutting down */
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149 "Stopping source peer\n");
150 op = GNUNET_TESTBED_peer_stop (NULL,
151 daemons[1],
152 &do_download, dc);
153 GNUNET_assert (NULL != op);
154}
155
156
157static void
158do_wait (void *cls,
159 const struct GNUNET_FS_Uri *uri,
160 const char *fn)
161{
162 struct DownloadContext *dc;
163
164 if (NULL == uri)
165 {
166 GNUNET_SCHEDULER_shutdown ();
167 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
168 "Timeout during upload attempt, shutting down with error\n");
169 ok = 1;
170 return;
171 }
172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173 "Waiting to allow content to migrate\n");
174 dc = GNUNET_new (struct DownloadContext);
175 dc->uri = GNUNET_FS_uri_dup (uri);
176 if (NULL != fn)
177 dc->fn = GNUNET_strdup (fn);
178 (void) GNUNET_SCHEDULER_add_delayed (MIGRATION_DELAY,
179 &stop_source_peer,
180 dc);
181}
182
183
184static void
185do_publish (void *cls,
186 struct GNUNET_TESTBED_RunHandle *h,
187 unsigned int num_peers,
188 struct GNUNET_TESTBED_Peer **peers,
189 unsigned int links_succeeded,
190 unsigned int links_failed)
191{
192 unsigned int i;
193
194 GNUNET_assert (2 == num_peers);
195 for (i = 0; i < num_peers; i++)
196 daemons[i] = peers[i];
197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
198 "Publishing %llu bytes\n",
199 (unsigned long long) FILESIZE);
200 GNUNET_FS_TEST_publish (daemons[1], TIMEOUT, 1, GNUNET_NO,
201 FILESIZE, SEED,
202 VERBOSE, &do_wait, NULL);
203}
204
205
206int
207main (int argc,
208 char *argv[])
209{
210 (void) GNUNET_TESTBED_test_run ("test-gnunet-service-fs-migration",
211 "fs_test_lib_data.conf",
212 2,
213 0, NULL, NULL,
214 &do_publish,
215 NULL);
216 GNUNET_DISK_directory_remove ("/tmp/test-gnunet-service-fs-migration/");
217 return ok;
218}
219
220
221/* end of test_gnunet_service_fs_migration.c */
diff --git a/src/fs/test_gnunet_service_fs_migration_data.conf b/src/fs/test_gnunet_service_fs_migration_data.conf
deleted file mode 100644
index fca6c5a29..000000000
--- a/src/fs/test_gnunet_service_fs_migration_data.conf
+++ /dev/null
@@ -1,10 +0,0 @@
1@INLINE@ test_fs_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-service-fs-migration/
4
5[testbed]
6OVERLAY_TOPOLOGY = CLIQUE
7
8[ats]
9WAN_QUOTA_IN = 3932160
10WAN_QUOTA_OUT = 3932160
diff --git a/src/fs/test_gnunet_service_fs_p2p.c b/src/fs/test_gnunet_service_fs_p2p.c
deleted file mode 100644
index bedcb7173..000000000
--- a/src/fs/test_gnunet_service_fs_p2p.c
+++ /dev/null
@@ -1,166 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file fs/test_gnunet_service_fs_p2p.c
23 * @brief test P2P routing using simple publish + download operation
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "fs_test_lib.h"
28
29#define VERBOSE GNUNET_NO
30
31/**
32 * File-size we use for testing.
33 */
34#define FILESIZE (1024 * 1024 * 1)
35
36/**
37 * How long until we give up on the download?
38 */
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
40
41#define NUM_DAEMONS 2
42
43#define SEED 42
44
45static const char *progname;
46
47static unsigned int anonymity_level;
48
49static struct GNUNET_TESTBED_Peer *daemons[NUM_DAEMONS];
50
51static int ok;
52
53static struct GNUNET_TIME_Absolute start_time;
54
55
56static void
57do_stop (void *cls)
58{
59 char *fn = cls;
60 struct GNUNET_TIME_Relative del;
61 char *fancy;
62
63 GNUNET_SCHEDULER_shutdown ();
64 if (0 ==
65 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (start_time,
66 TIMEOUT)).
67 rel_value_us)
68 {
69 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
70 "Timeout during download, shutting down with error\n");
71 ok = 1;
72 }
73 else
74 {
75 del = GNUNET_TIME_absolute_get_duration (start_time);
76 if (0 == del.rel_value_us)
77 del.rel_value_us = 1;
78 fancy =
79 GNUNET_STRINGS_byte_size_fancy (((unsigned long long) FILESIZE)
80 * 1000000LL / del.rel_value_us);
81 fprintf (stdout,
82 "Download speed was %s/s\n",
83 fancy);
84 GNUNET_free (fancy);
85 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86 "Finished download, shutting down\n");
87 }
88 if (NULL != fn)
89 {
90 GNUNET_DISK_directory_remove (fn);
91 GNUNET_free (fn);
92 }
93}
94
95
96static void
97do_download (void *cls, const struct GNUNET_FS_Uri *uri,
98 const char *fn)
99{
100 if (NULL == uri)
101 {
102 GNUNET_SCHEDULER_shutdown ();
103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
104 "Timeout during upload attempt, shutting down with error\n");
105 ok = 1;
106 return;
107 }
108 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Downloading %llu bytes\n",
109 (unsigned long long) FILESIZE);
110 start_time = GNUNET_TIME_absolute_get ();
111 GNUNET_FS_TEST_download (daemons[0], TIMEOUT,
112 anonymity_level, SEED, uri,
113 VERBOSE, &do_stop,
114 (NULL == fn)
115 ? NULL
116 : GNUNET_strdup (fn));
117}
118
119
120static void
121do_publish (void *cls,
122 struct GNUNET_TESTBED_RunHandle *h,
123 unsigned int num_peers,
124 struct GNUNET_TESTBED_Peer **peers,
125 unsigned int links_succeeded,
126 unsigned int links_failed)
127{
128 unsigned int i;
129
130 if (NULL != strstr (progname, "cadet"))
131 anonymity_level = 0;
132 else
133 anonymity_level = 1;
134 GNUNET_assert (NUM_DAEMONS == num_peers);
135 for (i = 0; i < num_peers; i++)
136 daemons[i] = peers[i];
137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing %llu bytes\n",
138 (unsigned long long) FILESIZE);
139 GNUNET_FS_TEST_publish (daemons[1], TIMEOUT,
140 anonymity_level, GNUNET_NO,
141 FILESIZE, SEED,
142 VERBOSE, &do_download, NULL);
143}
144
145
146int
147main (int argc, char *argv[])
148{
149 const char *config;
150
151 progname = argv[0];
152 if (NULL != strstr (progname, "cadet"))
153 config = "test_gnunet_service_fs_p2p_cadet.conf";
154 else
155 config = "fs_test_lib_data.conf";
156 (void) GNUNET_TESTBED_test_run ("test-gnunet-service-fs-p2p",
157 config,
158 NUM_DAEMONS,
159 0, NULL, NULL,
160 &do_publish, NULL);
161 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-fs-lib/");
162 return ok;
163}
164
165
166/* end of test_gnunet_service_fs_p2p.c */
diff --git a/src/fs/test_gnunet_service_fs_p2p_cadet.conf b/src/fs/test_gnunet_service_fs_p2p_cadet.conf
deleted file mode 100644
index 12e106968..000000000
--- a/src/fs/test_gnunet_service_fs_p2p_cadet.conf
+++ /dev/null
@@ -1,20 +0,0 @@
1@INLINE@ fs_test_lib_data.conf
2
3[fs]
4# FIXME: this option needs to be set for the
5# testcase to truly work; however, as the code
6# is not finished, not setting the option should
7# allow the test to at least pass for now...
8DISABLE_ANON_TRANSFER = YES
9
10# Do we cache content from other nodes? (may improve anonymity)
11CONTENT_CACHING = NO
12
13# Do we send unsolicited data to other nodes if we have excess bandwidth?
14# (may improve anonymity, probably not a good idea if content_caching is NO)
15CONTENT_PUSHING = NO
16
17#PREFIX = valgrind
18
19[cadet]
20#PREFIX = valgrind
diff --git a/src/fs/test_plugin_block_fs.c b/src/fs/test_plugin_block_fs.c
deleted file mode 100644
index f15d10b17..000000000
--- a/src/fs/test_plugin_block_fs.c
+++ /dev/null
@@ -1,86 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/test_plugin_block_fs.c
22 * @brief test for plugin_block_fs.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_block_lib.h"
27
28
29static int
30test_fs (struct GNUNET_BLOCK_Context *ctx)
31{
32 struct GNUNET_HashCode key;
33 char block[4];
34
35 memset (block, 1, sizeof(block));
36 if (GNUNET_OK !=
37 GNUNET_BLOCK_get_key (ctx,
38 GNUNET_BLOCK_TYPE_FS_DBLOCK,
39 block,
40 sizeof(block),
41 &key))
42 return 1;
43 if (GNUNET_OK !=
44 GNUNET_BLOCK_check_block (ctx,
45 GNUNET_BLOCK_TYPE_FS_DBLOCK,
46 block,
47 sizeof(block)))
48 return 2;
49 if (GNUNET_OK !=
50 GNUNET_BLOCK_check_query (ctx,
51 GNUNET_BLOCK_TYPE_FS_DBLOCK,
52 &key,
53 NULL, 0))
54 return 4;
55 GNUNET_log_skip (1, GNUNET_NO);
56 if (GNUNET_NO !=
57 GNUNET_BLOCK_check_query (ctx,
58 GNUNET_BLOCK_TYPE_FS_DBLOCK,
59 &key,
60 "bogus", 5))
61 return 8;
62 GNUNET_log_skip (0, GNUNET_YES);
63 return 0;
64}
65
66
67int
68main (int argc, char *argv[])
69{
70 int ret;
71 struct GNUNET_BLOCK_Context *ctx;
72 struct GNUNET_CONFIGURATION_Handle *cfg;
73
74 GNUNET_log_setup ("test-block", "WARNING", NULL);
75 cfg = GNUNET_CONFIGURATION_create ();
76 ctx = GNUNET_BLOCK_context_create (cfg);
77 ret = test_fs (ctx);
78 GNUNET_BLOCK_context_destroy (ctx);
79 GNUNET_CONFIGURATION_destroy (cfg);
80 if (ret != 0)
81 fprintf (stderr, "Tests failed: %d\n", ret);
82 return ret;
83}
84
85
86/* end of test_plugin_block_fs.c */
diff --git a/src/fs/test_pseudonym_data.conf b/src/fs/test_pseudonym_data.conf
deleted file mode 100644
index 5827721b8..000000000
--- a/src/fs/test_pseudonym_data.conf
+++ /dev/null
@@ -1,6 +0,0 @@
1# General settings
2[fs]
3
4[TESTING]
5WEAKRANDOM = YES
6